1、MINI2440 LED 驱动程序./app-led 1 回车灯全亮;./app-led 0 回车 灯全熄了。真是太令我激动。这可是在 linux下的操作。Linux 下的驱动程序。呵呵,以前我学单片机的时候,郭天祥讲的第一个实验就是点灯实验,那个时候也很激动,感觉真是神奇几句程序就能控制灯的亮,熄,就是从单片机的这个点灯实验开始,我也算是入了单片机的门,后面在电子设计大赛中,和帮别人做的一些东西中,获得了一些成就感。随后我又在 arm7 那块板子上做了裸板的流水灯实验。今天终于在传说中的 linux 中实现了点灯的实验,到现在我都搞了近一年的嵌入式学习了,今天算了感觉有了点门道,为什么这么说
2、呢,以前是有例程,但是不知道环境怎么搭建,也就没法做实验,学习当然就没有进步,而今天这个环境被搭建起来了,虽然说只是一个点灯实验,但是以后的大量的实验都可以做了,总算有了可以走的路了。一:看效果这个实验基于昨天搭建好的环境;minicom,linux 内核,NFS 起根文件系统,这个环境的搭建也一件相当痛苦的事情。现在在学习驱动程序就不过多的去关注这个东西,这个还得专门作为一个话题来学习。总之现在的环境就是 minicom 中是开发板上面的 linux 系统,这里的文件系统是通过 NFS 起根文件系统,也就是说这个路径是可以和宿主机的进行共享的,在宿舍机上写的程序利用交叉编译工具编译,然后拷贝
3、到 NFS 根文件系统,就可以在开发板上 linux 中运行,也就可以去控制开发板了。这里对内核所做的一小点修改就是内核配置时去掉 驱动程序 字符设备驱动LED Support for Mini2440/QQ2440 GPIO LEDs 免得它和我写的冲突。写好的驱动程序,和相应的应用测试程序位于宿主机的/home/532_526 下直接 make得 mini2440_leds_misc.ko 驱动模块,然后 arm-linux-gcc app-led.c -o app-led。生成 app-led 的应用程序。将 mini2440_leds_misc.ko 和 app-led 拷贝到 NFS
4、 起根文件系统的 tmp 目录下。然后换到 minicom 中的 linux。 ls tmp 就可以看到刚才拷贝过去的两个文件,执行 insmod mini2440_leds_misc.ko,按以前还得创建设备文件但是这个程序为我们自动创建了设备文件/dev/leds。然后./app-led 1 回车灯全亮;./app-led 0 回车 灯全熄了。二:点灯实验代码分析:(1)所用到的知识首先来看一下这个驱动程序中所用到一些知识。A:IOCTL 方法 这个在前面已经学习过了,它的实现主要分为两步:定义命令,实现命令。定义命令放在了 memdev.h 这个头文件中。如下代码:/* 定义幻数 */#
5、define MEMDEV_IOC_MAGIC k/* 定义命令 */#define MEMDEV_IOCON _IO(MEMDEV_IOC_MAGIC, 1)#define MEMDEV_IOCOFF _IO(MEMDEV_IOC_MAGIC, 2)#define MEMDEV_IOC_MAXNR 2由上面可知定义了两个命令,这也是这个驱动程序的整体功能,四个灯的全亮,和全灭。实现命令如下:static int sbc2440_leds_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long
6、 arg)int i = 0;/* 检测命令的有效性 */if (_IOC_TYPE(cmd) != MEMDEV_IOC_MAGIC) return -EINVAL;if (_IOC_NR(cmd) MEMDEV_IOC_MAXNR) return -EINVAL;/* 根据命令,执行相应的操作 */switch(cmd) case MEMDEV_IOCOFF:/*灯全灭*/cmd = 1;for(i=0; i/* 定义幻数 */#define MEMDEV_IOC_MAGIC k/* 定义命令 */#define MEMDEV_IOCON _IO(MEMDEV_IOC_MAGIC, 1)#
7、define MEMDEV_IOCOFF _IO(MEMDEV_IOC_MAGIC, 2)#define MEMDEV_IOC_MAXNR 2#endif /* _MEMDEV_H_ */在这个 memdev.h 中就定义了 ioctl 的两个命令。mini2440_leds_misc.c#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #i
8、nclude #include #include #include #include #include #include #include #include 前面这一大堆的头文件真是有点令人头疼。#include “memdev.h“#define DEVICE_NAME “leds“static unsigned long led_table = S3C2410_GPB5,S3C2410_GPB6,S3C2410_GPB7,S3C2410_GPB8,;上面查看原理图这是四个灯所接的芯片管脚。下面的功能控制都整成了宏,看起来很方便。static unsigned int led_cfg_tab
9、le = S3C2410_GPB5_OUTP,S3C2410_GPB6_OUTP,S3C2410_GPB7_OUTP,S3C2410_GPB8_OUTP,;下面是我前面分析过的控制函数,还是比较简单。当应用层的 ioctl(fd, cmd, arg)被调用时,系统将处理它能识别的命令;static int sbc2440_leds_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)int i = 0;/* 检测命令的有效性 */if (_IOC_TYPE(cmd) != MEMDE
10、V_IOC_MAGIC) return -EINVAL;if (_IOC_NR(cmd) MEMDEV_IOC_MAXNR) return -EINVAL;/* 根据命令,执行相应的操作 */switch(cmd) case MEMDEV_IOCOFF:/*灯全灭*/cmd = 1;for(i=0; i4; i+)s3c2410_gpio_setpin(led_tablei, cmd);return 0;case MEMDEV_IOCON:/*灯全亮*/for(i=0; i4; i+)s3c2410_gpio_setpin(led_tablei, !cmd);return 0;default:
11、return -EINVAL;/*文件操作结构体*/static struct file_operations dev_fops = .owner = THIS_MODULE,.ioctl = sbc2440_leds_ioctl,/将上面的 ioctl 操作传给 file_operations;static struct miscdevice misc = .minor = MISC_DYNAMIC_MINOR,.name = DEVICE_NAME,.fops = static int _init dev_init(void)int ret;int i;/*设置 GPIO 控制寄存器,GP
12、IO 设置为输出模式,默认下灯全灭*/for (i = 0; i 4; i+) s3c2410_gpio_cfgpin(led_tablei, led_cfg_tablei);s3c2410_gpio_setpin(led_tablei, 1);/*注册混杂型字符设备驱动*/ret = misc_register(printk (DEVICE_NAME“tinitializedn“);return ret;static void _exit dev_exit(void)/*注销混杂型字符设备驱动*/misc_deregister(module_init(dev_init);module_exi
13、t(dev_exit);MODULE_AUTHOR(“David Xie“);MODULE_LICENSE(“GPL“);三:深入分析s3c2410_gpio_cfgpin(led_tablei, led_cfg_tablei);s3c2410_gpio_setpin(led_tablei, 1);对 led 的操作就是通过这两个函数完成了,那肯定得去看看这两个函数是具体怎么实现的。记得以前做单片机的时候,或是做裸板程序的时候,先是看原理图,看 led 灯和哪个管脚相连。然后看那个管脚相关的寄存器,有数据寄存器和控制寄存器。将那个管脚通过设置控制寄存器配置成为输出端口。然后去往数据寄存器里面写 0 或 1 就开始控制灯的亮灭了。我相信这里也是这样做的。但是 linux 为了方便,统一,它做了很多的工作不只是为你这一个 led 驱动程序服务,对于所有的 io 口的操作它都统一起来了现在就深入的去看一下它到底是怎么实现的。本想好好的在源代码中去跟踪一下这两个函数,但我上网查了一天有一些资料但看起来真是费劲,然后自己去跟踪了一下太吃力了,就一句话就得绕几十个圈。下面这个链接讲的还行http:/ i0 口来说就要对其做两个工作:一个是先选择它的功能,另外一个就是给它赋值。