zoukankan      html  css  js  c++  java
  • Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动【转】

    本文转载自:https://blog.csdn.net/zqixiao_09/article/details/50858776

    版权声明:本文为博主原创文章,未经博主允许不得转载。    https://blog.csdn.net/zqixiao_09/article/details/50858776
           编写驱动的第一步仍是看原理图:



           可以看到,该蜂鸣器由 GPD0_0 来控制 ,查手册可知该I/O口由Time0 来控制,找到相应的寄存器:

    a -- I/O口寄存器及地址

          GPD0CON  0x114000a0

    b -- Time0 寄存器及地址

          基地址为:TIMER_BASE 0x139D0000

          这些物理寄存器地址都是相邻的,我们这里用偏移量来表示:

          寄存器名      地址偏移量            所需配置

            TCFG0          0x0000              [7-0]     0XFF

            TCFG1          0x0004              [3-0]     0X2              

            TCON            0x0008              [3-0]     0X2       0X9   0X0

            TCNTB0        0x000C             500

            TCMPB0       0x0010              250



           前面已经知道,驱动是无法直接操纵物理地址的,所以这里仍需物理地址向虚拟地址的转换,用到 ioremap() 函数、writel()函数、readl()函数:

    1、地址映射操作

    unsigned int   *gpd0con;
    void *timer_base;<span style="white-space:pre">    </span>//之所以是void类型,偏移量为4时,只是移动4个字节,方便理解
     
    gpd0con = ioremap(GPD0CON,4);
    timer_base = ioremap(TIMER_BASE , 0x14);
    2、Time0初始化操作(这里使用的已经是虚拟地址)

           这里现将数据从寄存器中读出,修改后再写回寄存器,具体寄存器操作可以移步Exynos4412裸机开发——PWM定时器:   

    writel((readl(gpd0con)&~(0xf<<0)) | (0x2<<0),gpd0con);
    writel ((readl(timer_base +TCFG0  )&~(0xff<<0)) | (0xff <<0),timer_base +TCFG0);
    writel ((readl(timer_base +TCFG1 )&~(0xf<<0)) | (0x2 <<0),timer_base +TCFG1 );
    3、装载数据,配置占空比

    writel(500, timer_base +TCNTB0  );
    writel(250, timer_base +TCMPB0 );
    writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x2 <<0),timer_base +TCON );
    4、相关控制函数

    void beep_on(void)
    {
        writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x9 <<0),timer_base +TCON );
    }
     
    void beep_off(void)
    {
        writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x0 <<0),timer_base +TCON );
    }

    下面是驱动程序,这里我们用到了 write() read() ioctl() 函数,具体解析移步:

    驱动程序:beep.c

    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/device.h>
    #include <asm/io.h>
    #include <asm/uaccess.h>
     
    static int major = 250;
    static int minor=0;
    static dev_t devno;
    static struct class *cls;
    static struct device *test_device;
     
    #define GPD0CON       0x114000a0
    #define TIMER_BASE    0x139D0000           
    #define TCFG0         0x0000               
    #define TCFG1         0x0004                            
    #define TCON          0x0008             
    #define TCNTB0        0x000C          
    #define TCMPB0        0x0010           
     
    static unsigned int *gpd0con;
    static void *timer_base;
    #define  MAGIC_NUMBER    'k'
    #define  BEEP_ON    _IO(MAGIC_NUMBER    ,0)
    #define  BEEP_OFF   _IO(MAGIC_NUMBER    ,1)
    #define  BEEP_FREQ   _IO(MAGIC_NUMBER   ,2)
     
    static void fs4412_beep_init(void)
    {
        gpd0con = ioremap(GPD0CON,4);
        timer_base = ioremap(TIMER_BASE,0x14);
        
        writel ((readl(gpd0con)&~(0xf<<0)) | (0x2<<0),gpd0con);
        writel ((readl(timer_base +TCFG0  )&~(0xff<<0)) | (0xff <<0),timer_base +TCFG0);
        writel ((readl(timer_base +TCFG1 )&~(0xf<<0)) | (0x2 <<0),timer_base +TCFG1 );
     
        writel (500, timer_base +TCNTB0  );
        writel (250, timer_base +TCMPB0 );
        writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x2 <<0),timer_base +TCON );
    }
     
    void fs4412_beep_on(void)
    {
        writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x9 <<0),timer_base +TCON );
    }
     
    void fs4412_beep_off(void)
    {
        writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x0 <<0),timer_base +TCON );
    }
     
     
    static int beep_open (struct inode *inode, struct file *filep)
    {
     //    fs4412_beep_on();
        return 0;
    }
     
    static int beep_release(struct inode *inode, struct file *filep)
    {
         fs4412_beep_off();
         return 0;
    }
     
    #define BEPP_IN_FREQ 100000
    static void beep_freq(unsigned long arg)
    {
        writel(BEPP_IN_FREQ/arg, timer_base +TCNTB0  );
        writel(BEPP_IN_FREQ/(2*arg), timer_base +TCMPB0 );
     
    }
     
    static long beep_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
    {
        switch(cmd)
        {
            case BEEP_ON:
                fs4412_beep_on();
                break;
            case BEEP_OFF:
                fs4412_beep_off();
                break;
            case BEEP_FREQ:
                beep_freq( arg );
                break;
            default :
                return -EINVAL;
        }
    }
     
    static struct file_operations beep_ops=
    {
        .open     = beep_open,
        .release = beep_release,
        .unlocked_ioctl      = beep_ioctl,
    };
     
    static int beep_init(void)
    {
        int ret;    
        devno = MKDEV(major,minor);
        ret = register_chrdev(major,"beep",&beep_ops);
     
        cls = class_create(THIS_MODULE, "myclass");
        if(IS_ERR(cls))
        {
            unregister_chrdev(major,"beep");
            return -EBUSY;
        }
        test_device = device_create(cls,NULL,devno,NULL,"beep");//mknod /dev/hello
        if(IS_ERR(test_device))
        {
            class_destroy(cls);
            unregister_chrdev(major,"beep");
            return -EBUSY;
        }    
        fs4412_beep_init();
        return 0;
    }
     
    void fs4412_beep_unmap(void)
    {
        iounmap(gpd0con);
        iounmap(timer_base);
    }
     
    static void beep_exit(void)
    {
        fs4412_beep_unmap();
     
        device_destroy(cls,devno);
        class_destroy(cls);    
        unregister_chrdev(major,"beep");
        printk("beep_exit ");
    }
     
    MODULE_LICENSE("GPL");
    module_init(beep_init);
    module_exit(beep_exit);
    makefile:

    ifneq  ($(KERNELRELEASE),)
    obj-m:=beep.o
    $(info "2nd")
    else
    #KDIR := /lib/modules/$(shell uname -r)/build
    KDIR := /home/fs/linux/linux-3.14-fs4412
    PWD:=$(shell pwd)
    all:
        $(info "1st")
        make -C $(KDIR) M=$(PWD) modules
        arm-none-linux-gnueabi-gcc test.c -o beeptest
        sudo cp beep.ko beeptest /tftpboot
    clean:
        rm -f *.ko *.o *.symvers *.mod.c *.mod.o *.order
    endif

    下面是是个简单的测试程序test.c,仅实现蜂鸣器响6秒的功能:
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <sys/ioctl.h>
     
    #define  MAGIC_NUMBER    'k'
    #define   BEEP_ON    _IO(MAGIC_NUMBER    ,0)
    #define   BEEP_OFF   _IO(MAGIC_NUMBER    ,1)
    #define   BEEP_FREQ   _IO(MAGIC_NUMBER    ,2)
     
    main()
    {
        int fd;
     
        fd = open("/dev/beep",O_RDWR);
        if(fd<0)
        {
            perror("open fail ");
            return ;
        }
     
        ioctl(fd,BEEP_ON);
     
        sleep(6);
        ioctl(fd,BEEP_OFF);    
     
        close(fd);
    }

    这是个音乐播放测试程序,慎听!!分别为《大长今》、《世上只有妈妈好》、《渔船》,这个单独编译一下
    /*
     * main.c : test demo driver
     */
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/ioctl.h>
    #include "pwm_music.h"
     
    /*ioctl 鍛戒护*/
    #define magic_number 'k'
    #define BEEP_ON _IO(magic_number,0)
    #define BEEP_OFF _IO(magic_number,1)
    #define SET_FRE _IO(magic_number,2)
     
     
     
    int main(void)
    {
        int i = 0;
        int n = 2;
        int dev_fd;
        int div;
        dev_fd = open("/dev/beep",O_RDWR | O_NONBLOCK);
        if ( dev_fd == -1 ) {
            perror("open");
            exit(1);
        }
     
        for(i = 0;i<sizeof(GreatlyLongNow)/sizeof(Note);i++ )
        {
            div = (GreatlyLongNow[i].pitch);
        
            ioctl(dev_fd, SET_FRE, div);
            ioctl(dev_fd, BEEP_ON);
            usleep(GreatlyLongNow[i].dimation * 100);
            ioctl(dev_fd, BEEP_OFF);
        }
        
        for(i = 0;i<sizeof(MumIsTheBestInTheWorld)/sizeof(Note);i++ )
        {
            div = (MumIsTheBestInTheWorld[i].pitch);
            ioctl(dev_fd, SET_FRE, div);
            ioctl(dev_fd, BEEP_ON);
            
            usleep(MumIsTheBestInTheWorld[i].dimation * 100);
            ioctl(dev_fd, BEEP_OFF);
        }
     
     
     
        for(i = 0;i<sizeof(FishBoat)/sizeof(Note);i++ )
        {
            div = (FishBoat[i].pitch);
            ioctl(dev_fd, SET_FRE, div);
            ioctl(dev_fd, BEEP_ON);
            usleep(FishBoat[i].dimation * 100);
            ioctl(dev_fd, BEEP_OFF);
        }
        return 0;
    }
    附所用头文件:
    #ifndef __PWM_MUSIC_H
    #define __PWM_MUSIC_H
     
    #define BIG_D
     
    #define PCLK (202800000/4)
     
    typedef struct
    {
        int pitch;
        int dimation;
    }Note;
    // 1            2        3         4             5              6       7
    // C             D         E          F              G             A         B
    //261.6256    293.6648   329.6276 349.2282   391.9954        440        493.8833
     
    //C澶ц皟
    #ifdef BIG_C
    #define DO 262
    #define RE 294
    #define MI 330
    #define FA 349
    #define SOL 392
    #define LA  440
    #define SI  494
    #define TIME 6000
    #endif
     
     //D澶ц皟
    #ifdef BIG_D
    #define DO 293
    #define RE 330
    #define MI 370
    #define FA 349
    #define SOL 440
    #define LA  494
    #define SI  554
    #define TIME 6000
    #endif
     
     
     
    Note MumIsTheBestInTheWorld[]={
        //6.                //_5         //3         //5    
        {LA,TIME+TIME/2}, {SOL,TIME/2},{MI,TIME},{SOL,TIME},
     
        //1^           //6_          //_5        //6-
        {DO*2,TIME},{LA,TIME/2},{SOL,TIME/2} ,{LA,2*TIME},
        // 3      //5_        //_6           //5
        {MI,TIME},{SOL,TIME/2},{LA,TIME/2},{SOL,TIME},
        // 3         //1_        //_6,
        {MI,TIME},{DO,TIME/2},{LA/2,TIME/2},
        //5_        //_3        //2-           //2.
        {SOL,TIME/2},{MI,TIME/2},{RE,TIME*2},{RE,TIME+TIME/2},
        //_3     //5            //5_            //_6
        {MI,TIME/2},{SOL,TIME},{SOL,TIME/2},{LA,TIME/2},
        // 3        //2            //1-            //5.
        {MI,TIME},{RE,TIME},{DO,TIME*2},{SOL,TIME+TIME/2},
        //_3        //2_        //_1         //6,_
        {MI,TIME/2},{RE,TIME/2},{DO,TIME/2},{LA/2,TIME/2},
        //_1        //5,--
        {DO,TIME/2},{SOL/2,TIME*3}
     
    };
     
     
    Note GreatlyLongNow[]={      
        // 2        3            3        3.                _2                1
        {RE,TIME}, {MI,TIME},{MI,TIME},{MI,TIME+TIME/2},{RE,TIME/2},{DO,TIME},
        //6,        1            2        1--                2            3            3
        {LA/2,TIME},{DO,TIME},{RE,TIME},{DO,TIME*3},{RE,TIME},{MI,TIME},{MI,TIME},
        //3.                _5            3            3            2            3
        {MI,TIME+TIME/2},{SOL,TIME/2},{MI,TIME},{MI,TIME},{RE,TIME},{MI,TIME},
        //3--        5            6            6          6.                _5
        {MI,TIME*3},{SOL,TIME},{LA,TIME},{LA,TIME},{LA,TIME+TIME/2},{SOL,TIME/2},
        // 3        3        5                6        5---            2            3
        {MI,TIME},{MI,TIME},{SOL,TIME},{LA,TIME},{SOL,TIME*3},{RE,TIME},{MI,TIME},
        // 3        2.                _3                3          2            3
        {MI,TIME},{RE,TIME+TIME/2},{MI,TIME/2},{MI,TIME},{RE,TIME},{MI,TIME},
        //6,        1_              _6,              6,-
        {LA/2,TIME},{DO,TIME/2},{LA/2,TIME/2},{LA/2,TIME*2},
        //2_        _2            2_                _1            6,
        {RE,TIME/2},{RE,TIME/2},{RE,TIME/2},{DO,TIME/2},{LA/2,TIME},
        //2_        _2            2_                _1              6,
        {RE,TIME/2},{RE,TIME/2},{RE,TIME/2},{DO,TIME/2},{LA/2,TIME},
        // 2        3        1            2.                    _3            5
        {RE,TIME},{MI,TIME},{DO,TIME},{RE,TIME+TIME/2},{MI,TIME/2},{SOL,TIME},
        //6_        _6                6_            _5            3
        {LA,TIME/2},{LA,TIME/2},{LA,TIME/2},{SOL,TIME/2},{MI,TIME},
        //2_        _2            2_                _1            6,
        {RE,TIME/2},{RE,TIME/2},{RE,TIME/2},{DO,TIME/2},{LA/2,TIME},
        //6,        5,.                      _6,             6,--
        {LA/2,TIME},{SOL/2,TIME+TIME/2},{LA/2,TIME/2},{LA/2,TIME*3},
        //2_        _2            2_                _1            6,
        {RE,TIME/2},{RE,TIME/2},{RE,TIME/2},{DO,TIME/2},{LA/2,TIME},
        //2_        _2            2_                _1              6,
        {RE,TIME/2},{RE,TIME/2},{RE,TIME/2},{DO,TIME/2},{LA/2,TIME},
        // 2        3        1            2.                    _3            5
        {RE,TIME},{MI,TIME},{DO,TIME},{RE,TIME+TIME/2},{MI,TIME/2},{SOL,TIME},
        //6_        _6                6_            _5            3
        {LA,TIME/2},{LA,TIME/2},{LA,TIME/2},{SOL,TIME/2},{MI,TIME},
        //2_        _2            2_                _1            6,
        {RE,TIME/2},{RE,TIME/2},{RE,TIME/2},{DO,TIME/2},{LA/2,TIME},
        //6,        5,.                      _6,             6,--
        {LA/2,TIME},{SOL/2,TIME+TIME/2},{LA/2,TIME/2},{LA/2,TIME*3}
     
    };
    Note FishBoat[]={ //3.                _5            6._                    =1^             6_
        {MI,TIME+TIME/2},{SOL,TIME/2},{LA,TIME/2+TIME/4},{DO*2,TIME/4},{LA,TIME/2},
        //_5            3 -.        2          1.             _3             2._
        {SOL,TIME/2},{MI,TIME*3},{RE,TIME},{DO,TIME+TIME/2},{MI,TIME/2},{RE,TIME/2+TIME/4},
        //=3            2_            _1         2--            3.                _5
        {MI,TIME/4},{RE,TIME/2},{DO,TIME/2},{RE,TIME*4},{MI,TIME+TIME/2},{SOL,TIME/2},
        // 2        1        6._                    =1^                 6_            _5
        {RE,TIME},{DO,TIME},{LA,TIME/2+TIME/4},{DO*2,TIME/4},{LA,TIME/2},{SOL,TIME/2},
        //6-         5,.                    _6,            1._                    =3
        {LA,TIME*2},{SOL/2,TIME+TIME/2},{LA/2,TIME/2},{DO,TIME/2+TIME/4},{MI,TIME/4},
        //2_            _1         5,--
        {RE,TIME/2},{DO,TIME/2},{SOL/2,TIME*4},
        //3.                _5            6._                    =1^            6_
        {MI,TIME+TIME/2},{SOL,TIME/2},{LA,TIME/2+TIME/4},{DO*2,TIME/4},{LA,TIME/2},
        //_5            3-.            5_            _6            1^_                   _6
        {SOL,TIME/2},{MI,TIME*3},{SOL,TIME/2},{LA,TIME/2},{DO*2,TIME+TIME/2},{LA,TIME/2},
        //5._                    =6            5_          _3            2--
        {SOL,TIME/2+TIME/4},{LA,TIME/4},{SOL,TIME/2},{MI,TIME/2},{RE,TIME*4},
        //3.                _5            2._                    =3            2_            _1
        {MI,TIME+TIME/2},{SOL,TIME/2},{RE,TIME/2+TIME/4},{MI,TIME/4},{RE,TIME/2},{DO,TIME/2},
        //6._                =1^                6_            _5            6-            1.
        {LA,TIME/2+TIME/4},{DO*2,TIME/4},{LA,TIME/2},{SOL,TIME/2},{LA,TIME*2},{DO,TIME+TIME/2},
        //_2           3_            _5                2_            _3            1--
        {RE,TIME/2},{MI,TIME/2},{SOL,TIME/2},{RE,TIME/2},{MI,TIME/2},{DO,TIME*4}
    };
    #endif


    编译好程序后
    # insmod beep.ko

    #mknod /dev/beep c 250 0

    #./music

    便会听到悦耳的音乐了!

  • 相关阅读:
    VTK初学一,动画加AVI录制终于做出来了
    QCamera获取摄像头图像(转载)
    VTK初学一,比较常见的错误2
    myeclipse2014鼠标单击后光标位置背景底色为白色太难看,行号显示
    记一次跟二房东公司(非中介个人房源无中介费)租房的经历
    求16进制数据或运算后的值(即多个16进制相加的和)
    error LNK2001: 无法解析的外部符号 "public: char * __thiscall
    如何利用指向数组的指针得到数组元素个数?
    C++判断字符串是否为空的一个小问题
    C++开发中BYTE类型数组转为对应的字符串
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/10251534.html
Copyright © 2011-2022 走看看