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

    便会听到悦耳的音乐了!

  • 相关阅读:
    python 的基础 学习 第六天 基础数据类型的操作方法 字典
    python 的基础 学习 第五天 基础数据类型的操作方法
    python 的基础 学习 第四天 基础数据类型
    ASP.NET MVC 入门8、ModelState与数据验证
    ASP.NET MVC 入门7、Hellper与数据的提交与绑定
    ASP.NET MVC 入门6、TempData
    ASP.NET MVC 入门5、View与ViewData
    ASP.NET MVC 入门4、Controller与Action
    ASP.NET MVC 入门3、Routing
    ASP.NET MVC 入门2、项目的目录结构与核心的DLL
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/10251534.html
Copyright © 2011-2022 走看看