
《智能家居系统》5
发布日期:2021-05-08 03:56:14
浏览次数:16
分类:精选文章
本文共 5583 字,大约阅读时间需要 18 分钟。
Linux驱动程序开发
1. 设置一个模块
在Linux系统中,每个驱动程序都可以看作是一个独立的模块。驱动开发的第一步是创建一个模块,用来存放驱动的相关代码。
1.1 模块的入口和出口
模块的入口和出口是通过函数接口实现的。在Linux中,模块的入口函数需要注册到内核中,以便系统能够加载和卸载该模块。
static int __init gec6818_led_init(void){ // 安装驱动 } static void __exit gec6818_led_exit(void){ // 卸载驱动 } module_init(gec6818_led_init); module_exit(gec6818_led_exit);
1.2 模块的描述
// 模块描述
#modinfo led_drv.ko
MODULE_AUTHOR("bobeyfeng@163.com");MODULE_DESCRIPTION("led driver for GEC6818");MODULE_LICENSE("GPL");MODULE_VERSION("V1.0");2. 定义一个cdev
cdev是Linux内核中用于描述字符设备的数据结构。在大多数字符设备驱动中,使用cdev模型来实现设备的注册和操作。
static struct cdev led_cdev;
3. 申请设备号
设备号是字符设备驱动的唯一标识符。在Linux系统中,可以通过申请设备号来注册字符设备。
if(led_major == 0){ ret = alloc_chrdev_region(&led_dev_num, led_minor, 1, "led_device"); } else{ led_dev_num = MKDEV(led_major, led_minor); ret = register_chrdev_region(led_dev_num, 1, "led_device"); } if(ret < 0){ printk("can not get led device number\n"); return ret; }
4. 提供文件操作接口
为了让应用程序能够与驱动程序交互,需要定义文件操作接口(file operations)。这些接口包括open、write、release等函数。
4.1 open函数
static int gec6818_led_open(struct inode *inode, struct file *filp){ printk("led driver is opening\n"); return 0; }
4.2 write函数
static ssize_t gec6818_led_write(struct file *filp, const char __user *buf, size_t len, loff_t *off){ // 接收应用程序写下来的数据,并用这些数据来控制LED灯 // 定义一个数据协议:应用程序写下来的数据有两个字节 // buf[1] -> 某一站灯:8, 9, 10, 11 // buf[0] -> LED灯的状态:1---亮,0---灯灭 ret; char led_flag[2]; if(len != 2){ return -EINVAL; } ret = copy_from_user(led_flag, buf, len); if(ret < 0){ return -EFAULT; } switch(led_flag[1]){ case 8: // GPIOC17 if(led_flag[0] == 1){ *(unsigned int *)GPIOCOUT &= ~(1<<17); } else if(led_flag[0] == 0){ *(unsigned int *)GPIOCOUT |= (1<<17); } else{ return -EINVAL; } break; case 9: // GPIOC8 if(led_flag[0] == 1){ *(unsigned int *)GPIOCOUT &= ~(1<<8); } else if(led_flag[0] == 0){ *(unsigned int *)GPIOCOUT |= (1<<8); } else{ return -EINVAL; } break; case 10: // GPIOC7 if(led_flag[0] == 1){ *(unsigned int *)GPIOCOUT &= ~(1<<7); } else if(led_flag[0] == 0){ *(unsigned int *)GPIOCOUT |= (1<<7); } else{ return -EINVAL; } break; case 11: // GPIOC12 if(led_flag[0] == 1){ *(unsigned int *)GPIOCOUT &= ~(1<<12); } else if(led_flag[0] == 0){ *(unsigned int *)GPIOCOUT |= (1<<12); } else{ return -EINVAL; } break; default: return -EINVAL; } return len; }
4.3 release函数
static int gec6818_led_release(struct inode *inode, struct file *filp){ printk("led driver is closing\n"); return 0; }
static const struct file_operations gec6818_led_fops = { .owner = THIS_MODULE, .write = gec6818_led_write, .open = gec6818_led_open, .release = gec6818_led_release, };
5. 初始化cdev并将其加入内核
// 4. 初始化cdev cdev_init(&led_cdev, &gec6818_led_fops); // 5. 将初始化好的cdev加入内核 ret = cdev_add(&led_cdev, led_dev_num, 1); if(ret < 0){ printk("cdev add error\n"); unregister_chrdev_region(led_dev_num, 1); return -EBUSY; }
6. 创建class和device
// 创建class led_class = class_create(THIS_MODULE, "led_class"); if(IS_ERR(led_class)){ ret = PTR_ERR(led_class); printk("led driver class create error\n"); cdev_del(&led_cdev); unregister_chrdev_region(led_dev_num, 1); return ret; } // 创建device led_device = device_create(led_class, NULL, led_dev_num, NULL, "led_drv"); if(IS_ERR(led_device)){ ret = PTR_ERR(led_device); printk("led driver device create error\n"); class_destroy(led_class); cdev_del(&led_cdev); unregister_chrdev_region(led_dev_num, 1); return ret; }
7. 获取物理地址对应的虚拟地址
// 8. 申请物理内存区 led_res = request_mem_region(0xC001C000, 0x1000, "GPIOC_MEM"); if(led_res == NULL){ printk("request memory error\n"); device_destroy(led_class, led_dev_num); class_destroy(led_class); cdev_del(&led_cdev); unregister_chrdev_region(led_dev_num, 1); return -EBUSY; } // 9. IO内存的动态映射 GPIOC_BASE = ioremap(0xC001C000, 0x1000); if(GPIOC_BASE == NULL){ printk("ioremap error\n"); release_mem_region(0xC001C000, 0x1000); device_destroy(led_class, led_dev_num); class_destroy(led_class); cdev_del(&led_cdev); unregister_chrdev_region(led_dev_num, 1); return -EFAULT; } GPIOCOUT = GPIOC_BASE + 0x00; // 0x00 GPIOCOUTENB = GPIOC_BASE + 0x04; // 0x04 GPIOCALTFN0 = GPIOC_BASE + 0x20; // 0x20 GPIOCALTFN1 = GPIOC_BASE + 0x24; // 0x24
8. 通过虚拟地址访问寄存器(裸机方法)
// LED的初始化,通过虚拟地址-->物理地址-->寄存器-->控制GPIO *(unsigned int *)GPIOCALTFN0 &= ~(3<<24)|(3<<16)|(3<<14)); *(unsigned int *)GPIOCALTFN0 |= (1<<24)|(1<<16)|(1<<14); *(unsigned int *)GPIOCALTFN1 &= ~(3<<2); *(unsigned int *)GPIOCALTFN1 |= (1<<2); *(unsigned int *)GPIOCOUTENB |=((1<<17) + (1<<12) + (1<<8) + (1<<7)); *(unsigned int *)GPIOCOUT |=((1<<17) + (1<<12) + (1<<8) + (1<<7));
思考
如何将一个数的第20位和第10位设置为1,而其他位保持不变?
a |= (1<<20); a |= (1<<10);
等价于:
a |= ((1<<20) + (1<<10));
发表评论
最新留言
关注你微信了!
[***.104.42.241]2025年04月15日 21时23分21秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
第01问:MySQL 一次 insert 刷几次盘?
2019-03-13
laravel server error 服务器内部错误
2019-03-15
一道简单的访问越界、栈溢出pwn解题记录
2019-03-15
Docker部署postgresql-11以及主从配置
2023-01-23
EnvironmentNotWritableError: The current user does not have write permissions to the target environm
2023-01-23
#C8# UVM中的factory机制 #S8.2.3# 重载sequence哪些情形
2023-01-24
java教师管理系统(ssm)
2023-01-24
el-select下拉框修改背景色
2023-01-24
elasticsearch 7.7.0 单节点配置x-pack
2023-01-24
Elasticsearch入门教程(Elasticsearch7,linux)
2023-01-24
ElasticSearch设置字段的keyword属性
2023-01-24
Elasticsearch面试题
2023-01-24
15个Python数据处理技巧(非常详细)零基础入门到精通,收藏这一篇就够了
2023-01-24
2024大模型行业应用十大典范案例集(非常详细)零基础入门到精通,收藏这一篇就够了
2023-01-24
2024年全球顶尖杀毒软件,从零基础到精通,收藏这篇就够了!
2023-01-24