ZYNQ中的UIO驱动和中断程序学习【Xilinx-Petalinux学习】_uio 及其中断

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

$ petalinux-build -c device-tree

中间有可能出现错误,一种情况是缺少libstdc++和glibc-devel,使用下面命令安装

yum install libstdc++.i686
yum install glibc-devel.i686 

还有一种情况就是在上面Vivado工程中提到的我们需要自己生成fsbl和pmufw文件,否则有时候会错误。当然我们刚才在petalinux-config中已经取消了它们的编译,这里应该不会出现这种情况。

在文件./components/plnx_workspace/device-tree-generation/pl.dtsi 中可以查看PL侧的设备树信息,里面包含了axi_gpio_0的设备树,需要用到。

打开文件./project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi
我们需要修改的设备树信息必须放在这个文件中才能有效,其他位置修改是不支持的。

注意:
假如需要添加其他自定义设备树文件,如直接从Xilinx提供的开发板BSP中会有对应板子的dtsi文件,首先需要在system-user.dtsi中加入文本
/include/ “board-conf.dtsi”
其次需要修改./project-spec/meta-user/recipes-bsp/device-tree/device-tree-generation_%.bbappend 这个文件,加入文本
file://board-user.dtsi \
两个必须同时修改了,才有效果,否则会出错说找不到文件。

下面我们来往system-conf.dtsi中加信息。
ZCU102

/include/ "system-conf.dtsi"
/ {
    amba_pl@0 {
        #address-cells = <2>;
        #size-cells = <2>;
        compatible = "simple-bus";
        ranges ;
        gpio@a0000000 {
            #gpio-cells = <2>;
            #interrupt-cells = <2>;
            compatible = "generic-uio";
            gpio-controller ;
            interrupt-controller ;
            interrupt-parent = <&gic>;
            interrupts = <0 93 4>;
            reg = <0x0 0xa0000000 0x0 0x10000>;
            xlnx,all-inputs = <0x0>;
            xlnx,all-inputs-2 = <0x0>;
            xlnx,all-outputs = <0x1>;
            xlnx,all-outputs-2 = <0x0>;
            xlnx,dout-default = <0x00000000>;
            xlnx,dout-default-2 = <0x00000000>;
            xlnx,gpio-width = <0x8>;
            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 = <&gic>;
            interrupts = <0 89 1>;
        };

        uio@1 {
            compatible = "generic-uio";
            status = "okay";
            interrupt-controller;
            interrupt-parent = <&gic>;
            interrupts = <0 90 2>;
        };

        uio@2 {
            compatible = "generic-uio";
            status = "okay";
            interrupt-controller;
            interrupt-parent = <&gic>;
            interrupts = <0 91 4>;
        };

        uio@3 {
            compatible = "generic-uio";
            status = "okay";
            interrupt-controller;
            interrupt-parent = <&gic>;
            interrupts = <0 92 8>;
        };
    };

    chosen {        
        bootargs = "earlycon clk_ignore_unused uio_pdrv_genirq.of_id=generic-uio";
        stdout-path = "serial0:115200n8";
    };
};

&uart1
{
    status = "disabled";
};

ZedBoard

/include/ "system-conf.dtsi"
/ {
    amba_pl {
        #address-cells = <1>;
        #size-cells = <1>;
        compatible = "simple-bus";
        ranges ;

        gpio@41200000 {
            #gpio-cells = <2>;
            #interrupt-cells = <2>;
            compatible = "generic-uio";
            gpio-controller ;
            interrupt-controller ;
            interrupt-parent = <&intc>;
            interrupts = <0 33 4>;
            reg = <0x41200000 0x10000>;
            xlnx,all-inputs = <0x0>;
            xlnx,all-inputs-2 = <0x0>;
            xlnx,all-outputs = <0x1>;
            xlnx,all-outputs-2 = <0x0>;
            xlnx,dout-default = <0x00000000>;
            xlnx,dout-default-2 = <0x00000000>;
            xlnx,gpio-width = <0x8>;
            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 2>;
        };

        uio@2 {
            compatible = "generic-uio";
            status = "okay";
            interrupt-controller;
            interrupt-parent = <&intc>;
            interrupts = <0 31 4>;
        };

        uio@3 {
            compatible = "generic-uio";
            status = "okay";
            interrupt-controller;
            interrupt-parent = <&intc>;
            interrupts = <0 32 8>;
        };
    };

    chosen {
        bootargs = "console=ttyPS0,115200 earlyprintk uio_pdrv_genirq.of_id=generic-uio";
        stdout-path = "serial0:115200n8";
    };

};

两个板子的内容不太一样,跟ZynqMP和Zynq的区别有关系,在这里不用太去考虑。只考虑dtsi文件中相同的地方。

首先因为我们四个中断号都没有硬件IP,所以PetaLinux并没有在pl.dtsi中给他们生成设备树信息,所以我们需要受动添加,如

uio@0 {
    compatible = "generic-uio";
    status = "okay";
    interrupt-controller;
    interrupt-parent = <&intc>;
    interrupts = <0 29 1>;
};

看interrupts = <0 29 1> 这个信息。
29代表了中断号,这个中断号是系统硬件中断号减去32得到的,1代表中断类型为上升沿触发。具体的可以去网上查一下。

看amba_pl下gpio@********中,跟PL.dtsi不同,将compatible的”xlnx,xps-gpio-1.00.a”改为了”generic-uio”,这样就将此axi_gpio_0改为了UIO的驱动类型。

看chosen的bootargs中增加了”uio_pdrv_genirq.of_id=generic-uio”。

ZynqMP和Zynq的一个区别需要注意,ZynqMP的interrupt-parent指向的是&gic,而Zynq指向了&intc。其实可以再看看其他的dtsi文件,可以发现,intc其实也是指向了cpu的gic,所以说实际上是一样的,并没有使用PL侧的INTC IP核中断。

ZynqMP还需要将uart1的status设置为disabled,不进行这个配置的话,系统会卡死在下面的log处

[    0.008300] Console: colour dummy device 80x25
[    0.012558] console [tty0] enabled
[    0.015924] bootconsole [cdns0] disabled

将uart1 disabled掉就可以正常启动了。具体原因不太清楚,在这里就先这样处理了。在ug1209中的说明也中关闭了uart1。

下面就可以进行编译了

$ petalinux-build

系统生成了新的文件目录/images/linux,将之前使用Xilinx SDK生成的fsbl.elf和pmufw.elf复制到到这个文件夹中。
生成boot.bin

//对于Zynq
$ petalinux-package --boot --fsbl=./images/linux/fsbl.elf --fpga --u-boot --force
//对于ZynqMP
$ petalinux-package --boot --fsbl=./images/linux/fsbl.elf --fpga --atf --pmufw --u-boot

//保存pre-built
$ petalinux-package --prebuilt --fpga ./images/linux/zed_video_wrapper.bit --force

将/images/linux目录下的boot.bin和image.ub复制到SD卡上,插到ZCU102上,启动板子。

输入用户名root,密码root
查看uio设备是否正常
这里写图片描述

发现少了两个uio
看前面的系统log,发现下降沿触发和低电平触发中断不可用,SW1和SW3不能用
这里写图片描述

只有AXI GPIO,SW0,SW2的中断可用
这里写图片描述

向上拨动SW0和SW2
这里写图片描述

再次拨动开关不能计数
是因为uio的终端处理函数被关闭了,需要调用write来重新打开

可以查看内核中的源码uio_pdrv_genirq.c和介绍https://01.org/linuxgraphics/gfx-docs/drm/driver-api/uio-howto.html
这里写图片描述

何晔老师写到

在结合驱动代码./drviver/uio/uio_pdrv_genirq.c)可知,每个UIO设备会有对应的/dev/uioX的设备节点。用户态驱动程序的读操作会阻塞直到UIO硬件中断发生。UIO的中断处理程序uio_pdrv_denirq_handler()会关闭该硬件中断。用户态驱动程序需要通过write函数来触发uio_pdrv_genirq_irqcontrol()以完成中断的使能和关闭。

使用echo 0x1 > /dev/uio1来写入,重新开启uio中断。
这里写图片描述

从何老师那里拷贝过来了两个测试代码,pin-uio-test.c和gpio-uio-test.c。对gpio-uio-test.c进行了修改,看了看LED灯的测试。

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;

    void *ptr;

    printf("GPIO UIO test.\n");
    while((c = getopt(argc, argv, "d:io:h")) != -1) {
        switch(c) {


![img](https://img-blog.csdnimg.cn/img_convert/0b4dc26d3d1f0e668ade5cb3d16c1871.png)
![img](https://img-blog.csdnimg.cn/img_convert/76246fe27a072918924e148f7bca88d4.png)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

rq_on = 1;

    void *ptr;

    printf("GPIO UIO test.\n");
    while((c = getopt(argc, argv, "d:io:h")) != -1) {
        switch(c) {


[外链图片转存中...(img-NlqDfZ7L-1715845273321)]
[外链图片转存中...(img-Ds87JA8f-1715845273322)]

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Zynq 上使用 UIO (Userspace I/O)允许用户空间程序访问硬件设备。以下是在 Zynq 上使用 UIO 的基本步骤: 1. 在设备树添加 UIO 节点。例如: ``` uio_pdrv_genirq_of: uio@0 { compatible = "generic-uio"; reg = <0x0 0x40000000 0x1000>; interrupts = <0x0 0x4 0x1>; interrupt-parent = <0x4>; }; ``` 2. 编写 UIO 驱动程序,将硬件设备与 UIO 节点关联起来。例如: ``` static struct platform_driver uio_driver = { .driver = { .name = "uio_pdrv_genirq", .owner = THIS_MODULE, }, .probe = uio_pdrv_genirq_probe, .remove = uio_pdrv_genirq_remove, }; static struct uio_info uio_info = { .name = "uio_example", .version = "0.0.1", .irq = UIO_IRQ_NONE, .mem = { .addr = (unsigned long)memory_start, .size = memory_size, .memtype = UIO_MEM_PHYS, .pgoff = 0, }, }; static int __init uio_example_init(void) { int ret = 0; struct resource *res; res = platform_get_resource_byname(NULL, IORESOURCE_MEM, "uio-pdrv-genirq.0"); if (!res) { printk(KERN_ERR "Failed to get memory resource\n"); return -ENODEV; } memory_start = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(memory_start)) { return PTR_ERR(memory_start); } memory_size = resource_size(res); ret = uio_register_device(&pdev->dev, &uio_info); if (ret) { printk(KERN_ERR "Failed to register UIO device\n"); return ret; } ret = platform_driver_register(&uio_driver); if (ret) { uio_unregister_device(&uio_info); printk(KERN_ERR "Failed to register UIO driver\n"); return ret; } printk(KERN_INFO "UIO device registered\n"); return 0; } static void __exit uio_example_exit(void) { platform_driver_unregister(&uio_driver); uio_unregister_device(&uio_info); printk(KERN_INFO "UIO device unregistered\n"); } module_init(uio_example_init); module_exit(uio_example_exit); MODULE_LICENSE("GPL"); ``` 3. 编译并加载 UIO 驱动程序。例如: ``` $ make $ insmod uio_example.ko ``` 4. 使用 UIO 设备。例如: ``` int fd = open("/dev/uio0", O_RDWR); if (fd < 0) { perror("open"); return -1; } void *addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (addr == MAP_FAILED) { perror("mmap"); close(fd); return -1; } // 使用 addr 访问硬件设备 munmap(addr, size); close(fd); ``` 注意,UIO 设备可以在用户空间程序使用 mmap 函数进行映射,从而访问硬件设备。在访问硬件设备之前,需要先调用 open 函数打开 UIO 设备,并调用 mmap 函数映射 UIO 设备的内存区域。最后,在访问完硬件设备后,需要调用 munmap 函数解除内存映射,并调用 close 函数关闭 UIO 设备文件描述符。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值