Linux中zynq定时器驱动,学会Zynq(9)定时器使用示例(PPI)

定时器资源

每个Cortex-A9处理器都有私有的32位定时器和32位看门狗定时器。这两种定时器都是32位的计数器,计数到0时产生中断;带有8位的预分频器,能够更好地控制中断周期;可配置为单次重载或自动重载模式;可配置初始值。它们的工作时钟固定为CPU频率的1/2(CPU_3x2x)。

两个CPU同时共享一个64位的全局定时器GT,这是一个递增的计数器,带有自动递增功能。全局定时器与私有定时器在内存中映射的地址空间相同。两个Cortex-A9有各自的64位的比较器,可以共同访问全局定时器,达到比较器值时产生一个私有中断。它的时钟也固定为CPU频率的1/2(CPU_3x2x)。

除了两个CPU的私有看门狗定时器,还有一个24位的系统看门狗SWDT,在发生灾难性的系统故障时发出信号(比如PS中的PLL工作失常)。SWDT可以工作在CPU频率的1/4或1/6(CPU_1x),也可以工作在设备外部或PL提供的时钟下,并向它们输出一个复位信号。

此外PS中还有两个TTC(Triple Timer Counters),每个TTC都有三个独立的定时器/计数器。TTC只能工作在CPU频率的1/4或1/6(CPU_1x),Zynq使用该定时器计算来自MIO管脚或PL的信号脉冲宽度。

私有定时器和看门狗定时器、全局定时器属于PPI;SWDT和TTC属于SPI。各种定时器资源之间的关系如下:aa1af056c1b6e23484c0393ae949fe65.png

私有定时器的使用

几种定时器中,私有定时器是最常用的,使用双核时可能会用到全局定时器。私有定时器是CPU五种PPI中断源的一种,固定为上升沿敏感。

做一个简单的设计体会私有定时器的使用,每隔1s串口输出一次。Vivado中搭建硬件环境,使用最小系统即可,导出硬件到SDK中。

timer.h文件的代码如下:

#include #include "xadcps.h"

#include "xil_types.h"

#include "Xscugic.h"

#include "Xil_exception.h"

#include "xscutimer.h"

//---------------------------------------------------------

// 参数定义

//---------------------------------------------------------

#define TIMER_DEVICE_ID XPAR_XSCUTIMER_0_DEVICE_ID

#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID

#define TIMER_IRPT_INTR XPAR_SCUTIMER_INTR //定时器中断ID号

#define TIMER_LOAD_VALUE 0x13D92D3F //定时器装载值

//---------------------------------------------------------

// 函数申明

//---------------------------------------------------------

void SetupInterruptSystem(XScuGic *GicInstancePtr,

XScuTimer *TimerInstancePtr, u16 TimerIntrId);

void TimreInit(XScuTimer Timer, XScuGic Intc);

timer.c文件的代码如下:

#include "timer.h"

//---------------------------------------------------------

// 定时器中断处理程序

//---------------------------------------------------------

static void TimerIntrHandler(void *CallBackRef)

{

static int sec = 0; //计数

XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;

XScuTimer_ClearInterruptStatus(TimerInstancePtr);

sec++;

printf(" %d Second\n\r",sec); //每秒打印输出一次

}

//---------------------------------------------------------

// 定时器中断配置

//---------------------------------------------------------

void SetupInterruptSystem(XScuGic *GicInstancePtr,

XScuTimer *TimerInstancePtr, u16 TimerIntrId)

{

/* 初始化中断控制器 */

XScuGic_Config *IntcConfig;

IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);

XScuGic_CfgInitialize(GicInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress);

/* 设置中断异常 */

Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,

(Xil_ExceptionHandler)XScuGic_InterruptHandler, GicInstancePtr);

Xil_ExceptionEnable();

/* 设置定时器中断 */

XScuGic_Connect(GicInstancePtr, TimerIntrId,

(Xil_ExceptionHandler)TimerIntrHandler, (void *)TimerInstancePtr);

XScuGic_Enable(GicInstancePtr, TimerIntrId); //使能中断

XScuTimer_EnableInterrupt(TimerInstancePtr); //使能定时器中断

}

//---------------------------------------------------------

// 定时器初始化程序

//---------------------------------------------------------

void TimreInit(XScuTimer Timer, XScuGic Intc)

{

/* 私有定时器初始化 */

XScuTimer_Config *TMRConfigPtr;

TMRConfigPtr = XScuTimer_LookupConfig(TIMER_DEVICE_ID);

XScuTimer_CfgInitialize(&Timer, TMRConfigPtr,TMRConfigPtr->BaseAddr);

XScuTimer_LoadTimer(&Timer, TIMER_LOAD_VALUE); // 加载计数周期

XScuTimer_EnableAutoReload(&Timer); // 设置自动装载模式

SetupInterruptSystem(&Intc,&Timer,TIMER_IRPT_INTR); // 设置定时器中断

XScuTimer_Start(&Timer); // 启动定时器

}

mian.c文件的代码如下:

#include "timer.h"

static XScuTimer Timer; //定时器

static XScuGic Intc; //中断控制器

int main()

{

TimreInit(Timer, Intc);

while(1);

return 0;

}

相关API函数

查阅PPI列表,可以看到私有定时器的中断号为29。6f4299c65b1dd2666064af295fbedeef.png

1.私有定时器初始化

初始化部分中,我们看到了似曾相识的XScuTimer、XscuTimer_Config两个结构体、XScuTimer_LookupConfig()和XScuTimer_CfgInitialize()两个函数,只是从“GPIO设备”、“中断设备”换成了“定时器设备”。这算是Zynq中各种设备(device)初始化的套路,甚至传入的参数类型都是十分相近的,这里不再赘述。

/* 私有定时器初始化 */

XScuTimer_Config *TMRConfigPtr;

TMRConfigPtr = XScuTimer_LookupConfig(TIMER_DEVICE_ID);

XScuTimer_CfgInitialize(&Timer, TMRConfigPtr,TMRConfigPtr->BaseAddr);

2.加载计数周期

使用XScuTimer_LoadTimer()函数加载定时器的计数器中的值,其本质上只是操作Timer Load寄存器。函数原型采用宏定义,但也可以看作一个C语言风格的函数接口:

#define XScuTimer_LoadTimer(InstancePtr, Value)\

XScuTimer_WriteReg((InstancePtr)->Config.BaseAddr,\

XSCUTIMER_LOAD_OFFSET, (Value))

//可视作如下C语言函数

void XScuTimer_LoadTimer(XScuTimer *InstancePtr, u32 Value)

私有定时器是从装载值递减到0时发出中断。我们这里默认使用666MHz的CPU时钟,则私有定时器的工作时钟为333MHz,每1/333M秒减1。因此若想定时1s,则装载值为1/(1/333M)-1,将该值在timer.h中宏定义。

3.设置装载模式

本例程需要定时器一直工作,因此使用XScuTimer_EnableAutoReload()函数启用自动装载模式。不需要时使用XScuTimer_DisableAutoReload()函数禁用自动装载。这两个函数本质上也是宏定义,操作相关寄存器中的相应bit位。下面只给出等效的C语言模型:

void XScuTimer_EnableAutoReload(XScuTimer *InstancePtr)

void XScuTimer_DisableAutoReload(XScuTimer *InstancePtr)

4.设置定时器中断

这部分通过自定义函数来设置定时器的中断,过程和第8篇中PL中断的初始化过程基本相同。实际上用到中断时基本都要有这个过程:初始化中断控制器、设置中断异常、连接中断、使能中断。

我们这里使用的是定时器中断,连接函数如下。在定时器中断处理程序中,我们必须清除中断标志,因此XscuGic_Connect的第三个参数设置为定时器的控制设备,在调用中断状态清除函数时会用到该参数。

XScuGic_Connect(GicInstancePtr, TimerIntrId,

(Xil_ExceptionHandler)TimerIntrHandler, (void *)TimerInstancePtr);

5.清除定时器中断

中断处理程序中,除了计时和串口打印输出,还要调用XScuTimer_ClearInterruptStatus()函数清除中断状态。相关代码如下:

XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;

XScuTimer_ClearInterruptStatus(TimerInstancePtr);

该函数本质上也是宏定义,等效的C语言接口如下:

void XScuTimer_ClearInterruptStatus(XScuTimer *InstancePtr)

传入的XscuTimer*类型的参数在连接定时器中断时设置。使用时首先要将其从void*类型转换为XscuTimer*类型。

---------------------

文章来源:FPGADesigner的博客

*本文由作者授权转发,如需转载请联系作者本人

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值