Zigbee協議棧內核分析 - 按鍵分析
Jesse
協議棧按鍵代碼分析 - 思維導圖(放大可看)
一、綜述
上圖從協議棧的 main() 函數開始分析,羅列出了 main() 函數裏調用的函數。接下來我們將會對函數一個一個的分析,有關於 key 的函數我將會用黃色方框表示。
二、協議棧代碼分析(按照上圖逐步分析)
******************************************************** 輪詢 ********************************************************
1、osal_int_disable( INTS_ALL ); //關閉總中斷
2、HalDriverInit();
調用 HalKeyInit(); //按鍵初始化函數
HalKeyInit()
{
......
/*Initialize callback function */
pHalKeyProcessFunction = NULL; //初始化按鍵回調函數爲NULL
/*Start with key is not configured */
HalKeyConfigured = FALSE;
}
3、osal_init_system(); //系統初始化函數
osal_init_system(); 裏調用 osalInitTasks(); 初始化任務;
osalInitTasks()
{
......
SampleApp_Init( taskID ); //用戶在 應用層 自定義的任務初始化。
}
SampleApp_Init( taskID )
{
......
RegisterForKeys( SampleApp_TaskID );
......
}
在自定義的任務初始化裏面調用了 RegisterForKeys(SampleApp_TaskID ); ,裏面的 registeredKeysTaskID = task_id; ,這個很關鍵,它將用戶自定義的 SampleApp_Task 任務註冊爲按鍵任務。
4、osal_int_enable( INTS_ALL ); //使能了總中斷
5、InitBoard( OB_READY );
調用了 HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE,OnBoard_KeyCallback); 來配置按鍵。
HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE,OnBoard_KeyCallback)
{
... ...
(void)osal_set_event(Hal_TaskID, HAL_KEY_EVENT); //用 tasksEvents[task_id] |= event_flag; 來通知Hal_Task任務有消息,使Hal_Task任務進入就緒態
... ...
pHalKeyProcessFunction = cback; //將 OnBoard_KeyCallback 函數賦值給按鍵回調函數指針
}
6、最後就是進入我們的 osal_start_system(); 操作系統,無限循環啦。
首先是先執行:
do{
if (tasksEvents[idx]) // Task is highest priority that is ready.
{
break;
}
}while (++idx < tasksCnt);
來查詢是否有任務進入了就緒狀態。我們回去看看第 5 步,在第 5 步的時候有調用到 osal_set_event(Hal_TaskID,HAL_KEY_EVENT); 設置了一個消息給 Hal_Task 任務使Hal_Task任務進入了就緒態。
所以接下來協議棧要執行:
if(idx < tasksCnt) //如果任務是有登記的
{
uint16 events;
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState);
events = tasksEvents[idx];
tasksEvents[idx] = 0; // Clearthe 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);
}
events = (tasksArr[idx])( idx, events ); 這一句調用了Hal_Task的處理函數。讓我們進入 tasksArr[idx] 看看,有了,Hal_Task任務對應的Hal_ProcessEvent 硬件抽象層的事件處理函數,我們進去看看。
uint16 Hal_ProcessEvent( uint8 task_id,uint16 events )
{
... ...
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
/* Check for keys */
HalKeyPoll();
/* if interrupt disabled, do next polling */
if (!Hal_KeyIntEnable)
{
osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
}
#endif // HAL_KEY
... ...
}
我們先看一下 HalKeyPoll();函數:
HalKeyPoll()
{
......
if(newKeys && pHalKeyProcessFunction)
{
(pHalKeyProcessFunction)(newKeys, HAL_KEY_STATE_NORMAL); //調用了按鍵處理回調函數
}
......
}
按鍵輪詢函數裏調用了 (pHalKeyProcessFunction)(newKeys,HAL_KEY_STATE_NORMAL); 按鍵處理回調函數來處理按鍵事件,那麼這個函數指針到底指向哪呢?讓我們回去看看上面,有兩個地方出現了回調函數指針的賦值。
(1)在第 2 步的時候 pHalKeyProcessFunction = NULL; 初始化按鍵回調函數指針爲NULL;
(2)在第 5 步的時候調用了 HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback); 函數將 OnBoard_KeyCallback函數賦值給按鍵回調函數指針。
所以現在我們就去找 OnBoard_KeyCallback()看看它是怎麼處理按鍵事件的吧。
OnBoard_KeyCallback()
{
... ...
if ( OnBoard_SendKeys( keys, shift ) != ZSuccess )
... ...
}
OnBoard_SendKeys() 函數將發送一個消息給 registeredKeysTask 任務。
OnBoard_SendKeys()
{
......
if( registeredKeysTaskID != NO_TASK_ID )
{
// Send the address to the task
msgPtr = (keyChange_t *)osal_msg_allocate( sizeof(keyChange_t) );
if ( msgPtr )
{
msgPtr->hdr.event = KEY_CHANGE; //將消息打包
msgPtr->state = state;
msgPtr->keys = keys;
//將消息發送給按鍵任務
osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );
}
return ( ZSuccess );
}
......
}
看上面的代碼,協議棧首先將消息打包,然後通過 osal_msg_send() 函數將消息發送給 registeredKeysTask 任務(也就是用戶自定義的SampleApp_Task任務)。
我們進去 osal_msg_send()函數 裏看看它是怎麼發送消息的吧。
osal_msg_send()
{
......
//queue message
osal_msg_enqueue(&osal_qHead, msg_ptr );
//Signal the task that a message is waiting
osal_set_event( destination_task, SYS_EVENT_MSG );
......
}
看看上面的代碼,我把最重要的兩句列了出來:
首先是osal_msg_enqueue( &osal_qHead, msg_ptr ); ,將消息放入消息隊列中;
最後果然還是要用到我們的 osal_set_event( destination_task, SYS_EVENT_MSG ); 函數來通知任務有消息發過來了!那這次是通知那個任務呢,讓我們回去看看destination_task指的是什麼任務吧。原來是第 6 步的按鍵回調函數裏一直往裏面看可以看到osal_msg_send(registeredKeysTaskID, (uint8 *)msgPtr );,所以通知的是registeredKeysTask任務,也就是用戶自定義的SampleApp_Task任務。
然後 osal_run_system()又進入
do{
if (tasksEvents[idx]) // Task ishighest priority that is ready.
{
break;
}
}while (++idx < tasksCnt);
此時,託osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr);的福,我們的SampleApp_Task終於進入了就緒態。
那麼接下來的 events = (tasksArr[idx])( idx, events );,我們進入的是我們用戶自定義的 SampleApp_ProcessEvent 處理函數。
SampleApp_ProcessEvent()
{
if ( events & SYS_EVENT_MSG )
{
MSGpkt = (afIncomingMSGPacket_t*)osal_msg_receive( SampleApp_TaskID );
while ( MSGpkt )
{
switch ( MSGpkt->hdr.event )
{
// Received when a key is pressed
case KEY_CHANGE:
SampleApp_HandleKeys( ((keyChange_t*)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );
break;
… …
}
然後就進入了SampleApp_HandleKeys按鍵處理函數了!
就這樣,按鍵從協議棧系統一開始,到執行了一次用戶自定義的按鍵處理函數的過程就全部結束啦!
然後是不是按鍵就這樣結束了呢,那怎麼叫輪詢呢?!!!!
讓我們看回到第 6 步的 Hal_ProcessEvent處理函數裏面吧,它除了調用了按鍵輪詢函數外,還調用了osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);,這個函數的作用是 100ms 後再次讓Hal_TaskID任務進入準備態,此時就會無限循環的重複第 6 步,從而達到輪詢的功能!
******************************************************** 中斷 ********************************************************
void HalKeyConfig(bool interruptEnable, halKeyCBack_t cback)
{
if ((Hal_KeyIntEnable = interruptEnable))
{
HAL_KEY_CLR_INT(); // Clear spurious ints.
PICTL |= 0x01; // P1ICONL: Falling edge ints on pins 0-3.
P1IEN |= PUSH1_BV | PUSH2_BV; // Enable specific P1 bits for ints by bit mask.
IEN2 |= 0x10; // Enable general P1 interrupts.
}
else
{
(void)osal_set_event(Hal_TaskID, HAL_KEY_EVENT);
}
pHalKeyProcessFunction = cback;
}
如果參數爲 HAL_KEY_INTERRUPT_ENABLE,將執行 if 裏面的語句初始化按鍵中斷模式。也就是沒有執行 osal_set_event(Hal_TaskID, HAL_KEY_EVENT); 了,也就不會進行按鍵輪詢了,那麼當我們按下按鍵的時候,協議棧又是怎麼進行終端處理的呢?HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR )
{
HAL_ENTER_ISR();
if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT)
{
halProcessKeyInterrupt();
}
/*
Clear the CPU interrupt flag for Port_0
PxIFG has to be cleared before PxIF
*/
HAL_KEY_SW_6_PXIFG = 0;
HAL_KEY_CPU_PORT_0_IF = 0;
CLEAR_SLEEP_MODE();
HAL_EXIT_ISR();
}
當有按鍵按下的時候,則會進入這個外部中斷服務函數,調用 halProcessKeyInterrupt(); 來處理按鍵事件。void halProcessKeyInterrupt(void)
{
... ...
if (valid)
{
osal_start_timerEx(Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);
}
... ...
}
在這個按鍵中斷處理函數裏面我們又見到了非常熟悉的 osal_start_timerEx() 函數了,這個函數在這起什麼作用呢?