NXP JN5169使用滴答定时器进行精准延时(us、ms)
- 一、滴答定时器介绍
- 二、滴答定时器系统图
- 三、系统时钟
- 四、滴答定时器集成外设API函数介绍
- 1、void vAHI_TickTimerConfigure(uint8 u8Mode)
- 2、void vAHI_TickTimerInterval(uint32 u32Interval)
- 3、void vAHI_TickTimerWrite(uint32 u32Count)
- 4、uint32 u32AHI_TickTimerRead(void)
- 5、void vAHI_TickTimerIntEnable(bool_t bIntEnable)
- 6、bool_t bAHI_TickTimerIntStatus(void)
- 7、void vAHI_TickTimerIntPendClr(void)
- 8、void vAHI_TickTimerRegisterCallback(PR_HWINT_APPCALLBACK prTickTimerCallback)
- 五、实现代码
一、滴答定时器介绍
滴答定时器是一个硬件定时器,它来自外设时钟并且可以用来执行下面的操作:
1、软件的定时中断;
2、常规事件,例如软件定时器或操作系统的节拍;
3、高精度定时参考;
4、系统监控超时,比如在看门狗定时器中使用。
注:对于高精度的滴答定时器操作,外设时钟应在 16MHz 频率下运行,系统时钟源是外部晶体振荡器。
滴答定时器特点:
1、32位计数器
2、28位匹配值
3、可屏蔽定时器中断
4、单次触发,可重启和连续运行模式
二、滴答定时器系统图
1、系统图介绍
滴答定时器由一个连续的16 MHz时钟提供时钟,该时钟由模式控制模块的信号选通,馈入一个32位宽的可复位递增计数器。 匹配寄存器允许比较计数器和编程值。 通过软件对以16 MHz时钟周期测量的匹配值进行编程,范围为0至0x0FFFFFFF。 比较的输出可用于生成中断(如果允许中断),并可用于在不同模式下控制计数器。 配置定时器模式后,计数器也会复位。
2、单次触发模式
如果将模式编程为单次触发,则计数器将从零开始计数,直到达到匹配值为止。 将产生匹配信号,这将引起中断(如果允许),并且计数器将停止计数。 通过重新编程模式可以重启计数器。
3、可重启模式
如果将模式编程为可重启模式,则计数器的操作与单次模式相同,不同之处在于,当达到匹配值时,计数器将复位并从零开始计数。 当达到匹配值时(如果使能)将产生一个中断。
4、连续运行模式
连续模式操作类似于可重启模式,不同之处在于,当达到匹配值时,计数器不会复位,而是继续计数。 当达到匹配值时(如果使能),将产生一个中断。
三、系统时钟
系统控制器给 JN516x 微控制器提供时钟,分成 4 个主要模块:系统时钟域、外设时钟域、CPU 时钟域和一个 32kHz 的时钟域。
1、系统时钟域
系统时钟是一个高速参考时钟,芯片正式工作时外围时钟和 CPU 时钟都来自于该时钟。这个域的时钟来源于下面其中一个时钟源:
- 外部 32MHz 晶体振荡器;
- 内部高速 RC 振荡器;
晶体振荡器由器件引脚 4 和 5 连接的 32MHz 外部晶体来驱动。在晶体振荡器的作用下,这个域产生一个 32MHz 的系统时钟。
未校准的 RC 振荡器通常在 27MHz 下运行,但经过校准后可以运行在近似 32MHz 下。由于 RC 振荡器比晶体振荡器启动快得多,因此主要用于睡眠后的全速启动。
当系统时钟由 RC 振荡器提供时,无线收发器和一些外设不应该使用。
2、外设时钟域
外设时钟来自于系统时钟,用作包括调制解调器和基带处理器在内的片内外设的时钟参考。外设时钟的工作频率是系统时钟频率的一半。当系统时钟由外部 32MHz 的晶体振荡器提供时,外设时钟运行在 16MHz。
3、CPU 时钟域
CPU 时钟由系统时钟分频得到,用作微处理器和存储器子系统的时钟参考。
4、32kHz 时钟域
32kHz 时钟域主要在低功耗睡眠期间使用(还供 JN516x 器件上的随机数发生器使用)。当处于睡眠模式时,CPU 不运行,依靠中断来唤醒。中断可以由一个片内唤醒定时器产生,或者由一个外部源通过一个 DIO 引脚、一个片内比较器或一个片内脉冲计数器来产生。唤醒定时器由 32kHz域来驱动。这个域的 32kHz 时钟可以来源于下面其中一个时钟源:
- 内部 RC 振荡器;
- 外部晶体;
- 外部时钟模块;
晶体振荡器由 DIO9 和 DIO10 连接的一个外部 32kHz 晶体来驱动。如果使用外部晶体,外部时钟模块就连接到 DIO9。32kHz 域在芯片正常工作时仍然有效,可以以外设时钟为基准进行校准来提高计时精度。
四、滴答定时器集成外设API函数介绍
1、void vAHI_TickTimerConfigure(uint8 u8Mode)
此函数配置滴答定时器的操作模式并启用定时器, 它也可以用来禁用定时器。滴答定时器向上计数,直到计数与预定义的参考值匹配为止。
使用功能vAHI_TickTimerInterval()设置参考计数。可以启用一个达到参考计数时生成的中断。一旦vAHI_TickTimerConfigure()将其启用为上述任一模式,则滴答定时器将开始运行,而不管其计数器的状态如何。 实际上,要使用滴答计时器:
1.调用vAHI_TickTimerConfigure()禁用滴答计时器。
2.调用vAHI_TickTimerWrite()来为计数设置适当的起始值。
3.调用vAHI_TickTimerInterval()设置参考计数。
4.再次调用vAHI_TickTimerConfigure(),以所需的模式启动滴答定时器。
设备上电/复位时,滴答计时器被禁用。但是,建议始终按照上述函数调用顺序来启动计时器。如果在单发模式下启用了滴答定时器,则一旦停止(达到参考计数),即可通过以下方式再次启动计时器,使用vAHI_TickTimerWrite()设置另一个起始值。
形参:u8Mode 滴答定时器操作模式
在达到参考计数时应采取的行动:
E_AHI_TICK_TIMER_CONT (继续计数)
E_AHI_TICK_TIMER_RESTART (从零开始)
E_AHI_TICK_TIMER_STOP(停止计时器)
禁用滴答定时器:
E_AHI_TICK_TIMER_DISABLE (禁用定时器)
2、void vAHI_TickTimerInterval(uint32 u32Interval)
此功能设置Tick Timer的28位参考计数。
这是与滴答定时器的实际计数进行比较的值。 当计数达到该参考值时,将使用函数vAHI_TickTimerConfigure()确定采取的措施。 也可以启用在达到参考计数时生成的中断。
形参:u32Interval
参考计数(范围从 0 到 0x0FFFFFFF)。
3、void vAHI_TickTimerWrite(uint32 u32Count)
此函数设置滴答定时器的初始计数。 如果启用了定时器,它将立即从这个值开始计数。 通过指定计数为零,该函数可以用于重置 滴答计时器随时计数为零。
注:如果在定时器使能时调用这个函数,则定时器将立即从指定的值开始计数。
形参:u32Count
起始计数(范围从 0 到 0xFFFFFFFF)。
4、uint32 u32AHI_TickTimerRead(void)
此功能获取时钟计时器的当前值。
返回值:计时器的当前值
5、void vAHI_TickTimerIntEnable(bool_t bIntEnable)
此功能可用于启用滴答定时器中断,当滴答定时器计数达到使用函数vAHI_TickTimerInterval()指定的参考计数时,将产生该中断。
可以使用函数vAHI_TickTimerRegisterCallback()注册用户定义的回调函数,该函数在生成中断时调用。
注:滴答计时器中断可用于将CPU从Doze模式唤醒。
形参:bIntEnable 使能/禁用中断:
TRUE 使能中断
FALSE 禁用中断
6、bool_t bAHI_TickTimerIntStatus(void)
此功能获取滴答定时器的当前中断状态。
返回值:如果中断未决,则为TRUE,否则为FALSE
7、void vAHI_TickTimerIntPendClr(void)
该功能清除所有待处理的滴答定时器中断。
8、void vAHI_TickTimerRegisterCallback(PR_HWINT_APPCALLBACK prTickTimerCallback)
该函数注册了一个用户定义的回调函数,该事件将在触发滴答定时器中断时被调用。
注意,回调函数将在中断上下文中执行。 因此,必须确保它及时返回主程序。
已注册的回调函数仅在RAM保持供电的睡眠模式下保留。 如果在睡眠期间关闭RAM的电源并需要中断,则必须在唤醒时调用u32AHI_Init()之前重新注册回调函数。
形参:prTickTimerCallback 指向要注册的回调函数的指针
五、实现代码
使用BeyondStudio for NXP编写,SDK为JN-SW-4170,不带栈协议,仅外设功能实现。
1、宏定义
/* Value enumerations: Tick Timer */
#define E_AHI_TICK_TIMER_DISABLE (0x00) /* Disable tick timer */
#define E_AHI_TICK_TIMER_RESTART (0x01) /* Restart timer when match occurs */
#define E_AHI_TICK_TIMER_STOP (0x02) /* Stop timer when match occurs */
#define E_AHI_TICK_TIMER_CONT (0x03) /* Timer does not stop when match occurs */
#define TRUE (1) /* page 207 K+R 2nd Edition */
#define FALSE (0)
#define DELAY_TICK_TIME 16000000 /* 1s */
#define TICK_COUNT 16000 /* 1ms */
#define LED2 (1 << 0)
#define LED3 (1 << 1)
#define LED4 (1 << 2)
#define LED5 (1 << 3)
#define LED6 (1 << 4)
#define LED8 (1 << 5)
#define LED_DIO ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5))
#define LED_ON(Led) vAHI_DioSetOutput(0, Led)
#define LED_OFF(Led) vAHI_DioSetOutput(Led, 0)
2、x ms延时程序实现
PRIVATE void vAHI_DelayXms(uint32 n)
{
uint32 temp;
vAHI_TickTimerConfigure(E_AHI_TICK_TIMER_DISABLE);/* 关闭滴答定时器*/
vAHI_TickTimerWrite(0);/* 设置起始值*/
vAHI_TickTimerInterval(TICK_COUNT * n);/* 设置参照值*/
vAHI_TickTimerConfigure(E_AHI_TICK_TIMER_STOP);/* 到达参考值停止计数*/
vAHI_TickTimerIntEnable(FALSE);/* 关闭中断*/
do{
temp = u32AHI_TickTimerRead();
}while(temp < TICK_COUNT * n);//向上计数
vAHI_TickTimerConfigure(E_AHI_TICK_TIMER_DISABLE);/* 关闭滴答定时器 */
}
3、x us延时程序实现
PRIVATE vAHI_DelayXus(uint32 n)
{
uint32 temp;
vAHI_TickTimerConfigure(E_AHI_TICK_TIMER_DISABLE);/* 关闭滴答定时器*/
vAHI_TickTimerWrite(0);/* 设置起始值*/
vAHI_TickTimerInterval(16 * n);/* 设置参照值*/
vAHI_TickTimerConfigure(E_AHI_TICK_TIMER_STOP);/* 到达参考值停止计数*/
vAHI_TickTimerIntEnable(FALSE);/* 关闭中断*/
do{
temp = u32AHI_TickTimerRead();
}while(temp < 16 * n);//向上计数
vAHI_TickTimerConfigure(E_AHI_TICK_TIMER_DISABLE); /* 关闭滴答定时器*/
}
4、流水灯例程
/*LED初始化IO*/
PRIVATE void vLedsInit (void)
{
vAHI_DioSetDirection(0, LED_DIO);//DIO设置为输出方向
vAHI_DioSetPullup(LED_DIO, 0);//DIO设置为上拉模式
}
/*流水灯程序*/
PUBLIC void Flow_LED(void)
{
LED_ON(LED2);
LED_OFF(LED3);
LED_OFF(LED4);
LED_OFF(LED5);
LED_OFF(LED6);
LED_OFF(LED8);
vAHI_DelayXms(1000);
LED_OFF(LED2);
LED_ON(LED3);
LED_OFF(LED4);
LED_OFF(LED5);
LED_OFF(LED6);
LED_OFF(LED8);
vAHI_DelayXms(1000);
LED_OFF(LED2);
LED_OFF(LED3);
LED_ON(LED4);
LED_OFF(LED5);
LED_OFF(LED6);
LED_OFF(LED8);
vAHI_DelayXms(1000);
LED_OFF(LED2);
LED_OFF(LED3);
LED_OFF(LED4);
LED_ON(LED5);
LED_OFF(LED6);
LED_OFF(LED8);
vAHI_DelayXus(1000000);
LED_OFF(LED2);
LED_OFF(LED3);
LED_OFF(LED4);
LED_OFF(LED5);
LED_ON(LED6);
LED_OFF(LED8);
vAHI_DelayXus(1000000);
LED_OFF(LED2);
LED_OFF(LED3);
LED_OFF(LED4);
LED_OFF(LED5);
LED_OFF(LED6);
LED_ON(LED8);
vAHI_DelayXus(1000000);
}
/*主程序*/
PUBLIC void AppColdStart (void)
{
vAHI_WatchdogStop();
(void)u32AHI_Init();
vLedsInit();
LED_OFF(LED2);
LED_OFF(LED3);
LED_OFF(LED4);
LED_OFF(LED5);
LED_OFF(LED6);
LED_OFF(LED8);
while (1) {
Flow_LED();
}
}
/*热启动*/
PUBLIC void AppWarmStart (void)
{
AppColdStart();
}