keyboard Matrix矩阵键盘

本文详细介绍了在Linux内核4.1.15版本中,针对IMX6UL平台实现矩阵键盘驱动的过程。内容涵盖添加编译选项、设备树配置、硬件电路设计、调试步骤、源码分析以及可能遇到的问题。讲解了如何通过配置设备树节点实现按键映射,以及如何使用hexdump和自定义工具读取按键数据。同时,分析了内核源码中matrix_keypad_scan()函数的工作原理,解释了矩阵键盘扫描过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >




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(选中)

    
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

选中后,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

    
    
  • 1
  • 2

将新的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<span class="token operator">-</span>delay<span class="token operator">-</span>ms <span class="token operator">=</span> <span class="token operator">&lt;</span><span class="token number">20</span><span class="token operator">&gt;</span><span class="token punctuation">;</span>   <span class="token comment">/*防反跳延时,即:去抖延时*/</span>
col<span class="token operator">-</span>scan<span class="token operator">-</span>delay<span class="token operator">-</span>us <span class="token operator">=</span> <span class="token operator">&lt;</span><span class="token number">400</span><span class="token operator">&gt;</span><span class="token punctuation">;</span>  <span class="token comment">/*列扫描延时*/</span>

<span class="token comment">/*8行*/</span>
row<span class="token operator">-</span>gpios <span class="token operator">=</span> <span class="token operator">&lt;</span><span class="token operator">&amp;</span>gpio2 <span class="token number">11</span> GPIO_ACTIVE_LOW
             <span class="token operator">&amp;</span>gpio2 <span class="token number">12</span> GPIO_ACTIVE_LOW
             <span class="token operator">&amp;</span>gpio2 <span class="token number">10</span> GPIO_ACTIVE_LOW
             <span class="token operator">&amp;</span>gpio2 <span class="token number">8</span>  GPIO_ACTIVE_LOW
             <span class="token operator">&amp;</span>gpio2 <span class="token number">9</span>  GPIO_ACTIVE_LOW
             <span class="token operator">&amp;</span>gpio2 <span class="token number">13</span> GPIO_ACTIVE_LOW
             <span class="token operator">&amp;</span>gpio2 <span class="token number">14</span> GPIO_ACTIVE_LOW
             <span class="token operator">&amp;</span>gpio2 <span class="token number">15</span> GPIO_ACTIVE_LOW
            <span class="token operator">&gt;</span><span class="token punctuation">;</span>
<span class="token comment">/*3列*/</span>
col<span class="token operator">-</span>gpios <span class="token operator">=</span> <span class="token operator">&lt;</span><span class="token operator">&amp;</span>gpio2 <span class="token number">16</span> GPIO_ACTIVE_LOW
             <span class="token operator">&amp;</span>gpio2 <span class="token number">17</span> GPIO_ACTIVE_LOW
             <span class="token operator">&amp;</span>gpio2 <span class="token number">19</span> GPIO_ACTIVE_LOW
            <span class="token operator">&gt;</span><span class="token punctuation">;</span>
            
<span class="token comment">/* 举例释义:MATRIX_KEY(0x2, 0x1, KEY_X)表示将【第2行1列的按键】映射为【KEY_X】 */</span>
linux<span class="token punctuation">,</span>keymap <span class="token operator">=</span> <span class="token operator">&lt;</span>
    <span class="token comment">/*row0*/</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x0</span><span class="token punctuation">,</span> <span class="token number">0x0</span><span class="token punctuation">,</span> KEY_LEFT<span class="token punctuation">)</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x0</span><span class="token punctuation">,</span> <span class="token number">0x1</span><span class="token punctuation">,</span> KEY_DOWN<span class="token punctuation">)</span>

    <span class="token comment">/* row1 */</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x1</span><span class="token punctuation">,</span> <span class="token number">0x0</span><span class="token punctuation">,</span> KEY_UP<span class="token punctuation">)</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x1</span><span class="token punctuation">,</span> <span class="token number">0x1</span><span class="token punctuation">,</span> KEY_RIGHT<span class="token punctuation">)</span>

    <span class="token comment">/* row2 */</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x2</span><span class="token punctuation">,</span> <span class="token number">0x0</span><span class="token punctuation">,</span> KEY_M<span class="token punctuation">)</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x2</span><span class="token punctuation">,</span> <span class="token number">0x1</span><span class="token punctuation">,</span> KEY_X<span class="token punctuation">)</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x2</span><span class="token punctuation">,</span> <span class="token number">0x2</span><span class="token punctuation">,</span> KEY_ENTER<span class="token punctuation">)</span>

    <span class="token comment">/*row3*/</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x3</span><span class="token punctuation">,</span> <span class="token number">0x0</span><span class="token punctuation">,</span> KEY_N<span class="token punctuation">)</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x3</span><span class="token punctuation">,</span> <span class="token number">0x1</span><span class="token punctuation">,</span> KEY_O<span class="token punctuation">)</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x3</span><span class="token punctuation">,</span> <span class="token number">0x2</span><span class="token punctuation">,</span> KEY_I<span class="token punctuation">)</span>

    <span class="token comment">/*row4*/</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x4</span><span class="token punctuation">,</span> <span class="token number">0x0</span><span class="token punctuation">,</span> KEY_7<span class="token punctuation">)</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x4</span><span class="token punctuation">,</span> <span class="token number">0x1</span><span class="token punctuation">,</span> KEY_8<span class="token punctuation">)</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x4</span><span class="token punctuation">,</span> <span class="token number">0x2</span><span class="token punctuation">,</span> KEY_9<span class="token punctuation">)</span>

    <span class="token comment">/*row5*/</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x5</span><span class="token punctuation">,</span> <span class="token number">0x0</span><span class="token punctuation">,</span> KEY_4<span class="token punctuation">)</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x5</span><span class="token punctuation">,</span> <span class="token number">0x1</span><span class="token punctuation">,</span> KEY_5<span class="token punctuation">)</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x5</span><span class="token punctuation">,</span> <span class="token number">0x2</span><span class="token punctuation">,</span> KEY_6<span class="token punctuation">)</span>

    <span class="token comment">/*row6*/</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x6</span><span class="token punctuation">,</span> <span class="token number">0x0</span><span class="token punctuation">,</span> KEY_1<span class="token punctuation">)</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x6</span><span class="token punctuation">,</span> <span class="token number">0x1</span><span class="token punctuation">,</span> KEY_2<span class="token punctuation">)</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x6</span><span class="token punctuation">,</span> <span class="token number">0x2</span><span class="token punctuation">,</span> KEY_3<span class="token punctuation">)</span>

    <span class="token comment">/*row7*/</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x7</span><span class="token punctuation">,</span> <span class="token number">0x0</span><span class="token punctuation">,</span> KEY_MINUS<span class="token punctuation">)</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x7</span><span class="token punctuation">,</span> <span class="token number">0x1</span><span class="token punctuation">,</span> KEY_0<span class="token punctuation">)</span>
    <span class="token function">MATRIX_KEY</span><span class="token punctuation">(</span><span class="token number">0x7</span><span class="token punctuation">,</span> <span class="token number">0x2</span><span class="token punctuation">,</span> KEY_DOT<span class="token punctuation">)</span>
    <span class="token operator">&gt;</span><span class="token punctuation">;</span>
gpio<span class="token operator">-</span>activelow<span class="token punctuation">;</span>
status <span class="token operator">=</span> <span class="token string">"okay"</span><span class="token punctuation">;</span>

};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68

1.2.2 在iomuxc中添加pinctrl_复用节点

&iomuxc {
        ......
    <span class="token comment">/*lsy*/</span>
    pinctrl_key<span class="token punctuation">:</span> kppgrp<span class="token punctuation">{<!-- --></span>
         fsl<span class="token punctuation">,</span>pins <span class="token operator">=</span> <span class="token operator">&lt;</span>
             <span class="token comment">/* row 8行 */</span>
             MX6UL_PAD_ENET2_TX_DATA0__GPIO2_IO11   <span class="token number">0xb0b1</span>
             MX6UL_PAD_ENET2_TX_DATA1__GPIO2_IO12   <span class="token number">0xb0b1</span>
             MX6UL_PAD_ENET2_RX_EN__GPIO2_IO10      <span class="token number">0xb0b1</span>
             MX6UL_PAD_ENET2_RX_DATA0__GPIO2_IO08   <span class="token number">0xb0b1</span>
             MX6UL_PAD_ENET2_RX_DATA1__GPIO2_IO09   <span class="token number">0xb0b1</span>
             MX6UL_PAD_ENET2_TX_EN__GPIO2_IO13      <span class="token number">0xb0b1</span>
             MX6UL_PAD_ENET2_TX_CLK__GPIO2_IO14     <span class="token number">0xb0b1</span>
             MX6UL_PAD_ENET2_RX_ER__GPIO2_IO15      <span class="token number">0xb0b1</span>

             <span class="token comment">/* col 3列 */</span>
             MX6UL_PAD_SD1_CMD__GPIO2_IO16          <span class="token number">0x70a1</span>
             MX6UL_PAD_SD1_CLK__GPIO2_IO17          <span class="token number">0x70a1</span>
             MX6UL_PAD_SD1_DATA1__GPIO2_IO19        <span class="token number">0x70a1</span>
             <span class="token operator">&gt;</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">;</span>

   <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

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  */
    <span class="token comment">/*row1*/</span>
    <span class="token number">0x01000067</span>  <span class="token comment">/*Qt.Key_Up    -&gt; HEX:67 -&gt; DEC:103 -&gt; KEY_UP    */</span>
    <span class="token number">0x0101006A</span>  <span class="token comment">/*Qt.Key_Right -&gt; HEX:6A -&gt; DEC:106 -&gt; KEY_RIGHT */</span>

    <span class="token comment">/*row2*/</span>
    <span class="token number">0x0200004D</span>  <span class="token comment">/*Qt.Key_M     -&gt; HEX:4D -&gt; DEC:77  -&gt; KEY_KP6   */</span>
    <span class="token number">0x02010058</span>  <span class="token comment">/*Qt.Key_X     -&gt; HEX:58 -&gt; DEC:88  -&gt; KEY_F12   */</span>
    <span class="token number">0x0202001C</span>  <span class="token comment">/*Qt.Key_Return-&gt; HEX:1C -&gt; DEC:28  -&gt; KEY_ENTER */</span>

    <span class="token comment">/*row3*/</span>
    <span class="token number">0x0300004E</span>  <span class="token comment">/*Qt.Key_N     -&gt; HEX:4E -&gt; DEC:78  -&gt; KEY_KPPLUS*/</span>
    <span class="token number">0x0301004F</span>  <span class="token comment">/*Qt.Key_O     -&gt; HEX:4F -&gt; DEC:79  -&gt; KEY_KP1   */</span>
    <span class="token number">0x03020049</span>  <span class="token comment">/*Qt.Key_I     -&gt; HEX:49 -&gt; DEC:73  -&gt; KEY_KP9   */</span>

    <span class="token comment">/*row4*/</span>
    <span class="token number">0x04000008</span>  <span class="token comment">/*Qt.Key_7     -&gt; HEX:08 -&gt; DEC: 8  -&gt; KEY_7     */</span>
    <span class="token number">0x04010009</span>  <span class="token comment">/*Qt.Key_8     -&gt; HEX:09 -&gt; DEC: 9  -&gt; KEY_8     */</span>
    <span class="token number">0X0402000A</span>  <span class="token comment">/*Qt.Key_9     -&gt; HEX:0A -&gt; DEC:10  -&gt; KEY_9     */</span>

    <span class="token comment">/*row5*/</span>
    <span class="token number">0x05000005</span>  <span class="token comment">/*Qt.Key_4     -&gt; HEX:05 -&gt; DEC: 5  -&gt; KEY_4     */</span>
    <span class="token number">0x05010006</span>  <span class="token comment">/*Qt.Key_5     -&gt; HEX:06 -&gt; DEC: 6  -&gt; KEY_5     */</span>
    <span class="token number">0x05020007</span>  <span class="token comment">/*Qt.Key_6     -&gt; HEX:07 -&gt; DEC: 7  -&gt; KEY_6     */</span>

    <span class="token comment">/*row6*/</span>
    <span class="token number">0x06000002</span>  <span class="token comment">/*Qt.Key_1     -&gt; HEX:02 -&gt; DEC: 2  -&gt; KEY_1     */</span>
    <span class="token number">0x06010003</span>  <span class="token comment">/*Qt.Key_2     -&gt; HEX:03 -&gt; DEC: 3  -&gt; KEY_2     */</span>
    <span class="token number">0x06020004</span>  <span class="token comment">/*Qt.Key_3     -&gt; HEX:04 -&gt; DEC: 4  -&gt; KEY_3     */</span>

    <span class="token comment">/*row7*/</span>
    <span class="token number">0x0700002D</span>  <span class="token comment">/*Qt.Key_Minux -&gt; HEX:2D -&gt; DEC:45  -&gt; KEY_X     */</span>
    <span class="token number">0x0701000B</span>  <span class="token comment">/*Qt.Key_0     -&gt; HEX:0B -&gt; DEC:11  -&gt; KEY_0     */</span>
    <span class="token number">0x0702002E</span>  <span class="token comment">/*Qt.Key_Period-&gt; HEX:2E -&gt; DEC:46  -&gt; KEY_C     */</span>
   <span class="token operator">&gt;</span><span class="token punctuation">;</span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

二、硬件电路

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>; /列扫描延时/

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7



三、调试步骤

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 0000
   0000010 ef7a 59c3 16e6 0006 0001 0069 0001 0000 <---------0x69按键按下
   0000020 ef7a 59c3 16e6 0006 0000 0000 0000 0000
   0000030 ef7a 59c3 c3f9 0007 0004 0004 0000 0000
   0000040 ef7a 59c3 c3f9 0007 0001 0069 0000 0000 <---------0x69按键弹起
   0000050 ef7a 59c3 c3f9 0007 0000 0000 0000 0000

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这里只说右边4列含义:

右起第4列【type列】:事件类型,0001表示产生了按键事件,其他类型见【附加知识小节:Linux内核中的事件类型】

右起第3列【code列】:表示十六进制编码,如果是按键事件的话,则对应key的十六进制值,在内核源码include/uapi/linux/input.h中可以查到每个按键的十进制编码。

最右边2列【value列】:按键的值,0001 0000表示按下,0000 0000表示弹起

  • 1
  • 2
  • 3
  • 4
  • 5

3.1.2 类hexdump工具源码(极力推荐,使用起来很方便)

该工具使用起来非常人性化,不像上面那样,每个按键,键值啥的十六进制、十进制还要转换,而且很难看。

  1. 工具源码见另一篇博客:hexdump调试小工具——获取event事件信息、键盘按键信息等

    或github链接:【https://github.com/lishiyuan/tools_hexdump】

  2. 使用方法为:

    将源码进行交叉编译,生成可执行文件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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 该工具使用效果如下:

    root@imx6ulevk:~# ./read_linux_key_value 0
    /dev/input/event0
     evdev version: 1.0.1
     name: 20b8000.kpp
     features: 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; 
    
      
      
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    是不是很爽!

  • 3.2 调试流程

    3.2.1 大致调试流程如下图

    备注:具体流程分析见下一小节

    在这里插入图片描述

    3.2.2 调试流程具体步骤描述如下

    1. 先将每个GPIO分别设置为普通IO口,验证输入输出均可控(不管是采用裸机方式还是GPIO export shell命令的方式都行,这里不再赘述方法)。

      如果发现有GPIO不可控,则排查dts中是否有别的地方复用了该引脚,要全部屏蔽掉,或者检查硬件电路设计是否无误。

    2. 板卡【/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 {
    .......
    }
    .......
    }
    }
    }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    因此在板卡【/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小技巧

    1. 在源码c文件中添加log的时候,这里推荐一个小技巧,在文件顶部定义如下宏:

      //#define lsydebug(fmt,args...)    printk(KERN_ERR "[lsy]:%s:%d:" fmt,__func__,__LINE__,##args)
      #define lsydebug(fmt,args...)
      
         
         
      • 1
      • 2

      这样代码中添加log的时候可以像这样:

          ......
      
      <span class="token function">memset</span><span class="token punctuation">(</span>new_state<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span>new_state<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      
      <span class="token function">lsydebug</span><span class="token punctuation">(</span><span class="token string">"row = pdata-&gt;num_row_gpios = %d\r\n"</span><span class="token punctuation">,</span> pdata<span class="token operator">-&gt;</span>num_row_gpios<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token function">lsydebug</span><span class="token punctuation">(</span><span class="token string">"col = pdata-&gt;num_col_gpios = %d\r\n"</span><span class="token punctuation">,</span> pdata<span class="token operator">-&gt;</span>num_col_gpios<span class="token punctuation">)</span><span class="token punctuation">;</span>
      
      <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
      
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    当调试完毕以后,直接保留#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

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7



    四、调试过程中遇到的坑

    1. 通常情况,源码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节点是否包含自己定义的节点信息,若否,则必有问题,节点都未生效,功能肯定不对。
    2. 设备树中:行、列一定不能反!Linux内核规定matrix功能:行必须为输入,列必须为输出

      row-gpios成员为行,

      col-gpios成员为列。

    3. 设备树中: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

      0x00000069
      十六进制行序号列序号按键十六进制键值
    4. 上述编译选项menuconfig中该打开的功能必须打开。

    5. 设备树中:延时一定要调整合适,否则可能会出现按下一个按键,整行按键都被上报到event设备中。

      debounce-delay-ms = <20>;   /*防反跳延时,即:去抖延时*/
      col-scan-delay-us = <400>;  /*列扫描延时*/
      
         
         
      • 1
      • 2
    6. 设备树中: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列】键盘为例,其中:

    1. 列GPIO:输出,4列

      行GPIO:输入,3行

    2. 内核源码中定义了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               col3
       row0 --      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 col3
    row0 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]

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    5.2.3 matrix_keypad_scan() 函数源码解析

    该函数源码大体结构如下:

    1. 主要是2个双层for循环:

      • 第一个双层for循环用于获取当前所有按键状态,保存在new_state[]矩阵中;

      • 第二个双层for循环将new_state[]和last_key_state[]做比较,匹配每一个状态改变的按键,并将每个按键状态都上报input子系统。

    2. 备注:

      第二个双层for循环中有一行异或操作需要注意:

        bits_changed = keypad->last_key_state[col] ^ new_state[col];
      
         
         
      • 1

      不要把bits_changed的含义理解错了:
      注意亦或的含义:不同为1,所以该条语句执行完以后,bits_changed对应bit是1的话,仅仅是标识哪一行电平状态改变了,并不是代表该行电平!!!该行电平在new_state[col]的对应bit位保存!!!

    3. 下面是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;
      
      <span class="token function">dbg</span><span class="token punctuation">(</span><span class="token string">"Etnry scan!\r\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      
      <span class="token comment">/* de-activate all columns for scanning */</span>
      <span class="token function">activate_all_cols</span><span class="token punctuation">(</span>pdata<span class="token punctuation">,</span> false<span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">/*反激活全部列,扫描结束后再全部激活*/</span>
      
      <span class="token function">memset</span><span class="token punctuation">(</span>new_state<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span>new_state<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">/*先清0当前按键状态矩阵,避免检测错误*/</span>
      
      <span class="token comment">/* assert each column and read the row status out */</span>
      <span class="token comment">/* 一、该双层for循环:按顺序依次改变每列电平并获取对应行电平,保存在new矩阵中,后面和last矩阵对比判断是否状态改变 */</span>
      <span class="token keyword">for</span> <span class="token punctuation">(</span>col <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> col <span class="token operator">&lt;</span> pdata<span class="token operator">-&gt;</span>num_col_gpios<span class="token punctuation">;</span> col<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
      	<span class="token function">activate_col</span><span class="token punctuation">(</span>pdata<span class="token punctuation">,</span> col<span class="token punctuation">,</span> true<span class="token punctuation">)</span><span class="token punctuation">;</span>   <span class="token comment">/*激活某列*/</span>
      
      	<span class="token keyword">for</span> <span class="token punctuation">(</span>row <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> row <span class="token operator">&lt;</span> pdata<span class="token operator">-&gt;</span>num_row_gpios<span class="token punctuation">;</span> row<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
      		new_state<span class="token punctuation">[</span>col<span class="token punctuation">]</span> <span class="token operator">|</span><span class="token operator">=</span> <span class="token function">row_asserted</span><span class="token punctuation">(</span>pdata<span class="token punctuation">,</span> row<span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">&lt;&lt;</span> row<span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">/*获取每一行的电平,存放在对应bit*/</span>
          <span class="token punctuation">}</span>
      
      	<span class="token function">activate_col</span><span class="token punctuation">(</span>pdata<span class="token punctuation">,</span> col<span class="token punctuation">,</span> false<span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">/*反激活某列*/</span>
      <span class="token punctuation">}</span>
      
      <span class="token comment">/* 二、该双层for循环:将new和last作比较,每匹配到一个状态改变的按键,就向event设备发送一次数据*/</span>
      <span class="token keyword">for</span> <span class="token punctuation">(</span>col <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> col <span class="token operator">&lt;</span> pdata<span class="token operator">-&gt;</span>num_col_gpios<span class="token punctuation">;</span> col<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
      	uint32_t bits_changed<span class="token punctuation">;</span>
      
          <span class="token comment">/*
           * 先直接对整个变量(全部行状态)异或操作,
           * 如果任意一个bit(即:任意一行)状态改变,bits_changed必不为0
           * 说明至少有一行状态有改变
           */</span>
      	bits_changed <span class="token operator">=</span> keypad<span class="token operator">-&gt;</span>last_key_state<span class="token punctuation">[</span>col<span class="token punctuation">]</span> <span class="token operator">^</span> new_state<span class="token punctuation">[</span>col<span class="token punctuation">]</span><span class="token punctuation">;</span>
      
      	<span class="token keyword">if</span> <span class="token punctuation">(</span>bits_changed <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token comment">/*若所有行均无变化,则继续下一列*/</span>
      		<span class="token keyword">continue</span><span class="token punctuation">;</span>
      
      	<span class="token keyword">for</span> <span class="token punctuation">(</span>row <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> row <span class="token operator">&lt;</span> pdata<span class="token operator">-&gt;</span>num_row_gpios<span class="token punctuation">;</span> row<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
      		<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>bits_changed <span class="token operator">&amp;</span> <span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">&lt;&lt;</span> row<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token comment">/*通过row的移位,逐个判断每个bit位(每行)的状态是否有变化*/</span>
      			<span class="token keyword">continue</span><span class="token punctuation">;</span> <span class="token comment">/*若当前行无变化,则继续下一行*/</span>
      
              <span class="token comment">/*每碰到一个有变化的bit(即:变化行),则开始上报内核*/</span>
      		code <span class="token operator">=</span> <span class="token function">MATRIX_SCAN_CODE</span><span class="token punctuation">(</span>row<span class="token punctuation">,</span> col<span class="token punctuation">,</span> keypad<span class="token operator">-&gt;</span>row_shift<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">/*确定变化的行列,就取出dts中定义的code*/</span>
      		<span class="token function">input_event</span><span class="token punctuation">(</span>input_dev<span class="token punctuation">,</span> EV_MSC<span class="token punctuation">,</span> MSC_SCAN<span class="token punctuation">,</span> code<span class="token punctuation">)</span><span class="token punctuation">;</span>          <span class="token comment">/*向输入子系统上报输入设备产生的事件*/</span>
      		<span class="token function">input_report_key</span><span class="token punctuation">(</span>input_dev<span class="token punctuation">,</span> keycodes<span class="token punctuation">[</span>code<span class="token punctuation">]</span><span class="token punctuation">,</span> new_state<span class="token punctuation">[</span>col<span class="token punctuation">]</span> <span class="token operator">&amp;</span> <span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">&lt;&lt;</span> row<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">/*上报按键事件*/</span>
      	<span class="token punctuation">}</span>
      <span class="token punctuation">}</span>
      <span class="token function">input_sync</span><span class="token punctuation">(</span>input_dev<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/*通知接收者,一个报告上报完毕。至此通过双层for循环,上报了所有按下的按键*/</span>
      
      <span class="token function">memcpy</span><span class="token punctuation">(</span>keypad<span class="token operator">-&gt;</span>last_key_state<span class="token punctuation">,</span> new_state<span class="token punctuation">,</span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span>new_state<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">/*一次全键盘扫描完毕,当前状态覆盖上次状态*/</span>
      
      <span class="token function">activate_all_cols</span><span class="token punctuation">(</span>pdata<span class="token punctuation">,</span> true<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/*扫描结束,激活全部列*/</span>
      
      <span class="token comment">/* Enable IRQs again */</span>
      <span class="token function">spin_lock_irq</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>keypad<span class="token operator">-&gt;</span>lock<span class="token punctuation">)</span><span class="token punctuation">;</span>
      keypad<span class="token operator">-&gt;</span>scan_pending <span class="token operator">=</span> false<span class="token punctuation">;</span>
      

    enable_row_irqs(keypad);
    spin_unlock_irq(&keypad->lock);
    }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    5.3 其他几个重要函数

    5.3.1 matrix_keypad_parse_dt()函数源码解析

    1. 该函数作用为:解析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;
      
      <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>np<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
          <span class="token function">dev_err</span><span class="token punctuation">(</span>dev<span class="token punctuation">,</span> <span class="token string">"device lacks DT data\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
          <span class="token keyword">return</span> <span class="token function">ERR_PTR</span><span class="token punctuation">(</span><span class="token operator">-</span>ENODEV<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
      
      pdata <span class="token operator">=</span> <span class="token function">devm_kzalloc</span><span class="token punctuation">(</span>dev<span class="token punctuation">,</span> <span class="token keyword">sizeof</span><span class="token punctuation">(</span><span class="token operator">*</span>pdata<span class="token punctuation">)</span><span class="token punctuation">,</span> GFP_KERNEL<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>pdata<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
          <span class="token function">dev_err</span><span class="token punctuation">(</span>dev<span class="token punctuation">,</span> <span class="token string">"could not allocate memory for platform data\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
          <span class="token keyword">return</span> <span class="token function">ERR_PTR</span><span class="token punctuation">(</span><span class="token operator">-</span>ENOMEM<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
      
      pdata<span class="token operator">-&gt;</span>num_row_gpios <span class="token operator">=</span> nrow <span class="token operator">=</span> <span class="token function">of_gpio_named_count</span><span class="token punctuation">(</span>np<span class="token punctuation">,</span> <span class="token string">"row-gpios"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>  <span class="token comment">/*解析获取行、列总数量*/</span>
      pdata<span class="token operator">-&gt;</span>num_col_gpios <span class="token operator">=</span> ncol <span class="token operator">=</span> <span class="token function">of_gpio_named_count</span><span class="token punctuation">(</span>np<span class="token punctuation">,</span> <span class="token string">"col-gpios"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>nrow <span class="token operator">&lt;=</span> <span class="token number">0</span> <span class="token operator">||</span> ncol <span class="token operator">&lt;=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
          <span class="token function">dev_err</span><span class="token punctuation">(</span>dev<span class="token punctuation">,</span> <span class="token string">"number of keypad rows/columns not specified\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
          <span class="token keyword">return</span> <span class="token function">ERR_PTR</span><span class="token punctuation">(</span><span class="token operator">-</span>EINVAL<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
      
      <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">of_get_property</span><span class="token punctuation">(</span>np<span class="token punctuation">,</span> <span class="token string">"linux,no-autorepeat"</span><span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
          pdata<span class="token operator">-&gt;</span>no_autorepeat <span class="token operator">=</span> true<span class="token punctuation">;</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">of_get_property</span><span class="token punctuation">(</span>np<span class="token punctuation">,</span> <span class="token string">"linux,wakeup"</span><span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
          pdata<span class="token operator">-&gt;</span>wakeup <span class="token operator">=</span> true<span class="token punctuation">;</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">of_get_property</span><span class="token punctuation">(</span>np<span class="token punctuation">,</span> <span class="token string">"gpio-activelow"</span><span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">)</span><span class="token punctuation">)</span>  <span class="token comment">/*如果定义了该变量,表示软件层面为低电平有效,则激活端口时必须设置为低电平,具体见函数__activate_col()*/</span>
          pdata<span class="token operator">-&gt;</span>active_low <span class="token operator">=</span> true<span class="token punctuation">;</span>  
      
      <span class="token function">of_property_read_u32</span><span class="token punctuation">(</span>np<span class="token punctuation">,</span> <span class="token string">"debounce-delay-ms"</span><span class="token punctuation">,</span> <span class="token operator">&amp;</span>pdata<span class="token operator">-&gt;</span>debounce_ms<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">/*消抖延时*/</span>
      <span class="token function">of_property_read_u32</span><span class="token punctuation">(</span>np<span class="token punctuation">,</span> <span class="token string">"col-scan-delay-us"</span><span class="token punctuation">,</span>
                          <span class="token operator">&amp;</span>pdata<span class="token operator">-&gt;</span>col_scan_delay_us<span class="token punctuation">)</span><span class="token punctuation">;</span>
      
      gpios <span class="token operator">=</span> <span class="token function">devm_kzalloc</span><span class="token punctuation">(</span>dev<span class="token punctuation">,</span>
                   <span class="token keyword">sizeof</span><span class="token punctuation">(</span><span class="token keyword">unsigned</span> <span class="token keyword">int</span><span class="token punctuation">)</span> <span class="token operator">*</span>
                  <span class="token punctuation">(</span>pdata<span class="token operator">-&gt;</span>num_row_gpios <span class="token operator">+</span> pdata<span class="token operator">-&gt;</span>num_col_gpios<span class="token punctuation">)</span><span class="token punctuation">,</span>
                   GFP_KERNEL<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>gpios<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>
          <span class="token function">dev_err</span><span class="token punctuation">(</span>dev<span class="token punctuation">,</span> <span class="token string">"could not allocate memory for gpios\n"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
          <span class="token keyword">return</span> <span class="token function">ERR_PTR</span><span class="token punctuation">(</span><span class="token operator">-</span>ENOMEM<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span>
      
      <span class="token keyword">for</span> <span class="token punctuation">(</span>i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> pdata<span class="token operator">-&gt;</span>num_row_gpios<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span>           <span class="token comment">/*解析获取行GPIO*/</span>
          gpios<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">of_get_named_gpio</span><span class="token punctuation">(</span>np<span class="token punctuation">,</span> <span class="token string">"row-gpios"</span><span class="token punctuation">,</span> i<span class="token punctuation">)</span><span class="token punctuation">;</span> 
      
      <span class="token keyword">for</span> <span class="token punctuation">(</span>i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> pdata<span class="token operator">-&gt;</span>num_col_gpios<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span>           <span class="token comment">/*解析获取列GPIO*/</span>
          gpios<span class="token punctuation">[</span>pdata<span class="token operator">-&gt;</span>num_row_gpios <span class="token operator">+</span> i<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">of_get_named_gpio</span><span class="token punctuation">(</span>np<span class="token punctuation">,</span> <span class="token string">"col-gpios"</span><span class="token punctuation">,</span> i<span class="token punctuation">)</span><span class="token punctuation">;</span> 
      
      pdata<span class="token operator">-&gt;</span>row_gpios <span class="token operator">=</span> gpios<span class="token punctuation">;</span>
      pdata<span class="token operator">-&gt;</span>col_gpios <span class="token operator">=</span> <span class="token operator">&amp;</span>gpios<span class="token punctuation">[</span>pdata<span class="token operator">-&gt;</span>num_row_gpios<span class="token punctuation">]</span><span class="token punctuation">;</span>
      
      <span class="token keyword">return</span> pdata<span class="token punctuation">;</span>
      

    }
    #else
    static inline struct matrix_keypad_platform_data matrix_keypad_parse_dt(struct device dev)
    {
    dev_err(dev, “no platform data defined\n”);

    <span class="token keyword">return</span> <span class="token function">ERR_PTR</span><span class="token punctuation">(</span><span class="token operator">-</span>EINVAL<span class="token punctuation">)</span><span class="token punctuation">;</span>
    

    }
    #endif

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    5.3.2 matrix_keypad_init_gpio()函数源码解析

    1. 该函数作用为:后续补充

      
      
         
         
      • 1

    六、附加知识

    6.1 Linux内核中的事件类型

    1. 输入事件类型:可选的事件类型定义在 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 /* 压力状态事件 */
      
         
         
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12



    至此,结束!

    </article>
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值