嵌牛2

姓名 李泽浩 学号 21181214372 学院 广州研究院

转载自 https://blog.csdn.net/FPGADesigner/article/details/88675808

【嵌牛导读】定时器使用示例

【嵌牛鼻子】定时器

【嵌牛提问】定时器如何使用

【嵌牛正文】

定时器资源

每个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。各种定时器资源之间的关系如下:

私有定时器的使用

几种定时器中,私有定时器是最常用的,使用双核时可能会用到全局定时器。私有定时器是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);

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

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);                      // 启动定时器

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

mian.c文件的代码如下:

#include "timer.h"

static XScuTimer Timer;  //定时器

static XScuGic Intc;      //中断控制器

int main()

{

TimreInit(Timer, Intc);

    while(1);

    return 0;

}

1

2

3

4

5

6

7

8

9

10

11

12

相关API函数

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

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);

1

2

3

4

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)

1

2

3

4

5

私有定时器是从装载值递减到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)

1

2

4.设置定时器中断

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

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

XScuGic_Connect(GicInstancePtr, TimerIntrId,

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

1

2

5.清除定时器中断

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

XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;

XScuTimer_ClearInterruptStatus(TimerInstancePtr);

1

2

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

void XScuTimer_ClearInterruptStatus(XScuTimer *InstancePtr)

1

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

————————————————

版权声明:本文为CSDN博主「FPGADesigner」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/FPGADesigner/article/details/88675808

你可能感兴趣的:(嵌牛2)