搜索
bottom↓
回复: 1

【正点原子FPGA连载】第五章AXI GPIO按键控制LED实验--摘自【正点原子】领航者 ZYNQ 之嵌入式开发指南

[复制链接]

出0入234汤圆

发表于 2020-7-21 17:51:21 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2020-10-24 10:19 编辑

1)实验平台:正点原子领航者ZYNQ开发板
2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz_linhanz.html
4)对正点原子FPGA感兴趣的同学可以加群讨论:876744900
QQ群头像.png
5)关注正点原子公众号,获取最新资料


100846rel79a9p4uelap24.jpg

100846f1ce1fg14zbg0va4.png

第五章AXI GPIO按键控制LED实验


在“EMIO按键控制LED实验”中,我们通过EMIO实现了PS端与PL端的交互,而PS与PL最主要的连接方式则是一组AXI接口。AXI互联和接口作为ZYNQ PS和PL之间的桥梁,能够使两者协同工作,进而形成一个完整的、高度集成的系统。
本章我们将在PL端调用AXI GPIO IP核,并通过AXI4-Lite接口实现PS与PL中AXI GPIO模块的通信。本章包括以下几个部分:
55.1简介
5.2实验任务
5.3硬件设计
5.4软件设计
5.5下载验证


5.1简介
AXI GPIO IP核为AXI接口提供了一个通用的输入/输出接口。与PS端的GPIO不同,AXI GPIO是一个软核(Soft IP),即ZYNQ芯片在出厂时并不存在这样的一个硬件电路,而是由用户通过配置PL端的逻辑资源来实现的一个功能模块。而PS端的GPIO是一个硬核(Hard IP),它是一个生产时在硅片中实现的功能电路。
AXI GPIO可以配置成单通道或者双通道,每个通道的位宽可以单独设置。另外通过打开或者关闭三态缓冲器,AXI GPIO的端口还可以被动态地配置成输入或者输出接口。其顶层模块的框图如下所示:
11010515.gif

图 5.1.1 AXI GPIO框图

从图 2.1.1中可以看到,模块的左侧实现了一个32位的AXI4-Lite从接口,用于主机访问AXI GPIO内部各通道的寄存器。当右侧接口输入的信号发生变化时,模块还能向主机产生中断信号。不过只有在配置IP核时选择“使能中断”,才会启用模块的中断控制功能。
5.2实验任务
本章的实验任务是通过调用AXI GPIO IP核,使用中断机制,实现领航者底板上PL端按键控制核心板上PS端LED的功能。
5.3硬件设计
根据实验任务我们可以画出本次实验的系统框图,如下图所示:
11010846.gif

图 5.3.1 系统框图

在图 5.3.1中,PS端的M_AXI_GP0作为主端口,与PL端的AXI GPIO IP核以AXI4-Lite总线相连接。其中,AXI互联IP(AXI Interconnect)用于连接AXI存储器映射(memory-mapped)的主器件和从器件。通用中断控制器(GIC)用于管理来自PS或者PL的中断,并把这些中断发送到CPU。
首先创建Vivado工程,工程名为“axi_gpio”,然后创建Block Design设计(system.bd)并添加ZYNQ7 Processing System模块。接下来按照《“Hello World”实验》中的步骤2-7、2-8分别配置PS的UART和DDR控制器。需要特别注意的是,我们在《“Hello World”实验》的步骤2-10中,移除了PS中与PL端交互的接口信号,这些接口在我们本次实验中需要予以保留。
110101325.gif

图 5.3.2 PS中断配置界面

由于本次实验用到了PL端的中断,因此在ZYNQ7处理系统的配置界面左侧点击“Interrupts”标签以配置中断。然后在右侧的中断配置界面中分别勾选“Fabric Interrupts”和PL-PS Interrupt ports下面的“IRQ F2P[15:0]”,如图 5.3.2所示。
另外我们还要用到PS端的LED,因此需要在MIO Configuration界面勾选“GPIO MIO”,并取消其他GPIO信号的勾选,如下图所示:
110101648.gif

图 5.3.3 MIO配置

最后点击右下角的“OK”,本次实验ZYNQ处理系统就配置完成了。
ZYNQ7 PS配置完成后其接口如下图所示:
110101763.gif

图 5.3.4 ZYNQ7模块接口

相比于前面的几个实验,在图 5.3.4中多了一些接口信号,他们是PS使用AXI接口与PL端进行通信时所需要的信号:
M_AXI_GP0是通用(General Purpose)AXI接口,它包含了一组信号。首字母M表示PS作为主机(Master),PL中的外设作为从机(Slave)。而左侧的M_AXI_GP0_ACLK是这个接口的全局时钟信号,它是一个输入信号,M_AXI_GP0接口的所有信号都是在这个全局时钟的上升沿采样的。
FCLK_CLK0是PS输出的时钟信号,它将作为PL中外设模块的时钟源。这个时钟由PS中的IO PLL产生,频率范围可以从0.1到250MHz,在配置ZYNQ7 PS的时候,该时钟默认为50MHz。
FCLK_RESET0_N是由PS输出到PL的全局复位信号,低电平有效。
IRQ_F2P[0:0]是由PL输出到PS的中断信号。
接下来我们要在Block Design中添加PL端的AXI GPIO IP核,在Diagram窗口空白位置右击,然后选择“Add IP”。在弹出的IP目录中搜索“AXI GPIO”,最后双击搜索结果中的“AXI GPIO”将其添加到设计中,如下图所示:
110102373.gif

图 5.3.5 添加AXI GPIO IP核

添加完成后Diagram窗口如下图所示:
110102462.gif

图 5.3.6 添加AXI GPIO完成

接下来点击图 5.3.6中箭头1所指示的位置,在弹出的对话框左侧确认勾选processing_system7_0,然后点击“OK”。这一步骤会将ZYNQ7 PS模块的DDR和FIXED_IO端口引出。
然后点击图 5.3.6中箭头2所指示的位置,弹出对话框如下图所示:
110102743.gif

图 5.3.7 自动连接

在左侧勾选“All Automation”,下面列出了会自动连接的模块及其接口。点击“OK”,工具会自动连接AXI GPIO IP核的GPIO接口和S_AXI接口。
连接完成后,在Diagram窗口空白处右击,然后选择“Regenerate Layout”对设计进行重新布局,布局后的界面如下图所示:
110102952.gif

图 5.3.8 重新布局后的设计界面

从上图中可以看到,在执行了自动连接之后,工具自动添加了两个IP核,分别是AXI互联(AXI Interconnect)和处理器系统复位(Processor System Reseet)。
AXI Interconnect IP核用于将一个(或多个)AXI存储器映射的主器件连接到一个(或多个)存储器映射的从器件。在这里我们解释一下这个术语——互联(Interconnect):互联实际上是一个开关,它管理并指挥所连接的AXI接口之间的通信。图 5.3.8中橙色高亮的两组信号线表明,在这个设计中,AXI互联实现了由主器件(ZYNQ7 PS)到从器件(AXI GPIO)一对一的连接。它也可以实现一对多、多对一以及多对多的AXI接口连接。
Processor System Reseet IP核为整个处理器系统提供复位信号。它会处理输入端的各种复位条件,并在输出端产生相应的复位信号。在本次实验中,Processor System Reseet接收ZYNQ7 PS输出的异步复位信号FCLK_RESET0_N,然后产生一个同步到PL时钟源FCLK_CLK0的复位信号peripheral_aresetn,用于复位PL端的各外设模块。如下图所示:
110103578.gif

图 5.3.9 Processor System Reset

最后我们再来看一下设计中的时钟信号,如下图所示:
110103679.gif

图 5.3.10 时钟信号

从图 5.3.10中可以看到PL端所有外设模块的时钟接口都连接到了ZYNQ7 PS输出的时钟信号FCLK_CLK0上。需要注意的是,该时钟同样连接到了PS端M_AXI_GP0_ACLK端口,作为AXI GP接口的全局时钟信号。
大家可能已经注意到了,ZYNQ7 PS模块的中断接口IRQ_F2P没有自动连接,需要我们手动连接。在此之前我们首先要配置所添加的AXI GPIO模块,使能其中断功能。
双击AXI GPIO IP核,打开其配置界面如下图所示:
110104006.gif

图 5.3.11 AXI GPIO配置

在图 5.3.11中,我们需要修改红色方框所标注的两个位置。首先设置GPIO接口的位宽“GPIO Width”,最大可以支持32位。这里我们只需要连接一个按键,因此将其设置为1。另外我们还需要使能其中断功能,所以需要勾选“Enable Interrupt”。
我们也可以通过勾选图中的“All Inputs”或者“All Outputs”将GPIO指定为输入或者输出接口。这两个选项默认是没有勾选的,这样我们可以在程序中将其动态地配置成输入或者输出接口。大家需要注意箭头1所指示的参数 “Default Tri State Value”,它配置GPIO默认情况下的输入输出模式,当其为0xFFFFFFFF时,表明GPIO所有的位默认为输入模式。
另外勾选箭头2所指示的选项可以使能GPIO通道2,GPIO 2的配置与GPIO完全相同。该选项默认没有勾选,即该IP工作在单通道模式下。
AXI GPIO IP核配置完成后点击右下角的“OK”,回到Diagram界面之后会发现AXI GPIO IP核多了一个中断接口“ip2intc_irpt”。我们将鼠标指针放在该接口上,待其变成一支铅笔的样式后,按住左键将其与ZYNQ7 PS的中断接口“IRQ_F2P”连接起来。如下图所示:
110104650.gif

图 5.3.12 连接中断

在图 5.3.12中,我们还修改了AXI GPIO IP核引出的GPIO端口的名称。通过点击该端口,在左侧外部端口属性一栏中将其名称修改为“AXI_GPIO_KEY”,如下图所示:
110104840.gif

图 5.3.13 修改AXI GPIO端口名称

到这里我们的Block Design就设计完成了,在Diagram窗口空白处右击,然后选择“Validate Design”验证设计。验证完成后弹出对话框提示“Validation Successful”表明设计无误,点击“OK”确认。最后按快捷键“Ctrl + S”保存设计。
接下来在Source窗口中右键点击Block Design设计文件“system.bd”,然后依次执行“Generate Output Products”和“Create HDL Wrapper”。
在左侧Flow Navigator导航栏中找到RTL ANALYSIS,点击该选项中的“Open Elaborated Design”。然后在菜单栏中点击 Layout,在下拉列表中选择I/O Planning以打开I/O Ports窗口。我们将在 I/O Ports 窗口中对AXI GPIO引出的接口AXI_GPIO_KEY进行管脚分配,如下图所示:
110105327.gif

图 5.3.14 管脚分配

在图 5.3.14中,我们将AXI_GPIO_KEY分配到L20引脚上,该引脚最终与领航者ZYNQ底板上的按键PL_KEY0相连接。管脚分配完成后按快捷键Ctrl+S保存管脚约束。
最后在左侧Flow Navigator导航栏中找到PROGRAM AND DEBUG,点击该选项中的“Generate Bitstream”,对设计进行综合、实现、并生成Bitstream文件。
在生成Bitstream之后,在菜单栏中选择 File > Export > Export hardware导出硬件,并在弹出的对话框中,勾选“Include bitstream”。然后在菜单栏选择File > Launch SDK,启动SDK软件。
5.4软件设计
在SDK软件中新建一个BSP工程和一个空的应用工程,应用工程名为“axi_gpio”。然后为应用工程新建一个源文件“main.c”,我们在新建的main.c文件中输入本次实验的代码。代码的主体部分如下所示:
  1. 1   #include "stdio.h"
  2. 2   #include "xparameters.h"
  3. 3   #include "xgpiops.h"
  4. 4   #include "xgpio.h"
  5. 5   #include "xscugic.h"
  6. 6   #include "xil_exception.h"
  7. 7   #include "xil_printf.h"
  8. 8   #include "sleep.h"
  9. 9   
  10. 10  //宏定义
  11. 11  #define SCUGIC_ID   XPAR_SCUGIC_0_DEVICE_ID     //中断控制器 ID
  12. 12  #define GPIOPS_ID   XPAR_XGPIOPS_0_DEVICE_ID    //PS端  GPIO器件 ID
  13. 13  #define AXI_GPIO_ID XPAR_AXI_GPIO_0_DEVICE_ID   //PL端  AXI GPIO器件 ID
  14. 14  #define GPIO_INT_ID XPAR_FABRIC_GPIO_0_VEC_ID   //PL端  AXI GPIO中断 ID
  15. 15  
  16. 16  #define MIO_LED     0                           //PS LED 连接到 MIO0
  17. 17  #define KEY_CHANNEL 1                           //PL按键使用AXI GPIO通道1
  18. 18  #define KEY_MASK    XGPIO_IR_CH1_MASK           //通道1的位定义
  19. 19  
  20. 20  //函数声明
  21. 21  void instance_init();                           //初始化器件驱动
  22. 22  void axi_gpio_handler(void *CallbackRef);       //中断服务函数
  23. 23  
  24. 24  //全局变量
  25. 25  XScuGic          scugic_inst;                   //中断控制器  驱动实例
  26. 26  XScuGic_Config * scugic_cfg_ptr;                //中断控制器  配置信息
  27. 27  XGpioPs          gpiops_inst;                   //PS端 GPIO 驱动实例
  28. 28  XGpioPs_Config * gpiops_cfg_ptr;                //PS端 GPIO 配置信息
  29. 29  XGpio            axi_gpio_inst;                 //PL端 AXI GPIO 驱动实例
  30. 30  
  31. 31  int led_value = 1;                              //LED显示状态
  32. 32  
  33. 33  int main()
  34. 34  {
  35. 35      printf("AXI GPIO INTERRUPT TEST!\n");
  36. 36  
  37. 37      //初始化各器件驱动
  38. 38      instance_init();
  39. 39  
  40. 40      //配置PS GPIO
  41. 41      XGpioPs_SetDirectionPin(&gpiops_inst, MIO_LED, 1);      //设置 PS GPIO 为输出
  42. 42      XGpioPs_SetOutputEnablePin(&gpiops_inst, MIO_LED, 1);   //使能LED输出
  43. 43      XGpioPs_WritePin(&gpiops_inst, MIO_LED, led_value);     //点亮LED
  44. 44  
  45. 45      //配置PL AXI GPIO
  46. 46      XGpio_SetDataDirection(&axi_gpio_inst, KEY_CHANNEL, 1); //设置AXI GPIO通道1为输入
  47. 47      XGpio_InterruptEnable(&axi_gpio_inst, KEY_MASK);        //使能通道1中断
  48. 48      XGpio_InterruptGlobalEnable(&axi_gpio_inst);            //使能AXI GPIO全局中断
  49. 49  
  50. 50      //设置中断优先级和触发类型(高电平触发)
  51. 51      XScuGic_SetPriorityTriggerType(&scugic_inst, GPIO_INT_ID, 0xA0, 0x1);
  52. 52      //关联中断ID和中断处理函数
  53. 53      XScuGic_Connect(&scugic_inst, GPIO_INT_ID, axi_gpio_handler, &axi_gpio_inst);
  54. 54      //使能AXI GPIO中断
  55. 55      XScuGic_Enable(&scugic_inst, GPIO_INT_ID);
  56. 56  
  57. 57      //设置并打开中断异常处理功能
  58. 58      Xil_ExceptionInit();
  59. 59      Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
  60. 60              (Xil_ExceptionHandler)XScuGic_InterruptHandler, &scugic_inst);
  61. 61      Xil_ExceptionEnable();
  62. 62  
  63. 63      while(1);
  64. 64  
  65. 65      return 0;
  66. 66  }
  67. 67  
复制代码

在主函数中,首先调用自己编写的instance_init( )函数对各器件驱动进行初始化。然后分别配置PS端GPIO、PL端的AXI GPIO,如程序第40至48行所示。在配置PL端AXI GPIO时,我们需要使能其中断功能,包括AXI GPIO通道1的中断和全局中断。
接下来在程序第50至55行配置GIC。每一个中断源都有自己唯一的标识——中断号(ID),具体的数值可以在头文件xparameters_ps.h中查看。其中由PL产生的共享外设中断(SPI)共16个,中断ID分别为61到68,以及84到91。我们在程序第14行定义了一个宏GPIO_INT_ID,用于标识AXI GPIO的中断ID,它的值为61。
配置GIC首先需要设置中断ID所代表的中断源的优先级和触发类型。中断优先级共分为32个等级,0代表最高优先级,0xF8(10进制数248)代表最低优先级,各优先级之间的步进值为8。也就是说,支持的优先级分别为0、8、16、32……、248。中断触发类型分为高电平敏感类型和上升沿敏感类型。AXI GPIO在检测到输入接口的信号发生改变时,会产生一个电平类型的中断请求,高有效,因此将中断源AXI GPIO的触发类型设置为高电平敏感类型。
然后还需要将中断ID与其中断服务函数关联起来。中断服务函数axi_gpio_handler( )是需要我们自己编写的,用于响应和处理AXI GPIO中断的函数。除此之外,还要调用函数XScuGic_Enable(&scugic_inst, GPIO_INT_ID)来使能中断ID所对应的中断源。
最后我们需要初始化并设置ARM处理器的异常处理功能,如程序第57至61行所示。ARM处理器支持7种异常情况:复位、未定义指令、软件中断、指令预取中止、数据中止、中断请求(IRQ)和快速中断请求(FIQ)。每种异常也都有自己的ID标识,其中XIL_EXCEPTION_ID_INT用于标识中断请求(IRQ)异常。我们通过调用函数Xil_ExceptionRegisterHandler( XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler) XScuGic_InterruptHandler,  &scugic_inst )来给IRQ异常注册处理程序,它会将中断控制器GIC的中断处理程序与ARM处理器中的硬件中断处理逻辑连接起来。另外还要通过Xil_ExceptionEnable( )函数使能IRQ异常。
我们在程序中,除了main( )函数之外,另外还编写了两个函数:instance_init( )和axi_gpio_handler( void *CallbackRef )。代码如下所示:
  1. 68  //初始化各器件驱动
  2. 69  void instance_init()
  3. 70  {
  4. 71      //初始化中断控制器驱动
  5. 72      scugic_cfg_ptr = XScuGic_LookupConfig(SCUGIC_ID);
  6. 73      XScuGic_CfgInitialize(&scugic_inst, scugic_cfg_ptr, scugic_cfg_ptr->CpuBaseAddress);
  7. 74  
  8. 75      //初始化PS端  GPIO驱动
  9. 76      gpiops_cfg_ptr = XGpioPs_LookupConfig(GPIOPS_ID);
  10. 77      XGpioPs_CfgInitialize(&gpiops_inst, gpiops_cfg_ptr, gpiops_cfg_ptr->BaseAddr);
  11. 78  
  12. 79      //初始化PL端  AXI GPIO驱动
  13. 80      XGpio_Initialize(&axi_gpio_inst, AXI_GPIO_ID);
  14. 81  }
  15. 82  
  16. 83  //PL端 AXI GPIO 中断服务(处理)函数
  17. 84  void axi_gpio_handler(void *CallbackRef)
  18. 85  {
  19. 86      int key_value = 1;
  20. 87      XGpio *GpioPtr = (XGpio *)CallbackRef;
  21. 88  
  22. 89      print("Interrupt Detected!\n");
  23. 90      XGpio_InterruptDisable(GpioPtr, KEY_MASK);              //关闭AXI GPIO中断使能
  24. 91      key_value = XGpio_DiscreteRead(GpioPtr, KEY_CHANNEL);   //读取按键数据
  25. 92      if(key_value == 0){                                     //判断按键按下
  26. 93          led_value = ~led_value;
  27. 94          XGpioPs_WritePin(&gpiops_inst, MIO_LED, led_value); //改变LED显示状态
  28. 95      }
  29. 96      sleep(1);                                               //延时1s,按键消抖
  30. 97      XGpio_InterruptClear(GpioPtr, KEY_MASK);                //清除中断
  31. 98      XGpio_InterruptEnable(GpioPtr, KEY_MASK);               //使能AXI GPIO中断
  32. 99  }
复制代码

其中instance_init( )函数用于初始化设计中所使用的各个器件的驱动,包括PS端的GIC和GPIO,及PL端的AXI GPIO,如程序第68至81行所示。其中通用中断控制器(GIC)是PS中用于集中管理中断信号的资源,如果我们在程序中使用到了中断,就需要对其进行初始化及配置。
而axi_gpio_handler( void *CallbackRef )是中断服务(处理)函数,当CPU检测到AXI GPIO产生的中断后,需要执行该函数。如程序第83至99行所示,在中断服务函数中,我们先通过XGpio_DiscreteRead ( GpioPtr, KEY_CHANNEL )函数读取AXI GPIO通道1所连接的PL端按键的状态;当判断到按键按下时,通过XGpioPs_WritePin(&gpiops_inst, MIO_LED, led_value)函数修改PS端LED的显示状态。
对于电平敏感类型的中断,在中断服务函数响应了中断之后,需要将中断源的中断清除。如程序第97行所示,我们通过XGpio_InterruptClear( GpioPtr, KEY_MASK )函数清除AXI GPIO通道1的中断状态寄存器。
程序设计完成后,按快捷键Ctrl+S保存main.c文件,工具会自动进行编译。编译完成后控制台(Console)中会出现提示信息“Build Finished”,同时在应用工程的Binaries目录下可以看到生成的elf文件。
5.5下载验证
首先我们将下载器与领航者底板上的JTAG接口连接,下载器另外一端与电脑连接。然后使用Mini USB连接线将开发板左侧的USB_UART接口与电脑连接,用于串口通信。最后连接开发板的电源,并打开电源开关。
在SDK软件下方的SDK Terminal窗口中点击右上角的加号设置并连接串口。然后下载本次实验硬件设计过程中所生成的BIT文件,来对PL进行配置。最后下载软件程序,下载完成后,在下方的SDK Terminal中可以看到应用程序打印的信息“AXI GPIO INTERRUPT TEST!”,同时核心板上的PS端LED2(红色)点亮。
实验结果如下图所示:
1101011680.gif

图 5.5.1 下载验证

我们按下领航者底板上的PL端按键PL_KEY0然后释放,可以看到核心板上PS端的LED2熄灭。同时SDK Terminal窗口中先后打印出“Interrupt Detected!”信息,说明检测到AXI GPIO产生的中断,如下图所示:
1101011857.gif

图 5.5.2 串口终端中打印的信息

我们每次按下按键PL_KEY0,都可以看到核心板上LED2的显示状态发生改变,同时串口终端打印“Interrupt Detected!”信息,说明本次实验在领航者ZYNQ开发板上面下载验证成功。

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出16170入6148汤圆

发表于 2020-8-1 00:21:21 来自手机 | 显示全部楼层
打赏!

庆祝论坛“打赏”功能实施, 现在开始发技术主题,可以获得打赏
https://www.amobbs.com/thread-5735948-1-1.html
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-3-29 22:43

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表