C# 实现 rtc_Nordic下高精度RTC的设计

nordic芯片内置有RTC定时器功能,但这并不是全部的实时时钟功能,充其量是一个超低功耗的定时器(时钟一般为32768HZ)。但是nordic做的产品往往是和时间相关的(如手环、手表等等),如果不额外增加RTC芯片的情况下,实现实时时钟的功能,nordic芯片能否做到?答案是:能!

我们来假定一些实现的思路:

  1. 利用普通定时器设计成1秒定时器,设计高优先级中断,在中断中维护Unix时间戳。

2. 利用Apptimer,创建一个1秒定时器,在回调中维护Unix时间戳。

3. 利用RTC设计成1秒定时器,设计高优先级中断,在中断函数中维护Unix时间戳。

认真从时序上分析,

方案1,不符合低功耗的要求,舍弃。

方案2,受Apptimer定时器任务队列调度的影响,延时因素并不明确,将会导致定时不准。

方案3,看似符合要求,低功耗也符合。但我们再仔细核对一下时序,该定时器将会产生一个中断并且在中断里维护Unix时间戳,假定中断没有收到任何协议栈中断的打断,我们可以通过时差补偿将中断函数里维护时间戳的耗费的时间补偿回来,基本满足比较准确的定时。但如果考虑BLE高速通信下,RTC中断很可能被打断导致精准性引入一些不确定因素。

该如何解决这个问题?

RTC实现高精度的方法核心应该是绕开中断,如果硬件上没法实现,应该通过软件实现,说简单点就是通过查询的方法避开中断对精度的干扰。本文就是通过查询法设计一个高精度的RTC时钟。

呵呵,贴代码,不解释,看得懂的高手请进。核心思路注释已经被我删除,看明白的高手可以在评论区留言

rtc_app.c

static uint32_t rtc_unix_value = 0;

//设置UNIX时间戳
void     RTC_Set_Unix(uint32_t Unix_value)
{
	rtc_unix_value = Unix_value;
}

//获取UNIX时间戳
uint32_t RTC_Get_Unix(uint32_t RTC_count)
{
	static uint32_t last_rtc_value = 0xffffffff;
	uint32_t curr_timestamp;
	uint32_t time_interval;
	
        //关键1
	curr_timestamp = RTC_count >> 15;
	
	if(last_rtc_value == 0xffffffff)
	{
	    last_rtc_value = curr_timestamp;
	}
        //关键2
	if(curr_timestamp >= last_rtc_value)
	{
	    time_interval = curr_timestamp - last_rtc_value;
	}
        //关键3
	else	
	{
	    time_interval = (0x200-last_rtc_value) + curr_timestamp;
	}

	last_rtc_value = curr_timestamp;
	rtc_unix_value += time_interval;
	
	return rtc_unix_value;
}

下面,我们利用Noinit RAM设计一个实用的RTC系统,好处是系统将不受复位的影响持续计时。关于Noinit RAM的讲解请参考上一篇文章

书生:MDK平台下Nordic系列芯片Noinit RAM的设置和应用探讨​zhuanlan.zhihu.com

该设计通过查询RTC0的计数器,可以在Apptimer里每隔0.5-1秒查询一次Unix时间,由于没有被任何中断打断,理论上,这个RTC的秒偏差只取决于32768HZ低频晶振的精度。并且能省去多余的一个RTC的硬件开销和功耗。

//NoInit RAM
typedef struct 
{
    uint32_t power_flag;
    uint32_t unix_timemap;                
} noinit_data_t; 

#define __noinit__ __attribute__((at(0x2000FFF0)))   
__noinit__ noinit_data_t noinit_data;

int main()
{
    uint32_t last_timemap;
    
    //恢复RTC,信息丢失,则从flash恢复 2018年8月-2038年8月,如果没有flash记忆,可以默认一个出厂时间
    if((noinit_data.unix_timemap < 1533052800)||(noinit_data.unix_timemap > 2164204800))
    {
        RTC_Set_Unix(flash_storage_data.unix_timemap);
    }
    //从Noinit RAM里初始化Unix时间戳
    else
    {
        RTC_Set_Unix(noinit_data.unix_timemap);
    }

    while(1)
    {
        //RTC处理
	noinit_data.unix_timemap = RTC_Get_Unix((NRF_RTC0->COUNTER)&0x00ffffff);
	if(noinit_data.unix_timemap != last_timemap)
	{
	    LOG("unix_timemap=%drn",noinit_data.unix_timemap);
            /*
             在此处可添加代码,将Unix时间戳转化为当地时间
            */
	    last_timemap = noinit_data.unix_timemap;
	}
        nrf_delay_ms(500);
    }
}

该方法在几款量产的产品上经过验证,非常稳定实用。该方法同理也兼容任何有RTC定时器的MCU。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值