接下來我們再看main()函數中另一個跟OSAL相關的函數——osal_start_system(),也位於OSAL.c中。
void osal_start_system( void )
{
#if !defined ( ZBIT ) && !defined ( UBIT )
for(;;) // Forever Loop
#endif
{
osal_run_system();
}
}
一看這是個死循環,相當於單片機程序最後一行while(1);。這個函數最主要的部分還是osal_run_system(),找到它,也在OSAL.c中。
void osal_run_system( void )
{
uint8 idx = 0;
#ifndef HAL_BOARD_CC2538
osalTimeUpdate();
#endif
Hal_ProcessPoll();
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);
events = tasksEvents[idx];
tasksEvents[idx] = 0; // Clear the Events for this task.
HAL_EXIT_CRITICAL_SECTION(intState);
activeTaskID = idx;
events = (tasksArr[idx])( idx, events );
activeTaskID = TASK_NO_TASK;
HAL_ENTER_CRITICAL_SECTION(intState);
tasksEvents[idx] |= events; // Add back unprocessed events to the current task.
HAL_EXIT_CRITICAL_SECTION(intState);
}
#if defined( POWER_SAVING )
else // Complete pass through all task events with no activity?
{
osal_pwrmgr_powerconserve(); // Put the processor/system into sleep
}
#endif
/* Yield in case cooperative scheduling is being used. */
#if defined (configUSE_PREEMPTION) && (configUSE_PREEMPTION == 0)
{
osal_task_yield();
}
#endif
}
去掉條件編譯部分,最核心的是一個do-while循環,一個if判斷。
do-while循環:
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);
events = tasksEvents[idx];
tasksEvents[idx] = 0; // Clear the Events for this task.
HAL_EXIT_CRITICAL_SECTION(intState);
activeTaskID = idx;
events = (tasksArr[idx])( idx, events );
activeTaskID = TASK_NO_TASK;
HAL_ENTER_CRITICAL_SECTION(intState);
tasksEvents[idx] |= events; // Add back unprocessed events to the current task.
HAL_EXIT_CRITICAL_SECTION(intState);
}
這部分代碼應該是OSAL最核心最精髓的部分了。前面的循環中已經確定有事件發生了。HAL_ENTER_CRITICAL_SECTION(intState);和HAL_EXIT_CRITICAL_SECTION(intState);分別是關中斷和使能中斷,以防止在執行代碼時被中斷打斷。將事件表tasksEvents[]中的事件賦給events,然後該事件清零。接下來events = (tasksArr[idx])( idx, events );就是去處理事件了,這裏的tasksArr[]數組非常重要。下面的tasksEvents[idx] |= events;就是把沒有響應的事件再放回到tasksEvents[]中。
我們來看看這個非常重要的數組taskArr[],它被定義在OSAL_KeyFobDemo.c中。
// The order in this table must be identical to the task initialization calls below in osalInitTask.
const pTaskEventHandlerFn tasksArr[] =
{
LL_ProcessEvent, // task 0
Hal_ProcessEvent, // task 1
HCI_ProcessEvent, // task 2
#if defined ( OSAL_CBTIMER_NUM_TASKS )
OSAL_CBTIMER_PROCESS_EVENT( osal_CbTimerProcessEvent ), // task 3
#endif
L2CAP_ProcessEvent, // task 4
GAP_ProcessEvent, // task 5
GATT_ProcessEvent, // task 6
SM_ProcessEvent, // task 7
GAPRole_ProcessEvent, // task 8
GAPBondMgr_ProcessEvent, // task 9
GATTServApp_ProcessEvent, // task 10
KeyFobApp_ProcessEvent // task 11
};
數組內的成員看起來很面熟。最上面一行的註釋也寫得很清楚,表中的順序要和osalInitTask()中定義的一致。再來看這個數組的類型,是pTaskEventHandlerFn,這是個什麼東西?pTaskEventHandlerFn不是東西,是
typedef unsigned short (*pTaskEventHandlerFn)( unsigned char task_id, unsigned short event );
這個定義是一個函數指針,看起着很頭疼,很蛋疼。如果換一下:
typedef pTaskEventHandlerFn unsigned short (*)( unsigned char task_id, unsigned short event );
這樣或許理解起來要好一些。拿KeyFobApp_ProcessEvent的聲明來看,uint16 KeyFobApp_ProcessEvent( uint8 task_id, uint16 events ),這是符合pTaskEventHandlerFn的格式的,函數名就是指針,函數的地址。
tasksArr[]是一個函數指針數組,裏面保存了所有事件處理函數的地址。當有事件發生時,就執行events = (tasksArr[idx])( idx, events );一句,就是對應的tasksArr[]裏相應的那個事件的處理函數。
再看另一個數組,tasksEvents[]。tasksEvents[]聲明爲全局變量,是在osalInitTasks()中定義和初始化的:
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
這個數組的大小跟事件的數量是一致的,而且被osal_memset()初始化爲0.
這樣OSAL的運行機理基本清晰了,就是在do-while()循環中判斷tasksEvents[]中哪個事件非0,即事件被觸發了;然後在if中把該事件清0,執行tasksArr[]中定義好的事件處理函數,然後把沒有執行的事件再放回到tasksEvents[]中。這也是爲什麼在osalInitTask()中進行初始化的時候,初始化的順序要和tasksArr[]一致。
以上是我對OSAL的理解,因爲C語言的基本不夠瓷實,說得也很大白話。之所以敢這麼大膽貼出來,也是請大家多批評指正,讓我能得到提高。