ZIGBEE-通過協議棧實現按鍵功能
- 協議棧已經自帶了按鍵的驅動和使用函式。
- 網蜂 ZigBee 開發套件按鍵 S1 連線的是 P0.0 口,按鍵 S2 連線的是 P0.1 口。
- 在 ZMain.c 的 main 主函式中跟按鍵相關的有: HalDriverInit(); InitBoard( OB_READY );
- 按鍵流程分析
A.非中斷情況下:
第一步:進入 HalDriverInit()中的 HalKeyInit()
void HalKeyInit( void ) { /* Initialize previous key to 0 */ halKeySavedKeys = 0; /*這裡主要是初始化按鍵相關的引腳,HAL_KEY_SW_6 就是對應 P01*/ HAL_KEY_SW_6_SEL &= ~(HAL_KEY_SW_6_BIT); /* Set pin function to GPIO */ HAL_KEY_SW_6_DIR &= ~(HAL_KEY_SW_6_BIT); /* Set pin direction to Input */ /*而HAL_KEY_JOY 就是 TI 板子上的 J-STICK 搖桿,這裡我們不用用到,直接註釋或刪掉。*/ //HAL_KEY_JOY_MOVE_SEL &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin function to GPIO */ //HAL_KEY_JOY_MOVE_DIR &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin direction to Input*/ /* Initialize callback function */ pHalKeyProcessFunction = NULL; /* Start with key is not configured */ HalKeyConfigured = FALSE; }
第二步:分析 InitBoard( OB_READY )
void InitBoard( uint8 level ) { if ( level == OB_COLD ) { // IAR does not zero-out this byte below the XSTACK. *(uint8 *)0x0 = 0; // Interrupts off osal_int_disable( INTS_ALL ); // Check for Brown-Out reset ChkReset(); } else // !OB_COLD { /* Initialize Key stuff */ /*對按鍵的具體配置(重點):配置按鍵的檢測方式和按鍵的回撥函式 */ HalKeyConfig(HAL_KEY_INTERRUPT_ENABLE, OnBoard_KeyCallback); //HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback); } }
第三步:進入按鍵的檢測方式和按鍵的回撥函式 HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback);
void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback) { /* Enable/Disable Interrupt or */ Hal_KeyIntEnable = interruptEnable; /* Register the callback fucntion */ /* 註冊回撥函式---重點 */ pHalKeyProcessFunction = cback; /* Determine if interrupt is enable or not */ if (Hal_KeyIntEnable) { /* Rising/Falling edge configuratinn */ PICTL &= ~(HAL_KEY_SW_6_EDGEBIT); /* Clear the edge bit */ /* For falling edge, the bit must be set. */ #if (HAL_KEY_SW_6_EDGE == HAL_KEY_FALLING_EDGE) PICTL |= HAL_KEY_SW_6_EDGEBIT; #endif /* Interrupt configuration: * - Enable interrupt generation at the port * - Enable CPU interrupt * - Clear any pending interrupt */ HAL_KEY_SW_6_ICTL |= HAL_KEY_SW_6_ICTLBIT; HAL_KEY_SW_6_IEN |= HAL_KEY_SW_6_IENBIT; HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT); /* Rising/Falling edge configuratinn */ HAL_KEY_JOY_MOVE_ICTL &= ~(HAL_KEY_JOY_MOVE_EDGEBIT); /* Clear the edge bit */ /* For falling edge, the bit must be set. */ #if (HAL_KEY_JOY_MOVE_EDGE == HAL_KEY_FALLING_EDGE) HAL_KEY_JOY_MOVE_ICTL |= HAL_KEY_JOY_MOVE_EDGEBIT; #endif /* Interrupt configuration: * - Enable interrupt generation at the port * - Enable CPU interrupt * - Clear any pending interrupt */ HAL_KEY_JOY_MOVE_ICTL |= HAL_KEY_JOY_MOVE_ICTLBIT; HAL_KEY_JOY_MOVE_IEN |= HAL_KEY_JOY_MOVE_IENBIT; HAL_KEY_JOY_MOVE_PXIFG = ~(HAL_KEY_JOY_MOVE_BIT); /* Do this only after the hal_key is configured - to work with sleep stuff */ if (HalKeyConfigured == TRUE) { osal_stop_timerEx(Hal_TaskID, HAL_KEY_EVENT); /* Cancel polling if active */ } } else /* Interrupts NOT enabled */ { HAL_KEY_SW_6_ICTL &= ~(HAL_KEY_SW_6_ICTLBIT); /* don't generate interrupt */ HAL_KEY_SW_6_IEN &= ~(HAL_KEY_SW_6_IENBIT); /* Clear interrupt enable bit */ /********* 啟動 HAL_KEY_EVENT 事件 ***************/ osal_set_event(Hal_TaskID, HAL_KEY_EVENT); } /*從上面的配置我們知道按鍵檢測有兩種方式,一種為中斷,一種為定時檢測; 定 時 檢 測 的 話 在 配 置 時 就 會 直 接 啟 動 HAL_KEY_EVENT 事 件 , 那 麼 在 HAL_KEY_EVENT 中會做什麼事情呢?找到此事件的處理函式。 hal_drivers.c 中的 Hal_ProcessEvent()函式: */ /* Key now is configured */ HalKeyConfigured = TRUE; }
第四步:啟 動 HAL_KEY_EVENT 事 件 後,進入hal_drivers.c 中的 Hal_ProcessEvent()函式
/***************檢測按鍵*********************************/
if (events & HAL_KEY_EVENT)
{
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
/* Check for keys */
HalKeyPoll(); //檢測按鍵——重點
/* if interrupt disabled, do next polling */
//如果不是中斷方式的話就定時啟動此事件檢測按鍵 ,啟用定時器,定時啟動HAL_KEY_EVent事件。
if (!Hal_KeyIntEnable)
{
osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
}
#endif // HAL_KEY
return events ^ HAL_KEY_EVENT;
}
第五步:在上一步中定時進入HalKeyPoll()檢測按鍵。
void HalKeyPoll (void)
{
uint8 keys = 0; //檢測按鍵 S2 是否按下
// if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT)) /* Key is active HIGH */
// {
// keys = halGetJoyKeyInput();
// }
/*************************按鍵的檢測 **********************/
if (HAL_PUSH_BUTTON1()) //這裡需要改成低電平按下
{
keys |= HAL_KEY_SW_6;
}
/* If interrupts are not enabled, previous key status and current key status
* are compared to find out if a key has changed status.
當按鍵按下時就傳遞給上面註冊過的回撥函式 OnBoard_KeyCallback,傳遞進去的就是
相應的鍵值keys ;找到之前註冊的回撥函式OnBoard_KeyCallback(在 OnBoard.c 中)
*/
if (!Hal_KeyIntEnable)
{
//按鍵延時,防止按鍵按下發送多次按鍵事件
if (keys == halKeySavedKeys)
{
/* Exit - since no keys have changed */
return;
}
/* Store the current keys for comparation next time */
halKeySavedKeys = keys;
}
else
{
/* Key interrupt handled here */
}
/* Invoke Callback if new keys were depressed */
/* 呼叫註冊的回撥函式,即上面的 OnBoard_KeyCallback */
if (keys && (pHalKeyProcessFunction))
{
(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
}
}
第六步:當按鍵按下時就傳遞給上面註冊過的回撥函式 OnBoard_KeyCallback,傳遞 進 去 的 就 是 相 應 的 鍵 值 keys ; 找 到 之 前 注 冊 的 回 調 函 數OnBoard_KeyCallback(在 OnBoard.c 中) 。
void OnBoard_KeyCallback ( uint8 keys, uint8 state )// 回 調 函 數
{
uint8 shift;
(void)state;
shift = (keys & HAL_KEY_SW_6) ? true : false;
if ( OnBoard_SendKeys( keys, shift ) != ZSuccess )
{
// Process SW1 here
if ( keys & HAL_KEY_SW_1 ) // Switch 1
{
}
// Process SW2 here
if ( keys & HAL_KEY_SW_2 ) // Switch 2
{
//這裡就是通過 OnBoard_SendKeys( keys, shift )在傳送系統資訊給使用者的應用任務。
}
// Process SW3 here
if ( keys & HAL_KEY_SW_3 ) // Switch 3
{
}
// Process SW4 here
if ( keys & HAL_KEY_SW_4 ) // Switch 4
{
}
// Process SW5 here
if ( keys & HAL_KEY_SW_5 ) // Switch 5
{
}
// Process SW6 here
if ( keys & HAL_KEY_SW_6 ) // Switch 6
{
}
}
}
第七步:通過 OnBoard_SendKeys( keys, shift )傳送系統資訊給使用者的應用任務。
uint8 OnBoard_SendKeys( uint8 keys, uint8 state )
{
keyChange_t *msgPtr;
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 );
/*registeredKeysTaskID 任務 這個就 是 我 們 用 戶 根 據 自 己 的 需 要
選 擇 按 鍵 的 傳 遞 的 任 務 號 , 可 通 過RegisterForKeys( xxx_TaskID )
註冊,此工程中在 SampleApp_Init()中已經呼叫此函式註冊到 SampleApp_TaskID
中了,就是說按鍵資訊會傳遞到此任務中。 */
}
return ( ZSuccess );
}
else
return ( ZFailure );
}
第八步:傳遞到SampleApp_ProcessEvent( uint8 task_id, uint16 events )中的 SampleApp_HandleKeys()按鍵事件及處理函式.
uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )
{
afIncomingMSGPacket_t *MSGpkt;
(void)task_id; // Intentionally unreferenced parameter
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;
// Received when a messages is received (OTA) for this endpoint
case AF_INCOMING_MSG_CMD:
SampleApp_MessageMSGCB( MSGpkt );
break;
第九步:在SampleApp_HandleKeys()按鍵事件及處理函式中 根據具體的鍵值做相應的處理,這裡利用串列埠列印提示按鍵按下。
void SampleApp_HandleKeys( uint8 shift, uint8 keys )
{
(void)shift; // Intentionally unreferenced parameter
if ( keys & HAL_KEY_SW_6 )
{
/* This key sends the Flash Command is sent to Group 1.此鍵傳送Flash命令傳送到組1
* This device will not receive the Flash Command from this
* device (even if it belongs to group 1).
* 此裝置將不接收來自該裝置的快閃記憶體命令(即使它屬於第1組)
*/
/*按鍵事件:串列埠列印*/
HalUARTWrite(0,"KEY S2\n",7);
// SampleApp_SendFlashMessage( SAMPLEAPP_FLASH_DURATION );
}
if ( keys & HAL_KEY_SW_2 )
{
/* The Flashr Command is sent to Group 1.
* This key toggles this device in and out of group 1.
* If this device doesn't belong to group 1, this application
* will not receive the Flash command sent to group 1.
*FLASR命令傳送到第1組。
*此鍵可將該裝置切換到第1組。
*如果此裝置不屬於第1組,則此應用程式
*不會收到傳送到組1的Flash命令。
*/
HalUARTWrite(0,"KEY S3\n",7);
}
}
B.中斷方式
在 HalKeyConfig(HAL_KEY_INTERRUPT_ENABLE, OnBoard_KeyCallback);中設定成中斷檢測方式。設定成中斷檢測方式就不會定時啟動 HAL_KEY_EVENT 事件,這樣就會更加節省系統資源,所以一般都是使用中斷方式來檢測按鍵。當按下按鍵時會進入 P0 口中斷服務函式 hal_key.c中的HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR ) P0 口中斷服務函式 。
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)
{
bool valid=FALSE;
if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT) /* Interrupt Flag has been set */
{
HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT); /* Clear Interrupt Flag */
valid = TRUE;
}
//
// if (HAL_KEY_JOY_MOVE_PXIFG & HAL_KEY_JOY_MOVE_BIT) /* Interrupt Flag has been set */
// {
// HAL_KEY_JOY_MOVE_PXIFG = ~(HAL_KEY_JOY_MOVE_BIT); /* Clear Interrupt Flag */
// valid = TRUE;
// }
if (valid)
{
osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);
}
}
這裡就又跑去啟動 HAL_KEY_EVENT 事件了。