ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

nrf52832 利用app_timer 产生精确的1ms 心跳

2021-06-10 19:58:33  阅读:523  来源: 互联网

标签:RTC rtc int app timer event rtc2 APP 1ms


一、我的需求

应用层的调度依赖于一个周期为1ms的滴答心跳(SysTick),并且对这个心跳的精确度要求比较高。

二、存在的问题

初来乍到,对nordic 的sdk 并不熟悉,发现app 定时器 用起来挺方便,直接用它实现一个周期为1ms的周期定时器,然后在定期处理函数中进行SysTick 计数。
用了一段时间才发现,这种方式实现的1ms 中断,误差非常大,实测只有976 us,对应用层的影响比较大,需要更精确的心跳。

三、解决历程

3.1 利用 Systick Timer 实现心跳?

第一时间想到的就是用 Systick Timer 实现,因为之前用stm32 单片机,就是用它实现的。nrf52832 采用的也是arm 内核,应该也是有Systick Timer 。
从nordic sdk 的nrfx_systick.c 中可知,默认情况下,Systick timer 并没有启动。即使启动 Systick timer , sdk 中也是利用Systick Timer 进行阻塞的延时处理,并没有开启中断响应。
在网上查了一下,一般的说法是考虑到蓝牙产品的低功耗特性,sdk 默认不使用Systick Timer。暂时先放弃它。

3.2 利用RTC 时钟实现心跳?

3.2.1 为什么用rtc ?

nrf52832 有一个32.768k的外部晶振作为rtc的时钟源,功耗不高,精确度高。

3.2.2 片上rtc 资源

片上一共有三个rtc 资源。协议栈利用rtc0 进行调度,app 定时器利用rtc1,空闲的只有rtc2 。

3.2.3 利用rtc2 实现


static void rtc2_handler(nrfx_rtc_int_type_t int_type)
{
    if ( int_type == NRFX_RTC_INT_COMPARE0)
    {
	_ms_tick++;
	nrfx_rtc_counter_clear(&rtc2); 
    }
}

static void rtc2_config(void)
{
    uint32_t err_code;
    //定义rtc 初始化配置结构体,并使用默认参数初始化
    nrfx_rtc_config_t config = NRFX_RTC_DEFAULT_CONFIG;

    //freq = 32768/(prescaler + 1)
    config.prescaler = 0;
    err_code = nrfx_rtc_init(&rtc2,&config,rtc2_handler);
    APP_ERROR_CHECK(err_code);

    //设置rtc 通道0的比较值
    //tick : 1000000/32768 = 30.5175us
    //count: 1000/tick = 1000/30.5175 = 72.768
    // 32* 30.517 = 976
    // 33* 30.517 = 1007
    err_code = nrfx_rtc_cc_set(&rtc2,0,32,true);
    APP_ERROR_CHECK(err_code);

    nrfx_rtc_enable(&rtc2);
    
}

RTC 产生事件后,会进入中断服务函数irq_handler(), 而中断服务函数会禁止RTC 比较事件和比较事件中断。我们为了能周期性地产生中断,需要将这两个禁止屏蔽。

static void irq_handler(NRF_RTC_Type * p_reg,
                        uint32_t       instance_id,
                        uint32_t       channel_count)
{
    uint32_t i;
    uint32_t int_mask = (uint32_t)NRF_RTC_INT_COMPARE0_MASK;
    nrf_rtc_event_t event = NRF_RTC_EVENT_COMPARE_0;

    for (i = 0; i < channel_count; i++)
    {
        if (nrf_rtc_int_is_enabled(p_reg,int_mask) && nrf_rtc_event_pending(p_reg,event))
        {
            /* nrf_rtc_event_disable(p_reg,int_mask); */
            /* nrf_rtc_int_disable(p_reg,int_mask); */
            nrf_rtc_event_clear(p_reg,event);
            NRFX_LOG_DEBUG("Event: %s, instance id: %lu.", EVT_TO_STR(event), instance_id);
            m_handlers[instance_id]((nrfx_rtc_int_type_t)i);
        }
        int_mask <<= 1;
        event    = (nrf_rtc_event_t)((uint32_t)event + sizeof(uint32_t));
    }
    event = NRF_RTC_EVENT_TICK;
    if (nrf_rtc_int_is_enabled(p_reg,NRF_RTC_INT_TICK_MASK) &&
        nrf_rtc_event_pending(p_reg, event))
    {
        nrf_rtc_event_clear(p_reg, event);
        NRFX_LOG_DEBUG("Event: %s, instance id: %lu.", EVT_TO_STR(event), instance_id);
        m_handlers[instance_id](NRFX_RTC_INT_TICK);
    }

    event = NRF_RTC_EVENT_OVERFLOW;
    if (nrf_rtc_int_is_enabled(p_reg,NRF_RTC_INT_OVERFLOW_MASK) &&
        nrf_rtc_event_pending(p_reg, event))
    {
        nrf_rtc_event_clear(p_reg,event);
        NRFX_LOG_DEBUG("Event: %s, instance id: %lu.", EVT_TO_STR(event), instance_id);
        m_handlers[instance_id](NRFX_RTC_INT_OVERFLOW);
    }
}

3.3 重新回归app timer定时器

虽然用rtc2 实现了精确的1毫秒的中断,但是由于多启用了一个定时器,功耗高了,心理不爽。回来分析一下为什么app timer 实现的1ms 定时器误差那么大

3.3.1 io 口翻转辅助分析

利用io 口翻转查看了1ms 中断的时间间隔,发现这个时间稳定为976us。看起来这个调度本身是挺稳定的。

3.3.2 额外添加n个tick,凑足1ms,是否可行?

实际算了一下APP_TIMER_TICKS(1) 换算出来的 ticks 数是16 。其中APP_TIMER_CONFIG_RTC_FREQUENCY 默认为1,也就是16384 hz。1000000/16384 * 16 算出来的值刚好是976 。说明本身app 定时器的定时时间是很准确的。

3.3.3 APP_TIMER_TICKS 运算精度差

查看APP_TIMER_TICKS 的函数实现,发现APP_TIMER_TICKS(1) 理论上算出来是16.884,由于都是整形数据参与运算,返回的结果就截掉了后面的小数部分,返回基本整形16 ,造成最后的误差大。

3.3.4 提高时钟分频系数,降低误差

APP_TIMER_CONFIG_RTC_FREQUENCY 由1 调整 为0,时钟频率由16384 提到到32768,每个tick 的时间61.11 变成30.52 。也就是说,当误差为1个tick时,之前的最大误差是61.11 us,现在变成了30.52us。产生1ms 中断,理论上的中断周期是1007us,误差是7us。

标签:RTC,rtc,int,app,timer,event,rtc2,APP,1ms
来源: https://blog.csdn.net/gdut_liujiangyi/article/details/117790297

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有