1. 程式人生 > >09 在ZStack裡的定時器應用

09 在ZStack裡的定時器應用

ZStack裡的任務是基於事件來排程的, 裡面的定時器其實就是在約定的時間到後,設定指定任務的事件,從而讓設定定時的任務處理定時事件.

定時器封裝在工程目錄OSAL裡的OSAL_Timers.h標頭檔案裡:

  extern uint8 osal_start_timerEx( uint8 task_id, uint16 event_id, uint16 timeout_value ); //設定timeout_value毫秒後,系統會設定task_id任務號的任務發生event_id事件. 此函式建立的定時器是一次性的.

  extern uint8 osal_start_reload_timer( uint8
taskID, uint16 event_id, uint16 timeout_value ); //此定時器與上面的定時器相似,但可重複使用。 extern uint8 osal_stop_timerEx( uint8 task_id, uint16 event_id ); //停止定時器 extern uint32 osal_GetSystemClock( void ); //返回系統的時鐘,毫秒級別

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
此定時器功能是依賴SOC上的Timer2(MAC Timer)來實現的.
大概過程:

void osal_run_system( void ) //在系統任務的死迴圈檢查事件函式裡
{
  uint8 idx = 0
; osalTimeUpdate(); //根據Timer2的計時來更新系統時間 Hal_ProcessPoll(); ... } /////////////// void osalTimeUpdate( void ) { ... if ( elapsedMSec ) { osalClockUpdate( elapsedMSec ); osalTimerUpdate( elapsedMSec ); //更新系統裡定時器時間 } } /////////////////////////////////////////////////////////// //當呼叫osal_start_timerEx函式建立物件時: uint8 osal_start_timerEx( uint8 taskID, uint16 event_id, uint16 timeout_value ) { ...
newTimer = osalAddTimer( taskID, event_id, timeout_value ); ... } //////////////////////////// typedef struct { void *next; uint16 timeout; //此值為0時表示定時器已超時 uint16 event_flag; //記錄定時器超時後給task_id任務設定什麼事件 uint8 task_id; uint16 reloadTimeout; //重複性定時器用的初始計時值 } osalTimerRec_t; osalTimerRec_t *timerHead; //timerHead是全域性指標變數,也就是定時器的連結串列頭節點 osalTimerRec_t * osalAddTimer( uint8 task_id, uint16 event_flag, uint16 timeout ) { osalTimerRec_t *newTimer; osalTimerRec_t *srchTimer; // New Timer newTimer = osal_mem_alloc( sizeof( osalTimerRec_t ) ); //動態建立一個定時器物件 if ( newTimer ) { // 初始化定時器物件 newTimer->task_id = task_id; newTimer->event_flag = event_flag; newTimer->timeout = timeout; newTimer->next = (void *)NULL; newTimer->reloadTimeout = 0; if ( timerHead == NULL ) //如果還沒有頭節點,則創建出來的定時器物件就成為頭節點 timerHead = newTimer; else { srchTimer = timerHead; while ( srchTimer->next ) //如果已經有頭節點了,則在連結串列的尾部加入新的定時器物件 srchTimer = srchTimer->next; srchTimer->next = newTimer; } ... } ///////////////////// //再回到定時器的計時更新函式裡: void osalTimerUpdate( uint16 updateTime ) { halIntState_t intState; osalTimerRec_t *srchTimer; osalTimerRec_t *prevTimer; ... osal_systemClock += updateTime; //更新系統的計時時間 ... if ( timerHead != NULL ) //判斷定時器連結串列是否為空 { srchTimer = timerHead; prevTimer = (void *)NULL; while ( srchTimer ) //遍歷定時器連結串列裡的每個定時器物件,檢查是否已超時 { osalTimerRec_t *freeTimer = NULL; if (srchTimer->timeout <= updateTime) srchTimer->timeout = 0; else srchTimer->timeout = srchTimer->timeout - updateTime; if ( (srchTimer->timeout == 0) && (srchTimer->reloadTimeout) && (srchTimer->event_flag) ) //重複性的定時器發生超時的操作 { //下面這語句其實就是"tasksEvents[task_id] |= event_flag" osal_set_event( srchTimer->task_id, srchTimer->event_flag ); srchTimer->timeout = srchTimer->reloadTimeout;//重新設定要計時的初始值 } if ( srchTimer->timeout == 0 || srchTimer->event_flag == 0 ) //定時器超時,但沒有事件需要設定的操作 { if ( prevTimer == NULL ) timerHead = srchTimer->next; else prevTimer->next = srchTimer->next; freeTimer = srchTimer; srchTimer = srchTimer->next; } else { prevTimer = srchTimer; srchTimer = srchTimer->next; //移動到連結串列的下一個節點 } ... } }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
定時2秒控制led燈的實現:

MyApp.c

#include "OnBoard.h"
#include "MyApp.h"
#include "hal_led.h"

#define TIME_OUT_EVENT 0x4
#define TIME_LEN       2000

uint8  mytask_id; //用於存放本身的任務號


void MyApp_Init(uint8 task_id )
{
   mytask_id = task_id; 


   osal_start_reload_timer(task_id, TIME_OUT_EVENT, TIME_LEN);  
}



uint16 MyApp_ProcessEvent(uint8 task_id, uint16 events )
{
  if (events & TIME_OUT_EVENT)
  {
     HalLedSet(HAL_LED_ALL, HAL_LED_MODE_TOGGLE);
  }

  return 0;
}