LED驱动的简单实现(fl2440)
2015-03-18 12:51
246 查看
本片文章教大家如何入门一个简单的驱动程序。我们都知道驱动程序是用来操作设备来完成相应的功能的,所以它应该是由内核来调用的。
我们先看如何从用户空间来调用一个驱动程序,它的具体流程是怎样的?
Created with Raphaël 2.1.2应用程序系统调用接口内核设备操作yes
module_init(s3c_led_init);
module_exit(s3c_led_exit);
这两个函数我们一般放在驱动程序的末尾,当我们调用insmod注册一个驱动程序的时候,内核会先执行module_init这个函数,因为这个函数会把相应的操作放在文本段的前面,所以里面 的参数是我们初始化的代码。调用rmmod就会调用module_exit(s3c_led_exit);来释放设备号和设备,不详述。
register_chrdev_region函数表示注册一个设备号(主动注册你想要的设备号,注册前要注意不能与已有的设备号重复),它会向内核申请一个空闲的设备号,因为在内核中我们的驱动程序是通过设备号和与相应的设备联系起来。
alloc_chrdev_region表示动态申请一个设备号,申请过程由内核完成。
我们申请了一个设备号,就会涉及到释放它,此时用
unregister_chrdev_region函数
申请完设备号后,我们就要申请与之对应的字符设备,cdev_alloc()函数返回一个字符设备结构cdev的指针,然后我们要对这个设备进行初始化用函数 cdev_init(led_cdev, &led_fops);&led_fop是用来初始化函数的具体操作的地址,点灯就可以通过这个函数接口来具体执行。接下来告诉内核这个设备的信息cdev_add(led_cdev, devno, dev_count);如果一切顺利则注册成功。
然后我们要跳到led_fop这个函数去看一下怎样去操作设备。
这上面有两个具体操作的函数s3c_led_open和s3c_led_write。
在s3c_led_open中我完成了LED相应寄存器物理地址到虚拟地址的映射以及把GPBCON相应引脚设置输出模式。然后我又在
s3c_led_write中通过copy_from_user(&val, buf, count);来完成用户空间缓冲区数据到内核中的拷贝,然后通过传进来的数值来控制LED灯的亮灭。
用户程序打开相应的设备文件,然后用argv[1] “on/off”来完成值的控制,从而把值写进缓冲区。
然后insmod相应的驱动程序,在创建设备节点,最后用应用程序点灯就行了。
我们先看如何从用户空间来调用一个驱动程序,它的具体流程是怎样的?
Created with Raphaël 2.1.2应用程序系统调用接口内核设备操作yes
驱动代码部分
我们先来认识一下这两个函数:module_init(s3c_led_init);
module_exit(s3c_led_exit);
这两个函数我们一般放在驱动程序的末尾,当我们调用insmod注册一个驱动程序的时候,内核会先执行module_init这个函数,因为这个函数会把相应的操作放在文本段的前面,所以里面 的参数是我们初始化的代码。调用rmmod就会调用module_exit(s3c_led_exit);来释放设备号和设备,不详述。
static int __init s3c_led_init(void) { int result; dev_t devno; if (0 != dev_major) { devno = MKDEV(dev_major, 0); result = register_chrdev_region (devno, dev_count, DEV_NAME); } else { result = alloc_chrdev_region(&devno, dev_minor, dev_count, DEV_NAME); dev_major = MAJOR(devno); } if (result < 0) { printk(KERN_ERR "S3C %s driver can't use major %d\n", DEV_NAME, dev_major); } printk(KERN_DEBUG "S3C %s driver use major %d\n", DEV_NAME, dev_major); if(NULL == (led_cdev=cdev_alloc()) ) { printk(KERN_ERR "S3C %s driver can't alloc for the cdev.\n", DEV_NAME); unregister_chrdev_region(devno, dev_count); } led_cdev->owner = THIS_MODULE; cdev_init(led_cdev, &led_fops); result = cdev_add(led_cdev, devno, dev_count); if (0 != result) { printk(KERN_INFO "S3C %s driver can't reigster cdev: result=%d\n", DEV_NAME, result); goto ERROR; } printk(KERN_ERR "S3C %s driver[major=%d]installed successfully!\n",DEV_NAME, dev_major); return 0; ERROR: printk(KERN_ERR "S3C %s driver installed failure.\n", DEV_NAME); cdev_del(led_cdev); unregister_chrdev_region(devno, dev_count); return result; }
register_chrdev_region函数表示注册一个设备号(主动注册你想要的设备号,注册前要注意不能与已有的设备号重复),它会向内核申请一个空闲的设备号,因为在内核中我们的驱动程序是通过设备号和与相应的设备联系起来。
alloc_chrdev_region表示动态申请一个设备号,申请过程由内核完成。
我们申请了一个设备号,就会涉及到释放它,此时用
unregister_chrdev_region函数
申请完设备号后,我们就要申请与之对应的字符设备,cdev_alloc()函数返回一个字符设备结构cdev的指针,然后我们要对这个设备进行初始化用函数 cdev_init(led_cdev, &led_fops);&led_fop是用来初始化函数的具体操作的地址,点灯就可以通过这个函数接口来具体执行。接下来告诉内核这个设备的信息cdev_add(led_cdev, devno, dev_count);如果一切顺利则注册成功。
然后我们要跳到led_fop这个函数去看一下怎样去操作设备。
static struct file_operations led_fops={ .owner = THIS_MODULE, .open = s3c_led_open, .write = s3c_led_write, }; static int s3c_led_open(struct inode *inode, struct file *file) { //printk("open the led\n"); GPBCON = ioremap(S3C_GPB_BASE, S3C_GPB_LEN); GPBDAT = GPBCON + 1; *GPBCON &= ~((0x3<<(5*2)) | (0x3<<(6*2)) | (0x3<<(8*2)) | (0x3<<(10*2))); *GPBCON |= ((0x1<<(5*2)) | (0x1<<(6*2)) | (0x1<<(8*2)) | (0x1<<(10*2))); return 0; } static ssize_t s3c_led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { int val; //printk("write the led\n"); copy_from_user(&val, buf, count); if (val==1) { *GPBDAT &= ~((0x1<<5) | (0x1<<6) | (0x1<<8) | (0x1<<10)); } else { *GPBDAT |= ((0x1<<5) | (0x1<<6) | (0x1<<8) | (0x1<<10)); } return 0; }
这上面有两个具体操作的函数s3c_led_open和s3c_led_write。
在s3c_led_open中我完成了LED相应寄存器物理地址到虚拟地址的映射以及把GPBCON相应引脚设置输出模式。然后我又在
s3c_led_write中通过copy_from_user(&val, buf, count);来完成用户空间缓冲区数据到内核中的拷贝,然后通过传进来的数值来控制LED灯的亮灭。
应用程序部分
int main (int argc, char **argv) { int fd; int val = 1; fd = open("dev/led", O_RDWR); if (fd < 0) { printf("can't open!\n"); } if (argc != 2) { printf("usag :\n"); printf("%s on/off\n", argv[1]); } if (strcmp(argv[1],"on") == 0) { val = 1; } else{ val =0; } write(fd, &val, 4); return 0; }
用户程序打开相应的设备文件,然后用argv[1] “on/off”来完成值的控制,从而把值写进缓冲区。
然后insmod相应的驱动程序,在创建设备节点,最后用应用程序点灯就行了。
相关文章推荐
- 单片机定时驱动led程序2--c语言实现
- 自己动手写最简单的Android驱动---LED驱动的编写【转】
- 自己动手写最简单的Android驱动---LED驱动的编写
- 海思平台GPIO驱动最简单实现
- 简单字符设备驱动——LED驱动
- linux设备驱动第二篇:一个简单hello world驱动如何实现
- DMA设备驱动(三)————基于Linux3.4.2的dma设备驱动的简单实现
- 基于Linux内核的USB鼠标驱动的简单实现
- linux驱动学习3:实现一简单完整驱动(包括open,read,write,ioctl)
- LINUX下简单的LED驱动模型(转载)
- 由简单的LED驱动分析内核源码包中的s3c2410寄存器宏定
- 27 在H5上实现spi-tft屏的简单驱动
- (14)树莓派B+使用L298N驱动控制四驱车并实现一个简单的web控制端
- IDDD 实现领域驱动设计-一个简单业务用例的回顾和理解
- ATAPI(磁盘端口驱动)级文件保护简单实现
- Linux下fl2440之led驱动编写以及测试
- 使用Ruby实现简单的事物驱动的web应用的教程
- FL2440开发板 platform_led驱动及应用程序
- ARM11 paltform驱动代码完成,最简单的测试直接在装载设备中运行,实现秒读
- 中断驱动学习与实例——定时器0中断实现led流水灯