imx6ul添加keyboard Matrix矩阵键盘
文章目录
- 一、功能实现(代码)
- 1.1 添加编译选项
- 1.2 添加设备树节点
- 1.2.1 添加kpp节点
- 1.2.2 在iomuxc中添加pinctrl_复用节点
- 1.2.3 屏蔽其他设备节点占用的键盘GPIO
- 1.3 内核源码中按键映射写法
- 二、硬件电路
- 2.1 上拉模式:输入GPIO硬件设计为上拉
- 2.2 下拉模式:输入GPIO硬件设计为下拉
- 三、调试步骤
- 3.1 调试工具推荐
- 3.1.1 hexdump命令
- 3.1.2 类hexdump工具源码(极力推荐,使用起来很方便)
- 3.2 调试流程
- 3.2.1 大致调试流程如下图
- 3.2.2 调试流程具体步骤描述如下
- 3.3 源码中添加调试log小技巧
- 四、调试过程中遇到的坑
- 五、Matrix内核源码逻辑分析
- 5.1 Matrix整体功能逻辑图
- 5.2 键盘matrix_keypad_scan()函数扫描原理
- 5.2.1 键盘示意图
- 5.2.2 键盘和保存GPIO状态的数组定义
- 5.2.3 matrix_keypad_scan() 函数源码解析
- 5.3 其他几个重要函数
- 5.3.1 matrix_keypad_parse_dt()函数源码解析
- 5.3.2 matrix_keypad_init_gpio()函数源码解析
- 六、附加知识
- 6.1 Linux内核中的事件类型
Linux内核中按键驱动分为:较为复杂的【矩阵键盘keyboard Matrix】和简单的【按键驱动】。
【Matrix矩阵键盘】一般适用于多行多列多按键的情况,源码文件为:drivers/input/keyboard/matrix_keypad.c
【GPIO按键驱动】一般适用于少量按键,一个GPIO口对应一个按键的情况,源码文件为:driers/input/keyboard/gpio_keys.c
本次在imx6ul中实现矩阵键盘功能:Linux内核版本为4.1.15,cpu为imx6ul。
以下对矩阵键盘原理、源码、调试步骤等逐一进行分析,简单的单按键就暂时不写了。
一、功能实现(代码)
具体开发流程如下:
1.1 添加编译选项
内核源码根目录下执行make menuconfig,选中如下几个选项来添加Matrix相关编译选项:
Device Drivers ---> Input device support ---> <*> Event debugging (选中)[*] Keyboards ---> <*> GPIO Buttons(选中)<*> GPIO driven matrix keypad support(选中)<*> IMX keypad support(选中)
选中后,save保存,exit退出,使用【make zImage】编译生成新的zImage,路径为:【arch/arm/boot/】
lsy@ubuntu18:~/linux-4.1.15$ ls arch/arm/boot/
bootp compressed dts Image install.sh Makefile zImage
将新的zImage拷贝并烧写到板卡中,重启板卡,然后正式开始进行开发工作。
备注:如果觉得编译整个zImage麻烦,也可以使用【make modules】单独编译模块,将drivers/input/keyboard/matrix_keypad.c文件编译生成对应的.ko模块,然后使用insmod或者modprob加载到板卡内核中来进行调试也行。
1.2 添加设备树节点
此处以【8行*3列】矩阵键盘为例
一定注意:Linux内核中定义矩阵键盘必须且只能为:【行GPIO输入,列GPIO输出】!!!不能搞错!!!
1.2.1 添加kpp节点
备注:本示例中的linux,keymap按键映射以较为容易理解、且直观的方式实现,内核驱动源码中的示例以及网上很多demo中写法都是形如0x02010058的写法,这样不太直观,不好理解,后面会对这种写法含义做出说明,两种写法都是可以的。
/*lsy*/
&kpp {compatible = "gpio-matrix-keypad";pinctrl-names = "default";pinctrl-0 = <&pinctrl_key>;debounce-delay-ms = <20>; /*防反跳延时,即:去抖延时*/col-scan-delay-us = <400>; /*列扫描延时*//*8行*/row-gpios = <&gpio2 11 GPIO_ACTIVE_LOW&gpio2 12 GPIO_ACTIVE_LOW&gpio2 10 GPIO_ACTIVE_LOW&gpio2 8 GPIO_ACTIVE_LOW&gpio2 9 GPIO_ACTIVE_LOW&gpio2 13 GPIO_ACTIVE_LOW&gpio2 14 GPIO_ACTIVE_LOW&gpio2 15 GPIO_ACTIVE_LOW>;/*3列*/col-gpios = <&gpio2 16 GPIO_ACTIVE_LOW&gpio2 17 GPIO_ACTIVE_LOW&gpio2 19 GPIO_ACTIVE_LOW>;/* 举例释义:MATRIX_KEY(0x2, 0x1, KEY_X)表示将【第2行1列的按键】映射为【KEY_X】 */linux,keymap = </*row0*/MATRIX_KEY(0x0, 0x0, KEY_LEFT)MATRIX_KEY(0x0, 0x1, KEY_DOWN)/* row1 */MATRIX_KEY(0x1, 0x0, KEY_UP)MATRIX_KEY(0x1, 0x1, KEY_RIGHT)/* row2 */MATRIX_KEY(0x2, 0x0, KEY_M)MATRIX_KEY(0x2, 0x1, KEY_X)MATRIX_KEY(0x2, 0x2, KEY_ENTER)/*row3*/MATRIX_KEY(0x3, 0x0, KEY_N)MATRIX_KEY(0x3, 0x1, KEY_O)MATRIX_KEY(0x3, 0x2, KEY_I)/*row4*/MATRIX_KEY(0x4, 0x0, KEY_7)MATRIX_KEY(0x4, 0x1, KEY_8)MATRIX_KEY(0x4, 0x2, KEY_9)/*row5*/MATRIX_KEY(0x5, 0x0, KEY_4)MATRIX_KEY(0x5, 0x1, KEY_5)MATRIX_KEY(0x5, 0x2, KEY_6)/*row6*/MATRIX_KEY(0x6, 0x0, KEY_1)MATRIX_KEY(0x6, 0x1, KEY_2)MATRIX_KEY(0x6, 0x2, KEY_3)/*row7*/MATRIX_KEY(0x7, 0x0, KEY_MINUS)MATRIX_KEY(0x7, 0x1, KEY_0)MATRIX_KEY(0x7, 0x2, KEY_DOT)>;gpio-activelow;status = "okay";
};
1.2.2 在iomuxc中添加pinctrl_复用节点
&iomuxc {....../*lsy*/pinctrl_key: kppgrp{fsl,pins = </* row 8行 */MX6UL_PAD_ENET2_TX_DATA0__GPIO2_IO11 0xb0b1MX6UL_PAD_ENET2_TX_DATA1__GPIO2_IO12 0xb0b1MX6UL_PAD_ENET2_RX_EN__GPIO2_IO10 0xb0b1MX6UL_PAD_ENET2_RX_DATA0__GPIO2_IO08 0xb0b1MX6UL_PAD_ENET2_RX_DATA1__GPIO2_IO09 0xb0b1MX6UL_PAD_ENET2_TX_EN__GPIO2_IO13 0xb0b1MX6UL_PAD_ENET2_TX_CLK__GPIO2_IO14 0xb0b1MX6UL_PAD_ENET2_RX_ER__GPIO2_IO15 0xb0b1/* col 3列 */MX6UL_PAD_SD1_CMD__GPIO2_IO16 0x70a1MX6UL_PAD_SD1_CLK__GPIO2_IO17 0x70a1MX6UL_PAD_SD1_DATA1__GPIO2_IO19 0x70a1>;};.......
};
1.2.3 屏蔽其他设备节点占用的键盘GPIO
非常重要:dts中所有键盘使用的GPIO在其他设备节点要是被占用,要全部屏蔽掉,方法此处不再赘述。
1.3 内核源码中按键映射写法
网上很多demo,包括驱动源码中的demo按键映射写法是如下这样写的,这样写也是可以的,只是没有上述代码看起来直观,一眼就能看出来映射的哪个按键而已。
linux中的按键映射文件为:include/uapi/linux/input.h文件,该文件中有键盘所有按键对应的十进制数,查到以后需要换算成十六进制,然后写成下述格式。
linux,keymap = </*row0*/0x00000069 /*Qt.Key_Left -> HEX:69 -> DEC:105 -> KEY_LEFT */0x0001006C /*Qt.Key_Down -> HEX:6C -> DEC:108 -> KEY_DOWN *//*row1*/0x01000067 /*Qt.Key_Up -> HEX:67 -> DEC:103 -> KEY_UP */0x0101006A /*Qt.Key_Right -> HEX:6A -> DEC:106 -> KEY_RIGHT *//*row2*/0x0200004D /*Qt.Key_M -> HEX:4D -> DEC:77 -> KEY_KP6 */0x02010058 /*Qt.Key_X -> HEX:58 -> DEC:88 -> KEY_F12 */0x0202001C /*Qt.Key_Return-> HEX:1C -> DEC:28 -> KEY_ENTER *//*row3*/0x0300004E /*Qt.Key_N -> HEX:4E -> DEC:78 -> KEY_KPPLUS*/0x0301004F /*Qt.Key_O -> HEX:4F -> DEC:79 -> KEY_KP1 */0x03020049 /*Qt.Key_I -> HEX:49 -> DEC:73 -> KEY_KP9 *//*row4*/0x04000008 /*Qt.Key_7 -> HEX:08 -> DEC: 8 -> KEY_7 */0x04010009 /*Qt.Key_8 -> HEX:09 -> DEC: 9 -> KEY_8 */0X0402000A /*Qt.Key_9 -> HEX:0A -> DEC:10 -> KEY_9 *//*row5*/0x05000005 /*Qt.Key_4 -> HEX:05 -> DEC: 5 -> KEY_4 */0x05010006 /*Qt.Key_5 -> HEX:06 -> DEC: 6 -> KEY_5 */0x05020007 /*Qt.Key_6 -> HEX:07 -> DEC: 7 -> KEY_6 *//*row6*/0x06000002 /*Qt.Key_1 -> HEX:02 -> DEC: 2 -> KEY_1 */0x06010003 /*Qt.Key_2 -> HEX:03 -> DEC: 3 -> KEY_2 */0x06020004 /*Qt.Key_3 -> HEX:04 -> DEC: 4 -> KEY_3 *//*row7*/0x0700002D /*Qt.Key_Minux -> HEX:2D -> DEC:45 -> KEY_X */0x0701000B /*Qt.Key_0 -> HEX:0B -> DEC:11 -> KEY_0 */0x0702002E /*Qt.Key_Period-> HEX:2E -> DEC:46 -> KEY_C */>;
二、硬件电路
2.1 上拉模式:输入GPIO硬件设计为上拉
原电路图包含其他功能,此处不贴原图了,就上个示意图就好,每个交叉口代表一个按键。
按键原理此处不再赘述,只说大概(matrix_keypad.c源码中的键盘原理分析见后面)。
流程大致如下:
- 1、行:全部设置为输入,且上拉,则默认总是检测到高电平。
- 2、列:从左到右,逐列输出低电平(每次只能有一列是低电平)
- 3、行:从上到下,逐行检测哪行出现低电平。
- 4、检测到低电平的行,对应此时激活的列,就可以判断哪个按键导通(按下)了。
备注:
Linux内核本身自带的Matrix机制比这个稍微复杂一些,但是:检测当前每个按键的状态(按下还是弹起)逻辑跟这个是一样的,后面会详细讲到。
大致来讲:
Linux内核定义2个数组,分别用于保存:【上一次按键状态last_key_state[]】和【本次按键状态new_state[]】 ,通过对比两次按键状态发现状态改变的按键,并逐一上报内核。(按键按下或弹起都算状态改变)
2.2 下拉模式:输入GPIO硬件设计为下拉
另外,上图电路图中行(输入)为上拉,如果电路硬件设计为下拉的话,则设备树中可以做如下修改:
一、修改默认电平:
将设备树节点中行和列的【GPIO_ACTIVE_LOW】全部修改为【GPIO_ACTIVE_HIGH】
将【gpio-activelow;】修改为【gpio-activehigh;】二、调整去抖延时和列扫描延时
debounce-delay-ms = <100>; /*防反跳延时,即:去抖延时*/
col-scan-delay-us = <2000>; /*列扫描延时*/
三、调试步骤
3.1 调试工具推荐
调试按键的时候,/dev/input/路径下:event0是矩阵按键设备,event1是普通按键设备,event2 是电阻屏触摸设备
3.1.1 hexdump命令
使用hexdump命令调试,按键按下以后产生的数据,以及含义解析如下:
root@imx6ulevk:~# hexdump /dev/input/event0
/* 事件序号 tv_sec tv_usec type code value */0000000 ef7a 59c3 16e6 0006 0004 0004 0000 00000000010 ef7a 59c3 16e6 0006 0001 0069 0001 0000 <---------0x69按键按下0000020 ef7a 59c3 16e6 0006 0000 0000 0000 00000000030 ef7a 59c3 c3f9 0007 0004 0004 0000 00000000040 ef7a 59c3 c3f9 0007 0001 0069 0000 0000 <---------0x69按键弹起0000050 ef7a 59c3 c3f9 0007 0000 0000 0000 0000
这里只说右边4列含义:
右起第4列【type列】:事件类型,0001表示产生了按键事件,其他类型见【附加知识小节:Linux内核中的事件类型】右起第3列【code列】:表示十六进制编码,如果是按键事件的话,则对应key的十六进制值,在内核源码include/uapi/linux/input.h中可以查到每个按键的十进制编码。最右边2列【value列】:按键的值,0001 0000表示按下,0000 0000表示弹起
3.1.2 类hexdump工具源码(极力推荐,使用起来很方便)
该工具使用起来非常人性化,不像上面那样,每个按键,键值啥的十六进制、十进制还要转换,而且很难看。
工具源码见另一篇博客:hexdump调试小工具——获取event事件信息、键盘按键信息等
或github链接:【https://github.com/lishiyuan/tools_hexdump】
使用方法为:
将源码进行交叉编译,生成可执行文件read_linux_key_value(名字看自己喜好了),然后拷贝到板卡,使用命令
//Linux中交叉编译 arm-linux-gnueabihf-gcc -o read_linux_key_value xxx.c//拷贝read_linux_key_value到板卡执行下面命令, ./read_linux_key_value 0 <----0表示设备event0
该工具使用效果如下:
root@imx6ulevk:~# ./read_linux_key_value 0 /dev/input/event0evdev version: 1.0.1name: 20b8000.kppfeatures: unknown keys/buttons reserved repeat /dev/input/event0: open, fd = 3 Thu Sep 21 17:27:47 2017.869136 type 0x0004; code 0x0004; value 0x00000000; Misc Thu Sep 21 17:27:47 2017.869136 type 0x0001; code 0x0069; value 0x00000001; Key 105 (0x69) press Thu Sep 21 17:27:47 2017.869136 type 0x0000; code 0x0000; value 0x00000000; Thu Sep 21 17:27:47 2017.998944 type 0x0004; code 0x0004; value 0x00000000; Misc Thu Sep 21 17:27:47 2017.998944 type 0x0001; code 0x0069; value 0x00000000; Key 105 (0x69) release Thu Sep 21 17:27:47 2017.998944 type 0x0000; code 0x0000; value 0x00000000;
是不是很爽!
3.2 调试流程
3.2.1 大致调试流程如下图
备注:具体流程分析见下一小节
3.2.2 调试流程具体步骤描述如下
先将每个GPIO分别设置为普通IO口,验证输入输出均可控(不管是采用裸机方式还是GPIO export shell命令的方式都行,这里不再赘述方法)。
如果发现有GPIO不可控,则排查dts中是否有别的地方复用了该引脚,要全部屏蔽掉,或者检查硬件电路设计是否无误。
板卡【/dev/input/】目录下是否正确生成event0设备:
若否,则首先排查自己添加的kpp节点中【行列属性】以及【linux,keymap属性】中行列描述是否正确;
如果kpp正确,则排查板卡【/sys/firmware/devicetree/base】目录下设备kpp节点是否符合预期,自己的加的kpp是否生效:
备注:
【/sys/firmware/devicetree/base】目录就是整个dts,按照最顶层设备树文件imx6ul.dtsi中的设备节点逐个展开,整个dts的节点以及属性全部展现在sysfs中。此外,还有一个软连接也是指向该路径的【ls /proc/device-tree】。
具体可以查阅该博客【转:致驱动工程师的一封信】。
按照imx6ul设备树描述,追到最顶层设备树【imx6ul.dtsi】文件中,发现kpp节点描述有如下层级关系:
//imx6ul-14x14-evk.dts中引用了imx6ul.dtsi //追到imx6ul.dtsi中,该文件结构如下:/ {.......soc {.......aips1: aips-bus@02000000 {.......kpp: kpp@020b8000 {.......} .......}} }
因此在板卡【/sys/firmware/devicetree/base/soc/aips-bus@02000000】目录下查阅该节点是否包含自己定义的节点信息,如果否,则说明自己定义的节点必定有问题,都未生效,功能肯定不对。
使用上面提到【hexdump /dev/input/event0】命令或【类hexdump工具】来进行功能验证,验证方法见上一小节【调试工具推荐】,如果发现按键数据不对,继续后续步骤
观察按键有无反应,用示波器抓波是否存在波形,分析波形是否符合预期;
阅读内核源码,在内核源码中增加log,打印输出,查看上报的数据是否正确;
备注:源码路径为【drivers/input/keyboard/matrix_keypad.c】,通常情况下,源码是无需更改的,除非自己要定制特殊功能!毕竟经过全世界各种大佬们的验证,肯定比自己牛x多了,出现逻辑问题的几率非常非常小。一般情况最多就是在源码中增加一些printk打印变量log,观测value,帮自己分析问题,定位问题。
具体问题具体分析,见下一节【调试过程中遇到的坑】
3.3 源码中添加调试log小技巧
在源码c文件中添加log的时候,这里推荐一个小技巧,在文件顶部定义如下宏:
//#define lsydebug(fmt,args...) printk(KERN_ERR "[lsy]:%s:%d:" fmt,__func__,__LINE__,##args) #define lsydebug(fmt,args...)
这样代码中添加log的时候可以像这样:
......memset(new_state, 0, sizeof(new_state));lsydebug("row = pdata->num_row_gpios = %d\r\n", pdata->num_row_gpios);lsydebug("col = pdata->num_col_gpios = %d\r\n", pdata->num_col_gpios);......
当调试完毕以后,直接保留#define lsydebug(fmt,args…)即可,调试log也不会出现了,不用一个个全删除,其他好处自行体会。
备注:或者也可以这样:
//#define DEBUG 1#ifdef DEBUG #define lsydebug(fmt,args...) printk(KERN_ERR "[lsy]:%s:%d:" fmt,__func__,__LINE__,##args) #else #define lsydebug(fmt,args...) #endif
四、调试过程中遇到的坑
通常情况,源码matrix_keypad.c无需做修改,仅仅在调试的时候需要增加一些log信息用于观测变量值而已。也就是说配置内核自带matrix矩阵键盘功能只需要配置dts即可。
配置完dts后,如果无误,会在板卡/dev/input/目录下生成event0设备,如果发现该目录下并未生成该设备,则有很大一种可能性是:dts中配置节点行列有误,导致内核无法识别matrix,所以未生成event0设备,排查思路如下:
- 查看dts中节点【row-gpios、col-gpios行列描述】与【linux,keymap属性行列描述】是否正确,具体规则见后面几条。
- 查看【/sys/firmware/devicetree/base/soc/aips-bus@02000000】目录下查阅kpp节点是否包含自己定义的节点信息,若否,则必有问题,节点都未生效,功能肯定不对。
设备树中:行、列一定不能反!Linux内核规定matrix功能:行必须为输入,列必须为输出。
row-gpios成员为行,
col-gpios成员为列。
设备树中:linux,keymap属性键值映射,行列也不能反,反了就乱。
正确格式如下两种任选其一都行(推荐第一种,容易理解,且更加直观):
(1)MATRIX_KEY(0x3, 0x1, KEY_O):表示将第3行,第1列按键映射为KEY_O,
(2)0x03020069:表示将第3行,第2列按键映射为0x69对应的按键。按键键值可以在include/uapi/linux/input.h中找到,需要注意:该文件中#define键值为十进制。比如0x69对应十进制为105,在文件中查得105对应按键:KEY_LEFT
0x 00 00 0069 十六进制 行序号 列序号 按键十六进制键值 上述编译选项menuconfig中该打开的功能必须打开。
设备树中:延时一定要调整合适,否则可能会出现按下一个按键,整行按键都被上报到event设备中。
debounce-delay-ms = <20>; /*防反跳延时,即:去抖延时*/ col-scan-delay-us = <400>; /*列扫描延时*/
设备树中:kpp节点中如果定义了属性:gpio-activelow,就是告诉软件层面【低电平有效】,激活端口时,必须设置为低电平。应用在函数【__activate_col()】中。
五、Matrix内核源码逻辑分析
源码路径为【drivers/input/keyboard/matrix_keypad.c】,通常情况下,源码是无需更改的,除非自己要定制特殊功能!
此处对源码逻辑、扫描原理、以及代码中部分重要函数进行分析:
5.1 Matrix整体功能逻辑图
后续补充
5.2 键盘matrix_keypad_scan()函数扫描原理
5.2.1 键盘示意图
为了简单,以【3行,4列】键盘为例:
5.2.2 键盘和保存GPIO状态的数组定义
键盘原理中,列GPIO必须配置为输出,行GPIO必须配置为输入,注意一定更不能搞反!!
以【3行,4列】键盘为例,其中:
列GPIO:输出,4列
行GPIO:输入,3行
内核源码中定义了2个数组:【旧状态】数组last_key_state[],和【新状态】数组new_state[],都是u32类型(整型int)。
其中:
数组 含义 【旧状态数组 last_key_state[]】 用来保存上一次每行GPIO的电平状态 【新状态数组 new_state[]】 用来保存此次每行GPIO的电平状态 【数组成员】 键盘有m列,则数组就都有m个成员(数组角标为col)
一个数组成员,对应一列
如:
new_state[0] 用于保存第0列电平状态
new_state[1] 用于保存第1列电平状态
new_state[2] 用于保存第2列电平状态
new_state[3] 用于保存第3列电平状态
last_key_state[col]同理【数组每个成员bit位】 对应键盘n行,每个bit位的0/1代表每一行的状态(成员bit为row)
一个bit,对应一行
如:
每个成员的 bit0 用于保存第0行输入电平状态
每个成员的 bit1 用于保存第1行输入电平状态
每个成员的 bit2 用于保存第2行输入电平状态
last_key_state[col]每个bit位同理查找对应按键原理 这样,
1、函数外层for循环—>先匹配到数组成员(列),
2、然后函数函数内层for循环—>通过移位方式,匹配到某个bit位(行),
3、则自然而然的查找到【某列-某行】的按键。此例子中:
last和new数组分别都有4个成员(因为有4列),每个成员有效位为bit0、bit1、bit2(共3个有效bit,因为有3行)。
每个成员有效bit为代表了对应行的输入电平状态。
如下示意图:每一列就是一个u32位的变量,此例子中低3位有效,表示有效行3行。
LAST:上一个状态last_key_state[0] last_key_state[1] last_key_state[2] last_key_state[3]col0 col1 col2 col3row0 -- bit[0] bit[0] bit[0] bit[0]row1 -- bit[1] bit[1] bit[1] bit[1]row2 -- bit[2] bit[2] bit[2] bit[2]NEW:当前状态new_state[0] new_state[1] new_state[2] new_state[3]col0 col1 col2 col3row0 -- bit[0] bit[0] bit[0] bit[0]row1 -- bit[1] bit[1] bit[1] bit[1]row2 -- bit[2] bit[2] bit[2] bit[2]
5.2.3 matrix_keypad_scan() 函数源码解析
该函数源码大体结构如下:
主要是2个双层for循环:
第一个双层for循环用于获取当前所有按键状态,保存在new_state[]矩阵中;
第二个双层for循环将new_state[]和last_key_state[]做比较,匹配每一个状态改变的按键,并将每个按键状态都上报input子系统。
备注:
第二个双层for循环中有一行异或操作需要注意:
bits_changed = keypad->last_key_state[col] ^ new_state[col];
不要把bits_changed的含义理解错了:
注意亦或的含义:不同为1,所以该条语句执行完以后,bits_changed对应bit是1的话,仅仅是标识哪一行电平状态改变了,并不是代表该行电平!!!该行电平在new_state[col]的对应bit位保存!!!下面是matrix_keypad_scan()函数的源码,我在里面加了一些注释如下:
/** This gets the keys from keyboard and reports it to input subsystem*/ static void matrix_keypad_scan(struct work_struct *work) {struct matrix_keypad *keypad = container_of(work, struct matrix_keypad, work.work);struct input_dev *input_dev = keypad->input_dev;const unsigned short *keycodes = input_dev->keycode;const struct matrix_keypad_platform_data *pdata = keypad->pdata;uint32_t new_state[MATRIX_MAX_COLS];int row, col, code;dbg("Etnry scan!\r\n");/* de-activate all columns for scanning */activate_all_cols(pdata, false); /*反激活全部列,扫描结束后再全部激活*/memset(new_state, 0, sizeof(new_state)); /*先清0当前按键状态矩阵,避免检测错误*//* assert each column and read the row status out *//* 一、该双层for循环:按顺序依次改变每列电平并获取对应行电平,保存在new矩阵中,后面和last矩阵对比判断是否状态改变 */for (col = 0; col < pdata->num_col_gpios; col++) {activate_col(pdata, col, true); /*激活某列*/for (row = 0; row < pdata->num_row_gpios; row++) {new_state[col] |= row_asserted(pdata, row) ? (1 << row) : 0; /*获取每一行的电平,存放在对应bit*/}activate_col(pdata, col, false); /*反激活某列*/}/* 二、该双层for循环:将new和last作比较,每匹配到一个状态改变的按键,就向event设备发送一次数据*/for (col = 0; col < pdata->num_col_gpios; col++) {uint32_t bits_changed;/** 先直接对整个变量(全部行状态)异或操作,* 如果任意一个bit(即:任意一行)状态改变,bits_changed必不为0* 说明至少有一行状态有改变*/bits_changed = keypad->last_key_state[col] ^ new_state[col];if (bits_changed == 0) /*若所有行均无变化,则继续下一列*/continue;for (row = 0; row < pdata->num_row_gpios; row++) {if ((bits_changed & (1 << row)) == 0) /*通过row的移位,逐个判断每个bit位(每行)的状态是否有变化*/continue; /*若当前行无变化,则继续下一行*//*每碰到一个有变化的bit(即:变化行),则开始上报内核*/code = MATRIX_SCAN_CODE(row, col, keypad->row_shift); /*确定变化的行列,就取出dts中定义的code*/input_event(input_dev, EV_MSC, MSC_SCAN, code); /*向输入子系统上报输入设备产生的事件*/input_report_key(input_dev, keycodes[code], new_state[col] & (1 << row)); /*上报按键事件*/}}input_sync(input_dev); /*通知接收者,一个报告上报完毕。至此通过双层for循环,上报了所有按下的按键*/memcpy(keypad->last_key_state, new_state, sizeof(new_state)); /*一次全键盘扫描完毕,当前状态覆盖上次状态*/activate_all_cols(pdata, true); /*扫描结束,激活全部列*//* Enable IRQs again */spin_lock_irq(&keypad->lock);keypad->scan_pending = false; enable_row_irqs(keypad);spin_unlock_irq(&keypad->lock); }
5.3 其他几个重要函数
5.3.1 matrix_keypad_parse_dt()函数源码解析
该函数作用为:解析dts中矩阵键盘的配置
#ifdef CONFIG_OF /*解析dts中矩阵键盘的配置*/ static struct matrix_keypad_platform_data *matrix_keypad_parse_dt(struct device *dev) {struct matrix_keypad_platform_data *pdata;struct device_node *np = dev->of_node; unsigned int *gpios;int i, nrow, ncol;if (!np) {dev_err(dev, "device lacks DT data\n");return ERR_PTR(-ENODEV);}pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);if (!pdata) {dev_err(dev, "could not allocate memory for platform data\n");return ERR_PTR(-ENOMEM);}pdata->num_row_gpios = nrow = of_gpio_named_count(np, "row-gpios"); /*解析获取行、列总数量*/pdata->num_col_gpios = ncol = of_gpio_named_count(np, "col-gpios");if (nrow <= 0 || ncol <= 0) {dev_err(dev, "number of keypad rows/columns not specified\n");return ERR_PTR(-EINVAL);}if (of_get_property(np, "linux,no-autorepeat", NULL))pdata->no_autorepeat = true;if (of_get_property(np, "linux,wakeup", NULL))pdata->wakeup = true;if (of_get_property(np, "gpio-activelow", NULL)) /*如果定义了该变量,表示软件层面为低电平有效,则激活端口时必须设置为低电平,具体见函数__activate_col()*/pdata->active_low = true; of_property_read_u32(np, "debounce-delay-ms", &pdata->debounce_ms); /*消抖延时*/of_property_read_u32(np, "col-scan-delay-us",&pdata->col_scan_delay_us);gpios = devm_kzalloc(dev,sizeof(unsigned int) *(pdata->num_row_gpios + pdata->num_col_gpios),GFP_KERNEL);if (!gpios) {dev_err(dev, "could not allocate memory for gpios\n");return ERR_PTR(-ENOMEM);}for (i = 0; i < pdata->num_row_gpios; i++) /*解析获取行GPIO*/gpios[i] = of_get_named_gpio(np, "row-gpios", i); for (i = 0; i < pdata->num_col_gpios; i++) /*解析获取列GPIO*/gpios[pdata->num_row_gpios + i] = of_get_named_gpio(np, "col-gpios", i); pdata->row_gpios = gpios;pdata->col_gpios = &gpios[pdata->num_row_gpios];return pdata; } #else static inline struct matrix_keypad_platform_data *matrix_keypad_parse_dt(struct device *dev) {dev_err(dev, "no platform data defined\n");return ERR_PTR(-EINVAL); } #endif
5.3.2 matrix_keypad_init_gpio()函数源码解析
该函数作用为:后续补充
六、附加知识
6.1 Linux内核中的事件类型
输入事件类型:可选的事件类型定义在 include/uapi/linux/input.h 文件中
#define EV_SYN 0x00 /* 同步事件 */ #define EV_KEY 0x01 /* 按键事件 */ #define EV_REL 0x02 /* 相对坐标事件 */ #define EV_ABS 0x03 /* 绝对坐标事件 */ #define EV_MSC 0x04 /* 杂项(其他)事件 */ #define EV_SW 0x05 /* 开关事件 */ #define EV_LED 0x11 /* LED */ #define EV_SND 0x12 /* sound(声音) */ #define EV_REP 0x14 /* 重复事件 */ #define EV_FF 0x15 /* 压力事件 */ #define EV_PWR 0x16 /* 电源事件 */ #define EV_FF_STATUS 0x17 /* 压力状态事件 */
至此,结束!
imx6ul添加keyboard Matrix矩阵键盘相关推荐
- keyboard Matrix矩阵键盘
文章目录 一.功能实现(代码) 1.1 添加编译选项 1.2 添加设备树节点 1.2.1 添加kpp节点 1.2.2 在iomuxc中添加pinctrl_复用节点 1.2.3 屏蔽其他设备节点占用的键 ...
- 【STM32扫描4x4矩阵键盘模块】 4x4 matrix keypad interface
[STM32扫描4x4矩阵键盘模块] 4x4 matrix keypad interface 4x4矩阵键盘模块 矩阵键盘是将多个按键排布成类似矩阵形式的键盘组.为了减少IO资源的占用,将键盘组的每一 ...
- 11.嵌入式控制器EC实战 KBC矩阵键盘Keyboard开发流程
文章目录 笔记本矩阵键盘概述 矩阵键盘的扫描方式 行列扫描方式 逐行逐列扫描方式 EC中矩阵键盘是如何扫描并得到码值的(代码分析) 笔记本矩阵键盘概述 在EC中支持两种形式的键盘,一种是PS/2接口的 ...
- 矩阵键盘简易计算机设计报告,矩阵键盘显设计报告..doc
摘要 在日常生活中,我们经常要用到键盘来实现对电子装置的控制.小到手表手机,中到电视电脑,大到各种复杂仪器,都需要通过各种按键来实现各种操作.本次课程设计以按键控制显示为主题,以MSP430G2553 ...
- 行列式的计算机应用答辩ppt,矩阵键盘的研究毕业设计论文+开题报告+答辩PPT
摘要 矩阵式键盘乃是当今使用最为广泛的键盘模式,该系统以N个端口连接控制N*N个按键,及时在LED数码管上,单片机控制的是键盘显示系统,该系统可以对不同的按键进行实时显示,其核心是单片机和键盘矩阵电路 ...
- 利用c51进行数模转换并在液晶屏上显示_基于C51单片机的智能计算器、矩阵键盘、lcd1602...
目录 1绪论.............................................................................................. ...
- linux下矩阵键盘驱动
1_>路径:linux\drivers\input\keyboard 2_>文件名:matrix_keypad.c 说明:这是一个平台驱动程序,在上面的.c文件里没有建一个设备,只有驱动 ...
- WINCE6.0添加特定的软件键盘
*******************************LoongEmbedded******************************** 作者:LoongEmbedded 时间:201 ...
- 汉高澳大利亚matrix矩阵计算器
我在梦中的超级计算机超级计算机锯,使用大量阵列的cpu记忆,完成并行计算.一个手机制造商由于使用普通机械提供的服务,往往造成停机.是铁道部列车网络售票的事实. 无法使用云服务.上万台计算机并行处理,因 ...
最新文章
- 这两年的人工智能淘金热 真正赚钱的公司是这几家
- c#调用命令行遇到带空格的路径
- java newline_Java BufferedWriter.newLine()方法示例
- Spring Cloud构建微服务架构(一)服务注册与发现
- 本地连接虚拟机的redis
- Driver 初始化顺序
- 快速搭建开发环境(Vs Code)
- 不做etl sql 怎么直接取_我们可以不再使用ETL了吗?
- c语言实验编码sdut,C语言实验一(1)
- Win10“混合现实门户”图标更新:加入Fluent Design风格
- 我为什么鼓励你读计算机博士
- 接触【专利写作】的必备知识(包含少部分撰写技巧)
- 如何使用SQL对数据进行分析和可视化
- oracle安装使用
- HTML女朋友生日祝福、老婆生日快乐源代码,可定制照片+音乐+表白话
- 制作android动态壁纸,如何使用LibGDx制作Android动态壁纸?
- html求正方形面积,利用js计算正方形的面积的方法介绍
- 大学生暑期社会实践活动
- 超火的举牌加油小人生成器MATLAB复刻版(app designer)
- mockito的入门与使用
热门文章
- 我是怎么走出迷茫,找到自己热爱的事业的。
- Win7无需第三方工具设置Wifi热点
- CentOS 7 部署TiDB 2.1.4集群
- matlab环面,基于Matlab和UG8.0一次包络环面蜗杆建模方法研究
- flutter的UI帧率FPS监测
- 华三交换机IRF堆叠 典型配置
- mac book pro 灰屏 屏幕闪烁 色差 低温 重物导致压迫
- 方正台式计算机初始bios密码,方正台式计算机BIOS设置U盘启动
- android微信支付插件,AppCan文档中心-Android微信插件接入指引
- Caffe / PyQt4 / 等各种坑 (Ubuntu 18 / macOS Mojave / Docker)