09 在ZStack里的定时器应用

时间:2022-12-12 23:30:46

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;
}