基于ZYNQ的中断的使用(4)

项目简述

前面的文章我们已经讲解了ZYNQ的共享中断、软中断,每种中断的类型我们都进行了相应的讲解。ZYNQ的三种中断类型还差一个私有中断我们没有进行讲解,接下来我们将以CPU的私有定时器为例来进行相应私有中断的讲解。

工程描述:CPU利用私有定时器进行定时,每隔1s产生相应的中断,并且通过串口进行相应的打印。

本次实验所用到的软硬件环境如下:
1、VIVADO 2019.1
2、米联客MZ7015FA开发板

私用中断简述

包含:全局定时器,私有看门狗定时器,私有定时器以及来自 PL 的 FIQ/IRQ。ZYNQ 每个 CPU 连接 5 个私有外设中断,所有中断的触发类型固定不变。并且来自 PL 的快速中断信号 FIQ 和中断信号 IRQ 反向。尽管在 ICDICFR1 寄存器内反应它们是低电平触发,但是 PS-PL 接口中为高电平触发。如图所示:
在这里插入图片描述
本篇博客主要利用上面的CPU 私有定时器为例来讲解私用中断,ZYNQ 每个 ARM core 都有自己的私有定时器,私有定时器的工作频率为 CPU 的一半。 ARM 工作频率为 666MHZ,则私有定时器的频率为 333MHz.
私有定时器的特性如下:
(1) 32 位计数器,达到零时产生一个中断
(2) 8 位预分频计数器,可以更好的控制中断周期
(3)可配置一次性或者自动重加载模式
(4)定时器时间可以通过下式计算:
定时时间 = 1/定时器频率*(预加载值+1)

PS端设计

这里PL端只需要例化一个ZYNQ的IP,其他部分不需要进行任何处理,所以PL端我们没有进行相应的设计。其中,Block Design设计如下:
在这里插入图片描述

PL端设计

这里的GUI的初始化与前面的一样,相当于增加了定时器的初始化,代码如下:

#include <stdio.h>
#include "xscugic.h"
#include "xparameters.h"
#include "sleep.h"
#include "xscutimer.h"

#define GIC_ID 				XPAR_PS7_SCUGIC_0_DEVICE_ID
#define TIMER_IRPT_INTR     XPAR_SCUTIMER_INTR
#define TIMER_DEVICE_ID     XPAR_XSCUTIMER_0_DEVICE_ID
#define TIMER_LOAD_VALUE    0x13D92D3F //1S

static XScuGic ScuGic;
static XScuGic_Config * ScuGicCfgPtr;

XScuTimer Timer;
XScuTimer_Config  *Config;

volatile int usec;
//initial gic & software intr
int initSwIntr();
//callback func
void TimerIntrHandler(void *CallBackRef);
int initimer();


int main()
{
	int status;
	status = initSwIntr();
	status = initimer();
	if(status != XST_SUCCESS){
		return status;
	}
	while(1){
		usleep(100000);

	}
    return 0;
}


int initSwIntr(){
	int status;
	Xil_ExceptionInit();
	ScuGicCfgPtr = XScuGic_LookupConfig(GIC_ID);
	status = XScuGic_CfgInitialize(&ScuGic,ScuGicCfgPtr,ScuGicCfgPtr->CpuBaseAddress);
	if(status != XST_SUCCESS){
		return status;
	}
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&ScuGic);
	status = XScuGic_Connect(&ScuGic,TIMER_IRPT_INTR,(Xil_ExceptionHandler)TimerIntrHandler,&Timer);
	if(status != XST_SUCCESS){
			return status;
	}
	XScuGic_Enable(&ScuGic,TIMER_IRPT_INTR);
	Xil_ExceptionEnable();

	return XST_SUCCESS;
}

int initimer(){
	int status;
	Config = XScuTimer_LookupConfig(TIMER_DEVICE_ID);
	status = XScuTimer_CfgInitialize(&Timer, Config, Config->BaseAddr);
    XScuTimer_LoadTimer(&Timer, TIMER_LOAD_VALUE);
    //自动装载
    XScuTimer_EnableAutoReload(&Timer);
	XScuTimer_Start(&Timer);
	XScuTimer_EnableInterrupt(&Timer);//一定等定时器初始化好了之后再开始使能定时器中断
	return status;
}

void TimerIntrHandler(void *CallBackRef){
    XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;
    XScuTimer_ClearInterruptStatus(TimerInstancePtr);
    usec++;
    printf(" %d Second\n\r",usec);  //每秒打印输出一次
}

这里需要特别注意的一点是,一定等定时器初始化好了之后再开始使能定时器中断,否则将无法成功运行。在这里插入图片描述
需要清除中断状态寄存器中的状态:
在这里插入图片描述
装载寄存器的初值:
在这里插入图片描述
设置自动填装初值模式:
在这里插入图片描述
启动定时器:
在这里插入图片描述

下板测试

将上面的代码进行下班测试,结果如下:
在这里插入图片描述
可以看出每隔1s,PS端进行一次打印,从而证明了实验的正确性。

总结

创作不易,认为文章有帮助的同学们可以关注、点赞、转发支持。为行业贡献及其微小的一部分。对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群:
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章