嵌入式Linux字符设备入门之--LED驱动详解

2019-07-13 02:37发布

一、实验环境 PC机:Redhat9.0  内核版本:Linux2.4 开发板:FL2440     内核版本:Linux2.6      CPUs3c2440 二、实验步骤 1、硬件连接。        FL2440有四个可编程控制的共阳极LED,分别与CPUGPB5,GPB6,GPB8,GPB10相连。 2、驱动程序        由于是刚刚接触驱动,实在是不知道怎么写,只好用开发板自带的led驱动了(手动分配设备号),我还写了一个自动分配设备号的程序。我所做的就是分析这个程序,争取把它给读懂。下面是开发板led驱动的源程序。 程序里的注释是我参考网上的资料自己加的,主要参考的是mini2440led驱动程序的注释内容,不知道对不对! a:手动分配设备号 /*-----------------s3c2440_leds.c--------------------------------*/ #include #include #include #include #include #include //????????????????? #include //??????????????? #include #include #include #include   #define DEVICE_NAME       "leds"    //设备名 #define LED_MAJOR 231           //主设备号     /*----------------------------------------------------------------------------------*/ //定义引脚的寄存器数组(无符号长整形,对应于引脚的地址)   static unsigned long led_table [] = {          S3C2410_GPB5,        S3C2410_GPB6,        S3C2410_GPB8,        S3C2410_GPB10,        };   /*---------------------------------------------------------------------------------*/ //定义引脚功能,为输出(无符号整形)   static unsigned int led_cfg_table [] = {                S3C2410_GPB5_OUTP,            //0x01<<10 defined in  refg-gpio.h        S3C2410_GPB6_OUTP,        S3C2410_GPB8_OUTP,        S3C2410_GPB10_OUTP, };   /*-------------------------------------------------------------------------------*/ //测试程序调用此函数,实现对led的控制 //设备节点,文件描述符,LED灯状态,LED灯编号四个命令参数   static int s3c2440_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; /* Invalid argument,非法参数 */                 }                                //低电平有效,用户程序传来的cmd取反                 s3c2410_gpio_setpin(led_table[arg], !cmd);//设置数据寄存器gpbdatcmd的非               return 0;        default:               return -EINVAL;        } } /*----------------------------------------------------------------------------*/     static struct file_operations s3c2440_leds_fops = {        .owner    =     THIS_MODULE,        .ioctl       =     s3c2440_leds_ioctl, }; /*---------------------------------------------------------------------------*/ //初始化函数 static int __init s3c2440_leds_init(void) {        int ret;        int i;          ret = register_chrdev(LED_MAJOR, DEVICE_NAME, &s3c2440_leds_fops);//注册设备        if (ret < 0)        {          printk(DEVICE_NAME " can't register major number/n");          return ret;        }          devfs_mk_cdev(MKDEV(LED_MAJOR, 0), S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, DEVICE_NAME);        //上句的作用是干甚??????          for (i = 0; i < 4 ; i++)        {         /*-----------------------------------------------------------------------*/                   //设置GPIO对应的配置寄存器GPIOCON为输出状态,在头文件               //linux-2.6.12/include/asm-arm/arch-s3c2410/hardware.h中的函数原型为:         //extern void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function);                          s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);           /*-----------------------------------------------------------------------*/         //设置GPIO对应的数据寄存器GPIODAT为高电平               //在模块加载结束后,四个LED应该是全部都是不发光状态                 s3c2410_gpio_setpin(led_table[i], 1);                 /*-----------------------------------------------------------------------*/        }          printk(DEVICE_NAME " initialized/n");        return 0; } /*------------------------------------------------------------------------------------*/ //注销设备 static void __exit s3c2440_leds_exit(void) {        devfs_remove(DEVICE_NAME);        unregister_chrdev(LED_MAJOR, DEVICE_NAME); }   /*------------------------------------------------------------------------------------*/ module_init(s3c2440_leds_init);//执行insmod是调用该函数 module_exit(s3c2440_leds_exit);//执行rmmod是调用该函数 /*-----------------------------------------------------------------------------------*/   b:自动分配设备号 #include #include #include #include #include #include #include #include #include #include #include   #define DEVICE_NAME "leds"   static unsigned long led_table [] = {                      S3C2410_GPB5,                      S3C2410_GPB6,                      S3C2410_GPB8,                      S3C2410_GPB10, };   static unsigned int led_cfg_table [] = {                      S3C2410_GPB5_OUTP,                      S3C2410_GPB6_OUTP,                      S3C2410_GPB8_OUTP,                      S3C2410_GPB10_OUTP, };   static int s3c2440_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 s3c2440_leds_fops = {               .owner = THIS_MODULE,               .ioctl = s3c2440_leds_ioctl, };   static struct miscdevice misc = {               .minor = MISC_DYNAMIC_MINOR,               .name = DEVICE_NAME,               .fops = &s3c2440_leds_fops, };   static int __init s3c2440_leds_init(void) {        int ret;        int i;          for (i = 0; i < 4; i++)        {        s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);        s3c2410_gpio_setpin(led_table[i], 1);        }        ret = misc_register(&misc);          printk (DEVICE_NAME"/tinitialized/n");        return ret; }   static void __exit s3c2440_leds_exit(void) {        misc_deregister(&misc); } module_init(s3c2440_leds_init); module_exit(s3c2440_leds_exit); 这个程序我没有做注释,参考上一个程序吧!不过这里有些需要说明的,用insmod命令能加载到内核,用lsmod命令也可以查到,但是运行测试程序时却出不来该有的现象,并且提说找不到文件。(下面做了解释) 3、编译驱动程序 写个Makefile文件编译,2.6版本的内核好像只能这么编译驱动,不能在命令行里一步步的通过命令编译,不过2.4内核的可以。(不知道是不是这么回事?) 当然Makefile我也不会写,用的板子带的那个!   #Makefile for s3c2440_leds.c obj-m :=s3c2440_leds.o KERNELDIR ?=/usr/linux-2.6.12 PWD := $(shell pwd) default:        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules   make后生成了四个文件             我只知道s3c2440_leds.ko是我想要的,其他三个不道是干嘛的? 4、将驱动加载到内核        有两种方式: 一是把驱动程序直接编译进内核;细节问题我没有怎么研究,大致上就是写好驱动程序之后,放到内核代码的目录里,当然还得修改几个文件,与内核一同编译。这样内核启动后会自动加载,免去很多麻烦,但要占用资源! 二是动态的将驱动加载进内核;这种方式比较适合实验开发,挺方便的,就是每次用到驱动是都要手动加载,我采用的就是这种方式!不过有这种方式有关前题,就是你的内核必须支持动态的加载模块,这个在你配置内核的时候要选择此项支持! 到你的内核的目录下,make menuconfig,第三项“Loadable module support”,选上“Enable loadable module support”,其他的随便,我就多选了个“Module unloading”。然后,重新编译内核。(参考的别人的,我没弄过!!!)    下面是加载的过程:        PC机的根目录挂到开发板的tmp目录下        #mount 192.168.2.32:/ /tmp        加载驱动模块        #insmod s3c2440_led.ko        可以看看有没有加载上        #lsmod                      Module           size                usedby                  tainted:P                      S3c2440_leds 1664         0                    Ok了,加载成功,现在可以写个测试程序测试一下了!        顺便说一下如何卸载驱动模块吧!        #rmmod s3c2440_leds.ko          注意:如果你加载的是a驱动程序,可以到/dev目录下查看一下,发现在字符设备中有关叫leds的设备,设备号231。但是如果你加载的是b驱动程序,你在/dev目录下是找不到leds的,不过你可以进的一个叫misc的目录,原来leds在这里,我试验内核分给它的设备号是63,不道为啥没有想a程序那样直接出现在/dev目录下。这也带来了麻烦,就是写测试程序时,针对两种不同的驱动程序,测试程序也略有不同!(后面会说,见测试程序)   5、测试程序 /*-----------------------led--------------------------------------------*/ /*这个程序实现的是跑马灯*/ #include "stdio.h" #include "unistd.h" #include "sys/types.h" #include "sys/ioctl.h" #include "stdlib.h" #include "termios.h" #include "sys/stat.h" #include "fcntl.h" #include "sys/time.h" int main(void) {               int on=1;        int led;        int fd;        fd=open("/dev/leds",0); /*如果有加载的是b驱动程序模块,这里要写出:fp=open(“/dev/misc/leds”,0)*/        if(fd<0)        {               perror("open device leds");               exit(1);        }        printf("leds test show,press ctrl+c to exit /n");        while(1)        {               for(led=0;led<4;led++)               {                      ioctl(fd,on,led);                      usleep(50000);               }               on=!on;        }        close(fd);        return 0; } 6、编译测试程序,完成测试 将测试程序交叉编译,在板子上执行: #./led 看效果吧,漂亮!!!!! 三、实验总结: 这个实验其实做了好多天,在做之前也做了很多准备,因为以前也没接触过这些,也没有什么基础,确实挺费劲的!而且我之前看的都是2.4内核的驱动,2.4的驱动与2.6<