OSAL工作機制分析
協議棧代碼main()函數分析
ZMain文件->ZMain.c->main() 在這裏我們重點了解osal_start_system()函數
int main( void )
{
//關閉所有終端
osal_int_disable( INTS_ALL );
//硬件板子的初始化,比如led的初始化
HAL_BOARD_INIT();
//監測電壓,確保電壓能使CC2530運行
zmain_vdd_check();
// 初始化板子的I/O
InitBoard( OB_COLD );
// 初始化硬件驅動
HalDriverInit();
// 初始化NV系統
osal_nv_init( NULL );
// MAC的初始化
ZMacInit();
// 擴展地址的初始化
zmain_ext_addr();
// 初始化NV條目
zgInit();
#ifndef NONWK
//AF層的初始化,在禁止NONWK的時候需要初始化AF層
afInit();
#endif
//初始化操作系統
//維護事件表的函數在這個函數裏邊-》osalInitTasks();
osal_init_system();
// 開中斷
osal_int_enable( INTS_ALL );
// 板級終初始化
InitBoard( OB_READY );
// 設備信息顯示
zmain_dev_info();
//如果定義了LCD,那麽執行LCD初始化
#ifdef LCD_SUPPORTED
zmain_lcd_init();
#endif
#ifdef WDT_IN_PM1
//如果定義了看門狗,那麽執行使能看門狗函數
WatchDogEnable( WDTIMX );
#endif
//系統執行的入口,註意正常情況是不會運行到此函數的下一句,
//也就是return語句的,因為進入此函數以後會一直在裏面循環執行任務,並不會跳出次循環。
osal_start_system();
return 0;
} // main()
分析osal_start_system()函數,通過go to的方式,跳轉到函數中去
void osal_start_system( void )
{
#if !defined ( ZBIT ) && !defined ( UBIT )
for(;;) // Forever Loop //進入for死循環,相當於while(1);
#endif
{
osal_run_system(); //到這裏進入我們osal系統,
}
}
OSAL的運行機制
ZigBee 協議棧僅僅是 ZigBee 協議的具體實現。OSAL 就是一種支持多任務運行的系統資源分配機制。在 ZigBee 協議棧中,OSAL 負責調度各個任務的運行,如果有事件發生了,則會調用相應的事件處理函數進行處理。
那麽,事件和任務的事件處理函數是如何聯系起來的呢?
ZigBee 中采用的方法是:建立一個事件表,保存各個任務的對應的事件,建立另一個函數表,保存各個任務的事件處理函數的地址,然後將這兩張表建立某種對應關系,當某一事件發生時則查找函數表找到對應的事件處理函數即可。
分析osal_run_system()函數,通過go to的方式,跳轉到函數中去,分析我們OSAL的運行機制。
void osal_run_system( void )
{
uint8 idx = 0;
osalTimeUpdate();
Hal_ProcessPoll();
//查詢任務事件表是否有事件發生,從高優先級的任務中開始查詢
// const uint8 tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] ); 初始化任務數,並且進行初始化
do {
if (tasksEvents[idx]) // Task is highest priority that is ready.
{
break;
}
} while (++idx < tasksCnt);
if (idx < tasksCnt)
{
uint16 events;
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState); /* 進入臨界區---保存EA狀態然後置EA = 0 */
//將發生任務的事件取出來放到events變量中,並將tasksEvents[idx]清零
// uint16 *tasksEvents; 指向事件表的首地址,
//協議棧中的事件采用獨熱碼的方式,
// ZigBee 協議棧使用一個 unsigned short 型的變量,因為 unsigned short 類型占兩個字節,
// 即 16 個二進制位,因此,可以使用每個二進制位表示一個事件,我們來看下協議棧定義
// 的系統事件SYS_EVENT_MS G,十六進制:0x8000,二進制0b100000000000000 0。
//它用的就是高位來表示該事件:在系統初始化時,所有任務的事件初始化為 0,
events = tasksEvents[idx];
tasksEvents[idx] = 0; // Clear the Events for this task.
HAL_EXIT_CRITICAL_SECTION(intState); /* 退出臨界區---恢復EA狀態 */
activeTaskID = idx;
// 聲明函數指針
// typedef unsigned short (*pTaskEventHandlerFn)( unsigned char task_id, unsigned short event );
// 函數指針數組,指向任務事件處理函數
// const pTaskEventHandlerFn tasksArr[] = {}
// 在任務事件處理函數中通過eturn (events ^ SYS_EVENT_MSG);返回任務中未處理的事件
events = (tasksArr[idx])( idx, events );
activeTaskID = TASK_NO_TASK;
HAL_ENTER_CRITICAL_SECTION(intState); /* 進入臨界區---保存EA狀態然後置EA = 0 */
// 將未處理的任務中的事件重新寫回到事件表中
tasksEvents[idx] |= events; // Add back unprocessed events to the current task.
HAL_EXIT_CRITICAL_SECTION(intState); /* 退出臨界區---恢復EA狀態 */
}
#if defined( POWER_SAVING ) //進入低功耗模式
else // Complete pass through all task events with no activity?
{
osal_pwrmgr_powerconserve(); // Put the processor/system into sleep
}
#endif
}
事件表的分配空間:
查看main()函數中的系統初始化函數osal_init_system();——> 任務初始化函數osalInitTasks();
void osalInitTasks( void )
{
uint8 taskID = 0;
//給事件表分配空間通過osal_mem_alloc函數,
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
//使用osal_memset函數給事件表全部初始化為0
osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
macTaskInit( taskID++ );
nwk_init( taskID++ );
Hal_Init( taskID++ );
#if defined( MT_TASK )
MT_TaskInit( taskID++ );
#endif
APS_Init( taskID++ );
#if defined ( ZIGBEE_FRAGMENTATION )
APSF_Init( taskID++ );
#endif
ZDApp_Init( taskID++ );
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
ZDNwkMgr_Init( taskID++ );
#endif
SampleApp_Init( taskID ); //用戶自己添加的任務初始化函數
}
任務事件處理函數分配空間:
const pTaskEventHandlerFn tasksArr[] = {
macEventLoop,
nwk_event_loop,
Hal_ProcessEvent,
#if defined( MT_TASK )
MT_ProcessEvent,
#endif
APS_event_loop,
#if defined ( ZIGBEE_FRAGMENTATION )
APSF_ProcessEvent,
#endif
ZDApp_event_loop,
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
ZDNwkMgr_event_loop,
#endif
SampleApp_ProcessEvent //用戶自己的任務中的事件處理函數
};
註意:任務初始化函數中每個任務的初始化函數必須與任務事件處理數組中的任務事件處理函數一一對應。
在 ZigBee 協議棧中,有三個變量至關重要:
? tasksCnt—該變量保存了任務的總個數。
該變量的聲明為:uint8 tasksCnt,其中 uint8 的定義為:typedef unsigned char uint8
? tasksEvents—這是一個指針,指向了事件表的首地址。
該變量的聲明為:uint16 *tasksEvents,其中 uint16 的定義為:typedef unsigned short
uint16
? tasksArr—這是一個數組, 數組的每一項都是一個函數指針, 指向了事件處理函數
該數組的聲明為:const pTaskEventHandlerFn tasksArr[],其中 pTaskEventHandlerFn 的
定義為:typedef unsigned short (*pTaskEventHandlerFn)( unsigned char task_id, unsigned short
event ),這是定義了一個函數指針。tasksArr 數組的每一項都是一個函數指針,指向了事件
處理函數。
事件表 任務中的事件處理函數表
OSAL消息隊列
提到事件,我們就不得不提到消息。事件是驅動任務去執行某些操作的條件,當系統中產生了一個事件,OSAL 將這個事件傳遞給相應的任務後,任務才能執行一個相應的操作(調用事件處理函數去處理) 。
通常某些事件發生時,又伴隨著一些附加信息的產生,例如:從天線接收到數據後,會產生 AF_INCOMING _MSG_CMD 消息,但是任務的事件處理函數在處理這個事件的時候,還需要得到收到的數據。
因此,這就需要將事件和數據封裝成一個消息,將消息發送到消息隊列,然後在事件處理函數中就可以使用 osal_msg_receive 從消息隊列中得到該消息。afIncomingMSGPacket_t *MSGpkt; //根據消息類型聲明對應的接收消息的變量
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID ); //強制轉化成對應的消息類型
OSAL維護了一個消息隊列,每一個消息都會被放到這個消息隊列中去,當任務接收到時間後,可以從消息隊列中獲取屬於自己的消息,然後調用消息處理函數進行相應的處理即可。
OSAL中消息隊列如下圖所示:
每個消息都包含一個消息頭osal_mag_hdr_t和用戶自定義的消息,osal_msg_hdr_t結構體的定義為:
typedef struct
{
void *next;
uint16 len;
uint8 dest_id;
} osal_msg_hdr_t;
OSAL工作機制分析