在英创Linux主板上用GPIO实现矩阵键盘

 2019-7-29     作者:黄志超     [nemail]    
[lablebox]

  英创公司的Linux主板对于矩阵键盘的支持有多种方案可以实现,我们在以前推出过使用i2c扩展的方案,具体情况可以参考ETA202模块的资料:《ETA202 I2C键盘扩展模块使用手册》,这种方案的好处是只需要使用I2C总线的两条信号线SCL和SDA,可以节约主板上的GPIO资源。ESMARC系列主板都拥有32位GPIO资源,为了更好利用硬件资源,英创公司推出了GPIO接矩阵键盘的方案,在IO资源满足需求的情况下,可以使用这套方案,优点是可以节约硬件扩展的成本,并且软件上使用Linux标准的input设备接口就可以操作,不需要额外做任何工作。


  GPIO支持矩阵键盘是通过IO中断来实现的,当检测到有按键按下,就会触发中断,扫描键盘来判断具体的按键动作。支持的矩阵键盘最大范围为4×5,我们以英创公司的矩阵键盘模块为例:


英创Linux主板支持矩阵键盘.png


  使用的管脚为GPIO16~GPIO24,与ESMARC评估底板连接测试,连接方式如下:


英创Linux主板支持矩阵键盘.png


  具体的信号排列如下(CN18):


信号及说明PIN#信号及说明
GPIO16 -> 用作 ROW012GPIO17 -> 用作 COL0
GPIO18 -> 用作 ROW134GPIO19 -> 用作 COL1
GPIO20 -> 用作 ROW256GPIO21 -> 用作 COL2
GPIO22 -> 用作 ROW378GPIO23 -> 用作 COL3
GPIO24 -> 用作 ROW4910GPIO25
GPIO261112GPIO27
GPIO281314GPIO29
GPIO301516GPIO31
+5V1718+5V
GND1920GND


  英创公司已经将驱动文件制作成内核驱动模块的形式放入文件系统中了,驱动名称为matrix_keypad.ko,用户要使用该功能,只需要加载驱动模块即可。驱动模块放在/lib/modules/<kernel-version>/下面,例如使用ESM335x主板,因为ESM335x的内核版本为4.1.6,输入命令为:insmode /lib/modules/4.1.6/matrix_keypad.ko,注意如果使用的主板为ESM7000,ESM6802,ESM6800H或者ESM6800V系列的板卡,可以使用命令:modprobe matrix_keypad,不需要代入路径。


  为了避免占用不必要的硬件资源,根据实际的需求,在加载驱动模块的时候可以代入参数指定横列的数值,参数col(1-4)代表列数,row(1-5)代表横排数,如果不代入参数,默认为支持col=4,row=5的矩阵键盘。以ESM335x为例,加载支持3×3大小矩阵键盘的命令为insmod /lib/modules/4.1.6/matrix_keypad.ko col=3 row=3,如下如所示:


英创Linux主板支持矩阵键盘.png

加载驱动


  当代入col和row的值小于最大值的时候,驱动占用的管脚资源是从COL0和ROW0开始的,例如上面设置的3×3大小,占用的管脚为COL0-COL2,ROW0-ROW2,对应的按键如下图所示:


英创Linux主板支持矩阵键盘.png

3×3对应按键


  驱动加载成功后,会在/dev/input目录下生成对应的event设备节点,系统根据当前的event设备数,会自动为生成设备节点增加序号。以ESM335x为例,生成的设备节点为/dev/input/even1,如下图:


英创Linux主板支持矩阵键盘.png

设备节点


  图中event0为ESM335x主板自带的触摸屏设备节点,所以加载驱动后矩阵键盘的设备节点就会自动命名为event2,通过这个设备节点,就可以通过程序读取按键的键值了。其中每一个按键都有一个对应的键值,英创公司使用的是标准的WINDOWS按键消息值,为方便客户评估,矩阵键盘的虚拟键码与英创矩阵键盘扩展模块(ETA201)完全对应,具体的虚拟键码如下所示:


 COL0COL1COL2COL3
ROW0VK_ESCAPE/0x1BVK_0/0x60VK_PERIOD/0xBEVK_BACK/0x08
ROW1VK_ADD/0x6BVK_1/0x61VK_2/0x62VK_BACK/0x08
ROW2VK_SUBTRACT/0x6DVK_4/0x64VK_5/0x65VK_BACK/0x08
ROW3VK_MULTIPLY/0x6AVK_7/0x67VK_8/0x68VK_9/0x69
ROW4VK_DIVIDE/0x6FVK_SPACE/0x20VK_DECIMAL/0x6EVK_RETURN/0x0D


  与虚拟键码对应的16进制值可在MSDN上找到:http://msdn.microsoft.com/zh-cn/library/ms927178(en-us).aspx>/u?


  软件上十分简单,在程序中先通过open函数打开矩阵键盘对应的设备节点,然后通过read函数就可以读取出信息,具体代码如下:


struct input_event input;
int fd, rd;
//打开设备节点
if ((fd = open ("/dev/input/event1", O_RDONLY)) == -1)
{
       printf ("open failed!\n");
       return -1;
}
 
rd = read(fd, (void*)&input, sizeof(input));
if(rd <= 0)
       printf ("rd: %d\n", rd);


  在上面的代码中可以看到,读取出来的是一个input_event结构体,这是Linux系统标准都文件定义的结构体,通过这个结构体可以获取到我们所需要的所有信息,下面就来介绍一下这个结构体:


/*
 * The event structure itself
*/
structinput_event {
       struct timeval time;
       __u16type;
       __u16code;
       __s32value;
};


  其中time的值为按键时间。type为事件类型,因为驱动支持的是矩阵键盘,所以这里的值总是为EV_KEY(0x01)。code为键值,具体的键值请参考上面的表格。value为按键事件的值,在事件类型为EV_KEY时,当按键按下时值为1,松开时值为0。


  读取按键操作的完整代码如下:


int main (int argc, char *argv[])
{
       struct input_event input;
       int fd, rd;
 
       //打开设备节点
       if ((fd = open ("/dev/input/event1", O_RDONLY)) == -1)
       {
              printf ("open failed!\n");
              return -1;
       }
 
       printf("Press any key.\n");
 
       while(1)
       {
              memset((void*)&input, 0, sizeof(input));
              //读取input设备信息
              rd = read(fd, (void*)&input, sizeof(input));
              if(rd <= 0)
                     printf ("rd: %d\n", rd);
 
              //判断按键动作
              if(input.type == 1)
              {
                     switch(input.value)
                     {
                     case 0:
                            printf("Key release\n");
                            break;
                     case 1:
                            printf("Key press\n");
                            break;
                     case 2:
                            printf("Key hold\n");
                            break;
                     default:
                            printf("Undifined value\n");
                     }
                     //打印键值
                     printf("Code: 0x%x\n", input.code);
              }
       }
       return 0;
}
[lablebox]