正点原子 发表于 2022-3-18 12:12:12

《领航者ZYNQ之嵌入式Linux开发指南_V2.0》第四十一章 使用AXI GPIO

1)实验平台:正点原子领航者V2 ZYNQ开发板
2)章节摘自【正点原子】《领航者ZYNQ之嵌入式Linux开发指南_V2.0》
3)购买链接:https://detail.tmall.com/item.htm?id=609032204975
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-329957-1-1.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子FPGA技术交流QQ群:90562473







第四十一章 使用AXI GPIO
       在前面所有驱动篇教程章节当中,我们只使用到了ZYNQ 7010/7020 PS端GPIO外设,包括驱动LED、蜂鸣器以及按键等,在《领航者ZYNQ之嵌入式开发指南》文档第五章教程“AXI GPIO按键控制LED实验”中给大家详细介绍过,PS端GPIO是一个硬核外设(Hard IIP),它是在SoC生产时在硅片中实现的功能逻辑电路,当然不仅仅只有GPIO,例如PS端的I2C、SPI、QSPI、SD等这些都是硬核外设。
       对于ZYNQ这种PL+PS(可以理解为ARM + FPGA)结构的SoC处理器来说,除了硬核外设,我们还可以使用软核外设。与硬核外设不同,软核即ZYNQ芯片在出厂时并不存在这样的一个硬件电路,而是由用户通过配置PL端的逻辑资源(硬件编程)来实现的一个功能逻辑电路模块。
       本章我们要介绍如何在vivado中调用AXI GPIO IP核,来实现一个软核GPIO外设,与《领航者ZYNQ之嵌入式开发指南》文档第五章教程“AXI GPIO按键控制LED实验”不一样的是,本章我们要在Linux环境下驱动并使用它。
       1.1AXI GPIO IP简介
       关于该IP的简介,在《领航者ZYNQ之嵌入式开发指南》文档第五章教程“AXI GPIO按键控制LED实验”中有给大家介绍过,所以这里就不再啰嗦了!如果想更加详细的了解,可以参考xilinx官方文档pg144-axi-gpio.pdf,在我们的资料光盘中也给大家了,路径为:领航者ZYNQ开发板资料盘(A盘)\8_ZYNQ&FPGA参考资料\Xilinx\Product Guide\pg144-axi-gpio.pdf。
       大家都知道,对于一个硬核外设来说,寄存器是我们与外设进行交互的接口,我们可以通过寄存器对外设进行配置、获取它的数据、驱动它等;那么对于软核外设来说,同样也是如此,都是通过寄存器的方式与其进行交互;所以不管是硬核还是软核,大家要明白一个道理,它们在本质上都是相同的。
       AXI GPIO IP模块内部同样提供了寄存器,与硬核外设不同,软核IP模块内部寄存器的基地址不是固定的,而是可以配置的,当我们在vivado中实例化一个IP核(调用一个IP核)的时候,会自动给它分配基地址,当然也是可以自己修改了,打开vivado的Address Editor界面,就可以看到我们的vivado设计中所有的软核IP实例化之后对应的寄存器基地址,例如:
图 41.1.1 查看寄存器基地址
       当vivado工程编译完成之后,这些基地址就可以与这些软核IP实现的模块对应起来。对于硬核外设来说,它们的寄存器基地址都是固定的,而寄存器的基地址在ZYNQ的参考手册上都会有说明。
       接下来带大家简单地看看AXI GPIO IP的寄存器内容,大家也可以参考xilinx官方文档pg144-axi-gpio.pdf,总体寄存器描述信息如下所示:
图 41.1.2 AXI PWM寄存器总述
       从图 41.1.2可以知道,AXI GPIO模块的寄存器数量比较少,比较简单,整体上主要分为数据寄存器、控制寄存器、中断相关寄存器,每一个都是32位寄存器,包括各个寄存器的地址偏移量在图中都有记录,接下来我们简单地聊一聊各个寄存器所提供的配置功能。
       1、数据寄存器
      数据寄存器有两个,分别为GPIO_DATA和GPIO2_DATA,因为AXI GPIO模块可以提供2组GPIO通道,每一组可以提供32个GPIO;GPIO_DATA是第一组通道的数据寄存器,而GPIO2_DATA是第二组通道的数据寄存器,来看看寄存器介绍:
图 41.1.3 GPIO_DATA寄存器
      作为输入模式,读寄存器得到的是输入的值,而写寄存器没有什么效果;对于输出模式,读寄存器得到的总是0,而写寄存器对应的是GPIO的输出值,每一个bit位对应一个GPIO。
       2、控制寄存器
       同样的控制寄存器也有两个,分别为GPIO_TRI和GPIO2_TRI,其实就是GPIO输入、输出控制寄存器,如下所示:
图 41.1.4 GPIO_TRI寄存器
       寄存器相应bit位配置为0表示作为输出模式,配置为1表示作为输入模式。
       3、全局中断使能寄存器
       GIER为全局中断使能寄存器,只有最高位有效,控制AXI GPIO模块的中断使能与禁止,如下所示:
图 41.1.5 GIER寄存器
       写入0禁止全局中断、写入1使能全局中断。
       4、中断使能寄存器
       IPIER为中断使能寄存器,只有最低两个bit位有效,如下所示:

图 41.1.6 IPISR寄存器
       该寄存器可以单独配置两组通道的中断使能、禁止。
       bit:配置通道1中断使能、禁止,设置为0表示禁止通道1中断功能,设置为1表示使能通道1中断功能。
       bit:配置通道2中断使能、禁止,设置为0表示禁止通道1中断功能,设置为1表示使能通道1中断功能。
       5、中断状态寄存器
       IPISR为中断状态寄存器,只有最低两个bit位有效,如下所示:

       它的中断状态非常的简单,只能做到通道级别的状态检查,并不能查询具体是某一个GPIO发生了中断事件,也不能配置GPIO中断触发方式,只能是边沿触发(上升沿和下降沿都可以),所以笔者认为这个IP模块对中断支持的支持不是很好。
      bit:读取的数据为0表示通道1没有发生中断事件,读取的数据为1则表示发生了中断事件。
      bit:读取的数据为0表示通道2没有发生中断事件,读取的数据为2则表示发生了中断事件。
      1.2vivado硬件设计
      在vivado设计中,我们可以画出AXI GPIO IP模块和ZYNQ PS部分之间的连接框图,如下:
图 41.2.1 系统框图
       在《领航者ZYNQ之嵌入式开发指南》文档第五章教程“AXI GPIO按键控制LED实验”中大家就已经见过了;在图中,PS端的M_AXI_GP0作为主端口,与PL端的AXI GPIO IP核以AXI4-Lite总线相连接,其中,AXI互联IP(AXI Interconnect)用于连接AXI存储器映射(memory-mapped)的主器件和从器件;通用中断控制器(GIC)用于管理来自PS或者PL的中断,并把这些中断发送到CPU。
       AXI GPIO与PS端连接示例如下所示:
图 41.2.2 AXI GPIO设计示例
       关于AXI GPIO IP核的配置信息在《领航者ZYNQ之嵌入式开发指南》文档第五章教程“AXI GPIO按键控制LED实验”中也给大家介绍过,所以这里不再重述!
       驱动篇教程当中我们使用了开发板出厂时对应的vivado工程,在这个vivado设计中,也使用到了AXI GPIO IP,用于读取检查LCD id信息。本章想向大家介绍的重点知识一共有三个内容:
       1、明白软核(Soft IP)和硬核(Hard IP)之间的差异和共同点,以及FPGA可编程硬件的本质。
       2、vivado工程设计当中如何使用AXI GPIO IP模块(如何调用),PS与PL之间的连接方式。
       3、在裸机或者是Linux系统中如何使用。
       以上三点当中,前面两个点都已经给大家详细介绍过,接下来就向大家介绍如何在Linux环境下使用AXI GPIO。经常会有很多人问,怎么在Linux系统下驱动、使用软核IP,如果你把笔者前面说到的内容理解了,这是不难的,不管是硬核IP还是软核IP,它们最终的效果都是对应一个电路逻辑功能模块,例如AXI GPIO,它们都提供了可编程的寄存器对其进行配置,实现相应的功能,对驱动开发工程师而言,它们都是一样的。
       例如你要读取AXI GPIO的输入值,首先你需要编程GPIO_TRI寄存器将GPIO配置为输入模式,然后你再去读取GPIO_DATA即可!不管你是在操作硬核外设还是软核外设,对于驱动开发工程师来说,寄存器始终是我们与外设进行交互的接口。
       1.3AXI GPIO设备树节点
       如果想通过软核(Soft IP)的方式来实现GPIO接口,那么可以通过调用AXI GPIO IP它来实现,例如图 41.2.2所示;编译工程导出hdf文件,按照第二十章所示步骤可以自动生成内核或u-boot所需的设备树文件,例如包括pcw.dtsi、pl.dtsi以及system-top.dts等。
       其中pl.dtsi设备树文件中就会自动产生AXI GPIO IP这个软核外设所对应的设备节点信息,例如:
示例代码 41.3.1 AXI GPIO设备树节点示例
gpio1: gpio@41200000 {
    #gpio-cells = <3>;
    clock-names = "s_axi_aclk";
    clocks = <&clkc 15>;
    compatible = "xlnx,axi-gpio-2.0", "xlnx,xps-gpio-1.00.a";
    gpio-controller ;
    reg = <0x41200000 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 = <0x3>;
    xlnx,gpio2-width = <0x20>;
    xlnx,interrupt-present = <0x0>;
    xlnx,is-dual = <0x0>;
    xlnx,tri-default = <0xFFFFFFFF>;
    xlnx,tri-default-2 = <0xFFFFFFFF>;
};       关于AXI GPIO IP对应的设备树节点属性描述信息大家可以查看内核源码Documentation/devicetree/bindings/gpio/gpio-xilinx.txt这篇文档,该文档中详细了记录了每一个属性所表达的含义。
       可以看到“#gpio-cells”这个属性等于3,表示如果我们要使用AXI GPIO需要提供3个参数,例如“led-gpio = <&gpio1 0 0 GPIO_ACTIVE_LOW>”,&gpio1表示引用gpio1节点,也就是AXI GPIO对应的设备节点,第一个0表示管脚编号,第二个0表示对应的通道(0表示第一个通道、8表示第二个通道),这两个连起来的意思就是:AXI GPIO模块的第一个通道的0号管脚;而最后一个GPIO_ACTIVE_LOW表示低电平有效。
      “gpio-controller”属性描述了该节点是一个GPIO控制器节点,reg属性描述了AXI GPIO模块所对应的地址空间,包括基地址和长度;compatible属性值有两个,“xlnx,axi-gpio-2.0”和“xlnx,xps-gpio-1.00.a”,在我们的Linux驱动源码中搜索这两个字符串,就可以找到对应的驱动源码文件,这里笔者直接给出它对应的驱动源文件,路径为内核源码目录drivers/gpio/gpio-xilinx.c,驱动代码是由xilinx官方提供的,因为这个IP由xilinx官方提供。
       当打开gpio-xilinx.c文件,找到如下内容:
示例代码 41.3.2 gpio-xilinx.c部分代码
767 static const struct of_device_id xgpio_of_match[] = {
768   { .compatible = "xlnx,xps-gpio-1.00.a", },
769   { /* end of list */ },
770 };
771 MODULE_DEVICE_TABLE(of, xgpio_of_match);
772
773 static struct platform_driver xilinx_gpio_driver = {
774   .probe = xgpio_of_probe,
775   .remove = xgpio_remove,
776   .driver = {
777         .name = "xilinx-gpio",
778         .of_match_table = xgpio_of_match,
779         .pm = &xgpio_dev_pm_ops,
780   },
781 };       在第768行中,可以看到该驱动源码的compatible对应的匹配字符串就是“xlnx,xps-gpio-1.00.a”,所以示例代码 41.3.1中的设备节点就能够和该驱动源码匹配成功!
       关于gpio-xilinx.c驱动源码这里笔者就不给大家分析了,寄存器相关的硬件操作都在这个驱动文件,除此之外还涉及到GPIO驱动框架相关的代码,这是后面的章节的内容,以后再给大家讲。内核默认配置使能了AXI GPIO的驱动,所以不需要我们menuconfig再去配置,可以直接使用,关于AXI GPIO的使用我们讲到这里了,在使用上跟PS端GPIO是一样的,比如在驱动代码中,of_get_named_gpio得到GPIO的编号、gpio_request或devm_gpio_request申请使用GPIO、gpio_direction_output设置GPIO为输出模式等等。
页: [1]
查看完整版本: 《领航者ZYNQ之嵌入式Linux开发指南_V2.0》第四十一章 使用AXI GPIO