OSAL概述
OSAL為Operating System Abstraction Layer,即操作系統抽象層,支持多任務運行,其中BLE協議棧、配置文件以及所有的應用程序(app)都在其上運行,它並不是一個傳統意義上的操作系統,但是實現了部分類似操作系統的功能,為了方便,下面簡稱OSAL系統。
1、OSAL簡要流程
初始化完成后,在app的init最后一般會啟動一個定時器或者直接set一個任務事件START_DEVICE_EVT,超時后進入app回調函數START_DEVICE_EVT事件分支處理,在該分支中啟動具體的信息采集、數據處理、數據顯示或發送等定時任務。
OSAL系統工作,判斷當前是否存在待處理的任務事件,有,則調用對應的回調函數進行處理,並在結束后重新設定定時器等待超時,若沒有任務事件,則進入休眠狀態,等待喚醒。
2、OSAL的main函數
任何一個程序都由main函數起,單片機程序中,一般順序為:
時鍾初始化——>相應外設初始化——>系統關鍵參數初始化——>進入死循環處理;
在OSAL的main函數中,同樣也是這些流程:
1 2 3 /************************************************************************************************** 4 5 * @fn main 6 7 * 8 9 * @brief Start of application. 10 11 * 12 13 * @param none 14 15 * 16 17 * @return none 18 19 ************************************************************************************************** 20 21 */ 22 23 int main(void) 24 25 { 26 27 /* Initialize hardware */ 28 HAL_BOARD_INIT(); 29 30 // Initialize board I/O 31 InitBoard( OB_COLD ); 32 33 /* Initialze the HAL driver */ 34 HalDriverInit(); 35 36 /* Initialize NV system */ 37 osal_snv_init(); 38 39 /* Initialize LL */ 40 41 /* Initialize the operating system */ 42 osal_init_system(); 43 44 /* Enable interrupts */ 45 HAL_ENABLE_INTERRUPTS(); 46 47 // Final board initialization 48 InitBoard( OB_READY ); 49 50 #if defined ( POWER_SAVING ) 51 52 osal_pwrmgr_device( PWRMGR_BATTERY ); 53 54 #endif 55 56 /* Start OSAL */ 57 osal_start_system(); // No Return from here 58 59 return 0; 60 61 } 62 63 64 65
解析:
HAL_BOARD_INIT(); //初始化晶振及預讀取程序
InitBoard( OB_COLD ); //在main函數中有兩處InitBoard();此為第一次初始化,此時OSAL 還未啟動,初始化IO及一些系統級寄存器,類似電腦上的BIOS 為系統的啟動做准備,個人認識,歡迎指正。
HalDriverInit(); //此處進行硬件初始化,定時器、AD轉換、LCD、LED、KEY等
osal_snv_init(); //初始化存儲區域
osal_init_system(); //初始化OSAL,此處系統初始化,內存初始化、消息隊列初始 化、定時器初始化、電源管理初始化及應用程序初始化均在該 函數中進行,在OSAL上編程,我們的任務app的初始化程序 在此處的應用程序初始化中進行,每一個應用程序在OSAL中 有且只有一個全局唯一標識,即本任務的taskID,在這里進行 分配,在之后的主循環中進行任務分配與事件處理時,使用的
即為這里分配的taskID
InitBoard( OB_READY ); //第二次調用該函數初始化,此時系統已啟動,在此處可將硬件
回調函數注冊至系統~~
osal_pwrmgr_device( PWRMGR_BATTERY ); //如果定義了 POWER_SAVING,電源管理啟動
osal_start_system(); //進入系統主循環
以上,各種初始化與設置,然后進入loop,,,,,,,,,,
for( ; ; )
{
//loop
}
3、OSAL的主循環
在osal_start_system() 中調用函數osal_run_system():
1 /********************************************************************* 2 * @fn osal_run_system 3 * 4 * @brief 5 * 6 * This function will make one pass through the OSAL taskEvents table 7 * and call the task_event_processor() function for the first task that 8 * is found with at least one event pending. If there are no pending 9 * events (all tasks), this function puts the processor into Sleep. 10 * 11 * @param void 12 * 13 * @return none 14 */ 15 void osal_run_system( void ) 16 { 17 uint8 idx = 0; 18 19 #ifndef HAL_BOARD_CC2538 20 osalTimeUpdate(); 21 #endif 22 23 Hal_ProcessPoll(); 24 25 do { 26 if (tasksEvents[idx]) // Task is highest priority that is ready. 27 { 28 break; 29 } 30 } while (++idx < tasksCnt); 31 32 if (idx < tasksCnt) 33 { 34 uint16 events; 35 halIntState_t intState; 36 37 HAL_ENTER_CRITICAL_SECTION(intState); 38 events = tasksEvents[idx]; 39 tasksEvents[idx] = 0; // Clear the Events for this task. 40 HAL_EXIT_CRITICAL_SECTION(intState); 41 42 activeTaskID = idx; 43 events = (tasksArr[idx])( idx, events ); 44 activeTaskID = TASK_NO_TASK; 45 46 HAL_ENTER_CRITICAL_SECTION(intState); 47 tasksEvents[idx] |= events; // Add back unprocessed events to the current task. 48 HAL_EXIT_CRITICAL_SECTION(intState); 49 } 50 #if defined( POWER_SAVING ) 51 else // Complete pass through all task events with no activity? 52 { 53 osal_pwrmgr_powerconserve(); // Put the processor/system into sleep 54 } 55 #endif 56 57 /* Yield in case cooperative scheduling is being used. */ 58 #if defined (configUSE_PREEMPTION) && (configUSE_PREEMPTION == 0) 59 { 60 osal_task_yield(); 61 } 62 #endif 63 }
分析這個函數之前先搞明白幾個主要的變量,
uint16 *tasksEvents;
typedef unsigned short (*pTaskEventHandlerFn)( unsigned char task_id, unsigned short event );
const pTaskEventHandlerFn tasksArr[] =
{
//........
};
uint8 tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] );
(1) tasksCnt,即OSAL內任務個數;
(2) tasksEvents,從定義上看,short類型指針,值域0x0000 ~ 0xFFFF,事實上,每個task只允許存在16個事件類型,在使用宏定義事件類型時,采用bit map方式進行定義,如0x0001、0x0002、0x0004……這樣做可以使 存在多個數據同時需要處理時,在處理完某一事件后,返回主循環,判斷繼續處理下一事件。每一個任務都包含一個系統事件SYS_EVENT_MSG,因此每個任務最多可以有15個自有事件,其在init時初始化為0;
(3) tasksArr[],函數指針數組,該指針數組元素為函數指針,具體為各模塊事件回調函數,編寫完本模塊事件回調函數之后,按照本模塊ID,將回調注冊至此處,在主循環中准備調用。
在本函數中,do{...}while();循環判斷是否存在待處理的任務事件,即tasksEvents[idx]是否為空,若存在則break,並進入后續if語句,先保存當前事件,並清空事件標志,調用(tasksArr[idx])( idx, events );處理任務事件並返回,此時event使用位圖方法定義的好處就體現了一點,一般在回調函數返回時,會return event ^ currntEVENT; 異或操作只處理當前位,表明當前事件處理完畢,若其他位不為0,則表示該任務還有其他事件待處理;回調函數返回后,保存事件標志位,進入下一次輪詢。