软件环境:vivado 2017.4 硬件平台:XC7Z020
这篇主要说下PL端由按钮产生的中断,在Linux系统下是怎样获取和处理的,工程依旧没变过还是那个工程。
然后设备树这里,其实Xilinx提供了两个button的驱动是可以直接加载使用的,分别是gpio-keys和gpio-keys-polled,这两个驱动在kernel中的位置是/kernel/drivers/input/keyboard/gpio_keys.c和gpio_keys_polled.c,主要差别是,使用gpio-keys驱动时按键的按下是可以给系统产生中断事件的,换句话说,你应用里open /dev/input/event0可以接收到输入事件,或者cat /proc/interrupts查看对应中断事件的计数是有变化的,另一方面,gpio-keys-polled驱动更类似于矩阵键盘,不使用gpio中断,依靠在配置设备树时候给不同的按键配置不同的code码,使用时不停的轮询,当有按键按下时,通过轮询区分不同code码来判断当前按下的是什么按键。这里以gpio-keys-polled来说一下整个操作过程。设备树如下。
/*
* CAUTION: This file is automatically generated by Xilinx.
* Version:
* Today is: Thu Aug 8 10:09:43 2019
*/
/ {
amba_pl: amba_pl {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges ;
axi_gpio_button: gpio@41210000 {
#gpio-cells = <2>;
#interrupt-cells = <2>;
clock-names = "s_axi_aclk";
clocks = <&clkc 15>;
compatible = "xlnx,xps-gpio-1.00.a";
gpio-controller ;
interrupt-controller ;
interrupt-names = "ip2intc_irpt";
interrupt-parent = <&intc>;
interrupts = <0 29 4>;
reg = <0x41210000 0x10000>;
xlnx,all-inputs = <0x0>;
xlnx,all-inputs-2 = <0x0>;
xlnx,all-outputs = <0x0>;
xlnx,all-outputs-2 = <0x0>;
xlnx,dout-default = <0x00000000>;
xlnx,dout-default-2 = <0x00000000>;
xlnx,gpio-width = <0x2>;
xlnx,gpio2-width = <0x20>;
xlnx,interrupt-present = <0x1>;
xlnx,is-dual = <0x0>;
xlnx,tri-default = <0xFFFFFFFF>;
xlnx,tri-default-2 = <0xFFFFFFFF>;
};
gpio-keys-polled {
compatible = "gpio-keys-polled";
#address-cells = <1>;
#size-cells = <0>;
poll-interval = <20>;
up {
label = "up";
gpios = <&axi_gpio_button 0 0>;
linux,code = <103>;
};
down {
label = "down";
gpios = <&axi_gpio_button 1 0>;
linux,code = <108>;
};
};
};
};
对了忘了说了,不论是gpio-led或者gpio-keys,只要涉及gpio操作的,生成设备树的时候,默认生成的#gpio-cells = <3>,记得切记切记切记要把3改成2,不然系统启动以后使用会有问题的。下面gpio-keys-polled是手动添加的,修改compatible后面双引号部分,来修改需要加载的驱动,因为我板子pl部分只有两个按键,所以这里只添加了up和down,更多的linux,code的对应关系可以在/kernel/include/uapi/linux/input-event-codes.h中得到。
另外还需要注意的是,在使用这两个驱动的时候,记得在配置kernel的时候,将如下部分使能。
Device Driver
----GPIO Support
--------Xilinx GPIO support
----Input device support
--------Keyboards
------------GPIO Buttons
------------Polled GPIO buttons
接下来测试代码贴一下,当有按键按下的时候,打印当前按的是哪个键。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <linux/input.h>
#define INPUT_EVENT "/dev/input/event0"
int main(void)
{
struct input_event ev;
int fd;
int key_code;
int size = sizeof(ev);
/* Read event0 */
fd = open(INPUT_EVENT, O_RDONLY);
if (fd < 0) {
printf("\nOpen " INPUT_EVENT " failed!\n");
return 1;
}
/* Read and parse event, update global variable */
while (1) {
if (read(fd, &ev, size) < size) {
printf("\nReading from " INPUT_EVENT " failed!\n");
return 1;
}
if (ev.value == 1 && ev.type == 1) {
key_code = ev.code;
if (key_code == KEY_DOWN) {
printf("key input KEY_DOWN \n");
} else if (key_code == KEY_UP) {
printf("key input KEY_UP \n");
}
usleep(1000);
}
}
}
/dev/input/event0对应的就是键盘的输入,通过对event0句柄的不断读取,来判断当前是否有值按下和按的是哪个键,说到这就不得不说下input_event这个结构体,它在/kernel/include/uapi/linux/input.h中有定义。
struct input_event {
struct timeval time; //按键时间
__u16 type; //按键类型
__u16 code; //按键值,什么键被按下
__s32 value; //是按下还是释放
};
code就是在/kernel/include/uapi/linux/input-event-codes.h中定义的按键对应的值,和设备树中注册的按键要有对应关系,type:
EV_KEY代表键盘,EV_REL,代表相对坐标,EV_ABS代表绝对坐标,同样在input-event-codes.h中都能看到定义。
value是按键事件的值,如果判断的对象ev.type是键盘(ev.type == EV_KEY),那么,当按键按下时,ev.value的值是1,当按键松开,ev.value的值是0 。