mini2440驱动奇谭——LED驱动与测试(动态加载)

来源:互联网 发布:网络宣传平台有哪些 编辑:程序博客网 时间:2024/04/30 12:01

我的博客:http://blog.csdn.net/muyang_ren

实现功能:开发板动态加载led驱动模块并能通过测试程序

系统:Ubuntu 14.04

驱动交叉编译内核:linux-2.6.32.2               //建立交叉编译

开发板:mini2440 (128M nandflash)     //关于怎么烧写linux到开发板请点击,Linux RootFs 选择rootfs_rtm_2440.img  (光盘目录:image/linux/rtm )

开发所需工具:NFS网络文件  minicom  vim

linux文件目录:/opt/FriendlyARM/mini2440/linux-2.6.32.2

自己驱动目录: /home/lianghuiyong/my2440drivers

注意:我的开发板内核被我裁剪了led驱动部分,包括之后要写的一些驱动,裁剪内核很简单,make menuconfig菜单后对应的驱动空格为空就行了


给大家推荐一个很好用的vim插件:ma6174,因为我用的是NFS网络挂载,使用了路由器上网,当大家不想用到路由器,用的是无线网的时候还想使用NFS网络挂载文件,其他没变,只是将网线修改下,我们上网的网线一般是按B接线排的,记得老师说过,只接局域网不上网的话只需要网线的一端是A排法,另一端是B排法。直接连开发板和电脑就行了。


(一)丶LED原理分析

盗图可耻啊,我还是盗图了,原谅我吧。。。

从图中可知:led一端是直接接着芯片上的引脚,另一端怎么接了个电阻?没电阻降压的话电流一接通,就led这么点阻值,芯片估计光荣了。要点亮led,电阻那端的电流是没法改变的,而且电阻那端是高电平,led没亮的情况下,是因为芯片那端也是提供高电平,要点亮led只需将芯片对应的芯片控制寄存器设置为输出,并将数据寄存器写入0 , 0为低电平,电流通过,led点亮!


(二)丶代码部分

驱动模块:

/*************************************************************************> File Name:LHY_led.c> Author:梁惠涌         > Mail: > Created Time: 2014年09月29日 星期一 20时34分50秒 ************************************************************************///在 linux-2.6.32.2/arch/arm/mach-s3c2410/include/mach 目录下#include<mach/regs-gpio.h>         // 和GPIO相关的宏定义 #include<mach/hardware.h>          //S3C2410_gpio_cfgpin                                   //S3C2410_gpio_setpin等gpio函数定义//在 linux-2.6.32.2/include/linux 目录下#include<linux/miscdevice.h>        //注册 miscdevide 结构体成员变量#include<linux/delay.h>             //这个应该是时延函数了#include<linux/kernel.h>            //内核,不用说#include<linux/module.h>            //驱动加载卸载函数定义#include<linux/init.h>              //初始化头文件#include<linux/mm.h>#include<linux/fs.h>                //注册file_operations结构体变量#include<linux/types.h>#include<linux/moduleparam.h>#include<linux/slab.h>#include<linux/errno.h>              #include<linux/ioctl.h>#include<linux/cdev.h>#include<linux/string.h>#include<linux/list.h>#include<linux/pci.h>#include<linux/gpio.h>              //gpio口定义文件(虚拟地址)//在 linux-2.6.32.2/arch/arm/include/asm 目录下#include<asm/irq.h>#include<asm/uaccess.h>#include<asm/atomic.h>#include<asm/unistd.h>#define DEVICE_NAME "leds"static unsigned long led_table[]={    S3C2410_GPB(5),  //在gpio-nrs.h(是gpio.h包含的头文件)中定义了虚拟地址    S3C2410_GPB(6),     S3C2410_GPB(7),     S3C2410_GPB(8), };static unsigned int led_cfg_table[]={    S3C2410_GPIO_OUTPUT,      //定义在regs-gpio.h头文件    S3C2410_GPIO_OUTPUT,    S3C2410_GPIO_OUTPUT,    S3C2410_GPIO_OUTPUT,};static int sbc2440_leds_ioctl(    struct inode *inode,    struct file *file,    unsigned int cmd,    unsigned long arg ){        switch(cmd){            case 0:            case 1:            if(arg>4){                return -EINVAL;            }            s3c2410_gpio_setpin(led_table[arg],!cmd);            return 0;         default:            return -EINVAL;        }        }//文件结构体static struct file_operations dev_fops={ .owner=THIS_MODULE,        //THIS_MODULE防止使用过程中被卸载  .ioctl =sbc2440_leds_ioctl,  //.ioctl 指向 sbc2440_leds_ioctl 函数};//混杂设备结构体static struct miscdevice misc ={    .minor = MISC_DYNAMIC_MINOR,     //动态分配次设备号(驱动编号)    .name  = DEVICE_NAME,            //驱动名为定义的 DEVICE_NAME    .fops  = &dev_fops,              //文件操作指针指向 dev_fops的地址};//模块加载并初始化函数static int __init dev_init(void){     int ret;    int i;        printk(KERN_ALERT "Hello,mini2440 led is installed!\n");    for(i=0;i<4;i++){        //配置led 引脚功能,led_table[i]为上面定义的led虚拟地址;        //led_cfg_table[i]为上面定义的功能(输出)        s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]);        //设置led状态,0为低电平,led点亮        s3c2410_gpio_setpin(led_table[i],0);    }        ret =misc_register(&misc);    printk(DEVICE_NAME"\t Lianghuiyong-leds\n");    return ret;}static void __exit dev_exit(void){    printk(KERN_ALERT"Good-bye,mini2440 led is removed!\n");    misc_deregister(&misc);}module_init(dev_init);module_exit(dev_exit);MODULE_LICENSE("GPL");              //GPL协议MODULE_AUTHOR("Lianghuiyong Inc.");



驱动makefile文件

PWD = $(shell pwd)KDIR =/opt/FriendlyARM/mini2440/linux-2.6.32.2/obj-m:= LHY_led.oall:$(MAKE) -C $(KDIR) M=$(PWD) CONFIG_DEBUG_SECTION_MISMATCH=yclean:rm -rf *.o *~core.depend. *.cmd *.ko *.mod.c .tmp_versionsrm -rf *.order Module.*insmod:insmod LHY_led.kormmod:rmmod LHY_ledactive:echo -e "$(MAKE) \n"$(MAKE) -C $(KDIR) M=$(PWD)



测试模块:

/*************************************************************************> File Name: ledceshi.c> Author:梁惠涌 > Mail: > Created Time: 2014年10月02日 星期日 01时10分03秒 ************************************************************************/#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<sys/ioctl.h>int main(int argc, char **argv){    int on;    int led_on;    int fd;    /*检查两个参数,如果不符合则退出*/    if(argc !=3 ||sscanf(argv[1],"%d",&led_on)!=1 || sscanf(argv[2],"%d",&on)!=1 || on <0 || on>1 || led_on<0 ||led_on>3){       fprintf(stderr,"Usage: leds led_on 0|1 \n");        exit(1);    }    fd = open("/dev/leds0", 0);    if(fd<0){        fd = open("/dev/leds",0);    }    if(fd<0){        perror("open device leds");        exit(1);    }    /*通过ioctl调用输入的led_on和on控制led*/    ioctl(fd,on,led_on);    close(fd);    return 0;}



测试文件曾用过官方的makefile文件来make,但是开发板运行时出现syntax error: word unexpected (expecting ")")错误,直接使用gcc吧

命令:arm-linux-gcc -g ledceshi.c(c文件) -o ledceshi(产生的可执行文件名)


开发板上的操作截图(这个绝不是盗图了。。。。。。)



(三)丶函数调用笔记

(一)S3C2410_GPB(5)

头文件中的宏定义

#define S3C2410_GPB(_nr)(S3C2410_GPIO_B_START + (_nr))S3C2410_GPIO_B_START = S3C2410_GPIO_NEXT(S3C2410_GPIO_A),#define S3C2410_GPIO_NEXT(__gpio) ((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 0)#define S3C2410_GPIO_B_NR(32)S3C2410_GPIO_A_START = 0,

我们只用到 S3C2410_GPB(_nr)这个宏定义,而这个虚拟地址怎么算?宏定义(第一行)和等式(第二行)很容易理解,

S3C2410_GPIO_NEXT(S3C2410_GPIO_A)这个呢?

S3C2410_GPIO_NEXT(S3C2410_GPIO_A)=S3C2410_GPIO_A_START(0)+S3C2410_GPIO_A_NR(32)+CONFIG_S3C_GPIO_SPACE+0,

而CONFIG_S3C_GPIO_SPACE的值?根据

#if CONFIG_S3C_GPIO_SPACE != 0#error CONFIG_S3C_GPIO_SPACE cannot be zero at the moment#endif

可知 CONFIG_S3C_GPIO_SPACE 只能为0才会讨论虚拟地址是多少,反推过去就知道 S3C2410_GPB(_nr)=32+ _nr 。

总结:S3C2410_GPn(_nr) ,n为 A、B、C、D等,S3C2410_GPn(_nr)=n×32+_nr  (A时n为0,B时为1,C为2 .......)


(二) s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]);  函数以S3C2410_GPB(5),S3C2410_GPIO_OUTPUT 实参分析

该函数的具体定义:

gpio-nrs.h 用到的定义 ---  S3C2410_GPIO_BANKB 

regs-gpio.h                    ---  S3C2410_GPIO_OUTPUT ,   S3C2410_GPIO_BANKB,    S3C24XX_GPIO_BASE(x),   S3C2410_GPIO_OFFSET(pin)          

                                         ---  S3C2410_GPIO_BASE(pin)

void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function) //根据上面的计算,可知pin=37  ,function 可根据宏定义  #define S3C2410_GPIO_OUTPUT  (0xFFFFFFF1) {    void __iomem *base = S3C24XX_GPIO_BASE(pin); //#define S3C24XX_GPIO_BASE(x)  S3C2410_GPIO_BASE(x)/*S3C24XX_GPIO_BASE(pin)的计算:(以下定义是在 /linux-2.6.32.2/arch/arm/mach-s3c2410/include/mach/map.h /linux-2.6.32.2/arch/arm/plat-s3c24xx/include/plat/map.h/linux-2.6.32.2/arch/arm/plat-s3c/include/plat/Map-base.h下)#define     S3C2410_GPIO_BASE(pin)       ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)#define     S3C24XX_VA_GPIO                   ((S3C24XX_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART)#define     S3C24XX_PA_GPIO                   S3C2410_PA_GPIO#define     S3C24XX_PA_UART                   S3C2410_PA_UART#define     S3C24XX_VA_UART                   S3C_VA_UART#define     S3C2410_PA_GPIO                   (0x56000000)#define     S3C2410_PA_UART                   (0x50000000)#define     S3C_VA_UART                       S3C_ADDR(0x01000000)    #define     S3C_ADDR(x)                      (S3C_ADDR_BASE + (x))#define     S3C_ADDR_BASE                     (0xF4000000) S3C24XX_GPIO_BASE(pin) = S3C2410_GPIO_BASE(x)   = ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)   = ((((pin) & ~31) >> 1) + (S3C24XX_PA_GPIO - S3C24XX_PA_UART) + S3C24XX_VA_UART)          = ((((pin) & ~31) >> 1) + (0x56000000- 0x50000000) + S3C_VA_UART)   = ((((pin) & ~31) >> 1) + (0x56000000- 0x50000000) + (S3C_ADDR_BASE + (0x01000000))   = ((((pin) & ~31) >> 1) + (0x56000000- 0x50000000) + ((0xF4000000) + (0x01000000))        = ((((pin) & ~31) >> 1) + (0xFB000000)   = 0xFB000010//((((pin) & ~31) >> 1)运算:((pin) & ~31) =100000,右移一位=10000,也就是十六进制0x10。                                                                                       //base变量存储的是0xFB000010虚拟地址           */    unsigned long mask;     unsigned long con;     unsigned long flags;     if (pin < S3C2410_GPIO_BANKB) {    // #define S3C2410_GPIO_BANKB   (32*1) ,GPB虚拟地址的开始,         // 成立则pin是指向GPA,否则指向GPA之外地址         mask = 1 << S3C2410_GPIO_OFFSET(pin); // #define S3C2410_GPIO_OFFSET(pin) ((pin) & 31) , ((pin) & 31) 也就是将第六位(包括)以上的数清零    } else {         mask = 3 << S3C2410_GPIO_OFFSET(pin)*2;     //S3C2410_GPIO_OFFSET(37)=(( 37 ) & 31 ) *2= 00101*2=10 ,mask=3<<10 }                                          //根据我的理解上面这个if语句,1 和 3分别代表了寄存器控制位的初始状态,S3C2410_GPIO_OFFSET(pin)*2 和  S3C2410_GPIO_OFFSET(pin)//只是求出控制寄存器的具体位,那么1 << S3C2410_GPIO_OFFSET(pin) 和3 << S3C2410_GPIO_OFFSET(pin)*2 就是将寄存器的某一控制位赋//予初始状态,之所以要分1 和 3、 S3C2410_GPIO_OFFSET(pin);和S3C2410_GPIO_OFFSET(pin)*2;    主要是因为只有GBA的控制状态位只要一//位表示就够了(两个功能),而其他的寄存器需要两位(四个功能)来表示。    switch (function) {         case S3C2410_GPIO_LEAVE:  //是否是最后一个基(虚拟)地址              mask = 0;               function = 0;              break;                   case S3C2410_GPIO_INPUT:         case S3C2410_GPIO_OUTPUT:         case S3C2410_GPIO_SFN2:         case S3C2410_GPIO_SFN3:        if (pin < S3C2410_GPIO_BANKB) {    //这个if和我上面分析的一样             function - = 1;            function &= 1;                 function <<= S3C2410_GPIO_OFFSET(pin);             } else {         //GPB的pin属于下面的情况                function &= 3;       //取 function 后两位,其余位清零 , function=0xFFFFFFF1 ,相与后=01                 function <<= S3C2410_GPIO_OFFSET(pin)*2;     //通过上面的if分析可以知道function=01<<10                 }            }       /*  modify the specified register wwith IRQs off */     local_irq_save(flags);    // 关中断,中断标志位存储于flags中,定义在<asm/system.h>文件中                  con = __raw_readl(base + 0x00);//base是上面的指针变量,存储的是0xFB000010 基地址,没找到函数的定义,con应该是读取控制寄存器的值     con &= ~mask;         //con对应位[10,11]置零,~mask = ~(3<<10)     con |= function;      //con对应位设置[10,11]为function后两位 01,function = 01 << 10                   __raw_writel(con, base + 0x00);    //将改变后的con写入寄存器    local_irq_restore(flags);         //开寄存器中断}
s3c2410_gpio_setpin(led_table[i],0); 函数 和 s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]); 大同小异,就不分析了。

盗用下网友的虚拟地址映射图(这样这张图才有意义吧,O(∩_∩)O哈哈~)



错误笔记(2.6.32.2内核):

1、/home/nfsshare/root_qtopia/Drive/LED/mini2440_leds.c:29: error: 'S3C2410_GPB5' undeclared here (not in a function)

解决方法:这是因为内核版本问题,gpio-nrs.h定义和手册的例子上用的有所不同:S3C2410_GPB5改为 S3C2410_GPB(5)


2、/home/lianghuiyong/my2440drivers/LED/LHY_led.c:40: error: 'S3C2410_GPB5_OUTP' undeclared here (not in a function)

解决方法:同样原因,在arch/arm/mach-s3c2410/include/mach/regs-gpio.h文件下 可以看到    

47行    #define S3C2410_GPIO_OUTPUT  (0xFFFFFFF1)

在S3C2410_GPB5_OUTP 改为 S3C2410_GPIO_OUTPUT


3、error: 'misc' undeclared (first use in this function)

error: variable 'dev_fops' has initializer but incomplete type

error: unknown field 'owner' specified in initializer
这都是没注意,写错了代码:file_operations写错了,misc也写的上下不一致,MISC_DYNAMIC_MINOR漏写了字母  


4、 line 1: syntax error: word unexpected (expecting ")")  错误

直接使用gcc编译

命令:arm-linux-gcc -g ledceshi.c(c文件) -o ledceshi(产生的可执行文件名)             


2 0
原创粉丝点击