在线课堂:https://www.100ask.net/index(课程观看) 论 坛:http://bbs.100ask.net/(学术答疑) 开 发 板:https://100ask.taobao.com/ (淘宝) https://weidongshan.tmall.com/(天猫) 交流群一:QQ群:869222007(鸿蒙开发/Linux/嵌入式/驱动/资料下载) 交流群二:QQ群:536785813(单片机-嵌入式) 公 众 号:百问科技
版本日期作者说明V12020韦东山技术文档本套视频面向如下三类学员:
- 有Linux驱动开发基础的人, 可以挑感兴趣的章节观看;
- 没有Linux驱动开发基础但是愿意学习的人,请按顺序全部观看,我会以比较简单的LED驱动为例讲解;
- 完全没有Linux驱动知识,又不想深入学习的人, 比如应用开发人员,不得已要改改驱动, 等全部录完后,我会更新本文档,那时再列出您需要观看的章节。
①看原理图:
a.确定引脚;
b.看芯片手册,确定如何操作引脚;
②写驱动程序;
起封装作用;
③写测试程序;
如下原理图,VCC经过一个限流电阻到达LED的一端,再通向芯片的引脚上。
当芯片引脚输出低电平时,电流从高电平流向低电平,LED灯点亮; 当芯片引脚输出高电平时,没有电势差,没有电流流过,LED灯不亮; 从原理图可以看出,控制了芯片引脚,就等于控制了灯。
在Linux里,操作硬件都是统一的接口,比如操作LED灯,需要先open,如果要读取LED状态就调用read,如果要操作LED就调用write函数,也可以通过ioctl去实现。 在驱动里,针对前面应用的每个调用函数,都写一个对应的函数,实现对硬件的操作。
可以看出驱动程序起封装作用,它让应用程序访问硬件变得简单,屏蔽了硬件更加复杂的操作。
如何写驱动程序?①分配一个file_operations结构体; ②设置: a. .open=led_open;把led引脚设置为输出引脚 b. .read=led_write;根据APP传入的值设置引脚状态
③注册(告诉内核),register_chrdev(主设备号,file_operations,name) ④入口函数 ⑤出口函数
在驱动中如何指定LED引脚?有如下三种方法: ①传统方法:在代码led_drv.c中写死; ②总线设备驱动模型: a. 在led_drv.c里分配、注册、入口、出口等 b. 在led_dev.c里指定引脚 ③使用设备树指定引脚 a. 在led_drv.c里分配、注册、入口、出口等 b. 在jz2440.dts里指定引脚
可以看到,’’‘无论何种方法,驱动写法的核心不变,差别在于如何指定硬件资源’’’。 对比下三种方法的优缺点。 假设这样一个情况,某公司用同一个芯片做了两款产品,其中一款是TV(电视盒子),使用Pin1作为LED的指示灯控制引脚,其中一款是Cam(监控摄像头),使用Pin2作为LED的指示灯控制引脚。
TV设备Cam设备优缺点1.传统方法led_drv.c①分配一个file_operations结构体;②设置: a .open=led_open;设置Pin1为输出引脚 b .read=led_read;根据APP传入的值设置引脚状态③注册(告诉内核)④入口函数⑤出口函数led_drv.c①分配一个file_operations结构体;②设置: a. .open=led_open;设置Pin2为输出引脚 b. .read=led_read;根据APP传入的值设置引脚状态③注册(告诉内核)④入口函数⑤出口函数优点:简单缺点:不易扩展,需要重新编译2.总线设备驱动模型led_drv.c①分配/设置/注册 platform_driver;② .probe: a 分配一个file_operations结构体; b .open=led_open;设置平台设备总指定的引脚为输出引脚 .read=led_read;根据APP传入的值设置引脚状态 c注册③ .driver{ .name }led_dev.c①分配/设置/注册 platform_device;② .resource:指定引脚;,name为Pin1led_dev.c①分配/设置/注册 platform_driver;② .resource:指定引脚;,name为Pin2优点:易扩展缺点:稍复杂,冗余代码太多,需要重新编译3.设备树led_drv.c①分配/设置/注册 platform_driver;② .probe: a 分配一个file_operations结构体; b .open=led_open;设置平台设备总指定的引脚为输出引脚 .read=led_read;根据APP传入的值设置引脚状态 c注册③ .driver{ .name }.dts指定资源内核根据dts生成的dtb文件分配/设置/注册platform_device.dts指定资源内核根据dts生成的dtb文件分配/设置/注册platform_device优点:易扩展缺点:稍复杂,冗余代码太多,需要重新编译 第02节_字符设备驱动的传统写法在上一节视频里我们介绍了三种编写驱动的方法,也对比了它们的优缺点,后面我们将使用比较快速的方法写出驱动程序,因为写驱动程序不是我们这套视频的重点,所以尽快的把驱动程序写出来,给大家展示一下。
这节视频我们使用传统的方法编写字符驱动程序,以最简单的点灯驱动程序为示例。 先回顾下写字符设备驱动的五个步骤: 1.2.3.分配/设置/注册file_operations 4.入口 5.出口 所谓分配file_operations,我们可以定义一个file_operations结构体,就不需要分配了。
static struct file_operations myled_oprs = {
.owner = THIS_MODULE, //表示这个模块本身
.open = led_open,
.write = led_write,
.release = led_release,
};
定义好了file_operations结构体,再去入口函数注册结构体。
static int myled_init(void)
{
major = register_chrdev(0, "myled", &myled_oprs);
return 0;
}
第一个参数:主设备号写0,让系统为我们分配; 第二个参数:设置名字,没有特殊要求; 第三个参数:file_operations结构体; 对应的出口操作进行相反向操作:
static void myled_exit(void)
{
unregister_chrdev(major, "myled");
}
然后用宏module_init对入口、出口函数进行修饰,表示它们和普通函数不一样:
module_init(myled_init);
module_exit(myled_exit);
module_init(myled_init)
实际就是int init_module(void) attribute((alias(“myled_init”)))
,表示myled_init
的别名是init_module
,以后就可以使用init_module
来引用myled_init
。
此外,还要加上GPL协议:
MODULE_LICENSE("GPL");
写到这里,驱动程序的框架已经搭建起来了,接下来实现具体的硬件操作函数:led_open()和led_write()。 在led_open()里把对应的引脚配置为输出引脚,在led_write()根据应用程序传入的数据点灯,让其输出高电平或低电平。 为了让程序更具有扩展性,把GPIO的寄存器放在一个数组里:
static unsigned int gpio_base[] = {
0x56000000, /* GPACON */
0x56000010, /* GPBCON */
0x56000020, /* GPCCON */
0x56000030, /* GPDCON */
0x56000040, /* GPECON */
0x56000050, /* GPFCON */
0x56000060, /* GPGCON */
0x56000070, /* GPHCON */
0, /* GPICON */
0x560000D0, /* GPJCON */
};
定义好了引脚的组,还得确定使用该组的哪个引脚,使用宏来确定哪个引脚:
#define S3C2440_GPA(n) (0
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?