ZYNQ-7000私有定時器中斷
轉自:https://blog.csdn.net/RZJMPB/article/details/50812579
本片文章將在ZYNQ的純PS裡實現私有定時器中斷。每個一秒中斷一次,在中斷函式裡計數加1,通過串列埠列印輸出。
*本文所使用的開發板是Miz702(相容zedboard)
PC 開發環境版本:Vivado 2015.2 Xilinx SDK 2015.2*
中斷原理
中斷對於保證任務的實時性非常必要,在ZYNQ裡集成了中斷控制器GIC(Generic Interrupt Controller).GIC可以接受I/O外設中斷IOP和PL中斷,將這些中斷髮給CPU。
中斷體系結構框圖圖下:
軟體中斷(SGI)
SGI通過寫ICDSGIR暫存器產生SGI.
共享中斷SPI
通過PS和PL內各種I/O和儲存器控制器產生。
私有中斷(PPI)
包含:全域性定時器,私有看門狗定時器,私有定時器以及來自PL的FIQ/IRQ。本文主要介紹PPI,其它的請參考官方手冊ug585_Zynq_7000_TRM.pdf。
ZYNQ每個CPU連結5個私有外設中斷,所有中斷的觸發型別都是固定不變的。並且來自PL的快速中斷訊號FIQ和中斷訊號IRQ反向,然後送到中斷控制器因此儘管在ICDICFR1暫存器內反應的他們是低電平觸發,但是PS-PL介面中為高電平觸發。如圖所示:
私有定時器
zynq中每個ARM core都有自己的私有定時器,私有定時器的工作頻率為CPU的一半,比如Miz702或者zedboard的ARM工作頻率為666MHZ,則私有定時器的頻率為333MHz.
私有定時器的特性如下:
(1)32為計數器,達到零時產生一箇中斷
(2)8位預分頻計數器,可以更好的控制中斷週期
(3)可配置一次性或者自動重載入模式
(4)定時器時間可以通過下式計算:
定時時間 = [(預分頻器的值 + 1) (載入值 + 1)]/定時器頻率
搭建硬體系統工程:
配置ZYNQ PS
把ZYNQ配置為只保留UART1,然後點選OK如圖所示
點選Run Connection Automation,取消掉Apply Board Preset,如圖
最終的框圖很簡潔,如圖:
建立軟體工程
建立一個Hello World工程
把Helloworld.c的程式碼修改如下:
#include <stdio.h>
#include "platform.h"
#include "xadcps.h"
#include "xil_types.h"
#include "Xscugic.h"
#include "Xil_exception.h"
#include "xscutimer.h"
//timer info
#define TIMER_DEVICE_ID XPAR_XSCUTIMER_0_DEVICE_ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define TIMER_IRPT_INTR XPAR_SCUTIMER_INTR
//#define TIMER_LOAD_VALUE 0x0FFFFFFF
#define TIMER_LOAD_VALUE 0x13D92D3F
static XAdcPs XADCMonInst; //XADC
static XScuGic Intc; //GIC
static XScuTimer Timer;//timer
static void SetupInterruptSystem(XScuGic *GicInstancePtr,
XScuTimer *TimerInstancePtr, u16 TimerIntrId);
static void TimerIntrHandler(void *CallBackRef);
int main()
{
XScuTimer_Config *TMRConfigPtr; //timer config
printf("------------START-------------\n");
init_platform();
//
//私有定時器初始化
TMRConfigPtr = XScuTimer_LookupConfig(TIMER_DEVICE_ID);
XScuTimer_CfgInitialize(&Timer, TMRConfigPtr,TMRConfigPtr->BaseAddr);
XScuTimer_SelfTest(&Timer);
//載入計數週期,私有定時器的時鐘為CPU的一般,為333MHZ,如果計數1S,載入值為1sx(333x1000x1000)(1/s)-1=0x13D92D3F
XScuTimer_LoadTimer(&Timer, TIMER_LOAD_VALUE);
//自動裝載
XScuTimer_EnableAutoReload(&Timer);
//啟動定時器
XScuTimer_Start(&Timer);
//set up the interrupts
SetupInterruptSystem(&Intc,&Timer,TIMER_IRPT_INTR);
while(1){
}
return 0;
}
void SetupInterruptSystem(XScuGic *GicInstancePtr,
XScuTimer *TimerInstancePtr, u16 TimerIntrId)
{
XScuGic_Config *IntcConfig; //GIC config
Xil_ExceptionInit();
//initialise the GIC
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
XScuGic_CfgInitialize(GicInstancePtr, IntcConfig,
IntcConfig->CpuBaseAddress);
//connect to the hardware
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
GicInstancePtr);
//set up the timer interrupt
XScuGic_Connect(GicInstancePtr, TimerIntrId,
(Xil_ExceptionHandler)TimerIntrHandler,
(void *)TimerInstancePtr);
//enable the interrupt for the Timer at GIC
XScuGic_Enable(GicInstancePtr, TimerIntrId);
//enable interrupt on the timer
XScuTimer_EnableInterrupt(TimerInstancePtr);
// Enable interrupts in the Processor.
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
}
static void TimerIntrHandler(void *CallBackRef)
{
static int sec = 0; //計數
XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;
XScuTimer_ClearInterruptStatus(TimerInstancePtr);
sec++;
printf(" %d Second\n\r",sec); //每秒列印輸出一次
}