开发环境 vivado 19.2 vitis
开发板 zynq7010
内容 按键按下,控制LED状态进行亮灭转换。
中断简介:中断是一种当满足要求的突发事件发生时通知处理器进行处理的信号。中断可以由硬件处理单元和外部设备产生,也可以由软件本身产生。对硬件来说,中断信号是一个由某个处理单元产生的异步信号,用 来引起处理器的注意。
中断触发可以是正沿、负沿、任一沿、低电平或高电平。使用INT_TYPE、INT_POLARITY和INT_ANY寄存器对触发灵敏度进行编程。
INT_POLARITY:该寄存器控制中断是低电平有效还是高电平有效(或下降沿敏感或上升沿敏感)。
INT_TYPE:该寄存器控制中断是边沿敏感还是电平敏感。
INT_ANY:只针对边沿触发,如果为1,则上升沿和下降沿都作为中断触发类型。
INT_STAT: 如果写1可以清除中断,如果读该寄存器则获取中断的状态。
INT_MASK: 只读寄存器,读取该寄存器可以获取哪些寄存器中断被打开,哪些被屏蔽如果某位为1,则代表中断被打开。
INT_DIS:向该寄存器的任何位写入 1 都会屏蔽该中断信号。从该寄存器读取会返回不可预测的值。
INT_EN:向该寄存器的任何位写入 1,可以启用/解除中断信号的掩码。从该寄存器读取将返回一个不 可预测的值。
如上图所示,我们可以看到有一个与门,与门的两个输入分别是INT STATE输出的信号和INT DIS输出的信号。这就意味着当某个引脚检测到中断,并且中断没有被屏蔽,相与的结果输出到GIC(PS的中断控制器),也就是输出一个中断请求信号到中断控制器,GPIO中断ID为52。
硬件环境配置
中断控制我们采用PS端的按键,控制核心板上PS的LED,也就是MIO来控制中断。如果要用PL端的按键来控制也是一样的,在硬件平台配置EMIO接口即可。因此硬件平台的搭建只需要把DDR、时钟、MIO和UART配置好即可,UART用来打印信息,也可以方便我们进行调试。
具体配置过程见CSDN
vitis配置及编程
我们在硬件平台配置好之后,把生成的比特流一起导出,在vitis里面创建工程,然后开始编程,在这里我们先给出最终的程序,然后再进行一个详细的说明,程序部分如下:
#include "stdio.h"
#include "xparameters.h"
#include "xgpiops.h"
#include "sleep.h"
#include "xscugic.h"
#define MIO0_LED 0
#define MIO12_KEY 11 //KEY 连接到 MIO11
#define GPIO_INTERRUPT_ID XPAR_XGPIOPS_0_INTR//GPIO中断号是52 //PS端GPIO中断ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID//通用中断控制器ID
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID//PS端GPIO器件ID
XGpioPs_Config *ConfigPtr;//定义ConfigPtr为结构体指针类型 (指针理解为地址)
XGpioPs Gpio;//定义GPIO驱动实例
XScuGic Intc;//中断驱动实例
XScuGic_Config *IntcConfig; /* Instance of the interrupt controller */
void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio,u16 GpioIntrId);
//void IntrHandler(void *CallBackRef, u32 Bank, u32 Status);
void IntrHandler();
u32 key_press = 0;
int main(){
u32 led_value = 0;
printf("GPIO interrupt TEST\n");
//根据器件的ID查找器件的配置信息
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
//初始化GPIO的驱动
XGpioPs_CfgInitialize(&Gpio, ConfigPtr,
ConfigPtr->BaseAddr);//&是取地址 ->表示结构体的第一个元素
//把GPIO的方向设置为输出(0输入/1输出)
XGpioPs_SetDirectionPin(&Gpio, MIO0_LED, 1);//(地址 引脚 方向)
//XGpioPs_SetDirectionPin(&Gpio, MIO7_LED, 1);//(地址 引脚 方向)
//XGpioPs_SetDirectionPin(&Gpio, MIO8_LED, 1);//(地址 引脚 方向)
//设置输出使能(0关闭 1打开)
XGpioPs_SetOutputEnablePin(&Gpio, MIO0_LED, 1);
XGpioPs_WritePin(&Gpio, MIO0_LED, 1);
SetupInterruptSystem(&Intc, &Gpio, GPIO_INTERRUPT_ID);
//点亮
while(1){
if(key_press){
led_value=~led_value;
key_press=0;
//中断处理之后,要清除中断状态,,UG585里面一个图的D触发器(state)
XGpioPs_IntrClearPin(&Gpio, MIO12_KEY);
XGpioPs_WritePin(&Gpio, MIO0_LED, led_value);//将led_value的值写入led
//延时消抖
usleep(500000);//延时200ms再打开中断使能信号
XGpioPs_IntrEnablePin(&Gpio, MIO12_KEY);//打开MIO引脚中断使能信号
}
}
return 0;
}
void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio,u16 GpioIntrId)
{
int Status;
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);//查找器件配置信息,并初始化
XScuGic_CfgInitialize(GicInstancePtr, IntcConfig,IntcConfig->CpuBaseAddress);
Xil_ExceptionInit();//初始化arm异常句柄
//来给IRQ异常注册处理程序
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
GicInstancePtr);
/*使能处理器中断*/
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);//只要用到中断,就是需要这3个exception函数
//关联中断处理函数 处理中断,结束之后再回到原过程 把GPIO的中断处理函数IntrHandler,关联到中断控制器
XScuGic_Connect(GicInstancePtr, GpioIntrId,
(Xil_ExceptionHandler)IntrHandler,
(void *)Gpio);
/* 为GPIO器件使能中断 */
XScuGic_Enable(GicInstancePtr, GpioIntrId);
/*设置MIO引脚为下降沿触发类型 */
XGpioPs_SetIntrTypePin(Gpio, MIO12_KEY, XGPIOPS_IRQ_TYPE_EDGE_FALLING /**< Interrupt Falling edge */);
/* Set the handler for gpio interrupts. */
//XGpioPs_SetCallbackHandler(Gpio, (void *)Gpio, IntrHandler);
/* Enable the GPIO interrupts of Bank 0. */
//XGpioPs_IntrEnable(Gpio, GPIO_BANK, (1 << Input_Pin));//整个BANK设置使能
XGpioPs_IntrEnablePin(Gpio, MIO12_KEY);//打开MIO引脚中断使能信号(不取地址的原因是因为最外面的函数定义本身就是指针)
}
//自己写一个中断服务函数IntrHandler
void IntrHandler(){
printf("interrupt detected!\n\r");
key_press = 1;
XGpioPs_IntrDisablePin(&Gpio, MIO12_KEY);
}
参数介绍:GPIO_INTERRUPT_ID,GPIO的中断号是52,具体可以参考UG585里面的文档说明,如下:
INTC_DEVICE_ID,它是通用中断控制器ID,表示的是0.
GPIO_DEVICE_ID因为我们只用了一个按键来控制中断,所以它表示的是0,如果有两个中断,那ID应该就是0和1了,这个ID的目的就是分辨,中断是从GPIO的哪个管脚产生的。
程序设计步骤:
1.由于我们是用的MIO来控制中断的产生,需要根据器件的ID查找期间的配置信息,并初始化GPIO的驱动,最后通过读写寄存器来配置GPIO的方向,输出使能等。
2.调用并修改SetupInterruptSystem函数,主要实现的功能有,首先查找器件配置信息并初始化(这里是对GIC而言的),其次就是三板斧(初始化ARM异常句柄,给IRQ异常注册处理程序,使能处理器中断)和关联中断处理函数,只要用到中断,三板斧必须加上,也就是三个有Exception的函数。最后,给GPIO器件设置中断、设置MIO引脚的触发类型(我给的程序里是下降沿触发)、打开MIO引脚中断使能信号。 补充一点,就是设置MIO引脚的触发类型和中断使能信号,这两个都可以对单独的引脚进行操作,也可以对整个BANK进行操作,用的函数不一样,程序里面被注释掉的两个函数就是对BANK进行操作的。
3.自定义一个中断服务函数,连接到关联中断处理函数(XScuGic_Connect)里面。
4.通过自定义的中断服务函数输出的信号,来实现自己想要的功能,需要注意的一点是,中断处理之后,我们需要给对应寄存器写1清除中断的状态,并通过控制中断使能和关闭来进行下一次的中断操作。
上板验证,按下按键,LED的状态发生改变,符合预期。