首先,zynq 7000 soc芯片具有2个ARM核。每个Cortex-a9处理器都有自己的专用32位定时器和32位看门狗定时器.两个处理器共享一个全局64位定时。这些定时器的时钟频率固定为CPU时钟频率(Cpu_6x2x)的1/2,例如AX7021的ARM工作频率为767MHZ,则私有定时器的频率为383.5MHZ。
同时,在系统级别上,有一个24位的看门狗定时器和两个16位的三重定时器/计数器(TTC)。
系统看门狗计时器的时钟频率为CPU频率的1/4或1/6(即Cpu_1x时钟),或是来自MIO引脚或PL的外部时钟。两个三重定时器/计数器(TTC)的时钟为CPU频率的1/4或1/6(即Cpu_1x时钟),或是来自MIO引脚或PL的外部时钟。
专用定时器加载寄存器 – 可将该寄存器用于自动重新加载模式,包含在使能自动重新加载时被重新加载到专用定时器计数器寄存器中的数值。
专用定时计数寄存器 (Private TImer Counter Register) –
这是真实计数器本身。使能后,一旦寄存器达到零,则会设置中断事件标志。
专用定时器控制寄存器 – 控制寄存器可使能或禁用定时器、自动重新加载模式以及中断生成,还包含定时器的预分频器。
专用定时器中断状态寄存器 – 该寄存器包含专用定时器中断状态事件标志。
首先介绍几个概念:
Exception:(中断异常处理),为保证在ARM处理器发生异常时不至于处于未知状态,在应用程序的设计中,首先要进行异常处理、
SCU:(Snoop Control Unit) ,维护多CPU核之间的数据缓存的一致性,主要作用是CPU控制缓存的操作。
GIC:(中断控制器),接收I/O外设中断IOP和PL中断,将它们发送给CPU。
PPI(private peripheral interrupts),专用外围设备中断,PPI包括全局定时器,专用看门狗定时器,专用定时器和PL端的FIQ/IRQ。
Zynq SoC可使用通用中断控制器(GIC)来处理中断。GIC可处理源自以下方面的中断:
中断属于异步事件,因此可能同时发生多个中断。为了解决这一问题,处理器会对中断进行优先级排序,从而首先服务于优先级别最高的中断挂起。
为了正确实现这一中断结构,需要编写两个函数:一是中断服务例程,用于定义中断发生时的应对措施;二是用于配置中断的中断设置。中断设置例程可重复使用,允许构建不同的中断。该例程适用于系统中的所有中断,将针对通用I/O(GPIO)设置和使能中断。
整个代码要实现的目的是设置一个计时器,1s递减,当定时器计数到0时会产生定时器中断,这时CPU会跳转到在定时器中断处理程序中,在处理程序中改变sec的值。相当于定时器发生一次中断,sec的数值就加1,再从串口信息中打印出来。
首先是整体代码:
#include
#include "xadcps.h"
#include "xil_types.h"
#include "Xscugic.h" //该文件包含配置驱动程序以及GIC的使用范围
#include "Xil_exception.h" //该文件包含Cortex-A9的异常函数
#include "xscutimer.h"
#define TIMER_DEVICE_ID XPAR_XSCUTIMER_0_DEVICE_ID//定义timer设备号
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID//定义中断控制器设备号(GIC中断控制器)
#define TIMER_IRPT_INTR XPAR_SCUTIMER_INTR//私有时钟中断
#define TIMER_LOAD_VALUE (767/2*1000*1000-1)//欲装载值
static XScuGic Intc; //中断
static XScuTimer Timer; //定时器
static void SetupInterruptSystem(XScuGic *GicInstancePtr, XScuTimer *TimerInstancePtr, u16 TimerIntrId);
static void TimerIntrHandler(void *CallBackRef);
int main() {
XScuTimer_Config *TMRConfigPtr; //timer config
printf("------------Timer Test-------------\n");
//私有定时器初始化
TMRConfigPtr = XScuTimer_LookupConfig(TIMER_DEVICE_ID);
XScuTimer_CfgInitialize(&Timer, TMRConfigPtr, TMRConfigPtr->BaseAddr);
XScuTimer_SelfTest(&Timer);
//加载计数周期,私有定时器的时钟为CPU的一半
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
//初始化SOC异常
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);
//连接到中断处理硬件
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); //每秒打印输出一次
}
代码解析:
#define TIMER_DEVICE_ID XPAR_XSCUTIMER_0_DEVICE_ID:定义一个timer计时器设备号。
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID:定义中断控制器设备号。
#define TIMER_IRPT_INTR XPAR_SCUTIMER_INTR:定义私有时钟中断。
#define TIMER_LOAD_VALUE (767/210001000-1):定义预装载值。(AX7021的ARM工作频率为767MHZ,则私有定时器的频率为383.5MHZ)
TMRConfigPtr = XScuTimer_LookupConfig(TIMER_DEVICE_ID):
查找timer的设备号,并将其返回到一个含有设备号和基地址的结构体中。
XScuTimer_CfgInitialize:
初始化timer。
XScuTimer_SelfTest(&Timer):
在计时器上运行自我测试。此测试清除控制寄存器中的计时器使能位,向计时器加载寄存器写入和验证读回的值与写入的值匹配,并恢复控制寄存器和计时器加载寄存器。
XScuTimer_LoadTimer(&Timer, TIMER_LOAD_VALUE):
向计时器写入装载值。
XScuTimer_EnableAutoReload(&Timer):使能自动装载值模式。
XScuTimer_Start(&Timer):开始计时。
SetupInterruptSystem(&Intc, &Timer, TIMER_IRPT_INTR):
设置中断系统函数。
Xil_ExceptionInit():
初始化ARM处理器异常处理(属于中断其中的一类,也是必须要执行的一步操作,防止出现异常情况无法判断)。
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT(Xil_ExceptionHandler)XScuGic_InterruptHandler, &my_Gic):
这一步基本是固定的,中断触发后统一由XScuGic_InterruptHandler先处理,然后在Handler Table中查找相应的处理函数。
XScuGic_InterruptHandler:
所有产生的中断都要经过GIC,GIC负责使能中断,废弃中断,赋予优先级,发送到CPU,中断发生时ARM处理器会先询问中断控制器哪一个外设产生的中断。
XScuGic_Connect:
将我们定义的中断服务函数地址映射到将Handler Table中。或者可以理解为GIC连接到中断处理硬件以实现对某硬件的中断控制作用。
XScuGic_Enable:
在GIC上使能定时器使能中断。(INPUT)
XScuTimer_EnableInterrupt:
使能定时器中断。(OUTPUT)
Xil_ExceptionEnableMask:
在处理器上使能中断。
XScuTimer_ClearInterruptStatus:
清除中断状态(类似于执行完返回原状态的一个函数)。