本文首先介绍了ZYNQ的定时器的相关内容,并学习使用ZYNQ芯片中的定时器进行操作测试。
vivado 18.3&SDK,PYNQ-Z2开发板。
ZYNQ有两个Cortex-A9处理器,每个Cortex-A9处理器都有自己的专用32位计时器和32位看门狗计时器。 两个都处理器共享一个全局64位计时器。 这些计时器始终以1/2的CPU频率计时(CPU_3x2x)。
在系统层级,有一个24位看门狗定时器和两个16位三重定时器/计数器。系统看门狗定时器的时钟频率为CPU频率(CPU_1x)的1/4或1/6,时钟也可以由来自MIO引脚或PL的外部信号。两个三重计时器/计数器时钟频率为在CPU频率(CPU_1x)的1/4或1/6,可以用于计算来自MIO引脚或来自PL的脉冲宽度。
下图为ZYNQ内部的定时器的系统框图。
在图中显示,定时器部分包括系统看门狗、CPU内部的看门狗定时器,CPU私有的定时器,全局定时器,三重定时器。其中所有的定时器都可以触发中断控制器进行中断控制的操作,看门狗定时器(无论是系统 的还是CPU内部的)都可以对系统进行一个复位操作。
关于CPU私有定时器和看门狗定时器均具有以下功能:
全局定时器是具有自动递增功能的64位递增计数器。 全局计时器在与专用计时器相同的地址空间中映射到内存中。 全局计时器仅在复位时以安全状态访问。 所有Cortex-A9处理器均可访问全局计时器。 每个Cortex-A9处理器都有一个64位比较器,用于在全局计时器达到比较器值时声明一个私有中断。全局定时器始终以CPU频率(CPU_3x2x)的1/2计时。
全局定时器(GT)寄存器功能表如下图:
除了两个CPU私有看门狗定时器之外,还有一个系统看门狗定时器(SWDT),可以在系统发生故障时,如PS PLL锁相失败,此时看门狗可以产生一个复位信号让程序重启,从而保证系统正常运行。 与CPU私有看门狗定时器不同的是,SWDT的时钟可以来自于外部设备或者PL端,用于提供一个复位信号输出到PL端口或者外部设备。
SWDT的主要功能如下:
系统看门狗定时器使能顺序如下:
TTC包含三个独立的计时器/计数器。 PS端中有两个TTC模块,总共六个计时器/计数器。 可以使用 nic301_addr_region_ctrl_registers.security_apb [ttc1_apb]寄存器位将TTC 1控制器配置为安全或非安全模式。 TTC控制器中的三个计时器具有相同的安全状态。
每个三重计时器/计数器都具有以下特征:
本次工程系统框图如下图所示。
调用UART、TIMER、GPIO资源进行开发设计,UART串口引脚直接连接到MIO,GPIO引脚连接到PL端的EMIO引脚,由ARM核进行配置定时器中断操作,实现定时中断进行流水灯操作。
新建工程,创建 block design。添加ZYNQ7 ip,根据本次工程需要对IP进行配置。勾选本次工程使用的资源。
硬件系统构建完成如下:
然后我们进行generate output product 然后生成HDL封装。这里用到了UART和GPIO,所以需要进行管脚分配。这里使用的是PYNQZ2开发板,该板卡可直接引用以下约束文件:
#LEDs
set_property -dict {
PACKAGE_PIN R14 IOSTANDARD LVCMOS33 } [get_ports {
EMIO_LED_tri_io[0] }]; #IO_L6N_T0_VREF_34 Sch=led[0]
set_property -dict {
PACKAGE_PIN P14 IOSTANDARD LVCMOS33 } [get_ports {
EMIO_LED_tri_io[1] }]; #IO_L6P_T0_34 Sch=led[1]
set_property -dict {
PACKAGE_PIN N16 IOSTANDARD LVCMOS33 } [get_ports {
EMIO_LED_tri_io[2] }]; #IO_L21N_T3_DQS_AD14N_35 Sch=led[2]
set_property -dict {
PACKAGE_PIN M14 IOSTANDARD LVCMOS33 } [get_ports {
EMIO_LED_tri_io[3] }]; #IO_L23P_T3_35 Sch=led[3]
完成约束后,进行综合和布局布线,生成bit流,然后点击导出硬件资源(包含bit流文件),接着launch SDK。
打开SDK后,新建application project。
在system.mss中可以打开相关参考文档辅助设计。
可以选择timer中断的例程进行参考设计,导入uart_intr_example例程模板,
在main.c中输入以下代码:
#include
#include "xparameters.h"
#include "xscutimer.h"
#include "xscugic.h"
#include "xgpiops.h"
#include "xil_exception.h"
#include "xil_printf.h"
//声明定义
#define GPIO_ID XPAR_PS7_GPIO_0_DEVICE_ID
#define TIMER_ID XPAR_PS7_SCUTIMER_0_DEVICE_ID
#define INTR_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define TIMER_IRPT_INTR XPAR_SCUTIMER_INTR
#define LOAD_VALUE 0X9AF8D9F//0.5s流水 系统时钟650M 定时器时钟325M 周期3ns
#define LED0 54
#define LED1 55
#define LED2 56
#define LED3 57
//声明示例结构体
XGpioPs GpioPs;
XScuTimer Timer;
XScuGic ScuGic;
//函数定义
void Emio_init();
void Timer_init();
void Timer_intr_init(XScuGic *intr, XScuTimer *time);
void Timer_IntrHandler(void *CallBackRef);
int main(){
//EMIO初始化
Emio_init();
//初始化定时器
Timer_init();
//初始化中断
Timer_intr_init(&ScuGic,&Timer);
//启动定时器
XScuTimer_Start(&Timer);
while(1);
return 0;
}
void Emio_init(){
XGpioPs_Config *XGpioPs_Cfg;
XGpioPs_Cfg = XGpioPs_LookupConfig(GPIO_ID);
XGpioPs_CfgInitialize(&GpioPs,XGpioPs_Cfg,XGpioPs_Cfg->BaseAddr);
XGpioPs_SetDirectionPin(&GpioPs,LED0,0x01);
XGpioPs_SetOutputEnablePin(&GpioPs,LED0,0x01);
XGpioPs_SetDirectionPin(&GpioPs,LED1,0x01);
XGpioPs_SetOutputEnablePin(&GpioPs,LED1,0x01);
XGpioPs_SetDirectionPin(&GpioPs,LED2,0x01);
XGpioPs_SetOutputEnablePin(&GpioPs,LED2,0x01);
XGpioPs_SetDirectionPin(&GpioPs,LED3,0x01);
XGpioPs_SetOutputEnablePin(&GpioPs,LED3,0x01);
}
void Timer_init(){
int Status;
XScuTimer_Config *ConfigPtr;
//初始化定时器
ConfigPtr = XScuTimer_LookupConfig(TIMER_ID);
XScuTimer_CfgInitialize(&Timer, ConfigPtr,ConfigPtr->BaseAddr);
//定时器自检测
Status = XScuTimer_SelfTest(&Timer);
if (Status != XST_SUCCESS) {
printf("timer selftest failed!\n");
}
printf("timer selftest success!\n");
//装载初值
XScuTimer_LoadTimer(&Timer, LOAD_VALUE);
//使能自动装载模式
XScuTimer_EnableAutoReload(&Timer);
}
void Timer_intr_init(XScuGic *intr, XScuTimer *time){
XScuGic_Config *IntcConfig;
//初始化定时器中断
IntcConfig = XScuGic_LookupConfig(INTR_DEVICE_ID);
XScuGic_CfgInitialize(intr,IntcConfig,IntcConfig->CpuBaseAddress);
//初始化中断异常函数
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
intr);
Xil_ExceptionEnable();
//设置中断服务函数
XScuGic_Connect(intr, TIMER_IRPT_INTR,
(Xil_ExceptionHandler)Timer_IntrHandler,
(void *)time);
//使能中断控制器
XScuGic_Enable(intr, TIMER_IRPT_INTR);
//使能定时器
XScuTimer_EnableInterrupt(time);
}
//中断处理函数
void Timer_IntrHandler(void *CallBackRef){
static char led_status=0x01;
XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;
if(XScuTimer_IsExpired(TimerInstancePtr)){
led_status = led_status<<1;
if(led_status == 0X10)
led_status =0x01;
XGpioPs_WritePin(&GpioPs,LED0,led_status);
XGpioPs_WritePin(&GpioPs,LED1,led_status>>1);
XGpioPs_WritePin(&GpioPs,LED2,led_status>>2);
XGpioPs_WritePin(&GpioPs,LED3,led_status>>3);
XScuTimer_ClearInterruptStatus(TimerInstancePtr);
}
}
在主函数中引用了Emio_init();
、Timer_init();
、Timer_intr_init(&ScuGic,&Timer);
分别完成初始化GPIO操作,初始化定时器,初始化中断定时器等操作。最后用XScuTimer_Start(&Timer);
启动定时器。
对于定时器初始化设置主要要对计数模式进行设置,对初始值进行装载配置
也就是调用下面的函数:
//装载初值
XScuTimer_LoadTimer(&Timer, LOAD_VALUE);
//使能自动装载模式
XScuTimer_EnableAutoReload(&Timer);
这里LOAD_VALUE表示需要计时或者计数的次数,因为这里的代码实现的是led的0.5s定时中断,系统CPU时钟为650M,所以定时器的时钟频率为325M,也就是周期为3.07ns,计算0.5s下的计数次数得到这里需要装载的初值。(也即为0X9AF8D9F)