上文中是通过EMIO实现PS端与PL端的交互,而PS与PL最主要的连接方式则是由一组AXI接口。AXI 互联接口作为 ZYNQ PS 和 PL 之间的桥梁,能够使两者协同工作,进而形成一个完整的、高度集成的系统。
AXI GPIO IP 核为 AXI 接口提供了一个通用的输入/输出接口。与 PS 端的 GPIO 不同,AXI GPIO 是一个软核(Soft IP),即 ZYNQ 芯片在出厂时并不存在这样的一个硬件电路,而是由用户通过配置 PL 端的逻辑资源来实现的一个功能模块。而 PS 端的 GPIO 是一个硬核(Hard IP),它是一个生产时在硅片中实现的功能电路。AXI GPIO 可以配置成单通道或者双通道,每个通道的位宽可以单独设置。另外通过打开或者关闭三态缓冲器,AXI GPIO 的端口还可以被动态地配置成输入或者输出接口。其顶层模块的框图如下所示:
从上图可以看到,模块的左侧实现了一个 32 位的 AXI4-Lite 从接口,用于主机访问 AXI GPIO 内部各通道的寄存器。当右侧接口输入的信号发生变化时,模块还能向主机产生中断信号。不过只有在配置IP 核时选择“使能中断”,才会启用模块的中断控制功能。
ZYNQ7ProcessingSystem中一些接口信号是用于PS使用AXI接口与PL端进行通信是所需要的信号。
进行Run Connection Automation操作。
此时Vivado自动添加了两个IP核,分别是 AXI 互联( AXI Interconnect)和处理器系统复位(Processor System Reseet)。
PL 端所有外设模块的时钟接口都连接到了 ZYNQ7 PS 输出的时钟信号FCLK_CLK0 上。需要注意的是,该时钟同样连接到了 PS 端 M_AXI_GP0_ACLK 端口,作为 AXI GP 接口的全局时钟信号。并将AXI GPIO IP的中断和ZYNQ7 Processing System的中断连接起来。
由于AXI GPIO按键中断和EMIO按键其实都差不多。这里就不过多的介绍,笔者也是按照官方的Example和EMIO按键中断完成程序实验。
Example:
#include "stdio.h"
#include "xscugic.h"
#include "xgpiops.h"
#include "xgpio.h"
#include "xparameters.h"
#include "sleep.h"
#include "xil_exception.h"
#define AXI_GPIO_DEVICE_ID XPAR_GPIO_0_DEVICE_ID
#define PS_GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
#define SCUGIC_DEVICE_ID XPAR_SCUGIC_0_DEVICE_ID
#define AXI_GPIO_INTR_ID XPAR_FABRIC_GPIO_0_VEC_ID
#define PS_LED0_MIO0 0
#define KEY_CHANNEL 1
#define KEY_MASK XGPIO_IR_CH1_MASK
int PerInstInit(XGpioPs *PsGpioInst,XGpio *AxiGpioInst,XScuGic *AxiIntr,u16 PsGpioDeviceId,u16 AxiGpioDeviceId,u16 ScugicDeviceId);
int SetupInterruptSystem(XScuGic *AxiIntr,XGpio *AxiGpio,u16 AxiGpioIntrId);
void AxiGpioHandler(void *CallBackRef);
int led_value = 1;
XGpioPs PsGpio;
XGpio AxiGpio;
XScuGic AxiIntr;
int main(void)
{
int status;
status = PerInstInit(&PsGpio, &AxiGpio, &AxiIntr, PS_GPIO_DEVICE_ID, AXI_GPIO_DEVICE_ID, SCUGIC_DEVICE_ID);
if(status != XST_SUCCESS)
{
printf("Initialize Failure!\n");
return XST_FAILURE;
}
XGpioPs_SetDirectionPin(&PsGpio,PS_LED0_MIO0, 0x1);
XGpioPs_SetOutputEnablePin(&PsGpio,PS_LED0_MIO0,0x1);
XGpioPs_WritePin(&PsGpio,PS_LED0_MIO0,led_value);
XGpio_SetDataDirection(&AxiGpio, KEY_CHANNEL, 0x1);
SetupInterruptSystem(&AxiIntr,&AxiGpio,AXI_GPIO_INTR_ID);
while(1);
return XST_SUCCESS;
}
int PerInstInit(XGpioPs *PsGpioInst,XGpio *AxiGpioInst,XScuGic *AxiIntr,u16 PsGpioDeviceId,u16 AxiGpioDeviceId,u16 ScugicDeviceId)
{
int status;
XScuGic_Config *ScugicConfigPtr;
XGpioPs_Config *PsGpioConfigPtr;
XGpio_Config *AxiGpioConfigPtr;
PsGpioConfigPtr = XGpioPs_LookupConfig(PsGpioDeviceId);
if(NULL == PsGpioConfigPtr)
return XST_FAILURE;
AxiGpioConfigPtr = XGpio_LookupConfig(AxiGpioDeviceId);
if(NULL == AxiGpioConfigPtr)
return XST_FAILURE;
ScugicConfigPtr = XScuGic_LookupConfig(ScugicDeviceId);
if(NULL == ScugicConfigPtr)
return XST_FAILURE;
status = XGpioPs_CfgInitialize(PsGpioInst, PsGpioConfigPtr, PsGpioConfigPtr->BaseAddr);
if(status != XST_SUCCESS)
return XST_FAILURE;
status = XGpio_CfgInitialize(AxiGpioInst,AxiGpioConfigPtr,AxiGpioConfigPtr->BaseAddress);
if(status != XST_SUCCESS)
return XST_FAILURE;
status = XScuGic_CfgInitialize(AxiIntr, ScugicConfigPtr, ScugicConfigPtr->CpuBaseAddress);
if(status != XST_SUCCESS)
return XST_FAILURE;
return XST_SUCCESS;
}
int SetupInterruptSystem(XScuGic *AxiIntr,XGpio *AxiGpio,u16 AxiGpioIntrId)
{
int status;
// 1.
Xil_ExceptionInit();
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler, AxiIntr);
// 2.
status = XScuGic_Connect(AxiIntr, AxiGpioIntrId, AxiGpioHandler, (void*)AxiGpio);
if(status != XST_SUCCESS)
return XST_FAILURE;
// 3.
XScuGic_Enable(AxiIntr, AxiGpioIntrId);
// 4.
XGpio_InterruptEnable(AxiGpio,KEY_MASK);
XGpio_InterruptGlobalEnable(AxiGpio);
XScuGic_SetPriorityTriggerType(AxiIntr, AxiGpioIntrId, 0,1);
return XST_SUCCESS;
}
void AxiGpioHandler(void *CallBackRef)
{
int key_value = 1;
XGpio *GpioPtr = (XGpio *)CallBackRef;
printf("Interrupt Detected!\n");
XGpio_InterruptDisable(GpioPtr, KEY_MASK);
key_value = XGpio_DiscreteRead(GpioPtr, KEY_CHANNEL);
if(key_value == 0)
{
led_value = ~led_value;
XGpioPs_WritePin(&PsGpio,PS_LED0_MIO0,led_value);
}
usleep(200000);
XGpio_InterruptClear(GpioPtr, KEY_MASK);
XGpio_InterruptEnable(GpioPtr, KEY_MASK);
}
由于笔者之前并没有学过单片机或者是ARM,所以笔者学到这里已经逐渐产生了疑问,这里先记录下来。看看之后有没有办法搞清楚,也欢迎网友给予我想要的答案,大家一起进步。
Q:**s32 XScuGic_Connect(XScuGic InstancePtr, u32 Int_Id,Xil_InterruptHandler Handler, void CallBackRef)
该函数的工作机制到底是什么,也就是中断在操作系统中是如何返会原程序的。Handler和CallBackRef这两者是怎么联系的?
Q:**void XScuGic_SetPriorityTriggerType(XScuGic *InstancePtr, u32 Int_Id,u8 Priority, u8 Trigger)**在本次实验中,由于AXI中断属于共享外设中断(SPI),
中断类型设置的High level sensitive.而硬件电路中按键连接方式如下图:
英文解释是高电平敏感(好像并不是我想的只要检测的IO口电平是高电平就产生一个中断),我实际去观察只有我按下按键(IO电平由高至低),才会产生中断。
Date : 2022-1-5
Author : QImua
City : Wuxi