ZYNQ-Vitis(SDK)裸机开发之(七)PS私有定时器Timer的使用

发布于:2024-04-18 ⋅ 阅读:(25) ⋅ 点赞:(0)

目录

一、ZYNQ定时器简介

二、编写Vitis工程

1.头文件timer_hdl.h:

2.头文件timer_hdl.c: 


例程开发环境:

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:

  1. 定义定时器设备ID、定时器中断ID、定时器计数重装载值

装载值计算:0.5*1000_000_000/(1000/333) - 1 = 9EC969F,其中0.5为0.5s,即定时器2秒触发一次中断,9EC969F即为0.5s对应的定时器重装载值

  1. 定义定时器操作结构体实例
  2. 声明定时器初始化函数、定时器中断初始化函数、定时器中断处理函数、定时器中断使能关闭函数
/*!
    \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: 

  1. 实现定时器初始化函数、定时器中断初始化函数、定时器中断处理函数、定时器中断使能关闭函数
  2. 定时器中断处理函数中,每次触发中断,对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◕∀◕)ノ