目录
例程开发环境:
SOC芯片:ZYNQ7020
开发环境:Vivado2020.2,Vitis2020.2
通过timer定时器,实现LED的固定频率的闪烁
一、ZYNQ定时器简介
在 ZYNQ 嵌入式系统中,定时器的资源是非常丰富的,每个 Cortex-A9 处理器都有各自独立的 32 位私有定时器和 32 位看门狗定时器,这两个 CPU 同时共享一个 64 位的全局定时器(Global Timer)。除此之外,PS 中还有一个 24 位的系统看门狗定时器(SWDT)和两个 TTC(Triple Timer Counters)。系统看门狗定时器可以在系统发生灾难性的故障时(如 PS 中的 PLL 工作异常)发出信号,使得系统程序重新启动,保证了系统安全可靠的运行。TTC 用于计算来自 MIO 引脚或 EMIO 引脚的信号脉冲宽度,每个 TTC 都有三个独立的计数器,其作用主要是:
(1)通过在每个计数时钟周期向上或向下计数来获得固定的时间间隔;
(2)PWM 输出,可以输出固定频率和占空比的方波;
(3)脉冲计数器,针对外部输入脉冲记录其脉冲宽度;
定时器的系统框图如下图所示:
图中的几种定时器都有连接到中断控制器(Interrupt Controller),我们可以很方便的使用定时器来完成定时器中断的实验。其中私有定时器(CPU Private Timer)是最为常用的,本次实验是基于私有定时器来完成定时器的中断实验。
私有定时器的时钟频率为 CPU 时钟频率的一半,如 ARM 的工作时钟频率为 666.666Mhz,则私有定时器的时钟频率为 333.333Mhz。私有定时器的特性如下:
(1)32 位计数器,当计数器递减至 0 后产生中断;
(2)8 位预分频计数器,可以更好的控制中断周期;
(3)可以配置单次定时或者自动重载模式;
(4)通过配置起始计数值来设置定时时间。
本项目工程介绍的是ZYNQ私有定时器,为PS核内置功能,不需要重新配置PS核以及重新搭建PL端,因此是在ZYNQ-Vitis(SDK)裸机开发之(一)串口实验工程基础上开发的,一些block design的设计方法,Vitis工程的建立方法等,均在该篇文章中进行了详细的讲解,大家可以去参考:
ZYNQ-Vitis(SDK)裸机开发之(一)串口收发使用:PS串口+PL串口、多个串口使用方法
二、编写Vitis工程
1.头文件timer_hdl.h:
- 定义定时器设备ID、定时器中断ID、定时器计数重装载值
装载值计算:0.5*1000_000_000/(1000/333) - 1 = 9EC969F,其中0.5为0.5s,即定时器2秒触发一次中断,9EC969F即为0.5s对应的定时器重装载值
- 定义定时器操作结构体实例
- 声明定时器初始化函数、定时器中断初始化函数、定时器中断处理函数、定时器中断使能关闭函数
/*!
\file timer_hdl.h
\brief firmware functions to manage timer
\version 2024-03-12, V1.0.0
\author tbj
*/
#ifndef TIMER_HDL_H
#define TIMER_HDL_H
#include "xscutimer.h"
#include "gpio_hdl.h"
#include "crc_cal.h"
#ifdef __cplusplus
extern "C" {
#endif
//定时器ID号、定时器中断ID号、定时器装载数
#define TIMER_DEVICE_ID XPAR_XSCUTIMER_0_DEVICE_ID
#define TIMER_IRPT_INTR XPAR_SCUTIMER_INTR
#define TIMER_LOAD_VALUE 0x13D92D3F//0xFFFF
//定时器中断实例
XScuTimer Timer0;
//定时器初始化函数
int timer_init(XScuTimer * TimerInstancePtr, u16 TimerDeviceId, u16 TimerIntrId);
//定时器中断初始化函数
int timer_intr_init(XScuGic *IntcInstancePtr, XScuTimer * TimerInstancePtr, u16 TimerIntrId);
//定时器中断处理函数
int Timer0IntrHandler(void *CallBackRef);
//关闭定时器函数
void TimerDisableIntrSystem(XScuGic *IntcInstancePtr, u16 TimerIntrId);
#ifdef __cplusplus
}
#endif
#endif /* TIMER_HDL_H */
2.头文件timer_hdl.c:
- 实现定时器初始化函数、定时器中断初始化函数、定时器中断处理函数、定时器中断使能关闭函数
- 定时器中断处理函数中,每次触发中断,对LED等的状态进行反转,来查看定时器中断是否好用,正常的话LED等会以定时器中断时间为固定频率来闪烁
/*!
\file timer_hdl.c
\brief firmware functions to manage timer
\version 2024-04-10, V1.0.0
\author tbj
*/
#include "timer_hdl.h"
/* 功能:定时器初始化
* 参数1:GpioPtr-GPIO对象指针
* 参数2:Gpio_Pin-GPIO对应pin
* 参数3:Gpio_value-GPIO读取值
*/
int timer_init(XScuTimer * TimerInstancePtr, u16 TimerDeviceId, u16 TimerIntrId){
int Status;
XScuTimer_Config *ConfigPtr;
//初始化定时器
ConfigPtr = XScuTimer_LookupConfig(TimerDeviceId);
Status = XScuTimer_CfgInitialize(TimerInstancePtr, ConfigPtr,
ConfigPtr->BaseAddr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
//定时器自检
Status = XScuTimer_SelfTest(TimerInstancePtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
//使能自动填充模式
XScuTimer_EnableAutoReload(TimerInstancePtr);
//配置计数值
XScuTimer_LoadTimer(TimerInstancePtr, TIMER_LOAD_VALUE);
//开始定时器计时
XScuTimer_Start(TimerInstancePtr);
//关闭定时器计时
//XScuTimer_Stop(TimerInstancePtr);
return XST_SUCCESS;
}
/* 功能:定时器中断初始化
* 参数1:GpioPtr-GPIO对象指针
* 参数2:Gpio_Pin-GPIO对应pin
* 参数3:Gpio_value-GPIO读取值
*/
int timer_intr_init(XScuGic *IntcInstancePtr, XScuTimer * TimerInstancePtr, u16 TimerIntrId){
int Status;
//初始化中断异常
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
IntcInstancePtr);
Xil_ExceptionEnable();
//绑定定时器中断函数
Status = XScuGic_Connect(IntcInstancePtr, TimerIntrId,
(Xil_ExceptionHandler)Timer0IntrHandler,
(void *)TimerInstancePtr);
if (Status != XST_SUCCESS) {
return Status;
}
//使能中断控制器
XScuGic_Enable(IntcInstancePtr, TimerIntrId);
//使能定时器中断
XScuTimer_EnableInterrupt(TimerInstancePtr);
return XST_SUCCESS;
}
/* 功能:定时器中断处理函数
* 参数1:CallBackRef回调
*/
int Timer0IntrHandler(void *CallBackRef){
//LED 状态,用于控制 LED 灯状态翻转
static int led_state = 0;
XScuTimer *timer_ptr = (XScuTimer *) CallBackRef;
if(led_state == 0)
led_state = 1;
else
led_state = 0;
//向指定引脚写入数据: 0 或 1
if(led_state == 1){
set_gpio_value(&EMIOLed0, LED0_GPIO_PL_EMIO, GPIO_VALUE_ON);
}else{
set_gpio_value(&EMIOLed0, LED0_GPIO_PL_EMIO, GPIO_VALUE_OFF);
}
//清除定时器中断标志
XScuTimer_ClearInterruptStatus(timer_ptr);
return XST_SUCCESS;
}
/* 功能:解除定时器和中断控制器的绑定
* 参数1:中断控制器
* 参数2:中断ID号
*/
void TimerDisableIntrSystem(XScuGic *IntcInstancePtr, u16 TimerIntrId)
{
/*
* Disconnect and disable the interrupt for the Timer.
*/
XScuGic_Disconnect(IntcInstancePtr, TimerIntrId);
}
3.Main.c中调用定时器初始化功能
//中断控制器实例
XScuGic GicIntrDevice;
int main()
{
//初始化GIC中断控制器
Gic_Intr_Init(&GicIntrDevice);
//私有定时器测试
#ifdef PRIVATE_TIMER_TEST
//初始化私有定时器
timer_init(&Timer0, TIMER_DEVICE_ID, TIMER_IRPT_INTR);
//初始化私有定时器中断
timer_intr_init(&GicIntrDevice, &Timer0, TIMER_IRPT_INTR);
#endif
while(1){
}
return 0;
}
创作不易,希望大家点赞、收藏、关注哦!!!ヾ(o◕∀◕)ノ