匿名
未登录
登录
百问网嵌入式Linux wiki
搜索
查看“ELADCMSecondEditionChapterFivePartXIV”的源代码
来自百问网嵌入式Linux wiki
名字空间
页面
讨论
更多
更多
页面选项
Read
查看源代码
历史
←
ELADCMSecondEditionChapterFivePartXIV
因为以下原因,您没有权限编辑本页:
您所请求的操作仅限于该用户组的用户使用:
用户
您可以查看与复制此页面的源代码。
__NOTITLE__ ==查询方式的按键驱动程序_编写框架== ===LED驱动回顾=== : 对于LED,APP调用open函数导致驱动程序的led_open函数被调用。在里面,把GPIO配置为输出引脚。安装驱动程序后并不意味着会使用对应的硬件,而APP要使用对应的硬件,必须先调用open函数。所以建议在驱动程序的open函数中去设置引脚。 : APP继续调用write函数传入数值,在驱动程序的led_write函数根据该数值去设置GPIO的数据寄存器,从而控制GPIO的输出电平。 : 怎么操作寄存器?从芯片手册得到对应寄存器的物理地址,在驱动程序中使用ioremap函数映射得到虚拟地址。驱动程序中使用虚拟地址去访问寄存器。 :: [[File:EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive_045.png|600px]] ===按键驱动编写思路=== : GPIO按键的原理图一般有如下2种: :: [[File:EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive_115.png|400px]] : 按键没被按下时,上图中左边的GPIO电平为高,右边的GPIO电平为低。 : 按键被按下后,上图中左边的GPIO电平为低,右边的GPIO电平为高。 : 编写按键驱动程序最简单的方法如下图所示: :: [[File:EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive_116.png|600px]] : 回顾一下编写驱动程序的套路: :: [[File:EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive_040.png|600px]] : 对于使用查询方式的按键驱动程序,我们只需要实现button_open、button_read。 ===编程:先写框架=== : 我们的目的写出一个容易扩展到各种芯片、各种板子的按键驱动程序,所以驱动程序分为上下两层: :: ① button_drv.c分配/设置/注册file_operations结构体 ::: 起承上启下的作用,向上提供button_open,button_read供APP调用。 ::: 而这2个函数又会调用底层硬件提供的p_button_opr中的init、read函数操作硬件。 :: ② board_xxx.c分配/设置/注册button_operations结构体 ::: 这个结构体是我们自己抽象出来的,里面定义单板xxx的按键操作函数。 : 这样的结构易于扩展,对于不同的单板,只需要替换board_xxx.c提供自己的button_operations结构体即可。 :: [[File:EmbeddedLinuxApplicationDevelopmentCompleteManualSecondEditionChapterFive_117.png|800px]] : 使用GIT下载所有源码后,本节源码位于如下目录: <syntaxhighlight lang="bash"> 01_all_series_quickstart\04_快速入门(正式开始)\ 02_嵌入式Linux驱动开发基础知识\source\ 04_button_drv\01_button_drv_template </syntaxhighlight> ====把按键的操作抽象出一个button_operations结构体==== : 首先看看button_drv.h,它定义了一个button_operations结构体,把按键的操作抽象为这个结构体: <syntaxhighlight lang="C"> 04 struct button_operations { 05 int count; 06 void (*init) (int which); 07 int (*read) (int which); 08 }; 09 10 void register_button_operations(struct button_operations *opr); 11 void unregister_button_operations(void); 12 </syntaxhighlight> : 再看看board_xxx.c,它实现了一个button_operations结构体,代码如下。 : 第 45 行调用register_button_operations函数,把这个结构体注册到上层驱动中。 <syntaxhighlight lang="C"> 37 static struct button_operations my_buttons_ops ={ 38 .count = 2, 39 .init = board_xxx_button_init_gpio, 40 .read = board_xxx_button_read_gpio, 41 }; 42 43 int board_xxx_button_init(void) 44 { 45 register_button_operations(&my_buttons_ops); 46 return 0; 47 } 48 </syntaxhighlight> ====驱动程序的上层:file_operations结构体==== : 上层是button_drv.c,它的核心是file_operations结构体,首先看看入口函数,代码如下。 : 第 83 行向内核注册一个file_operations结构体。 : 第 85 行创建一个class,但是该class下还没有device,在后面获得底层硬件的信息时再在class下创建device:这只是用来创建设备节点,它不是驱动程序的核心。 <syntaxhighlight lang="C"> 81 int button_init(void) 82 { 83 major = register_chrdev(0, "100ask_button", &button_fops); 84 85 button_class = class_create(THIS_MODULE, "100ask_button"); 86 if (IS_ERR(button_class)) 87 return -1; 88 89 return 0; 90 } 91 </syntaxhighlight> : 再来看看button_drv.c中file_operations结构体的成员函数,代码如下。 : 第 34 、 44 行都用到一个button_operations指针,它是从何而来? <syntaxhighlight lang="C"> 28 static struct button_operations *p_button_opr; 29 static struct class *button_class; 30 31 static int button_open (struct inode *inode, struct file *file) 32 { 33 int minor = iminor(inode); 34 p_button_opr->init(minor); 35 return 0; 36 } 37 38 static ssize_t button_read (struct file *file, char __user *buf, size_t size, loff_t *off) 39 { 40 unsigned int minor = iminor(file_inode(file)); 41 char level; 42 int err; 43 44 level = p_button_opr->read(minor); 45 err = copy_to_user(buf, &level, 1); 46 return 1; 47 } 48 49 50 static struct file_operations button_fops = { 51 .open = button_open, 52 .read = button_read, 53 }; </syntaxhighlight> : 上面第34、44行都用到一个button_operations指针,来自于底层硬件相关的代码。 : 底层代码调用register_button_operations函数,向上提供这个结构体指针。 : register_button_operations函数代码如下,它还根据底层提供的button_operations调用device_create,这是创建设备节点(第62行)。 <syntaxhighlight lang="C"> 55 void register_button_operations(struct button_operations *opr) 56 { 57 int i; 58 59 p_button_opr = opr; 60 for (i = 0; i < opr->count; i++) 61 { 62 device_create(button_class, NULL, MKDEV(major, i), NULL, "100ask_button%d", i); 63 } 64 } 65 </syntaxhighlight> ===测试=== : 这只是一个示例程序,还没有真正操作硬件。测试程序操作驱动程序时,只会导致驱动程序中打印信息。 : 首先设置交叉工具链,修改驱动Makefile中内核的源码路径,编译驱动和测试程序。 : 启动开发板后,通过NFS访问编译好驱动程序、测试程序,就可以在开发板上如下操作了: <syntaxhighlight lang="bash"> # insmod button_drv.ko // 装载驱动程序 [ 435.276713] button_drv: loading out-of-tree module taints kernel. # insmod board_xxx.ko # ls /dev/100ask_button* -l // 查看设备节点 crw------- 1 root root 236, 0 Jan 18 08:57 /dev/100ask_button0 crw------- 1 root root 236, 1 Jan 18 08:57 /dev/100ask_button1 # ./button_test /dev/100ask_button0 // 读按键 [ 450.886180] /home/book/source/04_button_drv/01_button_drv_template/board_xxx.c board_xxx_button_init_gpio 28, init gpio for button 0 [ 450.910915] /home/book/source/04_button_drv/01_button_drv_template/board_xxx.c board_xxx_button_read_gpio 33, read gpio for button 0 get button : 1 // 得到数据 </syntaxhighlight> ===课后怎业=== : 合并LED、BUTTON框架驱动程序:01_led_drv_template、01_button_drv_template,合并为:gpio_drv_template [[Category:嵌入式Linux应用开发完全手册第2版]]
返回至
ELADCMSecondEditionChapterFivePartXIV
。
导航
导航
WIKI首页
官方店铺
资料下载
交流社区
所有页面
所有产品
MPU-Linux开发板
MCU-单片机开发板
Linux开发系列视频
单片机开发系列视频
所有模块配件
Wiki工具
Wiki工具
特殊页面
页面工具
页面工具
用户页面工具
更多
链入页面
相关更改
页面信息
页面日志