该部分是基于基于ZYNQ-7000开发板的调试系列(5)继续展开的。这一部分主要是完成两个外部中断,一个是基于MIO的外部中断,另一个是基于EMIO的外部中断。其中EMIO的外部中断完成整个程序的开关,MIO的外部中断完成流水灯方向的变换。这两个外部中断均由开关触发。相关的资源可以参考基于ZYNQ-7000开发板的调试系列(4)。
这一部分内容也需要重新修改原理图,这里的主要问题是,尽管EMIO的通道不同,但是仍然无法完成中断。我也说不清楚这里到底发生了什么问题。
所以为了保险起见,之后还是需要再多添加一个GPIO口的IP核。
这里主要就是把一个GPIO核分为两个,并且为了避免之后出现混淆GPIO0和GPIO1的情况,将无需使能中断的,LED的GPIO口的IP核命名为axi_leds_5bits;将需要使能中断的,KEY的GPIO口的IP核命名为axi_key_1bit。当然还需要注意一点,由于需要使能GPIO的中断,需要将axi_key_1bit的中断的引脚与ZYNQ_PROCESSING_SYSTEM的中断引脚相连,这样才能完成中断的连接。
具体修改完成的Diagram图大概是这个样子的。
然后这里就没有其他需要修改的部分了,这里生成Bitstream即可。
这一部分的程序实际上和上一部分也较为一致,主要就是增多了两个GPIO口的中断,实际上方式和之前的中断都比较类似。
这一部分我们可以看到,这里的一共有16路中断可以使用,中断需要分别是从61-68,84-91。
[参考自Zynq中 PS接收PL中断]
这篇文章将这一部分的具体操作写得比较详细了,实际上中断序号的查找可以直接在TRM里找到,这里使用XPS_FPGA0_INT_ID,即61号中断。
需要使用到的函数如下:
void XGpio_InterruptEnable(
XGpio *InstancePtr,
u32 Mask
); // GPIO口使能中断
void XGpio_InterruptGlobalEnable(
XGpio *InstancePtr
); // 使能所有的GPIO口中断,如果该部分不被使能,则所有的GPIO中断均无法使用
void XScuGic_SetPriorityTriggerType(
XScuGic *InstancePtr,
u32 Int_Id,
u8 Priority, // 优先级分为32档分别是0x00、0x08、0x10、....0xF8,其中0x00优先级最高
u8 Trigger // 0x01表示为高电平触发、0x03表示为上升沿触发,有且只有这两种触发方式
); // 设置中断的优先级以及触发条件
中断内函数:
void XGpio_InterruptClear(
XGpio *InstancePtr,
u32 Mask
); // 清除GPIO中断标志位
u32 XGpio_DiscreteRead(
XGpio *InstancePtr,
unsigned Channel
); // 读取GPIO的值
具体的实现主要如下:
int Pl_intr_init(){
XGpio_InterruptEnable(&plKey, PL_KEY_CHANNEL);
XGpio_InterruptGlobalEnable(&plKey);
XScuGic_SetPriorityTriggerType(&psGic, PL_KEY_INTR_ID, 0xA0, 0x3);
status = XScuGic_Connect(&psGic, PL_KEY_INTR_ID, (Xil_ExceptionHandler)psIntrGpioPlHandler, (void *)&plKey);
if(status != XST_SUCCESS) return XST_FAILURE;
XScuGic_Enable(&psGic, PL_KEY_INTR_ID);
}
void psIntrGpioPlHandler(void* ref){
XGpio* ptr = (XGpio*) ref;
XGpio_InterruptClear(ptr, PL_KEY_CHANNEL);
usleep(20000);
if(XGpio_DiscreteRead(&plKey, PL_KEY_CHANNEL) & 0x01){
if(state0 == 0) state0 = 1;
else state0 = 0;
}
}
这一部分和刚刚一部分类似,但是需要注意的是,ZYNQ仅仅支持一个MIO的GPIO中断,即52号中断。
[参考自ZYNQ7000 TRM (Page.230)]
需要使用的函数如下:
void XGpioPs_SetIntrType(
XGpioPs *InstancePtr,
u8 Bank,
u32 IntrType, // 0代表电位触发,1代表跳变沿触发
u32 IntrPolarity, // 0代表低电平或下降沿有效,1代表高电平或上升沿有效
u32 IntrOnAny // 0代表仅对单一的跳变沿触发,1代表所有跳变沿均触发
); // 设置每一位的GPIO口的中断要求,每一位代表一个MIO
void XGpioPs_IntrEnable(
XGpioPs *InstancePtr,
u8 Bank,
u32 Mask
); // GPIO口中断使能
void XGpioPs_SetCallbackHandler(
XGpioPs *InstancePtr,
void *CallBackRef,
XGpioPs_Handler FuncPointer
); // 这里注册中断函数
这里需要注意的是这个函数:
s32 XScuGic_Connect(
XScuGic *InstancePtr,
u32 Int_Id,
Xil_InterruptHandler Handler,
void *CallBackRef
);
这个函数里面本来是用来注册自己写入的中断函数的,这里需要写入系统自带的中断函数,故而用上面的XGpioPs_SetCallbackHandler函数代替这一函数。
中断内函数:
void XGpioPs_IntrClearPin(
XGpioPs *InstancePtr,
u32 Pin
); // 消除中断标志位
u32 XGpioPs_ReadPin(
XGpioPs *InstancePtr,
u32 Pin
); // 读取一位GPIO口数据
具体实现如下:
void mio_intr_init(){
XGpioPs_SetIntrType(&psGpio, PS_BANK, 0x800, 0x800, 0x000);
XGpioPs_IntrEnable(&psGpio, PS_BANK, (1 << PS_KEY));
status = XScuGic_Connect(&psGic, PS_KEY_INTR_ID,
(Xil_ExceptionHandler)XGpioPs_IntrHandler,
(void *)&psGpio);
if(status != XST_SUCCESS) return XST_FAILURE;
XGpioPs_SetCallbackHandler(&psGpio, (void *)&psGpio, psIntrGpioPsHandler);
XScuGic_Enable(&psGic, PS_KEY_INTR_ID);
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
}
void psIntrGpioPsHandler(void* ref, u32 bank, u32 status){
XGpioPs_IntrClearPin(&psGpio, PS_KEY);
if(XGpioPs_ReadPin(&psGpio, PS_KEY)){
if(stateA == 0) stateA = 1;
else stateA = 0;
}
usleep(100000);
}
这里面中断函数主要是加了一个按键防抖的机制,用起来其实非常不好用,勉强凑合能用。而且我的PS_KEY的按键有点坏了,这个真的是用点尴尬…
最后是整个项目的所有代码:
#include "xparameters.h"
#include "xgpio.h"
#include "xgpiops.h"
#include "xttcps.h"
#include "xstatus.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "sleep.h"
#define PS_GIC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define LEDG1_INTR_ID XPAR_XTTCPS_0_INTR
#define LEDG2_INTR_ID XPAR_XTTCPS_3_INTR
#define PS_KEY_INTR_ID XPAR_XGPIOPS_0_INTR
#define PL_KEY_INTR_ID XPS_FPGA0_INT_ID
#define PS_KEY_INTR_ID XPAR_XGPIOPS_0_INTR
#define LED1_INTR_DEVICE_ID XPAR_XTTCPS_0_DEVICE_ID
#define LED2_INTR_DEVICE_ID XPAR_XTTCPS_3_DEVICE_ID
#define LED1_FREQ_HZ 2
#define LED2_FREQ_HZ 3
#define PL_GPIO_DEVICE_ID XPAR_AXI_LEDS_5BITS_DEVICE_ID
#define PL_KEY_DEVICE_ID XPAR_AXI_KEY_1BIT_DEVICE_ID
#define PS_GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
#define PL_LED_CHANNEL 1
#define PL_KEY_CHANNEL 1
#define PS_BANK XGPIOPS_BANK0
#define PL_LED0 0x01
#define PL_LED1 0x02
#define PL_LED2 0x04
#define PL_LED3 0x08
#define PL_LED4 0x10
#define PS_LED0 0x09
#define PS_LED1 0x00
#define PS_KEY 0x0B
typedef struct {
u32 OutputHz; /* Output frequency */
XInterval Interval; /* Interval value */
u8 Prescaler; /* Prescaler value */
u16 Options; /* Option settings */
} TmrCntrSetup;
volatile u8 led_status = 0x00;
volatile u8 stateA;
volatile u8 state0;
volatile u8 state1;
volatile u8 state2;
volatile u8 plLed;
volatile u8 psLed;
XGpio plGpio;
XGpioPs psGpio;
XGpio plKey;
XTtcPs psTimer1;
XTtcPs psTimer2;
XScuGic psGic;
static TmrCntrSetup timer1Setup = {LED1_FREQ_HZ, 0, 0, 0};
static TmrCntrSetup timer2Setup = {LED2_FREQ_HZ, 0, 0, 0};
int init();
void run();
int init_Gpio();
int init_Exti();
int init_Gpio_Pl();
int init_Gpio_Ps();
int init_Exti_Timer1();
int init_Exti_Timer2();
int init_Exti_Gic();
void psIntrTimer1Handler(void*);
void psIntrTimer2Handler(void*);
void psIntrGpioPlHandler(void*);
void psIntrGpioPsHandler(void*, u32, u32);
// 0
int main(){
int status;
status = init();
if(status != XST_SUCCESS) return XST_FAILURE;
run();
return 0;
}
// 1
int init(){
int status;
stateA = 1;
state0 = 1;
status = init_Gpio();
if(status != XST_SUCCESS) return XST_FAILURE;
status = init_Exti();
if(status != XST_SUCCESS) return XST_FAILURE;
return XST_SUCCESS;
}
void run(){
XTtcPs_Start(&psTimer1);
XTtcPs_Start(&psTimer2);
while(1){
switch(state1){
case 1: {
plLed = (plLed & 0x10) + 0x0E;
XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, plLed);
XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
}break;
case 2: {
plLed = (plLed & 0x10) + 0x0D;
XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, plLed);
XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
}break;
case 3: {
plLed = (plLed & 0x10) + 0x0B;
XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, plLed);
XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
}break;
case 4: {
plLed = (plLed & 0x10) + 0x07;
XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, plLed);
XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
}break;
case 0: {
plLed = (plLed & 0x10) + 0x0F;
XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, plLed);
XGpioPs_WritePin(&psGpio, PS_LED0, 0x00);
}break;
default:{
plLed = (plLed & 0x10) + 0x0F;
XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, plLed);
XGpioPs_WritePin(&psGpio, PS_LED0, 0x01);
}
}
switch(state2){
case 0:{
plLed = (plLed & 0x0F) + 0x00;
XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, plLed);
XGpioPs_WritePin(&psGpio, PS_LED1, 0x01);
} break;
case 1:{
plLed = (plLed & 0x0F) + 0x10;
XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, plLed);
XGpioPs_WritePin(&psGpio, PS_LED1, 0x00);
} break;
default:{
plLed = (plLed & 0x0F) + 0x10;
XGpio_DiscreteWrite(&plGpio, PL_LED_CHANNEL, plLed);
XGpioPs_WritePin(&psGpio, PS_LED1, 0x01);
}
}
}
}
// 2
int init_Gpio(){
int status;
status = init_Gpio_Pl();
if(status != XST_SUCCESS) return XST_FAILURE;
status = init_Gpio_Ps();
if(status != XST_SUCCESS) return XST_FAILURE;
// PL
XGpio_SetDataDirection(&plGpio, PL_LED_CHANNEL, 0x00);
XGpio_SetDataDirection(&plKey, PL_KEY_CHANNEL, 0x01);
// PS
XGpioPs_SetDirection(&psGpio, PS_BANK, 0x201);
XGpioPs_SetOutputEnable(&psGpio, PS_BANK, 0x201);
return XST_SUCCESS;
}
int init_Exti(){
int status;
TmrCntrSetup *t1;
TmrCntrSetup *t2;
status = init_Exti_Timer1();
if(status != XST_SUCCESS) return XST_FAILURE;
status = init_Exti_Timer2();
if(status != XST_SUCCESS) return XST_FAILURE;
status = init_Exti_Gic();
if(status != XST_SUCCESS) return XST_FAILURE;
// Timer //
t1 = &timer1Setup;
t2 = &timer2Setup;
t1->Options |= (XTTCPS_OPTION_INTERVAL_MODE | XTTCPS_OPTION_WAVE_DISABLE);
t2->Options |= (XTTCPS_OPTION_INTERVAL_MODE | XTTCPS_OPTION_WAVE_DISABLE);
XTtcPs_SetOptions(&psTimer1, t1->Options);
XTtcPs_CalcIntervalFromFreq(&psTimer1, t1->OutputHz, &(t1->Interval), &(t1->Prescaler));
XTtcPs_SetInterval(&psTimer1, t1->Interval);
XTtcPs_SetPrescaler(&psTimer1, t1->Prescaler);
XTtcPs_EnableInterrupts(&psTimer1, XTTCPS_IXR_INTERVAL_MASK);
XTtcPs_SetOptions(&psTimer2, t2->Options);
XTtcPs_CalcIntervalFromFreq(&psTimer2, t2->OutputHz, &(t2->Interval), &(t2->Prescaler));
XTtcPs_SetInterval(&psTimer2, t2->Interval);
XTtcPs_SetPrescaler(&psTimer2, t2->Prescaler);
XTtcPs_EnableInterrupts(&psTimer2, XTTCPS_IXR_INTERVAL_MASK);
// Key Pl
XGpio_InterruptEnable(&plKey, PL_KEY_CHANNEL);
XGpio_InterruptGlobalEnable(&plKey);
XScuGic_SetPriorityTriggerType(&psGic, PL_KEY_INTR_ID, 0xA0, 0x3);
// Key Ps
XGpioPs_SetIntrType(&psGpio, PS_BANK, 0x800, 0x800, 0x000);
XGpioPs_IntrEnable(&psGpio, PS_BANK, (1 << PS_KEY));
// Gic
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
&psGic);
status = XScuGic_Connect(&psGic, LEDG1_INTR_ID,
(Xil_ExceptionHandler)psIntrTimer1Handler,
(void *)&psTimer1);
if(status != XST_SUCCESS) return XST_FAILURE;
XScuGic_Enable(&psGic, LEDG1_INTR_ID);
status = XScuGic_Connect(&psGic, LEDG2_INTR_ID,
(Xil_ExceptionHandler)psIntrTimer2Handler,
(void *)&psTimer2);
if(status != XST_SUCCESS) return XST_FAILURE;
XScuGic_Enable(&psGic, LEDG2_INTR_ID);
status = XScuGic_Connect(&psGic, PL_KEY_INTR_ID,
(Xil_ExceptionHandler)psIntrGpioPlHandler,
(void *)&plKey);
if(status != XST_SUCCESS) return XST_FAILURE;
XScuGic_Enable(&psGic, PL_KEY_INTR_ID);
status = XScuGic_Connect(&psGic, PS_KEY_INTR_ID,
(Xil_ExceptionHandler)XGpioPs_IntrHandler,
(void *)&psGpio);
if(status != XST_SUCCESS) return XST_FAILURE;
XGpioPs_SetCallbackHandler(&psGpio, (void *)&psGpio, psIntrGpioPsHandler);
XScuGic_Enable(&psGic, PS_KEY_INTR_ID);
Xil_ExceptionEnable();
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
return XST_SUCCESS;
}
// 3
int init_Gpio_Pl(){
XGpio_Config *xGpioCfg;
xGpioCfg = XGpio_LookupConfig(PL_GPIO_DEVICE_ID);
if(xGpioCfg == (XGpio_Config *)NULL) return XST_FAILURE;
XGpio_CfgInitialize(&plGpio, xGpioCfg, xGpioCfg->BaseAddress);
xGpioCfg = XGpio_LookupConfig(PL_KEY_DEVICE_ID);
if(xGpioCfg == (XGpio_Config *)NULL) return XST_FAILURE;
return XGpio_CfgInitialize(&plKey, xGpioCfg, xGpioCfg->BaseAddress);
}
int init_Gpio_Ps(){
XGpioPs_Config *xGpioCfg;
xGpioCfg = XGpioPs_LookupConfig(PS_GPIO_DEVICE_ID);
if(xGpioCfg == (XGpioPs_Config *)NULL) return XST_FAILURE;
return XGpioPs_CfgInitialize(&psGpio, xGpioCfg, xGpioCfg->BaseAddr);
}
int init_Exti_Timer1(){
XTtcPs_Config *xGpioCfg;
xGpioCfg = XTtcPs_LookupConfig(LED1_INTR_DEVICE_ID);
if(xGpioCfg == (XTtcPs_Config *)NULL) return XST_FAILURE;
return XTtcPs_CfgInitialize(&psTimer1, xGpioCfg, xGpioCfg->BaseAddress);
} //
int init_Exti_Timer2(){
XTtcPs_Config *xGpioCfg;
xGpioCfg = XTtcPs_LookupConfig(LED2_INTR_DEVICE_ID);
if(xGpioCfg == (XTtcPs_Config *)NULL) return XST_FAILURE;
return XTtcPs_CfgInitialize(&psTimer2, xGpioCfg, xGpioCfg->BaseAddress);
} //
int init_Exti_Gic(){
XScuGic_Config *xGpioCfg;
xGpioCfg = XScuGic_LookupConfig(PS_GIC_DEVICE_ID);
if(xGpioCfg == (XScuGic_Config *)NULL) return XST_FAILURE;
return XScuGic_CfgInitialize(&psGic, xGpioCfg, xGpioCfg->CpuBaseAddress);
} //
void psIntrTimer1Handler(void *ref){
u32 statusEvent;
statusEvent = XTtcPs_GetInterruptStatus(&psTimer1);
XTtcPs_ClearInterruptStatus(&psTimer1, statusEvent);
if(0 != (XTTCPS_IXR_INTERVAL_MASK & statusEvent)){
if (stateA){
if(state0){
switch(state1){
case 0: state1 = 1; break;
case 1: state1 = 2; break;
case 2: state1 = 3; break;
case 3: state1 = 4; break;
case 4: state1 = 0; break;
default: state1 = 0;
}
}
else{
switch(state1){
case 0: state1 = 4; break;
case 1: state1 = 0; break;
case 2: state1 = 1; break;
case 3: state1 = 2; break;
case 4: state1 = 3; break;
default: state1 = 0;
}
}
}
else{
state1 = 0xFF;
}
}
}//
void psIntrTimer2Handler(void *ref){
u32 statusEvent;
statusEvent = XTtcPs_GetInterruptStatus(&psTimer2);
XTtcPs_ClearInterruptStatus(&psTimer2, statusEvent);
if(0 != (XTTCPS_IXR_INTERVAL_MASK & statusEvent)){
if (stateA){
switch(state2){
case 0: state2 = 1; break;
case 1: state2 = 0; break;
default: state2 = 0;
}
}
else{
state2 = 0xFF;
}
}
}//
void psIntrGpioPlHandler(void* ref){
XGpio* ptr = (XGpio*) ref;
XGpio_InterruptClear(ptr, PL_KEY_CHANNEL);
usleep(20000);
if(XGpio_DiscreteRead(&plKey, PL_KEY_CHANNEL) & 0x01){
if(state0 == 0) state0 = 1;
else state0 = 0;
}
}
void psIntrGpioPsHandler(void* ref, u32 bank, u32 status){
XGpioPs_IntrClearPin(&psGpio, PS_KEY);
if(XGpioPs_ReadPin(&psGpio, PS_KEY)){
if(stateA == 0) stateA = 1;
else stateA = 0;
}
usleep(100000);
}
该部分代码和之前没有太大的出入,主要是又修改了一下LED函数,使得其能够配合中断函数使用。至此,关于ZYNQ的中断部分全部完成。