上篇文章实现了了PS接受来自PL的中断,本片文章将在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的代码修改如下:
1 #include <stdio.h> 2 #include "platform.h" 3 #include "xadcps.h" 4 5 #include "xil_types.h" 6 #include "Xscugic.h" 7 #include "Xil_exception.h" 8 #include "xscutimer.h" 9 10 //timer info 11 #define TIMER_DEVICE_ID XPAR_XSCUTIMER_0_DEVICE_ID 12 #define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID 13 #define TIMER_IRPT_INTR XPAR_SCUTIMER_INTR 14 15 //#define TIMER_LOAD_VALUE 0x0FFFFFFF 16 #define TIMER_LOAD_VALUE 0x13D92D3F 17 18 static XAdcPs XADCMonInst; //XADC 19 static XScuGic Intc; //GIC 20 static XScuTimer Timer;//timer 21 22 static void SetupInterruptSystem(XScuGic *GicInstancePtr, 23 XScuTimer *TimerInstancePtr, u16 TimerIntrId); 24 25 static void TimerIntrHandler(void *CallBackRef); 26 27 int main() 28 { 29 XScuTimer_Config *TMRConfigPtr; //timer config 30 printf("------------START-------------\n"); 31 init_platform(); 32 // 33 //私有定时器初始化 34 TMRConfigPtr = XScuTimer_LookupConfig(TIMER_DEVICE_ID); 35 XScuTimer_CfgInitialize(&Timer, TMRConfigPtr,TMRConfigPtr->BaseAddr); 36 XScuTimer_SelfTest(&Timer); 37 //加载计数周期,私有定时器的时钟为CPU的一般,为333MHZ,如果计数1S,加载值为1sx(333x1000x1000)(1/s)-1=0x13D92D3F 38 XScuTimer_LoadTimer(&Timer, TIMER_LOAD_VALUE); 39 //自动装载 40 XScuTimer_EnableAutoReload(&Timer); 41 //启动定时器 42 XScuTimer_Start(&Timer); 43 44 45 //set up the interrupts 46 SetupInterruptSystem(&Intc,&Timer,TIMER_IRPT_INTR); 47 48 while(1){ 49 } 50 51 return 0; 52 } 53 54 void SetupInterruptSystem(XScuGic *GicInstancePtr, 55 XScuTimer *TimerInstancePtr, u16 TimerIntrId) 56 { 57 58 XScuGic_Config *IntcConfig; //GIC config 59 Xil_ExceptionInit(); 60 //initialise the GIC 61 IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID); 62 XScuGic_CfgInitialize(GicInstancePtr, IntcConfig, 63 IntcConfig->CpuBaseAddress); 64 //connect to the hardware 65 Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, 66 (Xil_ExceptionHandler)XScuGic_InterruptHandler, 67 GicInstancePtr); 68 //set up the timer interrupt 69 XScuGic_Connect(GicInstancePtr, TimerIntrId, 70 (Xil_ExceptionHandler)TimerIntrHandler, 71 (void *)TimerInstancePtr); 72 //enable the interrupt for the Timer at GIC 73 XScuGic_Enable(GicInstancePtr, TimerIntrId); 74 //enable interrupt on the timer 75 XScuTimer_EnableInterrupt(TimerInstancePtr); 76 // Enable interrupts in the Processor. 77 Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ); 78 } 79 80 static void TimerIntrHandler(void *CallBackRef) 81 { 82 83 static int sec = 0; //计数 84 XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef; 85 XScuTimer_ClearInterruptStatus(TimerInstancePtr); 86 sec++; 87 printf(" %d Second\n\r",sec); //每秒打印输出一次 88 89 }
上电测试
可以看到串口终端每秒输出一次,并且值加1递增