项目简述
前面的文章我们已经讲解了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端进行一次打印,从而证明了实验的正确性。
总结
创作不易,认为文章有帮助的同学们可以关注、点赞、转发支持。为行业贡献及其微小的一部分。对文章有什么看法或者需要更近一步交流的同学,可以加入下面的群: