一、AXI GPIO接口简介
是什么?是PL部分的一个IP软核,实现通用输入输出接口的功能,并通过AXI协议实现与处理系统通信,方便控制与拓展GPIO接口。
AXI GPIO IP 核为 AXI 接口提供了一个通用的输入/输出接口。 与 PS 端的 GPIO 不同, AXI GPIO 是一个软核( Soft IP),即 ZYNQ 芯片在出厂时并不存在这样的一个硬件电路, 而是由用户通过配置 PL 端的逻辑资源来实现的一个功能模块。 而 PS 端的 GPIO 是一个硬核( Hard IP) ,它是一个生产时在硅片中实现的功能电路。
AXI GPIO 可以配置成单通道或者双通道, 每个通道的位宽可以单独设置。 另外通过打开或者关闭三态缓冲器, AXI GPIO 的端口还可以被动态地配置成输入或者输出接口。其顶层模块的框图如下所示:
二、实验任务
本章的实验任务是通过调用 AXI GPIO IP 核,使用中断机制,实现底板上 PL 端按键 PL_KEY0 控制 PS端 LED0 亮灭的功能。
三、程序设计
系统框图
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 的中断信号。
PL 端所有外设模块的时钟接口都连接到了 ZYNQ7 PS 输出的时钟信号FCLK_CLK0 上。需要注意的是,该时钟同样连接到了 PS 端 M_AXI_GP0_ACLK 端口,作为 AXI GP 接口的全局时钟信号。
首先设置 GPIO 接口的位宽“ GPIO Width”,最大可以支持 32 位。这里我们只需要连接一个按键, 因此将其设置为 1。另外我们还需要使能其中断功能,所以需要勾选“ Enable Interrupt”。
也可以通过勾选图中的“ All Inputs”或者“ All Outputs”将 GPIO 指定为输入或者输出接口。 这两
个选项默认是没有勾选的, 这样我们可以在程序中将其动态地配置成输入或者输出接口。大家需要注意箭头 1 所指示的参数“ Default Tri State Value”,它配置 GPIO 默认情况下的输入输出模式,当其为 0xFFFFFFFF时, 表明 GPIO 所有的位默认为输入模式。 当其为 0x00000000 时,表明 GPIO 所有的位默认为输出模式。
#include "xparameters.h"
#include "xgpiops.h"
#include "xgpio.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "xplatform_info.h"
#include <xil_printf.h>
#include "sleep.h"
//以下常量映射到 xparameters.h 文件
#define GPIOPS_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID //PS 端 GPIO 器件 ID
#define AXI_GPIO_DEVICE_ID XPAR_AXI_GPIO_0_DEVICE_ID //PL 端 AXI GPIO 器件 ID
#define SCUGIC_ID XPAR_SCUGIC_0_DEVICE_ID //通用中断控制器 ID
#define AXI_GPIO_INT_ID XPAR_FABRIC_GPIO_0_VEC_ID //PL 端 AXI GPIO 中断 ID
#define MIO_LED 0 //LED 连接到 MIO0
#define KEY_CHANNEL1 1 //PL 按键使用 AXI GPIO 通道 1
#define KEY_CH1_MASK XGPIO_IR_CH1_MASK //通道 1 的中断位定义
void instance_init();
int setup_interrupt_system(XScuGic *gic_inst_ptr, XGpio *axi_gpio_inst_ptr,u16 AXI_GpioIntrId);
static void intr_handler(void *callback_ref);
XGpioPs gpiops_inst ; //PS 端 GPIO 驱动实例
XGpio axi_gpio_inst ; //PL 端 AXI GPIO 驱动实例
XScuGic scugic_inst ; //通用中断控制器驱动实例
int led_value = 1 ; //ps 端 LED2 显示状态
int main(void)
{
int status;
//初始化各器件驱动
instance_init();
xil_printf("AXI_Gpio interrupt test \r\n");
//设置 LED 所连接的 MIO 引脚的方向为输出并使能输出
XGpioPs_SetDirectionPin(&gpiops_inst, MIO_LED, 1);
XGpioPs_SetOutputEnablePin(&gpiops_inst, MIO_LED, 1);
XGpioPs_WritePin(&gpiops_inst, MIO_LED, led_value);
//建立中断,出现错误则打印信息并退出
status = setup_interrupt_system(&scugic_inst, &axi_gpio_inst,AXI_GPIO_INT_ID);
if (status != XST_SUCCESS) {
xil_printf("Setup interrupt system failed\r\n");
return XST_FAILURE;
}
return XST_SUCCESS;
}
//初始化各器件驱动
void instance_init()
{
XScuGic_Config *scugic_cfg_ptr;
XGpioPs_Config *gpiops_cfg_ptr;
//初始化中断控制器驱动
scugic_cfg_ptr = XScuGic_LookupConfig(SCUGIC_ID);
XScuGic_CfgInitialize(&scugic_inst, scugic_cfg_ptr,scugic_cfg_ptr->CpuBaseAddress);
//初始化 PS 端 GPIO 驱动
gpiops_cfg_ptr = XGpioPs_LookupConfig(GPIOPS_DEVICE_ID );
XGpioPs_CfgInitialize(&gpiops_inst, gpiops_cfg_ptr,piops_cfg_ptr->BaseAddr);
//初始化 PL 端 AXI GPIO 驱动
XGpio_Initialize(&axi_gpio_inst, AXI_GPIO_DEVICE_ID);
}
//建立中断系统,使能 KEY 按键中断
// @param GicInstancePtr 是一个指向 XScuGic 驱动实例的指针
// @param gpio 是一个指向连接到中断的 GPIO 组件实例的指针
// @param GpioIntrId 是 Gpio 中断 ID
// @return 如果成功返回 XST_SUCCESS, 否则返回 XST_FAILURE
int setup_interrupt_system(XScuGic *gic_inst_ptr, XGpio *axi_gpio_inst_ptr,u16 AXI_GpioIntrId)
{
//设置并使能中断异常
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler) XScuGic_InterruptHandler, gic_inst_ptr);
Xil_ExceptionEnable();
//设置中断源的优先级和请求类型(优先级为 0xA0,高电平请求)
XScuGic_SetPriorityTriggerType(gic_inst_ptr, AXI_GpioIntrId, 0xA0, 0x01);
//关联中断 ID 和中断处理函数
XScuGic_Connect(gic_inst_ptr, AXI_GpioIntrId,(Xil_ExceptionHandler) intr_handler, (void *) axi_gpio_inst_ptr);
//使能来自于 axi_Gpio 器件的中断
XScuGic_Enable(gic_inst_ptr, AXI_GpioIntrId);
//设置 AXI GPIO 通道 1 方向为输入
XGpio_SetDataDirection(axi_gpio_inst_ptr, KEY_CHANNEL1, 1);
XGpio_InterruptEnable(axi_gpio_inst_ptr, KEY_CH1_MASK);//使能通道 1 的中断
XGpio_InterruptGlobalEnable(axi_gpio_inst_ptr); //使能全局中断
return XST_SUCCESS;
}
//中断处理函数
// @param CallBackRef 是指向上层回调引用的指针
static void intr_handler(void *callback_ref)
{
XGpio *axi_gpio_inst_ptr = (XGpio *)callback_ref;
usleep(20000); //延时 20ms,按键消抖
if (XGpio_DiscreteRead(axi_gpio_inst_ptr, KEY_CHANNEL1) == 0)
{ //按键有效按下
print("Interrupt Detected!\r\n");
led_value = ~led_value;
XGpioPs_WritePin(&gpiops_inst, MIO_LED, led_value); //改变 LED 显示状态
XGpio_InterruptDisable(axi_gpio_inst_ptr, KEY_CH1_MASK);//关闭中断使能
}
XGpio_InterruptClear(axi_gpio_inst_ptr, KEY_CH1_MASK); //清除中断
XGpio_InterruptEnable(axi_gpio_inst_ptr, KEY_CH1_MASK);//使能中断
}