持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第24天,点击查看活动详情
上一文章介绍了 timer 的用法,并用 SDK 提示的接口实现 1ms 的定时功能,但不太准确。本文利用 RTC2 来实现毫秒级定时功能。
概述
除了定时器外,有时需要统计耗时或打上时间戳,很多系统都会提供时间戳函数接口。但 nrf52832 没有提供获取毫秒的函数,需要自己实现。使用 RTC2 产生毫秒级别计数,主要是用单独的 RTC,不影响应用层的软件定时器。
设计
时钟源设置为 32768 Hz,目标频率计算公式为: 32768 Hz /(PRESCALER+1)
,预分频系数 PRESCALER 计算公式:32768 Hz/目标频率 - 1
。PRESCALER 寄存器为 12 比特,计算得目标频率最小值为 8 Hz,此时每次时钟滴答中断时间间隔为 0.125 秒,经过8次可得到准确的1秒定时时间。不过并非本文所需。
设计如下接口:
- 初始化 rtc_init
- 获取时间时间戳 rtc_gettime
- 设置时间戳 rtc_settime
使用NRF_DRV_RTC_INSTANCE(2)
声明 RTC2 实例。设置全局的无符号整数s_timestamp
,其记录时间戳的值,在中断中累加该值,初始值为0,为测试溢出,实现另一接口rtc_settime
对其值进行修改。
方法1-992 Hz版本
代码
根据前面的公式,992 Hz 的预分频系统为32,公式 32768/(32+1) = 992.96
。
初始化函数如下:
void rtc_init(void)
{
ret_code_t err_code;
nrf_drv_rtc_config_t config = NRF_DRV_RTC_DEFAULT_CONFIG;
// 32: 32768/(32+1) = 992.96 计数为992或993 即1秒有992或993个tick,每个 1/992.96 = 1.007ms
config.prescaler = 32;
err_code = nrf_drv_rtc_init(&m_rtc, &config, rtc_handler);
APP_ERROR_CHECK(err_code);
nrf_drv_rtc_tick_enable(&m_rtc, true);
nrf_drv_rtc_enable(&m_rtc);
}
中断处理函数如下:
static void rtc_handler(nrf_drv_rtc_int_type_t int_type)
{
if(int_type == NRF_DRV_RTC_INT_TICK)
{
s_timestamp++;
}
}
获取、设置时间函数:
uint32_t rtc_gettime(void)
{
return s_timestamp;
}
void rtc_settime(uint32_t t)
{
s_timestamp = t;
}
测试
主要测试代码片段:
rtc_settime(4294967280);
for (int i = 0; i < 5; i++)
{
t1 = rtc_gettime();
delay_ms(1000);
t2 = rtc_gettime();
uint32_t delta = t2 - t1;
NRF_LOG_INFO("t1: %u t2: %u delta: %d", t1, t2, delta);
rtc_settime(0xffffffff - 100 - i);
}
为了测试溢出情况,通过rtc_settime
函数修改时间初始值, 结果如下:
00> <info> app: t1: 4294967280 t2: 979 delta: 995
00> <info> app: t1: 4294967195 t2: 892 delta: 993
00> <info> app: t1: 4294967194 t2: 891 delta: 993
00> <info> app: t1: 4294967193 t2: 890 delta: 993
00> <info> app: t1: 4294967192 t2: 889 delta: 993
非溢出的:
00> <info> app: t1: 0 t2: 992 delta: 992
00> <info> app: t1: 992 t2: 1985 delta: 993
00> <info> app: t1: 1985 t2: 2977 delta: 992
00> <info> app: t1: 2977 t2: 3969 delta: 992
00> <info> app: t1: 3969 t2: 4962 delta: 993
从日志可得到结论:溢出不会导致计时的准确性。
方法2-1000 Hz版本
代码
根据前面的公式,设置步骤为 1024 Hz,对应预分频系统为31,公式 32768/(31+1) = 1024
。由于每秒超过 1000 次 tick,需要在中断函数中削减过多的值,使其达到1000(左右)。
初始化与前面相差不大:
void rtc_init(void)
{
ret_code_t err_code;
nrf_drv_rtc_config_t config = NRF_DRV_RTC_DEFAULT_CONFIG;
// 31: 32768/(31+1) = 1024 计数为1024, 即1秒有1024个tick,每个tick为 1/1024 = 0.9765ms
config.prescaler = 31;
err_code = nrf_drv_rtc_init(&m_rtc, &config, rtc_handler);
APP_ERROR_CHECK(err_code);
nrf_drv_rtc_tick_enable(&m_rtc, true);
nrf_drv_rtc_enable(&m_rtc);
}
中断函数如下:
static void rtc_handler(nrf_drv_rtc_int_type_t int_type)
{
if(int_type == NRF_DRV_RTC_INT_TICK)
{
static uint16_t a = 0;
a += 1000;
if (a >= 1024)
{
a -= 1024;
s_timestamp++;
}
}
}
由于每秒1024个tick,因此在函数中判断,是为了保证1秒内 s_timestamp 累加 1000 次,经测试,有时为999、1001,虽然不是十分准确,算平均值已经很接近了。思路来自 nordic 官方论坛,具体见文末地址链接。
测试
测试代码片段如下:
//rtc_settime(4294967195);
for (int i = 0; i < 5; i++)
{
t1 = rtc_gettime();
delay_ms(1000);
t2 = rtc_gettime();
uint32_t delta = t2 - t1;
NRF_LOG_INFO("t1: %u t2: %u delta: %d", t1, t2, delta);
rtc_settime(0xffffffff - 100 - i);
}
结果:
00> <info> app: t1: 0 t2: 1000 delta: 1000
00> <info> app: t1: 1000 t2: 2001 delta: 1001
00> <info> app: t1: 2001 t2: 3002 delta: 1001
00> <info> app: t1: 3002 t2: 4003 delta: 1001
00> <info> app: t1: 4003 t2: 5004 delta: 1001
00> <info> app: t1: 5004 t2: 6004 delta: 1000
使用rtc_settime
设置时间戳初始值进行溢出测试,结果如下:
00> <info> app: t1: 4294967195 t2: 900 delta: 1001
00> <info> app: t1: 4294967194 t2: 898 delta: 1000
00> <info> app: t1: 4294967193 t2: 898 delta: 1001
00> <info> app: t1: 4294967192 t2: 897 delta: 1001
00> <info> app: t1: 4294967191 t2: 895 delta: 1000
溢出情况下,计时依然准确。
完整代码
完整代码如下:
#include <time.h>
#include "nrf_drv_rtc.h"
#include "nrf_drv_clock.h"
#include "nrfx_rtc.h"
#include "nrf_rtc.h"
#include "nrf_drv_rtc.h"
#include "rtc.h"
#include "nrf_log.h"
#include "nrf_log_ctrl.h"
#include "nrf_log_default_backends.h"
// RTC2
static const nrf_drv_rtc_t m_rtc = NRF_DRV_RTC_INSTANCE(2);
static void rtc_handler(nrf_drv_rtc_int_type_t int_type);
volatile uint32_t s_timestamp = 0;
static void rtc_handler(nrf_drv_rtc_int_type_t int_type)
{
if(int_type == NRF_DRV_RTC_INT_TICK)
{
// 每秒1024个tick,这里判断,是为了保证1秒内s_timestamp累加1000次
// 经测试,有时为999、1001,平均接近,不是十分准确
static uint16_t a = 0;
a += 1000;
if (a >= 1024)
{
a -= 1024;
s_timestamp++;
}
}
}
void rtc_init(void)
{
ret_code_t err_code;
nrf_drv_rtc_config_t config = NRF_DRV_RTC_DEFAULT_CONFIG;
// 8Hz: 32768/8-1 = 4095 设置此值,中断中判断7次
// config.prescaler = 4095;
// 32: 32768/(32+1) = 992.96 计数为992或993 即1秒有992或993个tick,每个 1/992.96 = 1.007ms
// 31: 32768/(31+1) = 1024 计数为1024, 即1秒有1024个tick,每个tick为 1/1024 = 0.9765ms
config.prescaler = 31;
err_code = nrf_drv_rtc_init(&m_rtc, &config, rtc_handler);
APP_ERROR_CHECK(err_code);
nrf_drv_rtc_tick_enable(&m_rtc, true);
nrf_drv_rtc_enable(&m_rtc);
}
void rtc_start(void)
{
}
void rtc_stop(void)
{
}
uint32_t rtc_gettime(void)
{
return s_timestamp;
}
void rtc_settime(uint32_t t)
{
s_timestamp = t;
}
参考: