参考文章
一、开发环境
- 软件平台:
Vivado 2018.2
Ubuntu18.04 虚拟机 - 硬件平台:
ZYNQ-MZ7100FA(米联客) - 编译环境(未使用PetaLinux):
参考《ZYNQ 修炼秘籍 LINUX 篇 基于 debian9 系统》
二、问题与解决方案
-
应用:
在用户层使用 axi_gpio,gpio设置为input,中断设置为上升沿触发,通过按键触发中断。
-
遇到问题:
按照参考文章中修改设备树后,/dev下无uio设备 -
解决方案:
- 在kernel/drivers/uio/uio_pdrv_genirq.c中第255行添加{.compatible = “generic-uio”}, 如下:
static struct of_device_id uio_of_genirq_match[] = {
{.compatible = "generic-uio"},
{ /* This is filled with module_parm */ },
{ /* Sentinel */ },
};
- 新建设备树文件system-user.dtsi(include SDK生成的设备树),内容如下:
(该工程使用SDK自动生成的设备树)
设备树中断号:uio0(29)和uio1(30)中断号是在axi_gpio中断号(31)基础上递减的(如果递增中断不会触发)。
/*
* CAUTION: This file is automatically generated by Xilinx.
* Version:
* Today is: Tue Sep 15 13:54:36 2020
*/
/include/ "system-top.dts"
/ {
amba_pl {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges ;
axi_gpio_0: gpio@41200000 {
#gpio-cells = <3>;
#interrupt-cells = <2>;
clock-names = "s_axi_aclk";
clocks = <&clkc 15>;
compatible = "generic-uio";
gpio-controller ;
interrupt-controller ;
interrupt-names = "ip2intc_irpt";
interrupt-parent = <&intc>;
interrupts = <0 31 1>;
reg = <0x41200000 0x10000>;
xlnx,all-inputs = <0x1>;
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>;
};
uio@0 {
compatible = "generic-uio";
status = "okay";
interrupt-controller;
interrupt-parent = <&intc>;
interrupts = <0 29 1>;
};
uio@1 {
compatible = "generic-uio";
status = "okay";
interrupt-controller;
interrupt-parent = <&intc>;
interrupts = <0 30 1>;
};
};
chosen {
bootargs = "uio_pdrv_genirq.of_id=generic-uio";
stdout-path = "serial0:115200n8";
};
};
三、测试程序
pin-uio-test.c
/*
* This application reads/writes GPIO devices with UIO.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
void usage(void)
{
printf("*argv[0] -d <UIO_DEV_FILE>\n");
printf(" -d UIO device file. e.g. /dev/uio0");
return;
}
int main(int argc, char *argv[])
{
int c;
int fd;
char *uiod;
unsigned i = 0;
unsigned icount;
int irq_on = 1;
int err;
printf("pin UIO test.\n");
while((c = getopt(argc, argv, "d:io:h")) != -1) {
switch(c) {
case 'd':
uiod=optarg;
break;
case 'h':
usage();
return 0;
default:
printf("invalid option: %c\n", (char)c);
usage();
return -1;
}
}
/* Open the UIO device file */
fd = open(uiod, O_RDWR);
if (fd < 1) {
perror(argv[0]);
printf("Invalid UIO device file:%s.\n", uiod);
usage();
return -1;
}
for(i = 0; ; ++i) {
/* Print out a message, for debugging. */
if (i == 0)
fprintf(stderr, "Started uio test driver.\n");
else
fprintf(stderr, "Interrupts: %d\n", icount);
/* enable IRQ, trigger the irqcontrol of driver */
write(fd, &irq_on, sizeof(irq_on));
/* Here we got an interrupt from the
device. Do something to it. */
err = read(fd, &icount, 4);
if (err != 4) {
perror("uio read:");
break;
}
}
return 0;
}
gpio-uio-test.c
/*
* This application reads/writes GPIO devices with UIO.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#define IN 0
#define OUT 1
#define GPIO_MAP_SIZE 0x10000
#define GPIO_DATA_OFFSET 0x00
#define GPIO_TRI_OFFSET 0x04
#define GPIO2_DATA_OFFSET 0x08
#define GPIO2_TRI_OFFSET 0x0C
#define GIER 0x011C
#define IP_IER 0x0128
#define IP_ISR 0x0120
void usage(void)
{
printf("*argv[0] -d <UIO_DEV_FILE> -i|-o <VALUE>\n");
printf(" -d UIO device file. e.g. /dev/uio0");
printf(" -i Input from GPIO\n");
printf(" -o <VALUE> Output to GPIO\n");
return;
}
int main(int argc, char *argv[])
{
int c;
int fd;
int direction=IN;
char *uiod;
int value = 0;
int valued = 0;
int irq_on = 1;
unsigned int icount;
int err;
void *ptr;
printf("GPIO UIO test.\n");
while((c = getopt(argc, argv, "d:io:h")) != -1) {
switch(c) {
case 'd':
uiod=optarg;
break;
case 'i':
direction=IN;
break;
case 'o':
direction=OUT;
valued=atoi(optarg);
break;
case 'h':
usage();
return 0;
default:
printf("invalid option: %c\n", (char)c);
usage();
return -1;
}
}
/* Open the UIO device file */
fd = open(uiod, O_RDWR);
if (fd < 1) {
perror(argv[0]);
printf("Invalid UIO device file:%s.\n", uiod);
usage();
return -1;
}
/* mmap the UIO device */
ptr = mmap(NULL, GPIO_MAP_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
/* Print Interrupt Registers */
value = *((unsigned *) (ptr + GIER));
printf("%s: GIER: %08x\n",argv[0], value);
value = *((unsigned *) (ptr + IP_IER));
printf("%s: IP_IER: %08x\n",argv[0], value);
value = *((unsigned *) (ptr + IP_ISR));
printf("%s: IP_ISR: %08x\n",argv[0], value);
/* Enable All Interrupts */
printf("%s: Enable All Interrupts in Regs\n", argv[0]);
/*AXI GPIO中断:只要有电平变化就会触发中断*/
*((unsigned *)(ptr + GIER)) = 0x80000000;//使能AXI GPIO IP核全局中断
*((unsigned *)(ptr + IP_IER)) = 0x1;//使能通道1中断
*((unsigned *)(ptr + IP_ISR)) = 0x1;//清中断
while(1){
/* Enable UIO interrupt */
write(fd, &irq_on, sizeof(irq_on));
if (direction == IN) {
/* Read from GPIO */
*((unsigned *)(ptr + GPIO_TRI_OFFSET)) = 255;
err = read(fd, &icount, 4);
if (err != 4) {
perror("uio read:");
break;
}
value = *((unsigned *) (ptr + GPIO_DATA_OFFSET));
if(value){
printf("\n%s: Interrupts: %d\n", argv[0], icount);
printf("%s: input: %08x\n",argv[0], value);
/* Print Interrupt Registers */
value = *((unsigned *) (ptr + GIER));
printf("%s: GIER: %08x\n",argv[0], value);
value = *((unsigned *) (ptr + IP_IER));
printf("%s: IP_IER: %08x\n",argv[0], value);
value = *((unsigned *) (ptr + IP_ISR));
printf("%s: IP_ISR: %08x\n",argv[0], value);
}
*((unsigned *)(ptr + IP_ISR)) = 0x1;//清中断
}
}
munmap(ptr, GPIO_MAP_SIZE);
return 0;
}
四、运行结果
说明:由于AXI_GPIO 输入电平变化一次就触发一次中断,所以按键按下和松开都会触发一次中断,测试程序中检测输入电平为1才打印一次,所以中断号是间隔的