linux输入设备自定义键盘input输入子系统gpio-keys按键驱动(代码片段)

“逛丢一只鞋” “逛丢一只鞋”     2022-12-10     547

关键词:

前言

设计板需要提供六个按键进行人机交互,起初准备使用CH455G键盘扫描芯片,设计键盘电路

在这里插入图片描述

随着旋转编码器Linux内核驱动的调试成功

Linux 输入设备调试详解(零基础开发)Rotary_Encoder旋转编码器模块(EC11)通用GPIO为例 挂载input输入子系统

对于更加简单,参考资料更加多的按键输入,可以尝试使用Linux内核驱动来解决,减少外围电路的设计负担,以及调试i2c的痛苦

有了之前调试旋转编码器的经验,这次也是信心十足,但整个过程还是花费了三天左右的时间,并且中间犯了一个常识性错误

查看include/uapi/linux/input.h

对于按键,先准备好input.h文件

include/uapi/linux/input.h

在这里插入图片描述
在这里插入图片描述

设备树添加

要使用Linux 内核自带的按键驱动程序很简单, 只需要根据Documentation/devicetree/bindings/input/gpio-keys.txt 这个文件在设备树中添加指定的设备节点即可,节点要求如下:

①、节点名字为“gpio-keys”。

②、gpio-keys 节点的compatible 属性值一定要设置为“gpio-keys”。

③、所有的KEY 都是gpio-keys 的子节点,每个子节点可以用如下属性描述自己:
gpios:KEY 所连接的GPIO 信息。
interrupts:KEY 所使用GPIO 中断信息,不是必须的,可以不写。
label:KEY 名字
linux,code:KEY 要模拟的按键,也就是示例代码58.1.2.4 中的这些按键。

④、如果按键要支持连按的话要加入autorepeat。

在设备树中添加所需

	gpio-keys 
		compatible = "gpio-keys";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_gpio_keys>;
		autorepeat;

		key-up 
			label = "key-up";
			gpios = <&gpio3 29 GPIO_ACTIVE_LOW>;
			gpio-key,wakeup;
			linux,code = <KEY_UP>;							// 103
		;

		key-down 
			label = "key-down";
			gpios = <&gpio1 4 GPIO_ACTIVE_LOW>;
			gpio-key,wakeup;
			linux,code = <KEY_DOWN>;						// 108
		;

		key-left 
			label = "key-left";
			gpios = <&gpio1 5 GPIO_ACTIVE_LOW>;
			gpio-key,wakeup;
			linux,code = <KEY_LEFT>;						// 105
		;

		key-right 
			label = "key-right";
			// gpios = <&gpio5 30 GPIO_ACTIVE_LOW>;			// MX6QDL_PAD_CSI0_DAT12__GPIO5_IO30
			// gpios = <&gpio6 1 GPIO_ACTIVE_LOW>;			// MX6QDL_PAD_CSI0_DAT15__GPIO6_IO01
			gpios = <&gpio2 20 GPIO_ACTIVE_LOW>;			// MX6QDL_PAD_EIM_A18__GPIO2_IO20
			gpio-key,wakeup;	
			linux,code = <KEY_RIGHT>;						// 106
		;

		key-enter 
			label = "key-enter";
			// gpios = <&gpio5 31 GPIO_ACTIVE_LOW>;			// MX6QDL_PAD_CSI0_DAT13__GPIO5_IO31
			// gpios = <&gpio6 2 GPIO_ACTIVE_LOW>;			// MX6QDL_PAD_CSI0_DAT16__GPIO6_IO02
			gpios = <&gpio2 18 GPIO_ACTIVE_LOW>;			// MX6QDL_PAD_EIM_A20__GPIO2_IO18
			gpio-key,wakeup;	
			linux,code = <KEY_ENTER>;						// 28
		;

		key-esc 
			label = "key-esc";
			// gpios = <&gpio6 0 GPIO_ACTIVE_LOW>;			// MX6QDL_PAD_CSI0_DAT14__GPIO6_IO00
			// gpios = <&gpio6 3 GPIO_ACTIVE_LOW>;			// MX6QDL_PAD_CSI0_DAT17__GPIO6_IO03
			gpios = <&gpio2 17 GPIO_ACTIVE_LOW>;			// MX6QDL_PAD_EIM_A21__GPIO2_IO17	
			gpio-key,wakeup;
			linux,code = <KEY_ESC>;							// 1
		;
		/*EC11按键添加*/
		key-ec11 
			label = "key-ec11";
			gpios = <&gpio2 22 GPIO_ACTIVE_LOW>;			// MX6QDL_PAD_EIM_A16__GPIO2_IO22	
			gpio-key,wakeup;
			linux,code = <KEY_1>;							// 2
		;

	;

注意:KEY 按键信息,名字设置为“key-enter”,对于这个按键linux,code = <KEY_ENTER>;,也就是回车键,效果和键盘上的回车键一样。KEY_ENTER的宏定义就在之前所说include/uapi/linux/input.h

添加pinctrl子系统信息

		pinctrl_gpio_keys: gpio_keysgrp 
			fsl,pins = <
				MX6QDL_PAD_EIM_D29__GPIO3_IO29 0x1b0b0
				MX6QDL_PAD_GPIO_4__GPIO1_IO04  0x1b0b0
				MX6QDL_PAD_GPIO_5__GPIO1_IO05  0x1b0b0

				/* 2021.07.19
				 * 添加三个key 右、确定、取消
				 */
				MX6QDL_PAD_EIM_A18__GPIO2_IO20  0x1b0b0
				MX6QDL_PAD_EIM_A20__GPIO2_IO18  0x1b0b0
				MX6QDL_PAD_EIM_A21__GPIO2_IO17  0x1b0b0

				/*
				 * 2021.07.21
				 * 添加 EC11按键
				 */
				MX6QDL_PAD_EIM_A16__GPIO2_IO22      			0x1b0b0		
			>;
		;

保存,编译dtb

make dtbs -32

在这里插入图片描述
在输出信息中可以看到,生成了我需要的imx6q-c-sabresd.dtb设备树文件

make menuconfig驱动

make menuconfig

搜索到了我们需要的普通GPIO的按键驱动
在这里插入图片描述

-> Device Drivers
	-> Input device support
		-> Generic input layer (needed for keyboard, mouse, ...) (INPUT [=y])
			-> Keyboards (INPUT_KEYBOARD [=y])
				->GPIO Buttons

在这里插入图片描述
选中以后就会在.config 文件中出现“CONFIG_KEYBOARD_GPIO=y”这一行,Linux 内核就会根据这一行来将KEY 驱动文件编译进Linux 内核。Linux 内核自带的KEY 驱动文件为drivers/input/keyboard/gpiokeys.cgpio_keys.c 采用了platform 驱动框架,在KEY 驱动上使用了input 子系统实现。

nano drivers/input/keyboard/gpio_keys.c

在这里插入图片描述
找到了驱动,按照调试内核驱动的老规矩,首先我们在内核驱动中先不开启gpio-keys驱动,而是提取出来编译成ko模块进行测试

所以在make menuconfig 找到驱动的位置后,取消掉驱动,然后编译,生成zImage文件

现在就可以把dtb文件和zImage文件烧录进板子了

编译ko模块

在编译驱动之前,先给驱动加上各种printk打印信息
在这里插入图片描述
处理完驱动,然后编写makefile

#	                 ,%%%%%%%%,
#	               ,%%/\\%%%%/\\%%
#	              ,%%%\\c''''J/%%%
#	    %.        %%%%/ o  o \\%%%
#	    `%%.      %%%%       |%%%
#	     `%%      `%%%%(__Y__)%%'
#	     //        ;%%%%`\\-/%%%'
#	     ((      /   `%%%%%%%'
#	      \\\\     .'           |
#	       \\\\   /        \\  | |
#	        \\\\/          ) | |
#	         \\          /_ | |__
#	         (____________))))))) 攻城狮 

# 调试驱动和应用程序用Makefile

# 编译模块

# 开发板Linux内核的实际路径 
# KDIR变量
KDIR:=/work/linux-4.1.15

#  获取当前目录
PWD:=$(shell pwd)

# obj-m表示将 chrdevbase.c这个文件 编译为 name.ko模块。
obj-m += gpio_keys.o

# 编译成模块
all:
	make -C $(KDIR) M=$(PWD) modules
clean:
	make -C $(KDIR) M=$(PWD) clean


大功告成,执行make,编译成ko模块
在这里插入图片描述
发送到板子

scp gpio_keys.ko root@192.168.0.232:/work

在这里插入图片描述
在这里插入图片描述

查看驱动运行状态

运行ko文件
在这里插入图片描述
没有任何错误,非常的完美,运气非常的不错

但是为了谨慎,还是要查看驱动的加载情况

查看/proc/bus/input/devices设备

查看/proc/bus/input/devices

nano /proc/bus/input/devices

可以看到我们gpio-keys
在这里插入图片描述

查看dev/input

在input节点下,可以看到我们的event2
在这里插入图片描述
从驱动加载情况上来看,驱动正常运行,没有问题

按键测试

这里测试按键的输入有好多种方法,这里列举几个

hexdump

hexdump /dev/input/event2

可以看到信号的输入,但是没有办法直观的判断是哪个按键的
在这里插入图片描述

应用程序

这里有一个简单的应用程序


//                          _ooOoo_                               //
//                         o8888888o                              //
//                         88" . "88                              //
//                         (| ^_^ |)                              //
//                         O\\  =  /O                              //
//                      ____/`---'\\____                           //
//                    .'  \\\\|     |//  `.                         //
//                   /  \\\\|||  :  |||//  \\                        //
//                  /  _||||| -:- |||||-  \\                       //
//                  |   | \\\\\\  -  /// |   |                       //
//                  | \\_|  ''\\---/''  |   |                       //
//                  \\  .-\\__  `-`  ___/-. /                       //
//                ___`. .'  /--.--\\  `. . ___                     //
//              ."" '<  `.___\\_<|>_/___.'  >'"".                  //
//            | | :  `- \\`.;`\\ _ /`;.`/ - ` : | |                 //
//            \\  \\ `-.   \\_ __\\ /__ _/   .-` /  /                 //
//      ========`-.____`-.___\\_____/___.-`____.-'========         //
//                           `=---='                              //
//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //
//            佛祖保佑       永不宕机     永无BUG                 //


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>

int main(int argc, char *argv[])

       struct input_event in_ev = 0;
       int fd = -1;

       /* 校验传参 */
       if (2 != argc) 
              fprintf(stderr, "usage: %s <input-dev>\\n", argv[0]);
              exit(-1);
       

       /* 打开文件 */
       if (0 > (fd = open(argv[1], O_RDONLY))) 
              perror("open error");
              exit(-1);
       

       for ( ; ; ) 

              /* 循环读取数据 */
              if (sizeof(struct input_event) !=
                read(fd, &in_ev, sizeof(struct input_event))) 
                     
                     perror("read error");
                     exit(-1);
              

              printf("type:%d code:%d value:%d\\n",
                in_ev.type, in_ev.code, in_ev.value);
       

执行交叉编译

$CC gpio_key_app.c -o gpio_key_app

在这里插入图片描述

通过网络发送到板子上

scp gpio_key_app root@192.168.0.232:/work

在这里插入图片描述
运行应用程序

 ./gpio_key_app /dev/input/event2

按动我们的按键,就可以看到按键的数据
在这里插入图片描述
为了可以更直观一些,可以使用这个应用程序

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>
#define NOKEY 0

int main()

	int keys_fd;	
        char ret[2]; 
	struct input_event t;
	char *dev;

    setvbuf(stdout, (char *)NULL, _IONBF, 0);//disable stdio out buffer;
//	dev = getenv("KEYPAD_DEV");
      
    keys_fd = open("/dev/input/keypad", O_RDONLY);
	if(keys_fd<=0)
	
        printf("open %s device error!\\n",dev);
		return 0;
	

	while(1)
		
            if(read(keys_fd,&t,sizeof(t))==sizeof(t)) 
		    if(t.type==EV_KEY)
			if(t.value==0 || t.value==1)
			
				//printf("%d \\n", t.code);
				switch(t.code)
				
					case 103:
			    		printf("key103 key-up %s\\n",(t.value)?"Presse":"Released");
			    	break;
			    	
			    	case 108:
			    		printf("key108 key-down %s\\n",(t.value)?"Pressed":"Released");
			    	break;
			    	
			    	case 106:
			    		printf("key106 key-right %s\\n",(t.value)?"pressed":"Released");
			    	break;  

					case 105:
						printf("key105 key-left %s\\n",(t.value)?"Released":"Pressed");
					break;
			    	
			    	case 28:
			    		printf("key28 key-enter %s\\n",(t.value)?"Pressed":"Released");
			    	break;
			    	
			    	case 1:
			    		printf("key1 key-esc %s\\n",(t.value)?"Released":"Pressed");
			        break;

					case 2:
			    		printf("key2 key-ec11 %s\\n",(t.value)?"Released":"Pressed");
			        break;
	    	
			    	default:
			    		break;
			    
			
		
		
	close(keys_fd);
	
        return 0;



编译

$CC key_app.c -o key_app

下载

 scp key_app root@192.168.0.232:/work

在这里插入图片描述
这个应用程序中已经调用了键盘的设备节点,所以直接打开就可以使用

 ./key_app

在这里插入图片描述

过程中问题及解决方法

在实际调试过程中显然没有这么顺利,过程中遇到了很多的问题,这里集中在一起进行记录

常见问题

①、是否使能Linux 内核KEY 驱动。

②、设备树中gpio-keys 节点是否创建成功。

③、在设备树中是否有其他外设也使用了KEY 按键对应的GPIO,但是我们并没有删除掉这些外设信息。检查Linux 启动log 信息,看看是否有类似下面这条信息:

gpio-keys gpio_keys:Failed to request GPIO 18, error -16

上述信息表示GPIO 18 申请失败,失败的原因就是有其他的外设正在使用此GPIO。

正常则是
在这里插入图片描述

按键硬件注意

对于按键的使用,这里一定要注意,需要拉高使用,拉高之后,电平才会稳定,按键的电平监测也会稳定,也不容易出现干扰
在这里插入图片描述
起初我就是没有意识到按键电平需要拉高的问题,板子上很多GPIO没有办法很好的按照理论拉高,还有就是因为使用了内核驱动,所以对于GPIO的内部操作是黑箱操作,不知道GPIO的电平究竟发生了什么

在对按键进行拉高之后,按键程序立刻有了输入

input子系统(代码片段)

...的输入信息的输入设备的呢?其实就是通过input输入子系统这套软件体系来完成的。从整体上来说,input输入子系统分为3层:上层(输入事件处理层)、 查看详情

输入子系统------键盘按键驱动程序(代码片段)

由上一节的输入子系统的框架分析可知,其分三层:设备驱动层,核心层,事件驱动层我们在为某种设备的编写驱动层,只需要关心设备驱动层,即如何驱动设备并获得硬件数据(如按下的按键数据),然后调用核心层提供的接... 查看详情

输入子系统

Linux的输入子系统不仅支持鼠标、键盘等常规的输入设备,而且还支持蜂鸣器、触摸屏等设备输入子系统又叫input子系统。其构建非常灵活,只需要调用一些简单的函数,就可以将一个输入设备的功能呈现给应用程序 #defineEV_S... 查看详情

android:自己开发应用,里边想自定义输入法键盘布局,但输入法服务仍然用系统输入法服务,应该怎么做?

Android:自己开发一个应用,里边想自定义输入法键盘布局,但输入法服务仍然用系统输入法服务,应该怎么做?有个“多多中文”网,里面有输入法生成器平台,免费下载,可以满足你提出的这些要求参考技术A要么自定义一个键... 查看详情

linuxinput子系统详解(代码片段)

1.模块概述1.1.相关资料和代码研究drivers/input/include/uapi/linux/input-event-codes.h2.模块功能linux核心的输入框架3.模块学习3.1.概述Linux输入设备种类繁杂,常见的包括触摸屏、键盘、鼠标、摇杆等;这些输入设备属于字符设备... 查看详情

input子系统

nput子系统简介structinput_event:对所有的输入类设备向应用层上报自己发生的输入事件做统一管理。(1)linux的input子系统解决了:不同的硬件在驱动层和应用层之间的信息的传输,最终能让应用层感知到所有的输入类设备所发生的... 查看详情

linux输入设备调试详解(零基础开发)rotary_encoder旋转编码器模块(ec11)通用gpio为例挂载input输入子系统(代码片段)

文章目录前言什么是Linux输入设备input输入子系统input输入子系统简介输入子系统设备驱动层实现原理input_dev、input_handler、input_handleinput_devinput_handlerinput_handle为什么用input_handle来关联input_dev和input_handler而不将input_dev和input_handler... 查看详情

linux学习笔记10——管道和重定义

计算机的组成有:运算器,控制器,存储器,输入输出设备。于是就有了地址总线,数据总线,控制总线。事实上在机器里面总线是复用的,一个总线三种功能。地址总线:内存寻址数据总线:传输数据控制总结:控制指令寄存... 查看详情

linux驱动开发input子系统(代码片段)

input子系统负责处理输入事件,属于字符设备。input子系统分为input驱动层、input核心层和input事件层。系统启动后,在/sys/class/input/路径下显示input子系统。input驱动层:输入设备的具体驱动程序,如按键驱动,... 查看详情

从自定义键盘复制/粘贴剪贴板

...问题描述】:我正在努力寻找在“其他系统应用程序”的输入字段上复制/粘贴图像的解决方案特别是,我创建了自己的自定义键盘,我可以在其中按下表情符号按钮。此时,将显示一个面板并列出一组图像(存储在设备上)。... 查看详情

如何使用应用内自定义键盘的按钮输入文本

】如何使用应用内自定义键盘的按钮输入文本【英文标题】:Howtoinputtextusingthebuttonsofanin-appcustomkeyboard【发布时间】:2016-02-0312:30:42【问题描述】:我制作了一个应用内自定义键盘,它取代了系统键盘,并在我点击UITextField时弹... 查看详情

输入子系统(input)框架解析(基于linux3.4.2)

我们自己写驱动的流程一般是:自己确定或由系统自动分配主设备号;建立fops结构;使用register_chrdev在初始化函数中进行注册;定义入口函数MODULE_INIT()和出口函数MODULE_EXIT()。但这种我们自己写的驱动程序,只有自己可以调用。... 查看详情

androiddialog中软键盘遮挡输入编辑框edittextt

参考技术A自定义dialog布局中有编辑框,点击弹出软键盘后软键盘遮挡编辑框1,需要设置dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN|WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);2,将dilog的自定义布局外层用ScrollView包... 查看详情

linux应用开发第三章输入系统应用开发(代码片段)

文章目录3输入系统应用开发3.1什么是输入系统?3.2输入系统应用框架描述3.3输入系统事件的读取与分析3.4输入系统应用编程实战一:通用USB鼠标事件读取3.5输入系统应用编程实战二:通用键盘事件读取3.6输入系统应... 查看详情

自定义键盘附件视图输入

】自定义键盘附件视图输入【英文标题】:CustomKeyboardAccessoryviewinput【发布时间】:2020-06-0721:34:39【问题描述】:我创建了一个自定义附件视图来补充标准ApplealphaiOS键盘。目的是添加一行数字键,以防止在键盘视图之间来回翻转... 查看详情

iOS 键盘扩展 - 向标准键盘添加按钮(系统范围的输入附件视图)

】iOS键盘扩展-向标准键盘添加按钮(系统范围的输入附件视图)【英文标题】:iOSKeyboardextension-addingbuttonstostandardkeyboard(system-wideinputaccessoryview)【发布时间】:2018-02-1814:00:13【问题描述】:基于docs我知道我可以通过实现键盘扩... 查看详情

input子系统

linux中input子系统的编程模型:命令行界面的输入类设备应用接口GUI界面structinput_eventstructinput_event{ structtimevaltime; __u16type; __u16code; __s32value;};通过该结构体将输入类设备的类别、特征等相关信息记录下来。为什么需要input子系统?... 查看详情

自定义键盘:从输入视图中获取文本

】自定义键盘:从输入视图中获取文本【英文标题】:CustomKeyboard:gettextfrominputview【发布时间】:2014-12-0620:24:16【问题描述】:我开发了一个iOS8的自定义键盘应用程序。而且我无法从输入视图中获取文本。任何帮助将不胜感激。... 查看详情