定时器作为 PS 的重要组成部分,可以不受 CPU 的干预,自己独立运行,来完成计时、定时、中断以及计算来自 MIO 或 EMIO 引脚的信号脉冲宽度。
在 ZYNQ 嵌入式系统中,每个 Cortex-A9 处理器都有各自独立的 32 位私有定时器和 32 位看门狗定时器,这两个 CPU 同时共享一个 64 位的全局定时器( GT)。PS 中还有一个 24 位的系统看门狗定时器( SWDT)和两个 TTC( Triple Timer Counters)。
系统看门狗定时器可以在系统发生灾难性的故障时(如 PS 中的 PLL 工作异常)发出信号, 使得系统程序重新启动,保证了系统安全可靠的运行。SWDT可以工作在CPU频率的1/4或1/6(CPU_1x),也可以工作在设备外部或PL提供的时钟下,并向它们输出一个复位信号。
TTC 用于计算来自 MIO 引脚或 EMIO 引脚的信号脉冲宽度,每个 TTC 都有三个独立的计数器。TTC只能工作在CPU频率的1/4或1/6(CPU_1x)
定时器的系统框如下图
图中的定时器连接到中断控制器( Interrupt Controller),可以很方便的使用定时器来完成定时器中断的实验。其中私有定时器( CPU Private Timer)是最为常用的。
私有定时器的时钟频率为 CPU 时钟频率的一半。如 ARM 的工作时钟频率为 666.666Mhz,则私有定时器的时钟频率为 333.333Mhz。
私有定时器的特性
①32 位计数器,当计数器递减至 0 后产生中断
②8 位预分频计数器,可以更好的控制中断周期③可以配置单次定时或者自动重载模式
④通过配置起始计数值来设置定时时间
由于 ARM 处理器自带了私有定时器,所以在定时器方面搭建嵌入式系统时,不需要额外添加定时器,除非使用其他定时器。
定时器初始化
int Status;
//私有定时器初始化
XScuTimer_Config *timer_cfg_ptr;
timer_cfg_ptr = XScuTimer_LookupConfig(TIMER_DEVICE_ID);
if (timer_cfg_ptr == NULL)
{
return XST_FAILURE;
}
Status = XScuTimer_CfgInitialize(&Timer, timer_cfg_ptr,timer_cfg_ptr->BaseAddr);
if (Status != XST_SUCCESS) //判断是否初始化成功
{
return XST_FAILURE;
}
XScuTimer_LoadTimer(&Timer, TIMER_LOAD_VALUE); // 加载计数周期
XScuTimer_EnableAutoReload(&Timer); // 设置自动装载模式
主要是初始化私有定时器和设置自动重装值(定时器溢出时间)以及模式 。
定时时间计算:
私有定时器的时钟频率为CPU频率的一半,这里ZYNQ默认工作频率650Mhz,则私有定时器的时钟频率为325Mhz,若定时时间为 T,则写入重装值为 :T / (1 /325Mhz ) 。
一般使用定时器基本上都有使用中断,所以还需要初始化定时器相关中断
//初始化中断控制器
XScuGic_Config *gic_cfg; //中断控制器配置信息
gic_cfg = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (gic_cfg == NULL)
{
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(&Intc,gic_cfg,gic_cfg->CpuBaseAddress);
if (Status != XST_SUCCESS) //判断是否初始化成功
{
return XST_FAILURE;
}
//初始化异常处理
Xil_ExceptionInit();
//CPU中断异常注册
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&Intc);
//使能处理器中断
Xil_ExceptionEnable();;
//设置定时器中断: timer_intr_handler 中断处理函数
XScuGic_Connect(&Intc, TIMER_IRPT_INTR,(Xil_ExceptionHandler)timer_intr_handler, &Timer);
XScuGic_Enable(&Intc,TIMER_IRPT_INTR);//使能 GIC 中的定时器中断
XScuTimer_EnableInterrupt(&Timer); //使能定时器中断
注意:定时器初始化一定要放在中断初始化的前面。
中断的初始化基本上都是固定的套路。这里设置中断函数为timer_intr_handler()
void timer_intr_handler(void *CallBackRef)
{
XScuTimer *timer_ptr = (XScuTimer *) CallBackRef;
....
//清除定时器中断标志
XScuTimer_ClearInterruptStatus(timer_ptr);
}