在本实验中,我们将通过调用AXI GPIO IP核,使用中断机制,实现底板上PL端按键控制PS端GPIO,并使用EMIO控制LED灯的亮灭。首先,axi_gpio与之前的GPIO的区别:之前的GPIO是硬核,是ps端实际存在的外设电路;而axi_gpio是软核,实现的时候需要由fpga的pl端去搭建。下面为实验步骤:
第一步:根据建立Vivado工程,本设计所使用的开发板为pynq-z2。再Create Block Design,先添加zynq,并对其进行设置:(1)添加UART,并设置1位宽的EMIO GPIO。(2)根据各自开发板更改Clock Congfiguration中Basic Clocking的输入频率。(3)根据各自开发板对DDR进行配置。(4)设置中断,如下图所示:
上述其他配置配置过程均在前面的笔记中有详细介绍,不懂可参考往期学习笔记。
第三步:自动引脚连线,如下图所示:
则将形成如下图所示的Block Design,此时会发现增加了两个IP核,其中AXI Interconnect IP核用于将一个或多个AXI存储器映射的主器件连接到一个或多个存储器映射的从器件。而互联实际上是一个开关,它管理并指挥所连接的AXI接口之间的通信。图中设计表明:AXI互联实现了由主器件ZYNQ7 PS到从器件AXI GPIO一对一的连接。它也可以实现一对多、多对一以及多对多的AXI接口连接。Processor System Reset IP为整个处理器系统提供复位信号,它会处理输入端的各种复位条件,并在输出端产生相应的复位信号。Processor System Reset 接收ZYNQ PS输出的异步复位信号FCLK_RESET0_N,然后产生一个同步到PL时钟源FCLK_CLK0的复位信号peripheral_aresetn,用于复位PL端的各外设模块。
然后进行如下设置:(1)与往期笔记相同的是,先将ZYNQ上GPIO_0的引脚Make External,该引脚将于PL端LED灯相连,因此可命名为GPIO_LED。(2)此外,在本实验中,上图中axi_gpio_0 IP的GPIO引脚将与PL端按键直接相连,因此可将该引脚改名为:AXI_GPIO_KEY,方便之后引脚分配;(3)而其中断将与第一步-4中设置的PL中断输入相连,将按键作为中断变量,因此将下图高亮的连线手动相连:
第四步:对该Block Design 依次Generate Output Products,Create HDL Wrapper,再在RTL ANALYSIS中Open Elaborated Design进行引脚分配:
完成上述步骤后,点击保存,再Generate Bitstream,在耐心等待之后再Export Hardware,记住选择Include bitstream。再launch SDK。
第五步:SDK中新建Empty Application。在src文件夹下添加新source file。参考pg144-axi-gpio中设计流程和官方例程,可设计出如下代码。
#include "stdio.h"
#include "xparameters.h"
#include "xgpiops.h"
#include "sleep.h"
#include "xscugic.h"
#include "xgpio.h"
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID //PS GPIO 器件ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID //中断控制器 器件ID
#define AXI_GPIO_DEVICE_ID XPAR_GPIO_0_DEVICE_ID //AXI_GPIO 器件ID
#define AXI_GPIO_INTERRUPT_ID XPAR_FABRIC_AXI_GPIO_0_IP2INTC_IRPT_INTR //AXI GPIO的中断号61
//核心板上PS EMIO0引脚:LED (EMIO从54开始计数)
#define EMIO0_LED 54
//AXI GPIO通道1
#define GPIO_CHANNEL1 1
XGpioPs_Config * ConfigPtr;
XScuGic_Config *IntcConfig; //Instance of the interrupt controller
XGpioPs Gpio; //The driver instance for GPIO Device.
XScuGic Intc;
XGpio AXI_Gpio; // The Instance of the GPIO Driver
//函数声明
void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpio *AXI_Gpio,u16 AXI_GpioIntrId);
static void IntrHandler();
u32 key_press=0;
int main(){
u32 led_value=1;
printf("AXI GPIO interrupt test\n");
//查找PS端GPIO的配置,并初始化
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);
//对AXI GPIO进行初始化
XGpio_Initialize(&AXI_Gpio, AXI_GPIO_DEVICE_ID);
//把GPIO的方向设置为输出,并打开输出使能(1表示输出、1表示打开使能)
XGpioPs_SetDirectionPin(&Gpio, EMIO0_LED, 1);
XGpioPs_SetOutputEnablePin(&Gpio, EMIO0_LED, 1);
//对AXI GPIO进行配置(最后一个参数对32位的AXI接口方向进行设置,1为输入)
XGpio_SetDataDirection(&AXI_Gpio, GPIO_CHANNEL1 ,0x00000001);
//将led_value的状态写入LED
XGpioPs_WritePin(&Gpio, EMIO0_LED,led_value);
//设置中断系统
SetupInterruptSystem(&Intc, &AXI_Gpio, AXI_GPIO_INTERRUPT_ID);
while(1){
if(key_press){
//判断当前按键的状态,如果是按键按下,就改变
if(XGpio_DiscreteRead(&AXI_Gpio, GPIO_CHANNEL1) == 0){
led_value = ~led_value;
key_press = 0;
//清除之前的中断状态寄
XGpio_InterruptClear(&AXI_Gpio,0x00000001);
//将led_value的状态写入LED
XGpioPs_WritePin(&Gpio, EMIO0_LED,led_value);
//延时按键消抖
usleep(200000);
//使能AXI_GPIO中断
XGpio_InterruptEnable(&AXI_Gpio,0x00000001);
}
}
}
return 0;
}
void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpio *AXI_Gpio,u16 AXI_GpioIntrId){
//查找器件GIC器件配置信息,并进行初始化
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
XScuGic_CfgInitialize(GicInstancePtr, IntcConfig,IntcConfig->CpuBaseAddress);
//初始化ARM处理器异常句柄
Xil_ExceptionInit();
//给IRQ异常注册处理程序,它会将中断控制器GIC的中断处理程序与ARM处理器中的硬件中断处理逻辑连接起来
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,GicInstancePtr);
//使能处理器中断
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
//关联中断处理函数 其中IntrHandler为中断子程序
XScuGic_Connect(GicInstancePtr, AXI_GpioIntrId,
(Xil_ExceptionHandler)IntrHandler,(void *)AXI_Gpio);
//为AXI_GPIO器件使能中断
XScuGic_Enable(GicInstancePtr, AXI_GpioIntrId);
//第三个参数表示中断优先级,0优先级最高,8位宽
//第四个参数表示中断类型:0x1类型为高有效电平敏感类型
XScuGic_SetPriorityTriggerType(GicInstancePtr, AXI_GpioIntrId,0xA0, 0x1);
//打开AXI_GPIO中断使能
XGpio_InterruptGlobalEnable(AXI_Gpio); //打开全局中断使能
XGpio_InterruptEnable(AXI_Gpio,0x00000001); //打开通道信号对应的中断使能
}
void IntrHandler(){
printf("interrupt test!\n\r");
key_press = 1;
XGpio_InterruptDisable(&AXI_Gpio,0x00000001); //屏蔽中断
}
然后连接上开发板,此时下载程序需要先配置PL,即在SDK工具栏的Xilinx中选择Program FPGA,默认参数就行,直接点击Program。完成后开发板上DONE灯将亮起,其是FPGA配置完成的标志。再按照学习笔记1的步骤下载软件程序即可实现功能:按下按键灯由亮变灭,再按一次灯由灭变亮,同时每按一次串口上都会打印一个 interrupt test!