-STACK按鍵的使用總結(jié)
#define HAL_KEY_SW_6_ENABLE // SW_6的IO端口 #define HAL_KEY_SW_6_PORT P0 //SW6接到IO端口的位數(shù) P0.1 #define HAL_KEY_SW_6_BIT HAL_KEY_BIT1 // SW_6的IO端口選擇 #define HAL_KEY_SW_6_SEL P0SEL // SW_6的IO端口方向 #define HAL_KEY_SW_6_DIR P0DIR // SW_6的IO端口中斷使能 #define HAL_KEY_SW_6_IEN IEN1 // SW_6的IO端口中斷使能的掩碼 #define HAL_KEY_SW_6_IENBIT HAL_KEY_BIT5 // SW_6的IO端口中斷的邊沿選擇 #define HAL_KEY_SW_6_EDGE HAL_KEY_RISING_EDGE // SW_6的IO端口邊沿掩碼 #define HAL_KEY_SW_6_EDGEBIT HAL_KEY_BIT0 // SW_6的IO端口總中斷 #define HAL_KEY_SW_6_ICTL PICTL // SW_6的IO端口總中斷掩碼 #define HAL_KEY_SW_6_ICTLBIT HAL_KEY_BIT3 // SW_6的IO端口中斷標(biāo)志位 #define HAL_KEY_SW_6_PXIFG P0IFG 按鍵主要使用的是IO來(lái)設(shè)置的,這里需要設(shè)置的參數(shù)主要有按鍵設(shè)置在哪個(gè)端口以及掩碼、按鍵中斷使能標(biāo)志以及掩碼、引起中斷的上升沿還是下降沿以及掩碼 涉及的主要寄存器有 PICTL端口輸入中斷控制
IEN1端口0總中斷使能
IEN2 端口1和2總中斷使能 比如需要設(shè)置HAL_KEY_SW_6為P0.4為輸入下降沿有效中斷設(shè)置如下: #define HAL_KEY_SW_6_ENABLE #define HAL_KEY_SW_6_PORT P0 #define HAL_KEY_SW_6_BIT HAL_KEY_BIT4 #define HAL_KEY_SW_6_SEL P0SEL #define HAL_KEY_SW_6_DIR P0DIR #define HAL_KEY_SW_6_IEN IEN1 #define HAL_KEY_SW_6_IENBIT HAL_KEY_BIT5 #define HAL_KEY_SW_6_EDGE HAL_KEY_FALLING_EDGE #define HAL_KEY_SW_6_EDGEBIT HAL_KEY_BIT0 #define HAL_KEY_SW_6_ICTL PICTL #define HAL_KEY_SW_6_ICTLBIT HAL_KEY_BIT4 #define HAL_KEY_SW_6_PXIFG P0IFG 比如需要設(shè)置HAL_KEY_SW_6為P2.1為輸入上升沿有效中斷設(shè)置如下: #define HAL_KEY_SW_6_ENABLE #define HAL_KEY_SW_6_PORT P2 #define HAL_KEY_SW_6_BIT HAL_KEY_BIT4 #define HAL_KEY_SW_6_SEL P2SEL #define HAL_KEY_SW_6_DIR P2DIR #define HAL_KEY_SW_6_IEN IEN2 #define HAL_KEY_SW_6_IENBIT HAL_KEY_BIT1 #define HAL_KEY_SW_6_EDGE HAL_KEY_RISING_EDGE #define HAL_KEY_SW_6_EDGEBIT HAL_KEY_BIT2 #define HAL_KEY_SW_6_ICTL PICTL #define HAL_KEY_SW_6_ICTLBIT HAL_KEY_BIT5 #define HAL_KEY_SW_6_PXIFG P2IFG 這樣設(shè)置后就可以正常使用KEY 中斷。 按鍵有兩種工作模式:輪詢(Poll)和中斷(Interrupt) 輪詢 按鍵處理函數(shù)是HalKeyPoll (void),這個(gè)函數(shù)會(huì)在HAL的事件處理Hal_ProcessEvent()中的HAL_KEY_EVENT事件處理過(guò)程中被調(diào)用,輪詢周期由#define HAL_KEY_POLLING_VALUE 100這邊定義,通過(guò)在配置函數(shù)中設(shè)置軟件定時(shí)器osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT,HAL_KEY_POLLING_VALUE);來(lái)啟動(dòng)定時(shí)器, 當(dāng)溢出的時(shí)候向HAL發(fā)出HAL_KEY_EVENT事件。 在事件處理函數(shù)中會(huì)檢查當(dāng)前是否使能中斷模式,如果使能的話就跳到中斷模式操作,否則重新啟動(dòng)定時(shí)器,輪詢時(shí)間按100ms算。 HalKeyPoll()函數(shù)中會(huì)調(diào)用按鍵回調(diào)函數(shù),根據(jù)按鍵狀態(tài)來(lái)處理按鍵信息。 中斷 對(duì)應(yīng)處理函數(shù)是 halProcessKeyInterrupt (void)會(huì)在對(duì)應(yīng)管腳的中斷處理函數(shù)中被調(diào)用。 根據(jù)配置函數(shù)的參數(shù)interruptEnable的情況來(lái)設(shè)置中斷或者輪詢。 中斷處理函數(shù)如下: 按鍵中斷處理函數(shù): 當(dāng)檢查到有按鍵按下則會(huì)啟動(dòng)一個(gè)定時(shí)事件,定時(shí)長(zhǎng)度有HAL_KEY_DEBOUNCE_VALUE定義。最終執(zhí)行函數(shù)還是在HAL的事件處理函數(shù)中的HAL_KEY_EVENT中被執(zhí)行。 區(qū)別 按鍵中斷和輪詢的主要區(qū)別是輪詢需要CPU定期的檢查按鍵狀態(tài),當(dāng)檢測(cè)到按鍵按下則發(fā)出HAL_KEY_EVENT消息到HAL層來(lái)執(zhí)行處理,中斷則是當(dāng)有按鍵按下立刻向HAL_KEY_EVENT發(fā)出消息,不需要CPU的檢查。輪詢的實(shí)時(shí)性比中斷要差點(diǎn),推薦用中斷方式。 處理流程圖 Z-stack中對(duì)按鍵的處理 在基于Z-stack的應(yīng)用程序設(shè)計(jì)中,HAL(硬件抽象層)是這樣運(yùn)行的: void osalInitTasks( void ) {…… Hal_Init( taskID++ ); …… } 打開(kāi) Hal_Init( ),似乎什么也沒(méi)有做,只是完成了一件事情,給這個(gè)任務(wù)一個(gè)ID,實(shí)際上,對(duì)硬件的初始化的工作,在任務(wù)啟動(dòng)之前都已經(jīng)開(kāi)始做了,任務(wù)的運(yùn)行,只是可以接收發(fā)給它的事件和消息。 那么,在HAL任務(wù)運(yùn)行之前,系統(tǒng)對(duì)任務(wù)做了些什么呢? 當(dāng)然是初始化。在ZMain.c文件中,有main函數(shù),這是所有程序的入口。由于硬件是所有程序運(yùn)行的基礎(chǔ),在這里要完成兩個(gè)重要的函數(shù): // Initialize board I/O InitBoard( OB_COLD ); // Initialze HAL drivers HalDriverInit(); 顯然,它們的執(zhí)行時(shí)間是早于任務(wù)的運(yùn)行,在InitBoard()完成對(duì)板級(jí)I/O的設(shè)置。進(jìn)去看一下: void InitBoard( byte level ) { if ( level == OB_COLD ) { // Interrupts off osal_int_disable( INTS_ALL ); } else // !OB_COLD { OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE; HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback); } } 代碼太多,不再全部列出,主要是完成對(duì)Led。Timer和key的配置。這里重點(diǎn)看按鍵有關(guān)的。由于InitBoard函數(shù)的參數(shù)是OB_COLD,郁悶的HalKeyConfig()沒(méi)有機(jī)會(huì)運(yùn)行。 在經(jīng)過(guò)耐心的等待之后,main()需要再次對(duì)開(kāi)發(fā)板初始化,調(diào)用函數(shù): InitBoard( OB_READY ); 機(jī)會(huì)來(lái)了,這時(shí)可以處理按鍵了, HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback)函數(shù)可以運(yùn)行,在該函數(shù)中定義了是以中斷的方式還是以查詢的方式檢測(cè)按鍵的狀態(tài),如果是查詢方式,使用: osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_POLLING_VALUE); 延遲100ms后向任務(wù)Hal_TaskID發(fā)送一個(gè)事件。在事件處理代碼中使用函數(shù) HalKeyPoll()查詢是否有按鍵按下。 如果是中斷方式,使用 osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE); 延遲25ms后使用函數(shù)HalKeyPoll()獲取按鍵值。
安裝IAR 8051 7.30B 運(yùn)行安裝程序EW8051-EV-730B.exe,這里說(shuō)一下如何快速的查找代碼,按下Ctrl+Shift+f 可以在整個(gè)項(xiàng)目中查找你想要的關(guān)鍵字,注意選擇和你workspace工作空間對(duì)應(yīng)的文件,通常有CC2430DB和CC2430EB兩個(gè)。把光標(biāo)放在函數(shù)名上,右鍵 選擇Go to definition fo XX就可以跳到該函數(shù)定義處,工具欄的Navigate Backward 和Navigate Forward 可以讓你來(lái)回穿梭,還有很多功能,這里不多說(shuō)了。 安裝ZigBee2006 下載Zigbee協(xié)議棧壓縮包swrc073d.zip,安裝后一般在C盤(pán)可以找到Texas Instruments文件夾,把它復(fù)制,考到D盤(pán),我的IAR裝在D盤(pán),有必要看下Documents里面的文檔,如Create New Application For The CC2430DB_F8W-2005-0033_.pdf如何新建項(xiàng)目;其它的就不多說(shuō)了,下面是按鍵的簡(jiǎn)單說(shuō)明,可以初步了解一下OSAL;例子目錄為: Texas Instruments\ZStack-1.4.3-1.2.1\Projects\zstack\Samples\SimpleApp\CC2430DB Workspace 選擇 simplecollectorEB ; 我們先從主函數(shù)說(shuō)起,如果不知主函數(shù)在哪,可以Ctrl+Shift+f輸入int main查找,...........為省略 ZSEG int main( void ) { // Turn off interrupts osal_int_disable( INTS_ALL ); ……………….. // Initialze HAL drivers HalDriverInit(); //HalKeyInit();初始化按鍵 ……………….. // Determine the extended address zmain_ext_addr(); //HalKeyRead();讀取按鍵 ………………….. osal_init_system(); //RegisterForKeys( sapi_TaskID ); 注冊(cè)按鍵任務(wù) //或許你的協(xié)議棧注冊(cè)按鍵不在這里面。那就是在應(yīng)用層任務(wù)初始化里面 …………………….. // Final board initialization InitBoard( OB_READY ); /*HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);配置 按鍵,默認(rèn)為輪詢方式*/ ………………… osal_start_system(); // No Return from here 進(jìn)入系統(tǒng)大循環(huán) } // main() 從主函數(shù)可以看出,里面都是初始化函數(shù)init,執(zhí)行過(guò)程HalDriverInit()àHalKeyInit();在HalKeyInit()里基本完成了相應(yīng)管腳的輸入輸出配置,然后到zmain_ext_addr();時(shí),判斷物理擴(kuò)展地址是否合法,如果不合法,則LED1一直閃爍,等while ( HAL_KEY_SW_5 != HalKeyRead() )按下把無(wú)效的地址初始化為有效地物理地址,然后到 osal_init_system();àosalInitTasks(); SAPI_Init( taskID ); RegisterForKeys( sapi_TaskID );注冊(cè)按鍵事件, //注意:可能你的協(xié)議棧注冊(cè)按鍵不在這里,而在應(yīng)用層任務(wù)初始化里面。你的協(xié)議?赡芤矝](méi)有SAPI_Init( taskID ); 最后InitBoard( OB_READY ); HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);配置按鍵為中斷方式還是輪詢方式,從 /* Initialize Key stuff */ OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE; HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback); 可以看出默認(rèn)是配置為輪詢方式的,這就是主函數(shù)大致對(duì)按鍵的處理過(guò)程,接下來(lái)從HalKeyConfig()入手, void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback) { #if (HAL_KEY == TRUE) /* Enable/Disable Interrupt or */ Hal_KeyIntEnable = interruptEnable; /* Register the callback fucntion */ pHalKeyProcessFunction = cback; //指向回調(diào)函數(shù) /* Determine if interrupt is enable or not */ if (Hal_KeyIntEnable) //如果設(shè)為中斷方式 { ………………..進(jìn)行一些中斷的相關(guān)配置 } else /* Interrupts NOT enabled */ //否則為輪詢方式 { ……………………. osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_POLLING_VALUE); /* Kick off polling */ } …………………….. } 可以看出,配置為輪詢方式是時(shí)啟動(dòng)osal_start_timerEx()函數(shù),那么這個(gè)函數(shù)是干什么的呢?這個(gè)是系統(tǒng)軟定時(shí)器,在HAL_KEY_POLLING_VALUE時(shí)間(100ms)內(nèi)會(huì)觸發(fā)系統(tǒng)任務(wù)事件,也就是觸發(fā)uint16 Hal_ProcessEvent( uint8 task_id, uint16 events );觸發(fā)時(shí)會(huì)把Hal_TaskID、HAL_KEY_EVENT兩個(gè)參數(shù)傳給Hal_ProcessEvent();然后看看Hal_ProcessEvent()里面又做了些什么事, uint16 Hal_ProcessEvent( uint8 task_id, uint16 events ) { uint8 *msgPtr; ………………………. if (events & HAL_KEY_EVENT) //按鍵處理 { #if (defined HAL_KEY) && (HAL_KEY == TRUE) /* Check for keys */ HalKeyPoll(); //查看是哪個(gè)鍵 /* if interrupt disabled, do next polling */ if (!Hal_KeyIntEnable) //如果還是輪詢方式,則再次啟動(dòng)osal_start_timerEx(); { osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100); } #endif // HAL_KEY return events ^ HAL_KEY_EVENT; } …………………….. } 函數(shù)里面執(zhí)行完HalKeyPoll();后,如果還是輪詢方式,則再一次啟動(dòng)osal_start_timerEx();如此一來(lái),就會(huì)每隔100ms循環(huán)進(jìn)入Hal_ProcessEvent()函數(shù)讀取按鍵,也就是說(shuō)系統(tǒng)每隔100ms掃描一次按鍵,那么HalKeyPoll()又是干什么的呢?我們繼續(xù)看看, void HalKeyPoll (void) { ……………… #if defined (HAL_KEY_SW_6_ENABLE) if (!(HAL_KEY_SW_6_PORT & HAL_KEY_SW_6_BIT)) /* Key is active low */ { keys |= HAL_KEY_SW_6; } #endif #if defined (HAL_KEY_SW_5_ENABLE) if (HAL_KEY_SW_5_PORT & HAL_KEY_SW_5_BIT) /* Key is active high */ { keys |= HAL_KEY_SW_5; } #endif ………….. 調(diào)用HalAdcRead()得出操縱桿的值,是通過(guò)AD進(jìn)來(lái)了模擬電壓值得出; /* Invoke Callback if new keys were depressed */ if (keys && (pHalKeyProcessFunction)) { (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL); //回調(diào)函數(shù) } } 該函數(shù)讀出按鍵值keys,并執(zhí)行了回調(diào)函數(shù)(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL); pHalKeyProcessFunction是在 void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback)里面pHalKeyProcessFunction = cback;進(jìn)行賦函數(shù)指針的,這樣我們就進(jìn)入回調(diào)函數(shù)了,我們來(lái)看一下回調(diào)函數(shù): void OnBoard_KeyCallback ( uint8 keys, uint8 state ) //回調(diào)函數(shù) { uint8 shift; // shift key (S1) is used to generate key interrupt // applications should not use S1 when key interrupt is enabled shift = (OnboardKeyIntEnable == HAL_KEY_INTERRUPT_ENABLE) ? false : ((keys & HAL_KEY_SW_6) ? true : false); if ( OnBoard_SendKeys( keys, shift ) != ZSuccess )//ZFailure,如果不成功則執(zhí)行下面 { // Process SW1 here if ( keys & HAL_KEY_SW_1 ) // Switch 1 { } ………………… } } 回調(diào)函數(shù)里面又調(diào)用了OnBoard_SendKeys( keys, shift );接著看 byte OnBoard_SendKeys( byte keys, byte state ) { keyChange_t *msgPtr; if ( registeredKeysTaskID != NO_TASK_ID )//之前是否RegisterForKeys( sapi_TaskID );注冊(cè)過(guò)? { // 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 ); } else return ( ZFailure ); } 如果之前注冊(cè)過(guò)按鍵事件,那么就會(huì)調(diào)用osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );發(fā)送系統(tǒng)消息,它又會(huì)調(diào)用 osal_set_event(registeredKeysTaskID, SYS_EVENT_MSG );設(shè)置事件發(fā)生標(biāo)志, byte osal_set_event( byte task_id, UINT16 event_flag ) { if ( task_id < tasksCnt ) { halIntState_t intState; HAL_ENTER_CRITICAL_SECTION(intState); // Hold off interrupts tasksEvents[task_id] |= event_flag; // Stuff the event bit(s) 置任務(wù)標(biāo)志, HAL_EXIT_CRITICAL_SECTION(intState); // Release interrupts } else return ( INVALID_TASK ); return ( ZSUCCESS ); } 然后觸發(fā)SAPI_ProcessEvent()應(yīng)用層處理事件,SAPI_ProcessEvent()再調(diào)用zb_HandleKeys()函數(shù)進(jìn)行最終的按鍵處理事件: void zb_HandleKeys( uint8 shift, uint8 keys ) { uint8 startOptions; uint8 logicalType; if ( keys & HAL_KEY_SW_5 )//我自己加的sw5按鍵處理 { P1_0=~P1_0; } ………….. } 經(jīng)過(guò)了層層函數(shù),最終到達(dá)了zb_HandleKeys()按鍵處理函數(shù),其中的各種函數(shù)關(guān)系我們應(yīng)該理清,這樣對(duì)整個(gè)系統(tǒng)的OSAL編程有一定的了解,其中按鍵有兩種處理方式,輪詢和中斷方式,系統(tǒng)默認(rèn)為輪詢方式, 下面再看一下中斷方式的過(guò)程: 如果修改InitBoard( OB_READY )里的 OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ ENABLE;//原HAL_KEY_INTERRUPT_DISABLE HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback); 那么會(huì)把按鍵配置為中斷方式,具體可看上面提到的HalKeyConfig()函數(shù);此時(shí)如有按鍵按下,則會(huì)進(jìn)入中斷服務(wù)函數(shù): HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR ) { /* P0IF is cleared by HW for CHVER < REV_E */ halProcessKeyInterrupt(); //按鍵的中斷處理 if( CHVER >= REV_E ) { …………………… } } 在中斷函數(shù)中會(huì)執(zhí)行halProcessKeyInterrupt()函數(shù),我們看看 void halProcessKeyInterrupt (void) { #if (HAL_KEY == TRUE) bool valid=FALSE; #if defined (HAL_KEY_SW_6_ENABLE) 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; } #endif #if defined (HAL_KEY_SW_5_ENABLE) if (HAL_KEY_SW_5_PXIFG & HAL_KEY_SW_5_BIT) /* Interrupt Flag has been set */ { HAL_KEY_SW_5_PXIFG = ~(HAL_KEY_SW_5_BIT); /* Clear Interrupt Flag */ valid = TRUE; } #endif if (valid) { osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);//25ms } #endif /* HAL_KEY */ } 我們終于發(fā)現(xiàn)了osal_start_timerEx()函數(shù),在HAL_KEY_DEBOUNCE_VALUE時(shí)間(25ms)后再次觸發(fā),用于按鍵去抖,然后osal_start_timerEx()會(huì)觸發(fā)Hal_ProcessEvent()函數(shù),這樣就和輪詢方式的后半部分是一樣的,也就是說(shuō)中斷法和輪詢法在前面的不同,一旦遇到Hal_ProcessEvent(),那么后面的也就一樣了,這就是整個(gè)按鍵的處理過(guò)程.
|