TI藍牙BLE 協議棧代碼學習——OSAL(下)

接下來我們再看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語言的基本不夠瓷實,說得也很大白話。之所以敢這麼大膽貼出來,也是請大家多批評指正,讓我能得到提高。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章