前面两篇已经介绍了TinyOS的优点,nesC的事件代码分析,可以通过视频去学会自己编写基本应用
如果说interface,event你还觉得TinyOS的nesC的引入吸引不了你的话,那么现在咱们进入个人认为TinyOS编程最难的地方也是整个最吸引我的地方
参数化接口(parameterized interface),其中我们早就接触到了-blink例程
大家要注意的是对于TinyOS来说,components包含configuration和module,interface都是可以把他理解成一个数组元素,不要死板的理解成一个C文件,他们都是一个元素,当然也可以实例化n个;这就是参数化接口的意义!也是本人最喜欢使用TinyOS的最重要的原因;
至此咱们可以想一下,假设n路传感器,代码实现(驱动是一样的),就可以使用参数接口化来编写代码,有一点点对象的意思
回忆一下blink代码:
- configuration BlinkAppC
- {
- }
- implementation
- {
- components MainC, BlinkC, LedsC;
- components new TimerMilliC() as Timer0;
- components new TimerMilliC() as Timer1;
- components new TimerMilliC() as Timer2;
-
-
- BlinkC -> MainC.Boot;
-
- BlinkC.Timer0 -> Timer0;
- BlinkC.Timer1 -> Timer1;
- BlinkC.Timer2 -> Timer2;
- BlinkC.Leds -> LedsC;
- }
TimerMilliC()的组件只有一份代码,为什么可以多个实例组件呢,这个也就是参数化接口的作用,相同的比较常常能看见的还有LedsC连接到底层发现IO口部分的参数化接口部分可以随意的设定led对应的IO口;
前面的blip和printf例程都使用到了timer,为什么没有讲解timer的用意也在此,这个参数化接口是nesC语言的杀手锏;
参数化接口有一点类似C语言的定义 uint_8 array[] = {0,0,0,0};的意思,只不过这个数组可以广域化一下,如components,interface,module等
带来的好处是显而易见的,一次编程,多次使用,如内存池pool,fragpool等,理解参数化编程最好的代码应该是fragpool
分析的是interface Timer<TMilli>,那么首先必须先看的是Timer.h,目录:tinyos-main-release_tinyos_2_1_2\tos\lib\timer
- // The TinyOS Timer structures are discussed in TEP 102.
- #ifndef TIMER_H
- #define TIMER_H
-
- // @note TSecond is an extension to be added in a successor to TEP 102
- typedef struct { int notUsed; } TSecond;
- typedef struct { int notUsed; } TMilli;
- typedef struct { int notUsed; } T32khz;
- typedef struct { int notUsed; } TMicro;
-
- #define UQ_TIMER_SECOND "HilTimerMicroC.Timer"
- #define UQ_TIMER_MILLI "HilTimerMilliC.Timer"
- #define UQ_TIMER_32KHZ "HilTimer32khzC.Timer"
- #define UQ_TIMER_MICRO "HilTimerMicroC.Timer"
-
- #endif
再来看看接口Timer,为什么blink的使用uses interface Timer<TMilli>也就是跟了参数,这是咱们之前没有分析过的Timer.nc,接口文件,目录:tinyos-main-release_tinyos_2_1_2\tos\lib\timer
- interface Timer<precision_tag>
- {
- // basic interface
- /**
- * Set a periodic timer to repeat every dt time units. Replaces any
- * current timer settings. Equivalent to startPeriodicAt(getNow(),
- * dt). The <code>fired</code> will be signaled every dt units (first
- * event in dt units).
- *
- * @param dt Time until the timer fires.
- */
- command void startPeriodic(uint32_t dt);
-
- /**
- * Set a single-short timer to some time units in the future. Replaces
- * any current timer settings. Equivalent to startOneShotAt(getNow(),
- * dt). The <code>fired</code> will be signaled when the timer expires.
- *
- * @param dt Time until the timer fires.
- */
- command void startOneShot(uint32_t dt);
-
- /**
- * Cancel a timer.
- */
- command void stop();
-
- /**
- * Signaled when the timer expires (one-shot) or repeats (periodic).
- */
- event void fired();
-
- // extended interface
- /**
- * Check if timer is running. Periodic timers run until stopped or
- * replaced, one-shot timers run until their deadline expires.
- *
- * @return TRUE if the timer is still running.
- */
- command bool isRunning();
-
- /**
- * Check if this is a one-shot timer.
- * @return TRUE for one-shot timers, FALSE for periodic timers.
- */
- command bool isOneShot();
-
- /**
- * Set a periodic timer to repeat every dt time units. Replaces any
- * current timer settings. The <code>fired</code> will be signaled every
- * dt units (first event at t0+dt units). Periodic timers set in the past
- * will get a bunch of events in succession, until the timer "catches up".
- *
- * <p>Because the current time may wrap around, it is possible to use
- * values of t0 greater than the <code>getNow</code>'s result. These
- * values represent times in the past, i.e., the time at which getNow()
- * would last of returned that value.
- *
- * @param t0 Base time for timer.
- * @param dt Time until the timer fires.
- */
- command void startPeriodicAt(uint32_t t0, uint32_t dt);
-
- /**
- * Set a single-short timer to time t0+dt. Replaces any current timer
- * settings. The <code>fired</code> will be signaled when the timer
- * expires. Timers set in the past will fire "soon".
- *
- * <p>Because the current time may wrap around, it is possible to use
- * values of t0 greater than the <code>getNow</code>'s result. These
- * values represent times in the past, i.e., the time at which getNow()
- * would last of returned that value.
- *
- * @param t0 Base time for timer.
- * @param dt Time until the timer fires.
- */
- command void startOneShotAt(uint32_t t0, uint32_t dt);
-
-
- /**
- * Return the current time.
- * @return Current time.
- */
- command uint32_t getNow();
-
- /**
- * Return the time anchor for the previously started timer or the time of
- * the previous event for periodic timers. The next fired event will occur
- * at gett0() + getdt().
- * @return Timer's base time.
- */
- command uint32_t gett0();
-
- /**
- * Return the delay or period for the previously started timer. The next
- * fired event will occur at gett0() + getdt().
- * @return Timer's interval.
- */
- command uint32_t getdt();
- }
那么对于cc2538cb平台,谁提供了Timer<TMilli>接口呢;
需要看TimerMilliC.nc ,TimerMilliP.nc(tinyos-main-release_tinyos_2_1_2\tos\system)
TimerMilliC.nc
- #include "Timer.h"
-
- generic configuration TimerMilliC() {
- provides interface Timer<TMilli>;
- }
- implementation {
- components TimerMilliP;
-
- // The key to unique is based off of TimerMilliC because TimerMilliImplP
- // is just a pass-through to the underlying HIL component (TimerMilli).
- Timer = TimerMilliP.TimerMilli[unique(UQ_TIMER_MILLI)];
- }
TimerMilliP.nc省略
通过查看cc2538cb平台的chips源码 目录tinyos-main-release_tinyos_2_1_2\tos\chips\cc2538\timer
找见HilTimerMilliC.nc 下面是部分代码,发现他提供了interface
- configuration HilTimerMilliC{
- provides interface Init;
- provides interface Timer<TMilli> as TimerMilli[ uint8_t num ];
- ...
- }
- implementation{
- ...
- }
nesC福利:
unique(..)------------提供唯一的id,每调用一次id+1,
如果把interface看成是数组的话,类似于数组的下标
uniqueCount(..)---计算使用unique总次数
new-------------------用于参数化接口的components的创建,实例化
Timer的库的写法是最复杂也是最全的nesC的参数化接口写法,最好是大家通过yeti2去查看图形连接关系,然后在eclipse下查看分析源码;
参数化接口格式
- interface xx<...>
- {
- command;
实际应用我常常使用下面的的方法一样可以达到参数化接口的目的,可以参考fragpool写法或者cc2538底层的io口的写法,interface可以简化写成简单的interface结构,改为configuration和module提供接口的时候改为
configuration/module name(xx){
provides interface_name[xx];
}
使用他采用components new name(unique(xx));
见仁见智吧,哪一种方法都行,喜欢第二种是代码阅读稍微直观一点。
在此大家可以看到components和interface的编程可以使用参数,使用new创建并完成实例化,实例个数限制和参数有关,更多的需要自己去阅读代码,这玩意只能自己去阅读清楚
例程目录tinyos-main-release_tinyos_2_1_2\apps\cc2538_Test\timerTest
包含文件:
Makefile
TimerAppC.nc---configuration
TimerC.nc
timertest例程为ms单位的定时器使用实验
Makefile
- COMPONENT=TimerAppC
- CFLAGS += -DUSE_TIMER_HANDLER
- #CFLAGS += -DUSE_UART_HANDLER
- #CFLAGS += -DUSE_RF_HANDLER
- CFLAGS += -DNOT_USE_PRINTFC_BUT_USE_PRINT
- include $(MAKERULES)
不做介绍,不清楚可以去看blink和printf讲解
TimerAppC.nc
- configuration TimerAppC
- {
- }
- implementation
- {
- components MainC, TimerC;
- components new TimerMilliC() as Timer1;
-
- TimerC -> MainC.Boot;
- TimerC.Timer1 -> Timer1;
- }
阅读省略
TimerC.nc
- /*******************************************************************
- *实验4----毫秒定时器实验
- *节点需求数1
- *编译命令make cc2538cb
- ********************************************************************/
-
- #include "Timer.h"
- #include "printf.h"
-
- module TimerC
- {
- uses interface Timer<TMilli> as Timer1;
- uses interface Boot;
- }
- implementation
- {
- task void time1_Task();
- uint16_t Timer_counter=0;
-
- /***************************************************
- *启动事件
- ****************************************************/
- event void Boot.booted()
- {
- /**开启一秒的周期性定时器(单位毫秒) Timer1**/
- call Timer1.startPeriodic( 1000 );
-
- }
-
- /***************************************************
- *任务time1_Task
- ****************************************************/
- task void time1_Task()
- {
- printf("Timer1 fired: %u\n", Timer_counter++);
- }
-
- /***************************************************
- *Timer1定时时间到事件
- ****************************************************/
- event void Timer1.fired()
- {
- /****提交time1_Task任务***/
- post time1_Task();
- }
-
- }
阅读省略
例程目录tinyos-main-release_tinyos_2_1_2\apps\cc2538_Test\TimerMicro
TimerMicro例程为us单位的定时器使用实验,代码省略