項目簡述
前面的文章我們已經講解了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端進行一次打印,從而證明了實驗的正確性。
總結
創作不易,認爲文章有幫助的同學們可以關注、點贊、轉發支持。爲行業貢獻及其微小的一部分。對文章有什麼看法或者需要更近一步交流的同學,可以加入下面的羣: