GPIO從入門(mén)到實(shí)戰(zhàn):引腳模式配置與嵌入式點(diǎn)燈按鍵詳解)
1. 什么是GPIO單片機(jī)伸出去的手GPIO這三個(gè)字母大概是所有嵌入式學(xué)習(xí)者第一天都會(huì)撞見(jiàn)的。我當(dāng)年第一次在開(kāi)發(fā)板上點(diǎn)亮一顆LED時(shí)看著那顆小燈亮起來(lái)確實(shí)興奮了好一陣。但說(shuō)實(shí)話那時(shí)候我對(duì)GPIO的理解也就停留在“能輸出高電平和低電平”這個(gè)層面。直到后來(lái)做了幾個(gè)項(xiàng)目回過(guò)頭來(lái)才真正摸透GPIO的脾性——它遠(yuǎn)不只是“點(diǎn)燈”這么簡(jiǎn)單它是單片機(jī)感知世界、控制世界的物理接口也是嵌入式開(kāi)發(fā)中最基礎(chǔ)也最容易被輕視的一個(gè)知識(shí)點(diǎn)。GPIO的全稱是General Purpose Input/Output中文叫通用輸入輸出引腳。通俗點(diǎn)說(shuō)它就是一個(gè)可以被軟件控制為高電平或者低電平的引腳。高電平代表邏輯1低電平代表邏輯0。單片機(jī)的邏輯就是靠這些引腳上的電平狀態(tài)來(lái)和外部硬件打交道的你給引腳一個(gè)高電平外部電路就能收到信號(hào)你從引腳讀取到一個(gè)電平狀態(tài)也就能知道外部設(shè)備當(dāng)前處于什么狀態(tài)。很多人容易忽略一個(gè)點(diǎn)GPIO不只是用來(lái)“控制”的它也負(fù)責(zé)“感知”??刂剖禽敵龇较蚋兄禽斎敕较?。一顆按鍵按下沒(méi)有、一個(gè)傳感器是否觸發(fā)、一個(gè)開(kāi)關(guān)是閉合還是斷開(kāi)這些都是通過(guò)GPIO的輸入功能來(lái)實(shí)現(xiàn)的。也就是說(shuō)GPIO就像單片機(jī)伸出去的無(wú)數(shù)只手——既能摸東西也能握東西既能讀狀態(tài)也能寫(xiě)狀態(tài)。對(duì)一個(gè)第一天接觸嵌入式的人來(lái)說(shuō)GPIO是最適合作為切入點(diǎn)的因?yàn)樗銐蚝?jiǎn)單簡(jiǎn)單到原理就一句話輸出就是控制引腳電壓輸入就是讀取引腳電壓。但這個(gè)點(diǎn)上衍生出來(lái)的內(nèi)容卻非常豐富——上下拉電阻、推挽輸出、開(kāi)漏輸出、復(fù)用功能、電氣特性、防抖處理、邏輯分析儀……每一個(gè)都可以展開(kāi)成一篇很長(zhǎng)的文章。你只有把GPIO真正吃透了后面學(xué)定時(shí)器、中斷、串口、I2C、SPI的時(shí)候才會(huì)覺(jué)得順理成章因?yàn)槟切┩庠O(shè)本質(zhì)上都是建立在引腳電平變化的基礎(chǔ)上的。2. 為什么GPIO是嵌入式學(xué)習(xí)的第一課2.1 從GPIO看懂芯片與外設(shè)的對(duì)話嵌入式開(kāi)發(fā)的本質(zhì)是讓芯片按照軟件的指令與外部世界交互。這種交互的最底層形式是什么就是引腳電平的變化。一個(gè)引腳從高變低對(duì)方就能感知到一個(gè)“事件”一個(gè)引腳長(zhǎng)時(shí)間保持高電平對(duì)方就知道這是一個(gè)持續(xù)的信號(hào)。GPIO就是這個(gè)對(duì)話通道本身。我見(jiàn)過(guò)很多新手直接跳去學(xué)串口、學(xué)PWM結(jié)果遇到問(wèn)題根本不知道怎么排查原因就是底子沒(méi)有打好。舉個(gè)例子你配置了一個(gè)串口發(fā)送數(shù)據(jù)用邏輯分析儀去抓引腳波形發(fā)現(xiàn)自己寫(xiě)的代碼確實(shí)把TX引腳的電平按照某個(gè)節(jié)奏拉高拉低了但數(shù)據(jù)就是不對(duì)。這時(shí)候你如果能看懂GPIO層面的波形——起始位、停止位、波特率對(duì)應(yīng)的電平翻轉(zhuǎn)時(shí)間你就知道問(wèn)題是出在波特率配置還是數(shù)據(jù)格式上。如果你連電平翻轉(zhuǎn)的物理意義都還不清楚就只能干瞪眼。2.2 GPIO對(duì)后續(xù)學(xué)習(xí)的輻射作用GPIO的知識(shí)幾乎覆蓋了后面所有外設(shè)的學(xué)習(xí)。學(xué)定時(shí)器你要用GPIO輸出方波學(xué)中斷你要通過(guò)GPIO邊沿觸發(fā)來(lái)響應(yīng)外部事件學(xué)串口你要配置引腳復(fù)用為UART功能學(xué)ADC你要把引腳配置為模擬輸入模式??梢哉f(shuō)GPIO是這些所有功能的地基。反過(guò)來(lái)如果你GPIO這部分理解得透徹很多后續(xù)知識(shí)點(diǎn)就變得很好理解。比如復(fù)用功能本質(zhì)上就是讓某個(gè)引腳不再作為普通GPIO使用而是把它交給芯片內(nèi)部的其他外設(shè)模塊去控制。這時(shí)候你如果理解了引腳本身只是“一根線”真正的差別在于這根線內(nèi)部的連接方式變了那復(fù)用功能這個(gè)概念就非常好懂了。我自己的體會(huì)是GPIO值得花的時(shí)間不是一天而是兩到三天。第一天弄清楚基本概念和模式配置第二天做幾個(gè)實(shí)際實(shí)驗(yàn)加深理解第三天整理常見(jiàn)問(wèn)題和排查方法。第一天的核心任務(wù)是把“引腳拉到高電平”和“讀取引腳電平”這兩件事徹底搞明白。3. GPIO的核心原理與配置模式3.1 輸出模式推挽與開(kāi)漏GPIO的輸出模式最常見(jiàn)的兩種就是推挽輸出和開(kāi)漏輸出。這個(gè)知識(shí)點(diǎn)到了實(shí)際項(xiàng)目里非常重要很多人一開(kāi)始不注意后面被坑了才回頭補(bǔ)課。推挽輸出英文叫Push-Pull Output。這種模式下引腳內(nèi)部有兩個(gè)晶體管一個(gè)負(fù)責(zé)把引腳拉到電源電壓高電平一個(gè)負(fù)責(zé)把引腳拉到地低電平兩個(gè)管子交替工作就像兩個(gè)人一推一拉所以叫推挽。這種模式的優(yōu)點(diǎn)是驅(qū)動(dòng)能力強(qiáng)輸出高電平的時(shí)候能夠提供比較大的電流能給LED、蜂鳴器這類器件提供足夠的驅(qū)動(dòng)。開(kāi)漏輸出英文叫Open-Drain Output。這種模式只保留了拉低的能力沒(méi)有拉高的能力——引腳要么被拉到低電平要么就是高阻態(tài)。要讓引腳輸出高電平必須外接一個(gè)上拉電阻把引腳“默認(rèn)”接到電源然后通過(guò)內(nèi)部管子決定什么時(shí)候拉低。聽(tīng)起來(lái)開(kāi)漏似乎很雞肋但實(shí)際上它非常有用。第一開(kāi)漏輸出可以實(shí)現(xiàn)電平轉(zhuǎn)換比如你的MCU是3.3V系統(tǒng)但外設(shè)需要5V電平這時(shí)候接一個(gè)5V的上拉電阻引腳輸出低就是0V引腳釋放就是5V這樣就實(shí)現(xiàn)了電平的轉(zhuǎn)換。第二多個(gè)開(kāi)漏引腳可以并聯(lián)接在一根線上任何一個(gè)引腳拉低整條線就是低電平這就是I2C總線用的“線與”機(jī)制。實(shí)際項(xiàng)目里驅(qū)動(dòng)LED、繼電器、蜂鳴器這類負(fù)載優(yōu)先用推挽輸出接I2C總線、需要電平兼容的場(chǎng)合用開(kāi)漏輸出。3.2 輸入模式浮空、上拉與下拉輸入模式根據(jù)內(nèi)部電阻的連接方式又分為浮空輸入、上拉輸入和下拉輸入。這個(gè)知識(shí)點(diǎn)雖然不起眼但實(shí)際操作中非常關(guān)鍵。浮空輸入就是引腳內(nèi)部不接任何電阻引腳電平完全取決于外部電路。這種模式適合外部電路已經(jīng)明確決定了電平的場(chǎng)景比如說(shuō)引腳已經(jīng)通過(guò)一個(gè)低阻抗的驅(qū)動(dòng)源接好了。但如果你直接接一個(gè)按鍵到浮空輸入引腳按鍵沒(méi)按下時(shí)引腳就處于懸空狀態(tài)電平不確定——它可能隨機(jī)漂移一會(huì)兒是0一會(huì)兒是1這就是很多按鍵實(shí)驗(yàn)“靈異事件”的根源。上拉輸入在引腳內(nèi)部接了一個(gè)電阻到電源引腳默認(rèn)就是高電平。這樣的好處是按鍵一端接引腳、另一端接地按下時(shí)引腳被拉低松開(kāi)時(shí)回到高電平邏輯非常明確不需要外接電阻。下拉輸入正好相反內(nèi)部接了電阻到地引腳默認(rèn)低電平按鍵一端接引腳、另一端接電源按下時(shí)引腳變成高電平。實(shí)際項(xiàng)目里按鍵檢測(cè)最常用的就是上拉輸入或下拉輸入。具體選哪個(gè)取決于按鍵的另一端接到哪里另一端接地就用上拉另一端接電源就用下拉。這一點(diǎn)一定要想清楚再接線不然按鍵邏輯就是反的。3.3 復(fù)用與模擬GPIO的兩種進(jìn)階形態(tài)GPIO還有一個(gè)重要特性引腳功能是可配置的。不用的引腳只是普通GPIO用的時(shí)候可以配置成其他外設(shè)功能的引腳。復(fù)用功能就是把引腳交給芯片內(nèi)部的某個(gè)外設(shè)模塊使用。串口的TX/RX、SPI的SCK/MOSI/MISO、I2C的SCL/SDA、定時(shí)器的PWM輸出通道這些功能往往都復(fù)用在同一組物理引腳上。配置復(fù)用功能之后這個(gè)引腳就不再受GPIO輸出數(shù)據(jù)寄存器控制了而是由對(duì)應(yīng)的外設(shè)模塊接管。模擬輸入和復(fù)用又不太一樣它把引腳直接連接到內(nèi)部的模擬采樣電路比如ADC的采樣保持電路。這種模式下引腳的電平狀態(tài)不再以數(shù)字0/1的形式被讀取而是采樣得到一個(gè)連續(xù)的模擬電壓值。所以ADC引腳必須配置成模擬輸入模式不能配置成普通GPIO輸入模式不然采樣結(jié)果會(huì)受到數(shù)字邏輯電路的影響精度很差。這三種模式的選擇邏輯可以總結(jié)成一句話普通數(shù)字信號(hào)用GPIO模式外設(shè)數(shù)字信號(hào)用復(fù)用模式模擬電壓信號(hào)用模擬模式。聽(tīng)起來(lái)很簡(jiǎn)單但實(shí)際項(xiàng)目里經(jīng)常有人忘了配置復(fù)用功能結(jié)果串口怎么都發(fā)不出數(shù)據(jù)查了半天才發(fā)現(xiàn)引腳模式?jīng)]設(shè)對(duì)。4. 實(shí)操?gòu)目炊韴D到點(diǎn)亮第一顆LED4.1 看懂原理圖再動(dòng)手很多新手拿到開(kāi)發(fā)板第一反應(yīng)是找代碼模板而不是先看原理圖。這個(gè)習(xí)慣不好。GPIO實(shí)驗(yàn)雖然簡(jiǎn)單但“引腳號(hào)接錯(cuò)了”這種錯(cuò)誤比代碼寫(xiě)錯(cuò)更隱蔽排查起來(lái)也更費(fèi)時(shí)間。舉個(gè)例子某款入門(mén)級(jí)開(kāi)發(fā)板上有一顆板載LED你以為是接到了某個(gè)引腳打開(kāi)原理圖一看實(shí)際上它接的是另一個(gè)引腳中間還串了一個(gè)限流電阻。如果你不看原理圖拿著網(wǎng)上的示例代碼直接改引腳很有可能程序里面寫(xiě)的引腳和硬件實(shí)際的連接根本不匹配LED自然不亮??丛韴D的時(shí)候重點(diǎn)關(guān)注三件事LED接在哪個(gè)引腳上、LED是接到電源還是接地這決定了控制邏輯是高電平點(diǎn)亮還是低電平點(diǎn)亮、有沒(méi)有串限流電阻。以最常見(jiàn)的接法為例LED一端接GPIO引腳另一端通過(guò)一個(gè)330歐姆電阻接到電源這種接法下GPIO輸出低電平時(shí)LED點(diǎn)亮因?yàn)殡娏鲝碾娫唇?jīng)電阻、LED流入引腳。如果LED一端接GPIO另一端接電源那點(diǎn)亮邏輯就反過(guò)來(lái)了GPIO輸出高電平時(shí)LED兩端電壓差小不亮GPIO輸出低電平時(shí)電流從電源經(jīng)LED流入引腳LED點(diǎn)亮。這個(gè)方向問(wèn)題新手第一次做實(shí)驗(yàn)最容易搞錯(cuò)。4.2 點(diǎn)亮LED的完整流程以一顆LED接在PA5引腳、另一端通過(guò)電阻接電源為例實(shí)現(xiàn)點(diǎn)亮的完整流程分三步。第一步是使能引腳所在端口的時(shí)鐘?,F(xiàn)在的MCU為了降低功耗外設(shè)時(shí)鐘默認(rèn)是關(guān)閉的必須先在時(shí)鐘控制寄存器里使能對(duì)應(yīng)端口的時(shí)鐘GPIO模塊才能工作。這一步很多人容易漏掉特別是從舊型號(hào)芯片遷移過(guò)來(lái)的開(kāi)發(fā)者因?yàn)槔闲酒喜恍枰獑为?dú)使能時(shí)鐘。第二步是配置引腳模式。目標(biāo)是把PA5配置成推挽輸出模式這個(gè)時(shí)候要設(shè)置引腳模式寄存器、輸出類型寄存器、輸出速度寄存器。輸出速度的選擇有講究LED這類低速信號(hào)選最低檔就足夠了不需要選高速高速反而可能導(dǎo)致信號(hào)過(guò)沖和電磁干擾。第三步是控制引腳電平。GPIO輸出高電平時(shí)引腳輸出一個(gè)高電平LED兩端電壓差不足不亮輸出低電平時(shí)引腳相當(dāng)于接地電流從電源流經(jīng)電阻和LED到引腳LED點(diǎn)亮。這三步的順序不能亂。如果先配置輸出寄存器再使能時(shí)鐘有些芯片在時(shí)鐘使能前的引腳行為是不確定的可能會(huì)產(chǎn)生一次意外的電平跳變外設(shè)收到一個(gè)錯(cuò)誤信號(hào)。下面是使用某主流32位MCU的庫(kù)函數(shù)方式編寫(xiě)的完整代碼。這段代碼的作用就是將PA5設(shè)置為推挽輸出模式并且控制LED以200毫秒的間隔閃爍。#include main.h // 此處省略系統(tǒng)時(shí)鐘初始化函數(shù) int main(void) { HAL_Init(); SystemClock_Config(); __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA端口時(shí)鐘 GPIO_InitTypeDef GPIO_InitStruct {0}; GPIO_InitStruct.Pin GPIO_PIN_5; // PA5引腳 GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; // 推挽輸出 GPIO_InitStruct.Pull GPIO_NOPULL; // 不需要上下拉 GPIO_InitStruct.Speed GPIO_SPEED_FREQ_LOW; // 低速即可 HAL_GPIO_Init(GPIOA, GPIO_InitStruct); while (1) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); // 輸出低電平LED點(diǎn)亮 HAL_Delay(200); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // 輸出高電平LED熄滅 HAL_Delay(200); } }這是庫(kù)函數(shù)的寫(xiě)法好處是代碼可讀性好、配置項(xiàng)目的名字直白。如果你用的是寄存器方式同樣的功能可能需要這樣實(shí)現(xiàn)往端口時(shí)鐘寄存器寫(xiě)一個(gè)使能位往模式寄存器對(duì)應(yīng)位寫(xiě)一個(gè)“輸出”的編碼值然后往輸出數(shù)據(jù)寄存器寫(xiě)1或0。寄存器方式更接近底層邏輯鏈路清晰但代碼長(zhǎng)、容易寫(xiě)錯(cuò)位。對(duì)初學(xué)者來(lái)說(shuō)庫(kù)函數(shù)方式是更好的起點(diǎn)但建議在跑通以后對(duì)照寄存器版再理解一遍。4.3 用按鍵控制LED輸入方向的基礎(chǔ)實(shí)驗(yàn)點(diǎn)亮LED只涉及了GPIO的輸出方向。為了把輸入方向也搞清楚我建議第一天就做一個(gè)按鍵控制LED的實(shí)驗(yàn)按下按鍵時(shí)LED亮松開(kāi)時(shí)LED滅。這個(gè)實(shí)驗(yàn)用到兩個(gè)引腳一個(gè)配置為輸入模式接按鍵一個(gè)配置為推挽輸出接LED。以PA0接按鍵、PA5接LED為例按鍵接法有兩種常見(jiàn)方式。第一種是按鍵一端接PA0、另一端接地PA0配置為上拉輸入平時(shí)PA0讀到的電平是高按下時(shí)被拉低第二種是按鍵一端接PA0、另一端接電源PA0配置為下拉輸入平時(shí)讀到低按下時(shí)變成高。這兩種接法對(duì)應(yīng)代碼里的判斷邏輯完全不同。第一種方式下檢測(cè)到低電平意味著按鍵按下第二種方式下檢測(cè)到高電平才意味著按鍵按下。寫(xiě)代碼之前先想清楚硬件接法是嵌入式開(kāi)發(fā)的習(xí)慣。按鍵檢測(cè)部分的代碼很簡(jiǎn)單讀取引腳電平如果滿足按下條件就翻轉(zhuǎn)LED的狀態(tài)。但有一個(gè)問(wèn)題值得專門(mén)處理機(jī)械按鍵在按下和松開(kāi)的瞬間物理觸點(diǎn)會(huì)彈跳產(chǎn)生一連串的高頻開(kāi)關(guān)信號(hào)持續(xù)大約5到20毫秒。如果不去抖你按一次按鍵程序可能檢測(cè)到好幾次按下LED狀態(tài)就會(huì)亂跳。軟件消抖的常規(guī)做法是檢測(cè)到按鍵狀態(tài)變化后延時(shí)10到20毫秒再讀一次引腳電平如果和之前的狀態(tài)一致才判定為有效按下。這段延時(shí)雖然會(huì)讓響應(yīng)有輕微的延遲但對(duì)人來(lái)說(shuō)完全感知不到換來(lái)的是穩(wěn)定的按鍵行為。按鍵控制LED的核心代碼邏輯大致如下// 假設(shè)PA0接按鍵按鍵另一端接地上拉輸入 // 假設(shè)PA5接LEDLED另一端經(jīng)電阻接電源GPIO低電平點(diǎn)亮 while (1) { if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) GPIO_PIN_RESET) { HAL_Delay(20); // 延時(shí)消抖 if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) GPIO_PIN_RESET) { HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // 翻轉(zhuǎn)LED狀態(tài) while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) GPIO_PIN_RESET); // 等待按鍵松開(kāi)避免一次按下多次觸發(fā) } } }這里有一個(gè)細(xì)節(jié)值得注意翻轉(zhuǎn)LED之后程序會(huì)等待按鍵松開(kāi)才繼續(xù)執(zhí)行。這樣做的目的是防止一次按下過(guò)程中電平抖動(dòng)或者長(zhǎng)時(shí)間按住導(dǎo)致LED被反復(fù)翻轉(zhuǎn)。但這個(gè)寫(xiě)法有一個(gè)副作用如果程序在等待按鍵松開(kāi)時(shí)去處理其他任務(wù)那些任務(wù)會(huì)被阻塞。在復(fù)雜項(xiàng)目里更合理的方式是用狀態(tài)機(jī)或者中斷去處理按鍵事件第一天可以先用這種簡(jiǎn)單寫(xiě)法理解邏輯后續(xù)再優(yōu)化。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄5.1 LED不亮應(yīng)該往哪個(gè)方向查GPIO實(shí)驗(yàn)最常見(jiàn)的失敗現(xiàn)象就是代碼明明寫(xiě)了LED卻紋絲不動(dòng)。根據(jù)我自己帶新人的經(jīng)驗(yàn)99%的問(wèn)題出在下面幾個(gè)地方。第一引腳號(hào)和實(shí)際接線不對(duì)應(yīng)。有的開(kāi)發(fā)板標(biāo)注不清或者你用的引腳號(hào)在板子上并不是你想象的那一個(gè)。排查方法很簡(jiǎn)單拿萬(wàn)用表蜂鳴檔測(cè)一下LED一端和芯片引腳之間的通斷確認(rèn)走線。第二LED點(diǎn)亮邏輯搞反了。前面說(shuō)了LED的接法決定了點(diǎn)亮電平。如果你一直輸出高電平但實(shí)際接法是“高電平熄滅”那LED自然不亮。代碼改成輸出低電平試試立刻就能確認(rèn)。第三時(shí)鐘沒(méi)使能。如果你用的是要求使能時(shí)鐘的芯片漏了這一步GPIO寄存器寫(xiě)了也白寫(xiě)引腳完全不響應(yīng)。這是最常見(jiàn)的“看著代碼沒(méi)問(wèn)題就是沒(méi)反應(yīng)”的原因。第四模式配置錯(cuò)了。比如說(shuō)引腳被默認(rèn)復(fù)用成了其他功能你沒(méi)有改過(guò)來(lái)GPIO控制信號(hào)根本到不了引腳。初始化代碼里要把引腳從復(fù)位狀態(tài)下的模式重新配置成普通的推挽輸出。排查這種問(wèn)題的高效路徑是先電壓表測(cè)引腳看代碼執(zhí)行后引腳電壓是否變化引腳電壓變了但LED不亮問(wèn)題在硬件引腳電壓沒(méi)變問(wèn)題在配置。用這個(gè)方法可以快速縮小范圍不至于瞎猜。5.2 按鍵實(shí)驗(yàn)的“幽靈觸發(fā)”按鍵掃描時(shí)還有一個(gè)很經(jīng)典的問(wèn)題程序剛上電按鍵沒(méi)有按LED卻自己就點(diǎn)亮了。遇到這種情況先別急著懷疑代碼邏輯大概率是引腳內(nèi)部上下拉電阻配置與外部電路的連接方式不匹配。舉個(gè)例子你把PA0配置成了浮空輸入按鍵一端接PA0另一端接地。按鍵沒(méi)按下的時(shí)候PA0引腳處于懸空狀態(tài)電平不確定可能讀到低電平程序就認(rèn)為是按鍵按下了。解決方法是把輸入模式改成上拉輸入讓按鍵沒(méi)按下時(shí)引腳保持在高電平。還有一個(gè)類似的問(wèn)題你配置的是上拉輸入但外部電路其實(shí)把按鍵接到了電源而不是地。按鍵按下時(shí)引腳被拉到高電平可能還把內(nèi)部上拉電阻和外部電源之間形成了一種奇怪的分壓關(guān)系。這種情況下按鍵邏輯和預(yù)期完全相反。解決方法是想清楚按鍵的另一端接的是什么再來(lái)決定使用上拉還是下拉。5.3 引腳懸空帶來(lái)的干擾和功耗問(wèn)題引腳配置為浮空輸入又沒(méi)有連接任何外部電路時(shí)引腳電平會(huì)在高低之間隨機(jī)跳動(dòng)。這種懸空狀態(tài)不僅會(huì)讓輸入讀到的數(shù)據(jù)毫無(wú)意義還會(huì)帶來(lái)額外的功耗——內(nèi)部電路會(huì)為這個(gè)不確定的電位產(chǎn)生動(dòng)態(tài)電流。所以不用的GPIO引腳建議統(tǒng)一配置成模擬輸入模式或者配置成帶上拉的輸入模式不要留成浮空輸入。這個(gè)習(xí)慣不僅能減少干擾還能降低整板的功耗。有些低功耗項(xiàng)目對(duì)睡眠電流要求苛刻懸空引腳就是漏電的一個(gè)重要來(lái)源。5.4 電氣特性為什么引腳會(huì)燒GPIO引腳不是萬(wàn)能電源它的驅(qū)動(dòng)能力是有限的。數(shù)據(jù)手冊(cè)上通常會(huì)標(biāo)一個(gè)絕對(duì)最大額定值以某款主流芯片來(lái)說(shuō)單個(gè)引腳的灌電流和拉電流一般不建議長(zhǎng)期超過(guò)20毫安。如果一個(gè)LED沒(méi)有串限流電阻直接接到GPIO上LED導(dǎo)通時(shí)的電流可能遠(yuǎn)超這個(gè)值時(shí)間長(zhǎng)了引腳就可能損壞。這就是LED必須串電阻的根本原因。電阻阻值的選擇也不復(fù)雜LED工作電流按10毫安算LED正向壓降按2伏算電源電壓3.3伏電阻上的壓降就是1.3伏電阻阻值約130歐姆取標(biāo)準(zhǔn)值330歐姆也行亮度稍低但更安全。再?gòu)?qiáng)調(diào)一次GPIO不是繼電器也不是功率輸出端。驅(qū)動(dòng)電機(jī)、繼電器、大功率LED這類高電流負(fù)載時(shí)必須通過(guò)三極管、MOS管或者專門(mén)的驅(qū)動(dòng)芯片來(lái)放大電流絕不能直接接在GPIO上。我見(jiàn)過(guò)不止一個(gè)新手第一次做“GPIO驅(qū)動(dòng)電磁鎖”的實(shí)驗(yàn)直接把鎖接在引腳上結(jié)果芯片當(dāng)場(chǎng)冒煙。引腳燒了是小事芯片燒了整塊板就廢了這個(gè)學(xué)費(fèi)交得太不值。6. 第一天學(xué)習(xí)GPIO的一點(diǎn)心得GPIO的內(nèi)容在嵌入式知識(shí)體系里所占的比例并不大但它處在“編程”和“硬件”的交界處是理解嵌入式開(kāi)發(fā)思維方式的一個(gè)窗口。你寫(xiě)的每一行代碼最后都要落腳到某一個(gè)引腳上的電平變化你讀到的每一個(gè)狀態(tài)也都是某個(gè)引腳電平的直接反映。學(xué)GPIO的第一天不要求快我建議把這件事做扎實(shí)點(diǎn)亮LED、讀按鍵、用邏輯分析儀抓波形、用萬(wàn)用表量電壓這幾個(gè)動(dòng)作都做一遍。尤其是邏輯分析儀它能把引腳電平隨時(shí)間變化的波形直觀地顯示出來(lái)你立刻就能明白“輸出高電平”“讀取到低電平”這些抽象概念在實(shí)際信號(hào)上是什么樣子。如果手頭還沒(méi)有邏輯分析儀拿萬(wàn)用表也是可行的。把萬(wàn)用表切換到直流電壓檔紅表筆接引腳黑表筆接地運(yùn)行代碼讓引腳交替輸出高低電平你會(huì)看到電壓讀數(shù)在0V和3.3V附近跳變。這種直觀的反饋比埋頭看一百頁(yè)芯片手冊(cè)都來(lái)得有效。GPIO之后你很快就會(huì)接觸中斷、定時(shí)器、串口每一個(gè)新知識(shí)點(diǎn)都建立在GPIO的基礎(chǔ)上。所以第一天慢一點(diǎn)真的不虧。