近日在阅读semtech的Lora-net/LoRaMac-node。此代码是LoRaWAN MAC层的node段的代码。

此代码中构建了一个定时器链表,此链表构建得非常的巧妙,现在和大家分享。

此定时器链表底层使用的是RTC的闹钟(Alarm)机制(将日历时间转换成时间戳时间),而非使用一个定时器产生一个固定的定时(比如1ms),然后定时刷新整个链表。

也就是说此RTC定时器并非产生一个嘀嗒定时器来定时检查定时器链表,而是直接根据链表你的表头来直接定时,一步到位。

用RTC的方法相比较嘀嗒定时器定时的方法,工作效率会明显提升,并不会因为链表中定时器数目的增加使得花费在刷新定时器上的时间增加,因为不需要遍历整个链表。但代码的实现难度会较高

假如程序刚开始执行,而且定时器链表为空,此时有4个定时事件需要放入链表,分别为 A 10ms B 30ms C 20ms D 40ms,

RTC闹钟链表:

其存储的结果会是这样:

事件名称 定时时间
A 10
C 10
B 10
D 10

而嘀嗒定时链表:

其存储的结果会是这样:

事件名称 定时时间
A 10
B 30
C 20
D 40

当时间过了5ms,RTC闹钟链表中存储的数据并不会发生任何变化,因为它是以RTC的闹钟来作为刷新依据的,而嘀嗒定时链表中的数据就全发生了变化

嘀嗒定时链表 变化得到情况如下:

事件名称 定时时间
A 5
B 25
C 15
D 35

再过5ms,此时A事件的定时时间就到了,需要被执行,在RTC闹钟链表中的表现是RTC Alarm中断触发,在嘀嗒定时链表中的表现是A事件的定时时间逐渐减少至0。当A事件被执行之后两种定时器链表中的存储都发生了变化,都是原先的链表的头指针指向原先的第二个节点,而原先的头节点被释放。

还是上述的例子,在定时器执行了7ms的时候,这时有个事件需要插入,为E 24ms,此时,两种链表对于此事件器的插入操作也会明显不同。

RTC闹钟插入之后

事件名称 定时时间
A 10
C 10
B 10
E 1
D 9

而嘀嗒定时器在插入之后为

事件名称 定时时间
A 3
B 13
C 23
D 33
E 24

以下是RTC闹钟的部分插入代码,其中可以看到他的定时器插入的逻辑


elapsedTime = TimerGetValue( );//获取距离上一次设置闹钟的时间
remainingTime = TimerListHead->Timestamp - elapsedTime;//remainingTime表示剩余的头节点中的事件剩余的定时事件,因为此链表是按顺序存储的,所以头节点中的定时时间一定是最少的 static void TimerInsertNewHeadTimer( TimerEvent_t *obj, uint32_t remainingTime )
{
TimerEvent_t* cur = TimerListHead; if( cur != NULL )//表头不为空,将新的定时器插入之前,将原先表头的定时器时间减去新定时器的定时时间,确保原先的定时器任务定时正常
{
cur->Timestamp = remainingTime - obj->Timestamp;
cur->IsRunning = false;
} obj->Next = cur;
obj->IsRunning = true;
TimerListHead = obj;
TimerSetTimeout( TimerListHead );//设置超时,等时间到的时候,会发生RTC报警
}

另外还有一点,此RTC中的1s并非物理时间的1s,在此具体的时间基准如下:

此项目中,使用的RTC的时钟源为32.768Khz的LSE,通过AsynchPrediv和SynchPrediv分频得到2.048KHz的RTCtick,计算公式为32.768/(3+1)/(3+1) = 2.048;

相关的配置代码如下:

	void RtcInit( void )
{
...
RtcHandle.Init.AsynchPrediv = 3;
RtcHandle.Init.SynchPrediv = 3;
...
} /*!
* RTC Time base in ms
*/
#define RTC_ALARM_TICK_DURATION 0.48828125 // 1 tick every 488us
#define RTC_ALARM_TICK_PER_MS 2.048 // 1/2.048 = tick duration in ms

由于原本每个tick相当于1s,而在这里,每个tick相当于0.48828125ms,小于1ms,所以在程序中能够实现ms级的定时任务。

RTC定时器的用法主要分为三步:

1. 初始化,注册回调函数
void TimerInit( TimerEvent_t *obj, void ( *callback )( void ) )//设置回调函数
2. 设置定时时间
void TimerSetValue( TimerEvent_t *obj, uint32_t value )
3. 开启定时时间
void TimerStart( TimerEvent_t *obj )

LoRaWAN移植笔记(一)__RTC闹钟链表的实现的更多相关文章

  1. LoRaWAN_stack移植笔记(四)__RTC

    stm32相关的配置 由于例程使用的主控芯片为STM32L151C8T6,而在本设计中使用的主控芯片为STM32L051C8T6,内核不一样,并且Cube库相关的函数接口及配置也会有不同,所以芯片的驱 ...

  2. tslib移植笔记(1)【转】

    本文转载自:https://blog.csdn.net/zijie_xiao/article/details/50740950 tslib移植笔记(1)2016-04-25 tslib背景[摘自百度] ...

  3. STemWin5.22移植笔记【转】

    来自:http://www.openedv.com/posts/list/27697.htm STemWin5.22移植笔记 网上关于emwin的资料很少,我在移植的时候查了很多资料,对我一个感觉是好 ...

  4. LoRaWAN stack移植笔记(五)__调试1

    先废话一小段 在将LoRaWAN的程序移植的过程中,调试发现了很多的问题. 做好记录工作,防止以后再踩坑 移植使用的是LoRaMac-node库,使用的是STM32L151CBT6 MCU,需要要移植 ...

  5. LoRaWAN stack移植笔记(六)_调试2

    前言 调试的过程中碰到的问题基本都是以前没有遇到过的,而且需要对整个协议栈及射频方面的工作流程较熟悉才能找到问题的原因,需要多读SX1276的数据手册以及与射频芯片的物理层通信例程和MAC层通信例程进 ...

  6. LoRaWAN_stack移植笔记(三)__SPI

    stm32相关的配置 由于例程使用的主控芯片为STM32L151C8T6,而在本设计中使用的主控芯片为STM32L051C8T6,内核不一样,并且Cube库相关的函数接口及配置也会有不同,所以芯片的驱 ...

  7. JZ2440 u-boot-2016.11、linux-4.17和busybox-1.28.4移植笔记

    2018年5月份开始在JZ2440上陆续移植了u-boot-2016.11.u-boot-spl-2016.11.linux-4.17和busybox-1.28.4,其中linux-4.17和busy ...

  8. java学习笔记(7)--链表

    标签(空格分隔):笔记 java其实已经将很多底层的数据结构进行了封装,虽然工作用不到,但是笔试和面试问的还是比较频繁的,而且这种面试题还是直接手撕代码,故专门总结一下. 1. 概念 1.1 链表(L ...

  9. Redis设计与实现读书笔记(二) 链表

    链表作为最基础的数据结构,在许多高级语言上已经有了很好的实现.由于redis采用C语言编写,需要自己实现链表,于是redis在adlist.h定义了链表类型.作者对于这部分没什么好说,源码比较简单,如 ...

随机推荐

  1. iPhone屏幕尺寸、分辨率及适配

    转:http://blog.csdn.net/phunxm/article/details/42174937 目录(?)[+]   1.iPhone尺寸规格 设备 iPhone 宽 Width 高 H ...

  2. HOWTO - Basic MSI安装包在安装运行过程中如何获取完整源路径

    有朋友问到如何在一个Windows Installer安装包中获取安装包源路径,就是在安装包运行过程中动态获取*.msi所在完整路径. 这个问题分两类,如果我们的安装包只是一个*.msi安装文件,那么 ...

  3. [转]PROE传动链条的装配教程

    转自: 原文连接:PROE动链条的装配教程 传动链条的装配  

  4. oprofile

    一.原理 在关注事件发生一定次数时,进行一次采样,记录下需要的信息(比如指令寄存器或栈寄存器信息). 二.参数 项 说明 eventname   要关注的事件名称,常用的事件名称及功能如下:   CP ...

  5. 深入学习golang(2)—channel

    Channel 1. 概述 “网络,并发”是Go语言的两大feature.Go语言号称“互联网的C语言”,与使用传统的C语言相比,写一个Server所使用的代码更少,也更简单.写一个Server除了网 ...

  6. php中抓取网页内容的代码

    方法一: 使用file_get_contents方法实现 $url = "http://news.sina.com.cn/c/nd/2016-10-23/doc-ifxwztru695114 ...

  7. Determining if a point lies on the interior of a polygon

    Determining if a point lies on the interior of a polygon Written by Paul Bourke  November 1987 Solut ...

  8. 【转】开放api接口签名验证

    不要急,源代码分享在最底部,先问大家一个问题,你在写开放的API接口时是如何保证数据的安全性的?先来看看有哪些安全性问题在开放的api接口中,我们通过http Post或者Get方式请求服务器的时候, ...

  9. Living one day at a time (update for a long time)

    1, http://acm.hdu.edu.cn/showproblem.php?pid=1228 2014-04-14  10:39:52 分析:字符串处理题... #include<iost ...

  10. System.Net.WebException : The remote server returned an error: (415) UNSUPPORTED MEDIA TYPE

    I am having problems with a bit of code that accesses a restful web service. Running this code, it e ...