文章目录
前言:最近在学习韦东山老师的arm驱动部分教学,本文记录一下自己对最基本的驱动框架结构的理解,以及最基本的led驱动实现的方法。本博客以米尔科技的rico board开发板为例,完成rico board的led驱动代码的实现过程。
备注:完整代码直接跳转到《led驱动代码(完整代码,亲测有效)》章节或跳转至《github工程链接》章节,中间各个小结的代码为实现过程(不完整),无法正确编译。
一、相关知识点(涉及接口、结构体、调用关系等)
一个软件系统可以分为以下四层:应用程序、库、内核、驱动,借用韦老师一副图,如下图:
即:应用层开发的功能,各个接口会通过库、通过内核调用到底层驱动程序的对应接口,从而执行对应的功能。

1.1 裸机开发步骤与驱动开发过程对比
1.1.1 裸机开发步骤
裸机开发时,我们需要按照如下过程进行开发。
步骤:
1、看电路图,查清楚led在哪个管脚,对应哪个GPIO口
2、看芯片手册,查看需要配置的相关寄存器地址——(主要查看需要配置哪些寄存器可以将GPIO配置成输出模式,地址可以在map地址映射章节找到)
3、看芯片手册,查清楚怎么样能将GPIO配置成输出高电平或低电平。
4、编译代码,烧写到板卡中,重新上电
1.1.2 Linux系统下LED驱动开发步骤
备注:裸机开发与带操作系统的驱动开发对寄存器的操作不同,裸机开发直接操作寄存器地址即可,而带linux操作系统时,必须要进行地址映射(有相关接口可以直接使用),映射完成后,再进行操作即可。
步骤:
1、撰写基本字符驱动框架代码(字符驱动代码框架实现步骤详见第2节:基本驱动框架实现流程)
2、看电路图,查清楚led在哪个管脚,对应哪个GPIO口
3、看芯片手册,查看需要配置的相关寄存器地址——(主要查看需要配置哪些寄存器可以将GPIO配置成输出模式,地址可以在map地址映射章节找到)
4、在驱动入口函数与出口函数中完成寄存器地址映射与取消映射
5、将led操作功能代码填充到驱动框架中
1.2 预备知识:写驱动时涉及的接口、结构体、宏等
| name | 含义 |
|---|---|
| struct file_operations | 结构体:该结构体中告诉每一个接口成员指向某个函数指针,指向的函数指针则为要实现的功能 |
| struct class | 类 |
| module_init(lxxx_init); | 注册驱动入口接口 |
| module_exit(xxx_exit); | 注册驱动出口接口 |
| MODULE_LICENSE(“Dual BSD/GPL”); | 声明 |
| xxx_init() | 驱动入口函数 |
| xxx_exit() | 驱动出口函数 |
| register_chrdev() | 注册模块,告诉内核让内核知道有这个模块,并且返回值为自动分配的主设备号 |
| unregister_chrdev() | 卸载驱动程序,告诉内核 |
| class_create() | 创建一个类 |
| device_create(major, …); | 创建一个设备,使用该接口创建设备以后,在板卡中insmod xxx之后,会自动生成/dev/设备,并且会将major主设备号分配给该设备 |
| device_destroy() | 删除设备,删除设备号 |
| class_destroy | 删除类 |
| ioremap(); | 映射寄存器地址 |
| iounmap(); | 删除映射关系 |
二、基本驱动框架
本小结,作为预备知识,先对字符驱动基本框架做一个简要描述,具体从两个方面进行描述,分别是:
- 2.1 基本驱动框架实现流程(思路)
- 2.2 基本驱动框架结构
2.1 基本驱动框架实现流程(思路)
撰写驱动框架时,按照以下驱动思路来写即可,后续代码实现,即可对比参照此流程来分析代码。
步骤如下:
1、实现结构体:【xxx_fops】。该结构体内部制定了相关的功能代码接口,从而应用能够知道哪个对应功能在哪个接口中实现。
2、实现步骤1中的相关函数(读写函数)框架,此时不需要实现函数具体细节,函数内部为空即可。
3、驱动入口函数:【xxx_init()】
xxx_init() {
(a)在函数前面定义全局变量【int major】,下一步用该变量接收系统自动分配的主设备号。
(b)驱动入口函数中实现注册函数,返回值为系统自动分配的主设备号:major = register_chrdev(0,设备名称,结构体);
}
4、驱动入口修饰函数:【modules_init();】
5、驱动出口函数:【xxx_exit();】
xxx_exit() {
(a)驱动出口函数中实现卸载函数:【unregister_chrdev(主设备号,设备名称)】
}
6、驱动出口修饰函数:【modules_exit();】
7、给sysfs文件系统提供更多驱动信息,这样上层应用因为【udev机制】便可自动创建设备节点,跟底层驱动代码中自动生成的主设备号保持一致,从而关联起来。(备注:udev机制个mdev机制基本可以认为是同一个东西,mdev是udev的简化版本)
(a)定义结构体class:【static struct class *xxx_class;】
(b)驱动入口函数xxx_init()中增加两个接口:【创建类class_create()】和【创建设备device_create()】
(c)驱动出口函数xxx_exit()中增加两个接口:【删除设备device_destroy()】和【删除类class_destroy()】
(d)注意:步骤b和步骤c中两个接口顺序是相反的!!
8、添加Licence:【MODULE_LICENSE(“Dual BSD/GPL”);】。
2.2 基本驱动框架结构
/* 头文件 */
#include xxxxxxx
/* 功能接口1 */
x1()
{
...
}
/* 功能接口2

1257

被折叠的 条评论
为什么被折叠?



