zoukankan      html  css  js  c++  java
  • 20150313 驱动模块分离概念

    20150313 驱动模块分离概念

    2015-03-13 Lover雪儿

        还记得以前刚开始学习编写程序的时候,无论再多的代码,再多的功能都是使劲的往同一个.C文件中塞,最后导致的直接结果就是,代码多,功能复杂,严重的妨碍了我们代码可移植性甚至良好的阅读性,接下来,我们开始来学习将一个驱动程序进行拆分,根据各种代码的性质或者功能来写入不同的.C文件中,此处,我们来尝试使用平台设备来实现IMX257蜂鸣器的驱动分离代码。

        前面我们实现了beep驱动,博客地址http://www.cnblogs.com/lihaiyan/p/4298105.html

        在本实验中,我们根据beep的性质,分为设备,驱动两个模块分别编写驱动程序,在设备的.C文件中,负责定义驱动的设备IO地址等可变的信息,而在驱动的.C文件中负责统一的内存地址IOREMAP映射,注册设备等不变的代码。这样,当我们程序要修改时,比如说如果我们要将beep修改为led的话,那么就只需要将设备的.C文件中的地址修改过来就可以了,大大的增强了代码的可移植性。

        接下来,我么开始步入正题:

    一、设备beep_dev.c的实现

    1.定义平台设备结构体

    如图所示,在平台设备结构体,需要注意两个地方,

    第一个是.name,它是用于保存在设备链中,利用它在驱动链中进行匹配,找到相应的驱动程序。

    第二个是.resources,它是一个资源结构体,用于保存我们IMX257板子的内存IO地址,当我们要更改硬件是,只需要修改资源结构体中的数据即可。简单的说,资源结构体主要的功能就是传递参数,将板子的内存地址传入beep_drv.c中,beep_drv.c只要通过platform_get_resource函数即可得到资源中保存的数据。

    2.定义资源结构体

    如图所示:

    在资源结构体数组中,

    数组0中存储了IMX257的IOMUX的基地址

    数组1中存储了IMX257的GPIO1的基地址,此处我们的beep为GPIO1_26

    数组2中存储了IMX257的beep在GPIO1地址的偏移

    在beep_drv.c将这些数据获取,然后分别ioremap进行地址映射

    3.接下来就是在分别在入口函数中申请平台设备,在出口函数中注销平台设备

    总结一下,可以发现我们的设备beep_dev.C文件中只做了一件事,那就是保存IMX257的地址,所以,如果我们以后要移植 驱动程序的话,只需要修改地址就够了。当然,蜂鸣器有点简单,在以后的程序可能就没这么简单了,不过总体的框架都是这样,实现了驱动模块分离。

    二、驱动beep_drv.c的实现

    1.平台驱动支持

        和上面差不多,此处我们先实现一个平台驱动的支持,也就是

    ①定义平台驱动结构体

    ②在入口函数中注册平台驱动

    ③在出口函数中卸载平台驱动

    此处就不再详细将这个,见下图:

    2.平台驱动探测函数probe实现

    接下来,重点来了,我们此处驱动程序的核心就是在probe这个函数。

    当我们的驱动和设备匹配成功之后,就会调用probe函数,在此函数中可以干任何我们想干的事。

    所以,我们呢,就利用此函数完成我们的硬件设备初始化的工作。

    ①得到前面设备beep_dev.c文件中的资源结构体的资源

    ②将前面的资源中的地址,通过ioremap函数分别映射地址

    ③注册字符设备驱动,创建类,然后再类下面创建设备节点

    如下图所示:

    3.平台驱动释放函数remove实现

    在remove函数中,自然就是将前面我们映射的地址取消映射,卸载字符设备驱动程序

    如图所示:

    4.实现file_opration结构体

    前面注册字符设备时,需要一个file_operation结构体,用于制定读写函数,如下图所示:

    5.实现打开函数beep_open

        前面,我们的probe函数中已经实现了IO地址的映射,以及设备号的自动申请,自动创建设备节点。但是,单独只有这些是还不够的,由于我们IMX257芯片的IO引脚为复用IO,总共有七中模式,要想实现特定的功能,必须还得对IO引脚进行配置,

        所以,我们此处,在打开函数中实现引脚的配置。

    ①配置GPIO引脚为模式5

    ②配置GPIO引脚为上拉模式,电压1.8v,CMOS输出

    ③配置蜂鸣器的GPIO1_26为输出模式

    ④将蜂鸣器引脚电平清零。

    经过前面几步,我们就实现了IMX257引脚的正确配置。

    如下图所示:

    6.实现写函数beep_write

    通过前面的额正确配置GPIO,我们就已经可以正确的使用beep了,接下来,我们通过写函数来将我们的数据传入内核,从而相应的控制蜂鸣器的正确鸣叫与停止。

    三 、应用程序beep_test.c的实现

    在测试程序中,我们只需要实现以下几步

    ①打开设备

    ②判断用户输入的命令

    ③根据用户的命令来相应的控制

    当我们用户输入

    ./beep_test on         蜂鸣器响

    ./beep_test off        蜂鸣器地址响

    ./beep_test on_off     鸣器响5声

    四、驱动测试:

    imx257板子上加载,如图所示:

    移除驱动:

    附 设备 beep_dev.c 程序:

     1 #include<linux/module.h>
     2 #include<linux/init.h>
     3 #include<linux/types.h>
     4 #include<linux/fs.h>
     5 #include<asm/system.h>
     6 #include<linux/device.h>
     7 #include<linux/platform_device.h>
     8 
     9 #define DRIVER_NAME "beep_dev"
    10 #define DEVICE_NAME "beep_dev"
    11 #define IORESOURCE_IOMUX_BASE       0x00000200
    12 #define IORESOURCE_GPIO1_BASE       0x00000400
    13 #define IORESOURCE_BEEP_BIT         0x00000800
    14 
    15 
    16 /* 定义资源结构体 */
    17 static struct resource beep_resource[] = {
    18     [0] =     {
    19         .start     = 0x43FAC000,//IOMUX基地址
    20         .end     = 0x43FAC000 + 0xFFF,
    21         .flags = IORESOURCE_IOMUX_BASE,
    22     },
    23     [1] =     {
    24         .start     = 0x53FCC000,//GPIO1基地址
    25         .end     = 0x53FCC000 + 0xFFF,
    26         .flags = IORESOURCE_GPIO1_BASE,
    27     },
    28     [2] = {
    29         .start = 26,        //beep为GPIO1_26
    30         .end = 26,
    31         .flags = IORESOURCE_BEEP_BIT,
    32     },
    33 };
    34 
    35 static void beep_release(struct device *dev){
    36 
    37 }
    38 
    39 /*分配/设置/注册一个platform_device 结构体*/
    40 static struct platform_device beep_dev = {
    41     .name             = DEVICE_NAME,
    42     .id                  = -1,
    43     .num_resources     = ARRAY_SIZE(beep_resource),
    44     .resource         = beep_resource,
    45     .dev             = {
    46         .release = beep_release,        
    47     },
    48 };
    49 
    50 //入口函数
    51 static int beep_dev_init(void){
    52         //注册一个平台设备
    53     platform_device_register(&beep_dev);
    54     return 0;
    55 }
    56 
    57 //出口函数
    58 static void beep_dev_exit(void){
    59     platform_device_unregister(&beep_dev);
    60 }
    61 
    62 module_init(beep_dev_init);
    63 module_exit(beep_dev_exit);
    64 
    65 MODULE_AUTHOR("Lover雪儿");
    66 MODULE_VERSION("0.1.0");
    67 MODULE_LICENSE("GPL");
    beep_dev.c

    附 驱动 beep_drv.c 程序:

      1 #include<linux/module.h>
      2 #include<linux/init.h>
      3 #include<linux/types.h>
      4 #include<linux/fs.h>
      5 #include<asm/system.h>
      6 #include<linux/platform_device.h>
      7 #include<linux/io.h>
      8 #include<linux/device.h>
      9 #include<linux/uaccess.h>
     10 
     11 #define DRIVER_NAME "beep_dev"
     12 #define DEVICE_NAME "beep_dev"
     13 
     14 #define IORESOURCE_IOMUX_BASE        0x00000200
     15 #define IORESOURCE_GPIO1_BASE        0x00000400
     16 #define IORESOURCE_BEEP_BIT            0x00000800
     17 
     18 static int major;        //主设备号
     19 static struct class *beep_class;//创建类,在类下面创建设备
     20 //定义寄存器
     21 static unsigned long base_iomux;      //iomux基址 0X 43FA C000 -  0X 43FA FFFF
     22 static unsigned long base_gpio1;    //gpio3      0X 53FC C000 -  0X 53FC FFFF
     23 #define MUX_CTL      (*(volatile unsigned long *)(base_iomux + 0x011c))// MUX_CTL模式选择  配置寄存器
     24 #define PAD_CTL      (*(volatile unsigned long *)(base_iomux + 0x0314))// PAD_CTL GPIO常用功能设置
     25 #define DR_GPIO1     (*(volatile unsigned long *)(base_gpio1 + 0x0000))// GPIO DR   数据寄存器  DR
     26 #define GDIR_GPIO1     (*(volatile unsigned long *)(base_gpio1 + 0x0004))// GPIO GDIR 方向控制寄存器  GDIR
     27 static int beep_pin;//引脚的偏移
     28 
     29 
     30 static int beep_open(struct inode *inode, struct file *file)
     31 {
     32     printk("<0>function open!
    
    ");
     33     //引脚配置
     34     //MUX_CTL
     35     MUX_CTL &= ~(0x07 << 0);    
     36     MUX_CTL |= (0X05 << 0);    //设置为ALT5  GPIO1_26 BEEP
     37     //PAD_CTL
     38     PAD_CTL &= ~(0x01<<13 | 0x01<<3 | 0x03<<1 | 0x01<<0);   //1.8v 不需要上拉下拉  CMOS输出 slew rate
     39     //GDIR_GPIO1    配置为输出模式
     40     GDIR_GPIO1 &= ~(0x01 << beep_pin);    
     41     GDIR_GPIO1 |= (0x01 << beep_pin);    //配置为输出模式    
     42 
     43     //DR_GPIO1        配置为输出0 点亮ERR_LED
     44     DR_GPIO1 &= ~(0x01 << beep_pin);        //将GPIO1_26清零
     45 
     46     return 0;
     47 }
     48 static ssize_t beep_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
     49 {
     50     int val;
     51     printk("<0>function write!
    
    ");
     52     if(copy_from_user(&val,buf,count))
     53         printk("<0>copy_from_user error!
    ");        
     54     if(val == 1){    //响蜂鸣器
     55         DR_GPIO1 |= (0x01 << beep_pin);        //将GPIO1_26置1
     56     }else{            //停蜂鸣器
     57         DR_GPIO1 &= ~(0x01 << beep_pin);        //将GPIO1_26清零
     58     }
     59     return count;
     60 }
     61 
     62 //构造file_operation结构体
     63 static struct file_operations beep_fops = {
     64     .owner    = THIS_MODULE,    /**/
     65     .open    = beep_open,
     66     .write    = beep_write,
     67 };
     68 
     69 
     70 
     71 //探测函数
     72 static int beep_probe(struct platform_device *pdev){
     73     struct resource *res;
     74     printk("<0> beep_probe,found beep
    
    ");
     75     /* 根据platform的资源进行ioremap */
     76     //获取资源 IOMUX 的地址
     77     res = platform_get_resource(pdev,IORESOURCE_IOMUX_BASE,0);
     78     //映射IOMUX的内存地址
     79     base_iomux = (unsigned long)ioremap(res->start,res->end - res->start);
     80     
     81     //获取资源 GPIO1 的地址
     82     res = platform_get_resource(pdev,IORESOURCE_GPIO1_BASE,0);
     83     //映射GP1IO1的内存地址
     84     base_gpio1 = (unsigned long)ioremap(res->start,res->end - res->start);
     85     
     86     //获取资源 LED灯 的偏移地址
     87     res = platform_get_resource(pdev,IORESOURCE_BEEP_BIT,0);
     88     beep_pin = res->start;
     89     
     90 
     91     /* 注册字符设备驱动程序 */
     92     major = register_chrdev(0,DEVICE_NAME,&beep_fops);
     93     //创建类,然后再类下面创建设备节点
     94     beep_class = class_create(THIS_MODULE,DEVICE_NAME);
     95     device_create(beep_class,NULL,MKDEV(major,0),NULL,DEVICE_NAME);//dev/beep_dev
     96 
     97     return 0;
     98 }
     99 
    100 static int beep_remove(struct platform_device *pdev){
    101     printk("<0> beep_remove, remove beep
    
    ");
    102     //删除类
    103     device_destroy(beep_class,MKDEV(major,0));
    104     class_destroy(beep_class);
    105     /* 卸载字符设备驱动程序 */
    106     unregister_chrdev(major,DEVICE_NAME);    /* iounmap */
    107     //解除地址映射
    108     iounmap((void __iomem *)base_iomux);
    109     iounmap((void __iomem *)base_gpio1);
    110     return 0;
    111 }
    112 
    113 //定义一个平台驱动结构体
    114 struct platform_driver beep_drv = {
    115     .probe = beep_probe,
    116     .remove = beep_remove,
    117     .driver = {
    118         .name = DRIVER_NAME,
    119     }
    120 };
    121 
    122 
    123 static int beep_drv_init(void){
    124     platform_driver_register(&beep_drv);
    125     return 0;
    126 }
    127 
    128 static void beep_drv_exit(void){
    129     platform_driver_unregister(&beep_drv);
    130 }
    131 
    132 module_init(beep_drv_init);
    133 module_exit(beep_drv_exit);
    134 
    135 MODULE_AUTHOR("Lover雪儿");
    136 MODULE_VERSION("0.1.0");
    137 MODULE_LICENSE("GPL");
    beep_drv.c

    附 应用程序 beep_test.c 程序:

     1 #include <sys/types.h>
     2 #include <sys/stat.h>
     3 #include <fcntl.h>
     4 #include <stdio.h>
     5 
     6 /* beep_test on
     7  * beep_test off
     8  */
     9 int main(int argc, char **argv)
    10 {
    11     int fd;
    12     int i = 0;
    13     int val = 1;
    14     fd = open("/dev/beep_dev", O_RDWR);
    15     if (fd < 0){
    16         printf("can't open!
    ");
    17     }
    18     if (argc != 2){
    19         printf("Usage :
    ");
    20         printf("%s <on|off|on_off>
    ", argv[0]);
    21         return 0;
    22     }
    23     if (strcmp(argv[1], "on") == 0){
    24         val  = 1;
    25         write(fd, &val, 4);
    26     }else if (strcmp(argv[1], "off") == 0){
    27         val = 0;
    28         write(fd, &val, 4);
    29     }else{
    30         i=5;
    31         while(i--){
    32             val = 0;
    33             write(fd, &val, 4);
    34             val  = 1;
    35             write(fd, &val, 4);
    36         }
    37         val = 0;
    38         write(fd, &val, 4);
    39     }
    40     return 0;
    41 }
    beep_test.c
  • 相关阅读:
    Redis 连接
    Redis 脚本
    Redis 事务
    Redis 发布订阅
    redis 字符串数据(string)
    Redis 键(key)
    Redis 命令
    Redis的五种数据类型
    java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory 解决方案
    在命令行中运行eclipse中创建的java项目
  • 原文地址:https://www.cnblogs.com/lihaiyan/p/4336165.html
Copyright © 2011-2022 走看看