【【FPGA中断的介绍附上 上个MicroBlaze 代码的解析】】

FPGA中断的介绍附上 上个MicroBlaze 代码的解析

我们 先附带上上一节 Micro Blaze 的 blockdesign 结构 和 代码
本次实验参考自 正点原子达芬奇开发板 Micro Blaze 开发
【【FPGA中断的介绍附上 上个MicroBlaze 代码的解析】】_第1张图片

我们 可以看出 我们圈画了一个中断控制器IP 还有一个是我们构建的软核 micro blaze 和挂载的BRAM 用来当作原本内核的 DDR 存储

我之前用的是 带硬核的 zynq 开发 ARM cpu 为设计 提供了很大的帮助

我们可以 利用 强大的FPGA端 构建出假的CPU 俗称 软核
好的 了解 这个之后 我们先了解一下 中断的基本概念和使用
1.中断 — 是 打断CPU 正常执行顺序的事件
2.中断处理函数 – CPU被动调用,当有紧急的事情发生的时候,会调用
3.中断源 – 产生中断的设备

设备要产生中断 就必须有一根中断请求线,并且将其连接到中断控制器上
中断控制器就是我们在blockdesign 中 添加的 那个 IP核
【【FPGA中断的介绍附上 上个MicroBlaze 代码的解析】】_第2张图片

根据输入的中断请求 给CPU内核一个中断信号,通过CPU某某设备产生了中断。外部硬件在通过INTR发送中断请求 同时读取了这个由外部硬件提供的编号

接下来我们附上代码

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

我们接下来 对代码进行解析

#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

包括按键和 LED 对应的 AXI GPIO的 ID,中断控制器 ID,中断源 ID 以及中断异常 ID
中断源就是指连接按键的 AXI GPIO 输出到中断控制器的中断信号。中断异常 ID(XIL_EXCEPTION_ID_INT)是一个适配所有处理器的宏定义,但在不同处理器中它对应的的值也不同,我们将其宏定义为 EXCEPTION_ID

中断控制器 就是中断控制器
中断异常ID 不同的中断会有不同的ID 我们读取ID 才知道 处理的是什么中断
接下来的是 在 器件初始化、设置输入输出、设置中断系统和中断异常处理四部分。

器件初始化 :

//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);

有GPIO的初始化 还有设置GPIO的 输入输出方向 还有 GPIO的LED 初始化都亮

//初始化中断控制器
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);

下面是对中断系统的描述
1.//初始化中断控制器

XIntc_Initialize(&Intc, INTC_DEVICE_ID);

先初始化中断控制器 就是图上的那个 我们定义了叫 INIC_DEVICE

2.//关联中断 ID 和中断服务函数

XIntc_Connect(&Intc,AXI_GPIO_INTR_ID,(Xil_ExceptionHandler)GpioHandler,&KEY_Gpio );

我们可以在这里面看到 (Xil_ExceptionHandler)GpioHandler
这个GPIOHandler是我们自己写的中断服务函数
关联中断源与 中断服务函数 就可以帮助我们确定 何时触发中断

//使能中断
XGpio_InterruptEnable(&KEY_Gpio, 1);

//使能全局中断
XGpio_InterruptGlobalEnable(&KEY_Gpio);
使能 AXI GPIO 通道中断和 AXI GPIO全局中断,只有全局中断使能后 XGpio_InterruptEnable 启用的中断才会被传递。

 //在中断控制器上启用中断向量
XIntc_Enable(&Intc,AXI_GPIO_INTR_ID);
//启动中断控制器
XIntc_Start(&Intc, XIN_REAL_MODE);

我们在上面绑定了中断源与 需要接下来处理的中断服务函数 然后开动小脑筋想一下 还缺什么
当然缺的是 把中断源绑定到 中断控制器上 通过XIntc_Enable(&Intc,AXI_GPIO_INTR_ID); 绑定之后 再启用中断控制器

5.下面的是中断异常处理 关联中断异常处理函数和中断异常 ID

Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(EXCEPTION_ID,(Xil_ExceptionHandler)XIntc_InterruptHandler,&Intc);
Xil_ExceptionEnable();

MicroBlaze 支持多种异常情况,每种异常也都有自己的 ID标识,其中 XIL_EXCEPTION_ID_INT 用于标识中断请求(IRQ)异常,在代码第 9 行将其宏定义为EXCEPTION_ID,其对应的值为 0。代码第 49 行就是使能处理器的异常处理。

先初始化中断异常处理
然后 关联中断异常处理函数和中断ID — 还有一个是(Xil_ExceptionHandler)XIntc_InterruptHandler 关联之后就知道我们选择我们出现的是什么样子的中断
最后 Xil_ExceptionEnable(); 使能处理器异常处理

下面一部分的while 循环 是用来调配中断接受后的操作

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; //中断标志清零
}
}

最后一段是自己写的 中断服务函数

void GpioHandler(void *CallbackRef){
XGpio *GpioPtr = (XGpio *)CallbackRef;
key_intr_flag = 1; //接收到中断,标志信号拉高
XGpio_InterruptDisable(GpioPtr, 1); //关闭中断
XGpio_InterruptClear(GpioPtr, 1); //清除中断
XGpio_InterruptEnable(GpioPtr, 1); //使能中断
}

当系统接收到中断信号时就将中断标志信号 key_intr_flag 拉高,
然后用 XGpio_InterruptDisable 函数关闭中断,
再调用 XGpio_InterruptClear(GpioPtr, 1)函数清除 AXI GPIO通道 1 的中断状态寄存器,
最后再使能 AXI GPIO 中断以等待下一次中断触发。

你可能感兴趣的:(FPGA学习,fpga开发)