AXI GPIO按键控制——ZYNQ学习笔记4

时间:2024-10-15 06:55:21

一、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);//使能中断
}

四、下载验证