接触LED驱动前,先要引入MMU的概念,MMU 全称叫做 Memory Manage Unit,也就是内存管理单元,MMU 主要完成的功能包括:
1、完成虚拟空间到物理空间的映射;
2、内存保护,设置存储器的访问权限,设置虚拟存储空间的缓冲特性。
常规的Linux都是需要MMU的(之前有个ucLinux,不要MMU的MCU可以跑,但印象中现在很少人用了),CPU和MCU的区别之一就是MMU。
按照常规的Linux字符设备驱动编写步骤,逐一介绍LED驱动:
1.确定设备号
确定设备号200,再注册字符设备驱动。
- retvalue = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);
- if(retvalue < 0){
- printk("register chrdev failed!\r\n");
- return -EIO;
- }
2.定义 file_operations 结构体
创建一个结构体,用于定义字符设备驱动程序对外提供的操作接口。主要包括led open、read、write,以及release接口。
- static struct file_operations led_fops = {
- .owner = THIS_MODULE,
- .open = led_open,
- .read = led_read,
- .write = led_write,
- .release = led_release,
- };
3.实现操作函数
实现字符设备驱动中定义的操作函数,根据设备的需求来进行相应的操作。
前面介绍的MMU虚拟内存地址概念用上了,怎么计算LED的地址需要参考硬件设计,这里的LED地址如下:
- #define GPIO1_DR_BASE (0X0209C000)
- #define GPIO1_GDIR_BASE (0X0209C004)
看个led write的实现函数,用于点亮/熄灭LED。
- static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
- {
- int retvalue;
- unsigned char databuf[1];
- unsigned char ledstat;
-
- retvalue = copy_from_user(databuf, buf, cnt);
- if(retvalue < 0) {
- printk("kernel write failed!\r\n");
- return -EFAULT;
- }
-
- ledstat = databuf[0];
-
- if(ledstat == LEDON) {
- led_switch(LEDON);
- } else if(ledstat == LEDOFF) {
- led_switch(LEDOFF);
- }
- return 0;
- }
4.注册和注销字符设备
使用register_chrdev()函数进行设备注册,实现放在led初始化函数中。
- retvalue = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);
- if(retvalue < 0){
- printk("register chrdev failed!\r\n");
- return -EIO;
- }
使用unregister_chrdev()函数进行设备注销,放在驱动退出函数中。
- unregister_chrdev(LED_MAJOR, LED_NAME);