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还不是很理解,还需要继续实验。

  

  

  

 

posted @ 2016-12-08 15:55  一只奋斗的考拉  阅读(1628)  评论(0编辑  收藏  举报