nRF5芯片外設GPIO和GPIOTE介紹
nRF51/nRF52同時包含GPIO和GPIOTE兩種外設,經常有人將兩者搞混,今天我們就來介紹一下這2種外設有什麽不同,及使用註意事項。
GPIO和GPIOTE都屬於芯片外設,但兩者功能完全不一樣,使用過程中不要將兩者混淆。GPIO就大家通常理解的普通IO口,用來對IO口進行讀寫等操作。因此,如果你需要讀某個IO口狀態,或者將某個IO口置1,那麽請使用nrf_gpio.h裏面的API,比如
nrf_gpio_cfg_input用來將IO設為輸入模式
nrf_gpio_pin_set用來輸出1到IO
除此之外,GPIO模塊還有兩個非常重要的功能:
- sense功能。當系統進入sleep模式(也稱system OFF模式),只能通過IO口喚醒復位。當某個IO口使能了sense功能,那麽它就可以用來喚醒sleep模式了。Sense使能的時候,可以配置高電平喚醒或者低電平喚醒。一般使用nrf_gpio_cfg_sense_input這個函數來使能IO口的sense功能。
- port event功能。通俗來說,port event其實就是IO口中斷,而且32個IO口共用同一個中斷標誌位:port event,檢測port event只需要內部低頻時鐘在工作,因此功耗非常低:0.2微安左右,但內部低頻時鐘只能用來檢測低精度的中斷事件,也就是說IO口的中斷脈沖要比較寬,比如像按鍵這種事件,就可以用port event來檢測,功能達到了功耗又低,兩全其美。這裏特別說明一下,port event狀態(一個中斷flag)是跟隨IO口電平的,比如檢測高電平有效,那麽只要IO口電平一直為高,那麽port event一直有效,該中斷標誌位無法通過軟件清除。這會產生兩個副作用:一是不斷進入port event中斷例程,二是前面也提到,port event是被32個IO口共用的,因此只要其中一個IO口一直有效,別的IO產生的port event就會被忽略。為此,在處理port event中斷的時候,nRF5 SDK app_button模塊將每個port event的極性設為toggle,也就是每進入一次port event handler,nRF5 SDK都會把port event的極性翻轉一次,比如將檢測為高有效變成檢測為低有效,這就相當於清除了port event中斷flag,從而避開上述描述的兩個副作用場景。由於GPIO模塊不能處理中斷,所以port event中斷實際是交給GPIOTE模塊來一起處理的。
GPIOTE,全稱GPIO Tasks and Events,GPIOTE首先是一個外設模塊,因此它遵守芯片外設最基本規則:每一個時刻每一個GPIO口只能被一個外設使用,因此當某一個IO口被用做GPIOTE了,那麽它就不能再作為普通GPIO來使用了,也就是上面提到的GPIO API將變得無效,此時必須使用nrfx_gpiote.h(老版本為nrf_drv_gpiote.h)裏面的API。Nordic將狀態機引入到每一個外設,也就是說,每一個外設都有自己的輸入(task),輸出(event)和狀態。GPIOTE的作用就是讓GPIO也具有task和event的功能,也就是說,對GPIOTE來說,將某一個IO口置1,其實是觸發TASKS_SET;檢測某一個IO口上升沿,其實是等待EVENTS_IN。讓IO口支持task和event機制,將為後面的PPI自動化操作打下基礎,關於PPI詳細說明,請參考“如何理解nRF5芯片外設PPI”。
前面也提到過,處理IO口中斷,必須通過GPIOTE模塊來做,GPIOTE支持兩種類型中斷:高精度的EVENTS_IN中斷以及低精度的EVENTS_PORT中斷(就是前面GPIO章節提到的Port event)。EVENTS_PORT主要用來檢測IO口高電平或者低電平,而EVENTS_IN用來檢測沿,即上升沿,下降沿或者雙沿。EVENTS_IN可以清0,EVENTS_PORT無法清0,兩者都是在GPIOTE_irq_handler裏面處理。EVENTS_IN和EVENTS_PORT兩者初始化區別如下所示:
nrf_drv_gpiote_in_config_t config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(false); //false表示低精度低功耗的Port event,每個IO口都可以作為port event,52832總共有32個port event err_code = nrf_drv_gpiote_in_init(pin_no, &config, gpiote_event_handler); nrf_drv_gpiote_in_config_t config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true); //true表示高精度高功耗的IN event,52832總共有8個IN event。註:這裏檢測的是雙沿 err_code = nrf_drv_gpiote_in_init(pin_no, &config, gpiote_event_handler);
IN event需要高頻時鐘,所以功耗比較高,在精度可以接受的情況下,優先推薦使用port event。
SDK自帶GPIOTE應用例程,感興趣的讀者請參考Keil5工程:SDK安裝目錄\examples\peripheral\gpiote\pca10040\blank\arm5_no_packs
SDK也自帶Sense例子,有興趣的讀者請參考Keil5工程:SDK安裝目錄\examples\peripheral\ram_retention\pca10040\blank\arm5_no_packs
關於Port event使用例子,可以參考Nordic的app_button模塊,比如ble_app_hrs就會用到這個模塊,大家可以去看一下app_button是如何使用port event的。
nRF5芯片外設GPIO和GPIOTE介紹