中断是一种当满足要求的突发事件发生时通知处理器进行处理的信号。中断可以由硬件处理单元和外
部设备产生,也可以由软件本身产生。对硬件来说,中断信号是一个由某个处理单元产生的异步信号,用
来引起处理器的注意。对软件来说,中断还是一种异步事件,用来通知处理器需要改变代码的执行,不过
轮询所产生的中断的过程是同步的。
实验之前我们需要了解一下 AXI INTC 这个 IP 核,AXI INTC 的全称是 AXI Interrupt Controller(AXI中断控制器),AXI 中断控制器(INTC)核能将来自外围设备的多个中断输入集中到单个中断输出,再将中断传输给系统处理器。通过用于 AMBA(高级微控制器总线体系结构高级可扩展接口)协议的 AXI 规范的从属接口访问用于检查,启用和确认中断的寄存器。AXI INTC(中断控制器)具有以下特点:AXI INTC IP 核可以通过 AXI4-Lite 接口访问,最高支持 32 个中断,中断控制器之间可以级联产生其他的中断信号并且支持快速中断模式。中断控制器的每个输入或输出都可以配置为电平或边缘敏感。中断信号间的优先级由向量的位决定,最低有效位(LSB,在本例中为位 0)具有最高优先级。中断控制器中包含中断使能寄存器,能够有选择地使能单个中断输入。该 IP 也可以配置软件中断功能,还能中断嵌套。
Regs Block(Registers Block 即寄存器模块):此模块包含控制寄存器和状态寄存器。它们都通过AXI4-Lite 从接口访问,每个寄存器均为四字节。
Int Det(Interrupt Detection 即中断检测):此模块用于检测中断输入。它可以配置每个中断输入为电平或边缘触发。
Irq Gen(Interrupt Generation 即中断生成):此模块具有以下功能:(1)从中断控制器生成最终输出中断。(2)中断灵敏度由配置参数决定。(3)检查控制寄存器(MER 和 IER)中用于中断生成的启用条件。(4)在确认后重置中断。(5)在 IVR(中断向量)寄存器中写入活动中断的向量地址,并为挂起的中断启用 IPR 寄存器。
我们对于这个IP 还是来总结一下
这个IP的作用是 接收到来自外部输入的中断(由Int Det 中断检测) 当我们就按测到之后,传递给Regs Block 寄存器模块 将信息考量处理之后 传递给 Irq Gen (中断生成模块) 在配置下,这个模块给外部输出一个中断
我们直接来看 实验任务
本章的实验任务是通过 AXI GPIO 检测按键状态产生中断信号,中断控制器检测到中断后,给处理器发送中断请求,处理器通过接收到的中断控制 LED 灯的亮灭。
系统框图中,按键 KEY 作为 AXI GPIO 的输入,LED 作为 AXI GPIO 的输出。当 AXI GPIO 检测到按键状态发生变化时,AXI GPIO 就会产生一个中断信号传入中断控制器(AXI Interrupt Controller),中断控制器生成中断输出信号,传入 MicroBlaze 处理器,MicroBlaze 处理器通过接收到的中断信号控制 LED。中断控制器通过 AXI Interconnect 与 MicroBlaze 互联,MicroBlaze 可以通过 AXI 接口对中断控制器进行配置。
我们创建一个 GPIO口 用来连接 按键 所以是1位的 数据位宽 并且 我们需要打开中断
我们需要连接 中断与 Micro Blaze 和 GPIO 按钮相连
#include "xparameters.h"
#include "xintc.h"
#include "xgpio.h"
#include "sleep.h"
#define KEY_DEV_ID XPAR_AXI_GPIO_0_DEVICE_ID //按键 AXI GPIO ID
#define LED_DEV_ID XPAR_AXI_GPIO_1_DEVICE_ID //LED AXI GPIO ID
#define INTC_DEVICE_ID XPAR_INTC_0_DEVICE_ID //中断控制器 ID
#define EXCEPTION_ID XIL_EXCEPTION_ID_INT //中断异常 ID
#define AXI_GPIO_INTR_ID XPAR_INTC_0_GPIO_0_VEC_ID //AXI GPIO 中断 ID
static XIntc Intc; //中断控制器实例
static XGpio KEY_Gpio; //GPIO 中断实例 按键
static XGpio LED_Gpio; //GPIO 实例
int led_value; //LED 值
int key_value; //按键值
int Intr_times = 0; //有效中断计数
int key_intr_flag = 0; //中断标志
void GpioHandler(void *CallbackRef);
int main(){
xil_printf("AXI GPIO INTERRUPT TEST!\r\n");
//AXI_GPIO 器件初始化
XGpio_Initialize(&KEY_Gpio, KEY_DEV_ID);
XGpio_Initialize(&LED_Gpio, LED_DEV_ID);
//为指定的 GPIO 信道设置所有独立信号的输入/输出方向
XGpio_SetDataDirection(&LED_Gpio, 1, 0);
//设置 LED 初始值
XGpio_DiscreteWrite(&LED_Gpio, 1, 0x0f);
XGpio_SetDataDirection(&KEY_Gpio, 1, 1);
//初始化中断控制器
XIntc_Initialize(&Intc, INTC_DEVICE_ID);
//关联中断 ID 和中断服务函数
XIntc_Connect(&Intc,AXI_GPIO_INTR_ID,(Xil_ExceptionHandler)GpioHandler,&KEY_Gpio );
//使能中断
XGpio_InterruptEnable(&KEY_Gpio, 1);
//使能全局中断
XGpio_InterruptGlobalEnable(&KEY_Gpio);
//在中断控制器上启用中断向量
XIntc_Enable(&Intc,AXI_GPIO_INTR_ID);
//启动中断控制器
XIntc_Start(&Intc, XIN_REAL_MODE);
//设置并打开中断异常处理
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(EXCEPTION_ID,
(Xil_ExceptionHandler)XIntc_InterruptHandler,&Intc);
Xil_ExceptionEnable();
while(1){
if(key_intr_flag){ //检测中断标志信号有效
key_value = XGpio_DiscreteRead(&KEY_Gpio, 1); //读取按键值
if(key_value == 0){ //检测按键是否按下
if(Intr_times == 0) //根据有效中断数点亮 LED
led_value = 0x01;
else if(Intr_times == 1)
led_value = 0x02;
else if(Intr_times == 2)
led_value = 0x04;
else
led_value = 0x08;
//按键按下后点亮对应 LED 灯
XGpio_DiscreteWrite(&LED_Gpio, 1, led_value);
xil_printf("i = %d\r\n",Intr_times); //打印当前的 Intr_times
Intr_times = (Intr_times + 1)%4; //将计数值约束在 0 到 3 之间
//延迟 1 秒消抖
sleep(1);
}
key_intr_flag = 0; //中断标志清零
}
}
return 0;
}
void GpioHandler(void *CallbackRef){
XGpio *GpioPtr = (XGpio *)CallbackRef;
key_intr_flag = 1; //接收到中断,标志信号拉高
XGpio_InterruptDisable(GpioPtr, 1); //关闭中断
XGpio_InterruptClear(GpioPtr, 1); //清除中断
XGpio_InterruptEnable(GpioPtr, 1); //使能中断
}
我们把分析 逻辑 以及 中断的使用 放在了 下一节上