contiki-rtimer
struct rtimer { rtimer_clock_t time; rtimer_callback_t func; void *ptr; }; typedef unsigned short rtimer_clock_t; typedef void (* rtimer_callback_t)(struct rtimer *t, void *ptr);
三个参数:时钟类型,回调函数,回调函数的参数
参考博客:http://blog.csdn.net/jiangjunjie_2005/article/details/44947899 (简称博客1)
http://blog.sina.com.cn/s/blog_686ee2910102vxce.html (简称博客2)
这个timer不再是一个链表,只是一个timer,去提供实时性的要求。只能串行使用rtimer定时器的中断回调功能。
rtimer用途
Contiki系统引入rtimer可以满足精准定时的需要,一些对时间极为敏感的模块(如MAC协议)依赖于rtimer。和etimer的粗粒度(常见为100Hz)不同,rtimer是细粒度(常见为10kHz,100us)定时器。根据经验(别人的经验),细粒度定时器如果中断频繁特别容易消耗CPU资源,为此,contiki设计rtimer时尽可能地减少中断,大部分应用场合为读取定时器计数值。
上图发现,rtimer没有常见的next指针来挂接后续节点,确实,contiki为了减少rtimer中断的爆发,只能挂接一个rtimer结构。如果同时挂接2个或以上的rtimer结构,那么最新挂接的有效,其它的rtimer结构将丢失。
rtimer中断时序
上图展示了rtimer的中断时序,当调用rtimer_arch_schedule()时,它设置rtimer定时器的中断间隔时长timeout,中断服务函数rtimer_irq_handler会调用rtimer->callback()回调函数。
注意2点,中断只允许发生一次(不懂,搞明白再回头来解释原因)(终于明白了博客1中版主所说的这句话的意思,在使用rtimer的时候,只能rtimer_set一次,不能反复产生到时中断,搞明白rtimer_set函数的内涵),即rtimer_arch_schedule()使能中断,而rtimer_irq_handler()禁止中断;rtimer->callback()是在中断状态下运行,特别注意防止竟态错误,例如:调用process_poll()合法,调用process_post()非法。(暂时不懂原因)
rtimer的移植
即,基于MCU实现rtimer-arch.c和rtimer-arch.h
大多数定时器(尤其是8位MCU)位宽为16位,即MAX=65535,而MAC协议往往需要1秒以上的定时周期,因此rtimer的频率必须小于30kHz。一个比较好的数值是10kHz,它既可以保证比较好的精度(间隔为100us),又具备6.5秒的满量程,这可以适应大多数的应用需要。
另外,大多数应用需要随机撤销和重启动rtimer,它可以通过添加2个函数来实现:
rtimer_arch_disable_irq()和rtimer_arch_enable_irq()。
对于contiki系统而言,其已经在/core/sys/rtimer.h&c中对rtimer相关结构进行了定义,对于特定平台而言,主要需要实现以下几个函数:
rtimer_arch_init(),针对特定平台的初始化操作,被rtimer_init()函数调用;
rtimer_arch_now(),用于获取当前的rtimer时间;
rtimer_arch_schedule(rtimer_clock_t t),传递一个唤醒时间,在特定时刻进行调度操作,调用rtimer_run_next()。
同时需要在rtimer_arch.h中定义RTIMER_ARCH_SECOND确定rtimer每秒的滴答数。
stm32移植准备
1)移植过程中,首先确定rtimer每秒的滴答数是多少,在本系统中,达到100us的精度就OK了,因此选用了10kHz的频率。
2)选定用于实现rtimer的定时器,stm32具备多个定时器,我所使用的是stm32f103,具备TIM1和TIM8等高级定时器,也有TIM2-TIM5等通用定时器,还有TIM6和TIM7等基本定时器。不知道用哪种定时器来实现rtimer更合适,知道的朋友可以来补充说明一下,这里暂时选用TIM2通用定时器。并用TIM2的TIM_IT_CC1中断来完成rtimer_run_next的调度。(因为常用的中断TIM_IT_Update,便搞不明白了定时器中断类型到底有什么区别,写个博客分析一下)
以上也是整理参考博客的内容,下面说明一下我是如何实现的
实现rtimer-arch.h文件
#include "contiki-conf.h" #include "rtimer.h" //确定rtimer每秒的滴答数 #define RTIMER_ARCH_SECOND 10000 /* 10kHz(100us)*/ //这里使用stm32的通用定时器2来实现 #define RTIMER_TIM TIM2 /*在TIM2的中断服务函数TIM2_IRQHandler中调用*/ void rtimer_irq_handler(void); /*针对特定平台的初始化操作,被rtimer_init()函数调用*/ void rtimer_arch_init(void); /*用于获取当前的rtimer时间*/ rtimer_clock_t rtimer_arch_now(void); /*传递一个唤醒时间,在特定时刻进行中断调度操作,中断中调用rtimer_run_next()*/ /*还不太理解这个唤醒时间是怎么个调度法,关键不理解函数中的TIM_SetCompare1()的意义*/ void rtimer_arch_schedule(rtimer_clock_t t); /*失能和使能超时中断*/ void rtimer_arch_disable_irq(void); void rtimer_arch_enable_irq(void);
实现rtimer-arch.c文件
1 #include "sys/rtimer.h" 2 #include "rtimer-arch.h" 3 #include "timer2.h" 4 5 #define DEBUG 0 6 #if DEBUG 7 #include <stdio.h> 8 #define PRINTF(...) printf(__VA_ARGS__) 9 #else 10 #define PRINTF(...) 11 #endif 12 13 void 14 rtimer_irq_handler(void) 15 { 16 17 /* Clear interrupt pending bit */ 18 TIM_ClearITPendingBit(RTIMER_TIM,TIM_IT_CC1); 19 20 21 /* Do interrupt only once! */ 22 /*中断到来后需要将中断禁止,避免多次产生中断*/ 23 rtimer_arch_disable_irq(); 24 25 //ENERGEST_ON(ENERGEST_TYPE_IRQ);//开启了节能中断 26 rtimer_run_next(); 27 //ENERGEST_OFF(ENERGEST_TYPE_IRQ); 28 29 return; 30 } 31 32 void 33 rtimer_irq_handler(void) 34 { 35 36 /* Clear interrupt pending bit */ 37 TIM_ClearITPendingBit(RTIMER_TIM,TIM_IT_CC1); 38 39 40 /* Do interrupt only once! */ 41 /*中断到来后需要将中断禁止,避免多次产生中断*/ 42 rtimer_arch_disable_irq(); 43 44 //ENERGEST_ON(ENERGEST_TYPE_IRQ);//开启了节能中断 45 rtimer_run_next(); 46 //ENERGEST_OFF(ENERGEST_TYPE_IRQ); 47 48 return; 49 } 50 51 void 52 rtimer_arch_init(void) 53 { 54 TIM2_Int_Init(); 55 56 return; 57 } 58 59 rtimer_clock_t 60 rtimer_arch_now(void) 61 { 62 rtimer_clock_t tT1, tT2; 63 64 do 65 { 66 /* Avoid race condition on reading counter of TIM1 */ 67 tT1 = TIM_GetCounter(RTIMER_TIM); 68 tT2 = TIM_GetCounter(RTIMER_TIM); 69 } while (tT1 != tT2); 70 71 return tT1; 72 } 73 74 void 75 rtimer_arch_schedule(rtimer_clock_t t) 76 { 77 /* Sets the TIM2 Capture Compare1 Register value */ 78 TIM_SetCompare1(RTIMER_TIM,t); 79 PRINTF("进入rtimer_arch_schedule\r\n"); 80 PRINTF("%d\r\n",rtimer_arch_now()); 81 82 /* MUST clear the remained flag of TIM2 compare */ 83 TIM_ClearFlag(RTIMER_TIM,TIM_FLAG_CC1); 84 85 /* Enable interrupt of Capture compare 1 */ 86 TIM_ITConfig(RTIMER_TIM,TIM_IT_CC1, ENABLE); 87 88 return; 89 } 90 91 void 92 rtimer_arch_disable_irq(void) 93 { 94 TIM_ITConfig(RTIMER_TIM,TIM_IT_CC1, DISABLE); 95 96 return; 97 } 98 99 void 100 rtimer_arch_enable_irq(void) 101 { 102 /* MUST clear the remained flag of TIM2 compare */ 103 TIM_ClearFlag(RTIMER_TIM,TIM_FLAG_CC1); 104 105 /* Enable interrupt of Capture compare 1 */ 106 TIM_ITConfig(RTIMER_TIM,TIM_IT_CC1, ENABLE); 107 108 return; 109 }
最后附上timer2.c的代码,来显示和rtimer的衔接
1 void TIM2_Int_Init(void) 2 { 3 u16 arr=65535; 4 u16 psc=(uint16_t)(SystemCoreClock / RTIMER_ARCH_SECOND) -1; 5 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct; 6 NVIC_InitTypeDef NVIC_InitStruct; 7 8 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//①定时器时钟使能 9 10 TIM_TimeBaseInitStruct.TIM_Period=arr;//设置自动重装载寄存器周期的值 11 TIM_TimeBaseInitStruct.TIM_Prescaler=psc;//设置时钟频率的预分频值 12 TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;//定时器向上计数 13 TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;//设置时钟分割 14 TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);//②初始化、配置定时器 15 16 TIM_ITConfig(TIM2,TIM_IT_CC1,ENABLE);//开启更新中断 17 18 TIM_Cmd(TIM2,ENABLE);//使能定时器 19 20 NVIC_InitStruct.NVIC_IRQChannel=TIM2_IRQn; 21 NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE; 22 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=0; 23 NVIC_InitStruct.NVIC_IRQChannelSubPriority=2; 24 NVIC_Init(&NVIC_InitStruct);//配置中断 25 26 } 27 //编写中断服务函数 28 void TIM2_IRQHandler(void) 29 { 30 31 if(TIM_GetITStatus(TIM2,TIM_IT_CC1)==SET) 32 { 33 TickCounter2++;//6.5535s产生一次中断 34 rtimer_irq_handler();//调用rtimer中的中断处理函数 35 36 } 37 /* leave interrupt */ 38 39 }
正确使用rtimer还不是很理解,还需要继续实验。
本文来自博客园,作者:一只奋斗的考拉,转载请注明原文链接:https://www.cnblogs.com/liu13526825661/p/6135780.html