本篇文章我們再回到基礎篇,難道你還以為我會講UDP?啊哈哈哈,UDP肯定是會講的,但是應用場景不是很多,我們放到后面再講,不過也是簡單一講,畢竟熟悉UDP協(xié)議的人來說,都知道UDP一種不可靠的傳輸協(xié)議,可以這樣形容“我(Client)只管發(fā),你(Server)愛收不收”,所以在一些實際應用場景中是不多見的,大多數(shù)時候我們還是要保證數(shù)據(jù)傳輸?shù)目煽啃裕瑴蚀_性!好了,不多扯了,開始今天的簡單卻很重要的知識學習~
我們先來了解一下什么是GPIO吧,雖然經(jīng)常說GPIO,但是你有沒有真正的去深入了解一下呢?隨著知識的不斷積累,有些時候去深入的了解某些東西,會讓你有很多新的收獲。
- 什么是GPIO
簡單來說就是通用輸入輸出端口,英文全稱:General Purpose Input Output。通俗地說,就是一些引腳,可以通過它們輸出高低電平,或者通過它們讀入引腳的狀態(tài)是高電平或是低電平。GPIO是個比較重要的概念,用戶可以通過GPIO口和硬件進行數(shù)據(jù)交互(如UART),控制硬件工作(如LED、蜂鳴器等),讀取硬件的工作狀態(tài)信號(如中斷信號)等。
- GPIO的八種模式
這里說的八種模式并不是指ESP8266的八種模式,據(jù)我所知ESP8266的GPIO口并沒有這么多模式,這里給大家說的是STM32 GPIO的八種模式,STM32單片機的八種模式相對來說是非常全的了,我們這里可以了解一下。
- 模擬輸入(GPIO_Mode_AIN)
- 浮空輸入(GPIO_Mode_IN_FLOATING)
- 下拉輸入(GPIO_Mode_IPD)
- 上拉輸入(GPIO_Mode_IPU)
- 開漏輸出(GPIO_Mode_Out_OD)
- 推挽輸出(GPIO_Mode_Out_PP)
- 復用開漏輸出(GPIO_Mode_Out_PP)
- 復用推挽輸出(GPIO_Mode_AF_PP)
這里就不再詳細展開敘述了,因為要把這八種模式都搞明白,一兩句話是說不完了,有時間在跟大家分享。
- GPIO可以用來做什么
我們?nèi)W一個東西,最重要是知道這個東西可以做什么,最終要落實到實際使用嘛,如果這個GPIO沒有實際使用價值,那我們也肯定不會去好好琢磨它了,簡單來說,實際使用場景非常多,比如我們常見的只有開關狀態(tài)的電子器件都可以用GPIO控制,或者間接使用GPIO去控制,舉個栗子!LED燈,納尼?怎么老是說一些跟燈有關的東西,啊哈哈哈哈,因為我還是比較喜歡閃閃發(fā)光的小玩意,我們都知道燈只有兩種狀態(tài),開或者關(不要跟我講還有壞了這種狀態(tài)!),開跟關的狀態(tài)其實就是有無電流經(jīng)過它,而有無電流經(jīng)過LED,最重要的是LED兩端要有一個電壓差,所以我們可以讓某一個管腳輸出高電平,這樣就會在LED兩端形成一定的電壓差,自然而然的燈就亮了,大家可以暫時這么理解(這里只是講給不怎么懂電子的人的~)。又比如還可以控制繼電器、還可以獲取按鍵狀態(tài),復雜一點的還可以模擬I2C總線,總之GPIO是很通用,但是也是非常重要的!
- ESP8266GPIO接口函數(shù)
其實單就ESP8266的GPIO口操作來說,我起初是很不適應的,如果你之前接觸的是STM32單片機開發(fā)的話,你會感到不適應,但是慢慢的熟悉一下,會發(fā)現(xiàn)其實很簡單,所以學習最重要的是摒棄固有思維,要能夠思維活躍一些。我們先看一下手冊當中對GPIO接口的說明,只有這個讀懂了,我們才能更好的去使用它。



我這里把幾個常用的給圈出來了,基本上我們使用這幾個就足夠了,GPIO中斷這塊大家可以好好看一下,有時候我們會用到外部中斷的,我們使用管腳去控制一個LED燈的基本流程就是先PIN_FUNC_SELECT(PIN_NAME, FUNC),然后就可以輸出高低電平或者設置為輸入,或者去讀取管腳狀態(tài),但是有一點我們需要注意的就是gpio16這個管腳,在官方的技術參考手冊中有說明,給大家截圖看一下:

什么意思呢?就是gpio16這個管腳不是GPIO這一伙里,使用它人家有自己的接口函數(shù),不跟其他管腳使用同樣的接口函數(shù),這里大家需要注意一些,那么為什么這個管腳這么特殊呢?其實主要時為了低功耗,要知道Wi-Fi的功耗是很大,但是我們?nèi)绻肊SP8266做一些便攜的設備,靠電池供電是不足以支撐很久的,所以在它沒有什么事情的時候,我們需要它進入睡眠模式,這時候整個芯片會關閉除RTC以外的所有模塊,此時工作電流是在μA級別,可以說是非常省電的,下面我們看一下官方是如何介紹的低功耗模式:

可以看出在深度睡眠模式下功耗是極低的,所以我們是可以利用ESP8266做一些可穿戴設備的,但是戴著出去的時候,我們要去連接哪一個Wi-Fi呢?這是很值得思考的,很顯然不能手機開熱點,那樣我們手機的功耗可就大大提高了,不過最近據(jù)說有家國內(nèi)公司叫連尚網(wǎng)絡的公司發(fā)布了自家的Wi-Fi衛(wèi)星,據(jù)說明年將會搭載長征系列火箭升空,估計到那時候可穿戴Wi-Fi設備才有真正的應用場景,目前還是基于BLE的居多。詳情戳卡片:
我們看一下三種模式睡眠模式的功耗對比吧!


不對,好像又有點扯多了?怎么睡眠模式又寫了這么多,好吧,轉(zhuǎn)回正題,大家對低功耗感興趣的話可以留言,改天我單獨整理一篇文章詳細介紹一下,要是沒有那就先不整理了,啊哈哈哈哈~
我們正式開始我們的點燈、按鍵點燈、定時器點燈等點燈之旅,不過還有個問題,我們這里如果是使用的NodeMCU(默認大家是使用的NodeMCU)話,我們還需要了解一下管腳定義,因為NodeMCU采用的管腳命名與ESP8266的默認管腳命名是不一樣的,所以我們需要對應起來,直接給大家上個圖片看一下,建議大家收藏一下,省的以后找起來麻煩。

話不多說?上代碼?得嘞,客官您里邊請~
/************************
* STATIC VARIABLES *
************************/
os_timer_t blue_led_timer;
os_timer_t key_read_timer;
/************************
* STATIC FUNCTIONS *
************************/
/**
* LED定時反轉(zhuǎn)函數(shù),1s狀態(tài)反轉(zhuǎn)一次
*/
static void ICACHE_FLASH_ATTR
blue_led_timer_toggle(void){
os_timer_disarm(&blue_led_timer);//取消定時器
uint32 status = GPIO_INPUT_GET(GPIO_ID_PIN(2));//獲取藍燈管腳狀態(tài)
GPIO_OUTPUT_SET(GPIO_ID_PIN(2),!status);//取反實現(xiàn)藍燈管腳電平反轉(zhuǎn),從而實現(xiàn)亮滅操作
os_timer_arm(&blue_led_timer, 1000, true);//使能定時器
}
/**
* 按鍵定時狀態(tài)讀取函數(shù),10ms獲取一次按鍵狀態(tài)
*/
static void ICACHE_FLASH_ATTR
key_read_value(void){
os_timer_disarm(&key_read_timer);//取消定時器
if(GPIO_INPUT_GET(GPIO_ID_PIN(14)) == 0x00){//如果按鍵按下,低電平
os_delay_us(20000);//延時20ms消抖
if(GPIO_INPUT_GET(GPIO_ID_PIN(14)) == 0x00){//再次判斷狀態(tài),防止誤觸
uint32 status = GPIO_INPUT_GET(GPIO_ID_PIN(4));//獲取當前管腳狀態(tài)
GPIO_OUTPUT_SET(GPIO_ID_PIN(4),!status);//取反實現(xiàn)管腳電平反轉(zhuǎn)
while(GPIO_INPUT_GET(GPIO_ID_PIN(14)));//等待按鍵松開
}
}
os_timer_arm(&key_read_timer, 10, true);//使能定時器
}
/**
*按鍵中斷服務函數(shù),高電平觸發(fā)
*/
static void ICACHE_FLASH_ATTR
key_intr_handler(void){
uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);//讀取GPIO寄存器狀態(tài),獲取中斷信息
uint8 level = 0;
GPIO_REG_WRITE(GPIO_STATUS_ADDRESS,gpio_status);//清楚中斷信息
if(gpio_status & (BIT(15))){//判斷是否是gpio15
if(GPIO_INPUT_GET(15)){//如果是高電平
GPIO_OUTPUT_SET(GPIO_ID_PIN(5),0);//熄滅紅燈
GPIO_OUTPUT_SET(GPIO_ID_PIN(12),1);//熄滅黃燈
GPIO_OUTPUT_SET(GPIO_ID_PIN(4),0);//熄滅綠燈
GPIO_OUTPUT_SET(GPIO_ID_PIN(2),0);//熄滅藍燈
os_timer_disarm(&key_read_timer);//取消按鍵定時器
os_timer_disarm(&blue_led_timer);//取消藍燈定時反轉(zhuǎn)定時器
}else{//如果是低電平
//不做處理
}
}else{//如果不是gpio15
//不做處理
}
}
/************************
* GLOBAL FUNCTIONS *
************************/
void ICACHE_FLASH_ATTR
gpio_test(void){
gpio_init();//初始化GPIO
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO5_U,FUNC_GPIO5);//紅燈
GPIO_OUTPUT_SET(GPIO_ID_PIN(5),1);//高電平點亮,常亮
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U,FUNC_GPIO12);//黃燈
GPIO_OUTPUT_SET(GPIO_ID_PIN(12),0);//低電平點亮,常亮
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U,FUNC_GPIO2);//藍燈
os_timer_disarm(&blue_led_timer);
os_timer_setfn(&blue_led_timer, (os_timer_func_t *) blue_led_timer_toggle,NULL);//定時回調(diào)函數(shù)
os_timer_arm(&blue_led_timer, 1000, true);//設置時間為1s
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO4_U,FUNC_GPIO4);//綠燈,按鍵觸發(fā),反轉(zhuǎn)
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U,FUNC_GPIO14);//按鍵
GPIO_DIS_OUTPUT(GPIO_ID_PIN(14));//按鍵設置為輸入模式
PIN_PULLUP_EN(PERIPHS_IO_MUX_MTMS_U);//引腳上拉使能
os_timer_disarm(&key_read_timer);
os_timer_setfn(&key_read_timer, (os_timer_func_t *) key_read_value,NULL);//定時回調(diào)函數(shù)
os_timer_arm(&key_read_timer, 10, true);//設置時間為10ms
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDO_U,FUNC_GPIO15);//中斷按鍵
GPIO_DIS_OUTPUT(GPIO_ID_PIN(15));//設置為輸入模式
PIN_PULLUP_DIS(PERIPHS_IO_MUX_MTDO_U);//下拉使能
ETS_GPIO_INTR_DISABLE();//禁止所有GPIO中斷
ETS_GPIO_INTR_ATTACH((void *)key_intr_handler,NULL);//注冊GPIO中斷處理函數(shù)
gpio_pin_intr_state_set(GPIO_ID_PIN(15),GPIO_PIN_INTR_HILEVEL);//設置高電平觸發(fā)中斷
ETS_GPIO_INTR_ENABLE();//使能所有GPIO中斷
}
代碼不是很多,都有注釋,大家可以自己看一下,最后我們來看一下實際運行效果,請原諒我這不標準的普通話,應該不用給大家加字幕吧?啊哈哈哈,請大家諒解~
視頻還在找托管方~
再附上一張手畫原理圖,電路連接相對簡單,就簡單一畫,應該很好看懂的。

源碼可以在這里下載:
歡迎大家Star,您的鼓勵是我最大的動力,有問題可以私信我,或者提交issues~
本系列文章在知乎同步更新,知乎搜索專欄:IAMLIUBO的神奇物聯(lián)網(wǎng)之旅
視頻可以去知乎上的文章查看~
QQ交流群:592587184