光原理與代碼實(shí)現(xiàn))
簡(jiǎn)介面向STM32初學(xué)者的按鍵控制LED燈亮度工程包基于STM32F103最小系統(tǒng)板利用六個(gè)按鍵調(diào)節(jié)六檔PWM占空比實(shí)現(xiàn)LED從微光到高亮的多級(jí)亮度切換。工程覆蓋通用輸入輸出接口的輸入輸出配置、定時(shí)器PWM波形生成、外部中斷觸發(fā)以及按鍵消抖策略完整演示了從檢測(cè)按鍵事件到更新捕獲比較寄存器、改變輸出信號(hào)平均電壓的軟硬件協(xié)同流程。壓縮包共202個(gè)文件整體約6.76MB核心為C語言源碼與頭文件另含Keil工程配置文件、編譯鏈接生成的目標(biāo)文件與映射文件、輔助說明文檔等類型覆蓋源碼閱讀、工程構(gòu)建與結(jié)果驗(yàn)證各環(huán)節(jié)。目前已有19477人學(xué)習(xí)下載資源內(nèi)部分模塊劃分清晰針對(duì)按鍵中斷與定時(shí)器輸出等關(guān)鍵功能均有獨(dú)立文件組織便于對(duì)照查閱適合正在學(xué)習(xí)STM32定時(shí)器與PWM應(yīng)用的開發(fā)者作為課程設(shè)計(jì)或入門實(shí)戰(zhàn)的參考模板。通過修改占空比參數(shù)還可擴(kuò)展為呼吸燈、按鍵長(zhǎng)按連續(xù)調(diào)光等進(jìn)階功能實(shí)踐價(jià)值較強(qiáng)。 最近總有人問我STM32 怎么用按鍵控制 LED 燈亮度很多朋友剛?cè)腴T點(diǎn)個(gè) LED 沒問題一說到“調(diào)節(jié)亮度”就卡住了。其實(shí)這個(gè)項(xiàng)目特別適合練手因?yàn)樗瑫r(shí)牽扯到按鍵輸入、PWM 輸出、中斷和定時(shí)器這些嵌入式開發(fā)的核心知識(shí)點(diǎn)做完這個(gè)項(xiàng)目你再去看舵機(jī)控制、電機(jī)調(diào)速、屏幕背光調(diào)節(jié)思路基本是通的。這篇筆記就把我從硬件電路到代碼實(shí)現(xiàn)再到踩坑排查的完整過程寫下來給正在做類似練習(xí)的同學(xué)一個(gè)參考。1. 整體設(shè)計(jì)與思路拆解1.1 為什么亮度控制必須用 PWM先把最核心的問題說透想讓 LED 可調(diào)亮度為什么大家第一反應(yīng)都是 PWM“亮度可調(diào)”說白了就是讓 LED 上的平均電流可調(diào)。你當(dāng)然可以在 LED 的回路里串一個(gè)可調(diào)電阻或者用 DAC 輸出一個(gè)可變的模擬電壓來控制驅(qū)動(dòng)管的導(dǎo)通程度這種方法叫“線性調(diào)節(jié)”。但它有個(gè)很大的毛病除了讓 LED 發(fā)光消耗的能量多余的功率全變成了熱量白白浪費(fèi)掉。尤其在電池供電的設(shè)備里這種方案基本不劃算。PWM脈沖寬度調(diào)制的思路完全不一樣。用一個(gè)頻率固定的方波去驅(qū)動(dòng) LED通過改變方波的“占空比”來控制 LED 在一個(gè)周期內(nèi)的平均導(dǎo)通時(shí)間。占空比越高平均電流越大LED 看起來就越亮占空比越低LED 看起來就越暗。我對(duì)新手最喜歡打的比方是PWM 調(diào)光就是你在快速開關(guān)燈。假設(shè)你一秒內(nèi)開關(guān)燈一百次燈開的時(shí)間占 30%人眼看起來就是“暗一點(diǎn)”燈開的時(shí)間占 80%人眼看起來就是“亮一點(diǎn)”。因?yàn)殚_關(guān)頻率夠高人眼完全察覺不到閃爍只感覺到亮暗變化。STM32 定時(shí)器輸出的 PWM 信號(hào)頻率和占空比都可以通過寄存器精確控制。這個(gè)方案的優(yōu)勢(shì)非常明顯幾乎不產(chǎn)生額外熱量效率接近 100%只需一個(gè) GPIO 引腳而且 STM32 的定時(shí)器硬件自動(dòng)產(chǎn)生 PWM 波形CPU 完全不用管可以在調(diào)節(jié)間隙去干別的事。這也是為什么幾乎所有 LED 驅(qū)動(dòng)、電機(jī)驅(qū)動(dòng)方案都在用 PWM。1.2 方案選型按鍵掃描用輪詢還是中斷定了用 PWM 之后還有個(gè)關(guān)鍵選擇按鍵怎么讀很多入門同學(xué)的第一反應(yīng)是“用外部中斷”。因?yàn)槁牭健鞍存I就是輸入”覺得外部中斷高級(jí)。在這個(gè)項(xiàng)目里我卻更推薦用定時(shí)器輪詢掃鍵。原因很簡(jiǎn)單這個(gè)項(xiàng)目只需要一個(gè)按鍵輪詢掃描的代碼邏輯非常清晰配合定時(shí)器固定周期去讀一次引腳天然就帶“采樣”的概念。外部中斷適合按鍵需要“立即響應(yīng)”的場(chǎng)景比如急停按鈕但從按下到識(shí)別出來單片機(jī)執(zhí)行一小段代碼也就是微秒級(jí)別的事人的手指頭根本感覺不出差別。中斷里最忌諱做耗時(shí)的操作比如延時(shí)消抖。如果你在中斷里用了 delay很容易造成主循環(huán)卡死或者其它中斷丟失這是新手最容易踩的坑。所以我最終選擇的方案是TIM 定時(shí)器產(chǎn)生 PWM 信號(hào)驅(qū)動(dòng) LED再用一個(gè)定時(shí)器或者復(fù)用同一個(gè)定時(shí)器的更新中斷周期性掃描按鍵配合軟件消抖和一個(gè)簡(jiǎn)單的狀態(tài)機(jī)來改變目標(biāo)占空比。2. 硬件細(xì)節(jié)解析按鍵電路、LED 驅(qū)動(dòng)與消抖2.1 STM32 按鍵電路怎么接才算合理按鍵電路看著簡(jiǎn)單但連接方式直接決定你要不要開內(nèi)部上拉以及代碼里怎么判斷“按下”。STM32 的 GPIO 在輸入模式下有三種常見接法接法電路形式按下時(shí)電平空閑時(shí)電平代碼判斷方式 A內(nèi)部上拉GPIO 配置為上拉輸入按鍵接 GND低電平高電平讀引腳為 0 表示按下方式 B外部上拉GPIO 浮空輸入 外部 10kΩ 上拉電阻按鍵接 GND低電平高電平讀引腳為 0 表示按下方式 C外部下拉GPIO 浮空輸入 外部 10kΩ 下拉電阻按鍵接 VCC高電平低電平讀引腳為 1 表示按下我最推薦方式 A電路最干凈只用一個(gè)按鍵兩條線內(nèi)部上拉是 STM32 硬件自帶的能力完全夠用。接線時(shí)按鍵一端接 GND另一端接 PA0 之類的 GPIO 引腳。這里有一個(gè)非常容易被忽略的點(diǎn)不要用“外部上拉 3.3V 直接進(jìn) GPIO”以外的做法去省電阻。有些同學(xué)圖省事按鍵一端接 3.3V一端接 GPIO也不開內(nèi)部上拉結(jié)果就是引腳懸空電平隨機(jī)跳變按鍵按下沒反應(yīng)松手反而觸發(fā)了。這種“引腳浮空”的錯(cuò)誤在初學(xué)者里出現(xiàn)率極高。2.2 LED 驅(qū)動(dòng)電路能不能直接把燈接在 IO 上很多人直接拿一個(gè) LED 串上一個(gè)電阻接到 STM32 的引腳上這是沒問題的但前提是電流不能超限。以 STM32F103 為例GPIO 的灌電流和拉電流能力大概在 ±25mA 左右實(shí)際上我們通常把工作電流控制在 10mA-20mA 以內(nèi)避免長(zhǎng)期滿負(fù)荷運(yùn)行導(dǎo)致芯片發(fā)熱或者引腳老化。單個(gè) LED 正常工作電流在 5-20mA 之間所以一個(gè) LED 直連引腳是可行的。電阻值怎么選LED 紅色普通指示燈的壓降大約 1.8V-2.0VSTM32 GPIO 輸出高電平大約 3.3V如果目標(biāo)電流是 10mA那么串聯(lián)電阻就是R (3.3V - 2.0V) / 0.01A 130Ω取整后選 100Ω 或 220Ω 都能接受。220Ω 時(shí)電流大約 6mA亮度偏暗一些但也足夠用100Ω 時(shí)電流約 13mA更亮一點(diǎn)。我自己習(xí)慣用 220Ω省電而且足夠肉眼觀察。如果你以后要用大功率 LED 或者 LED 燈帶那就不建議直接接在 GPIO 上了得用三極管或 MOS 管做驅(qū)動(dòng)開關(guān)這時(shí)候?qū)iT的高效 LED 驅(qū)動(dòng)芯片或者專用的恒流驅(qū)動(dòng)芯片會(huì)是更好的選擇但那是另一個(gè)話題了。2.3 按鍵消抖到底在消什么說到按鍵就繞不開“消抖”。很多人剛開始不理解一個(gè)機(jī)械開關(guān)怎么會(huì)有抖動(dòng)手指按下去的那一下金屬簧片不是一秒鐘之內(nèi)就穩(wěn)定閉合的而是在閉合和斷開之間來回彈跳幾次整個(gè)過程可能持續(xù) 5-20ms。如果不做處理單片機(jī)會(huì)以為你按了好幾次鍵——本來想調(diào)節(jié)一檔亮度結(jié)果直接跳了好幾檔。消抖有兩種思路硬件消抖在按鍵兩端并聯(lián)一個(gè) 0.1uF 的電容利用電容兩端電壓不能突變的特性把抖動(dòng)濾掉。好處是 CPU 代碼簡(jiǎn)單壞處是增加元件成本而且電容選大了會(huì)影響按鍵的響應(yīng)速度。軟件消抖目前最通用的做法是“延時(shí)消抖”初次檢測(cè)到按鍵引腳電平變化后延時(shí) 10-20ms再次讀取確認(rèn)。更高級(jí)的做法是狀態(tài)機(jī)掃描每 5ms 或者 10ms 掃描一次按鍵只有連續(xù)兩次以上電平才認(rèn)為有效。這也是我實(shí)際項(xiàng)目里最推薦的在主循環(huán)或定時(shí)器中斷里按固定周期去采樣既不阻塞 CPU又能保證消抖效果。3. 實(shí)操過程與核心環(huán)節(jié)實(shí)現(xiàn)3.1 用 STM32CubeMX 快速搭建工程現(xiàn)在做 STM32 開發(fā)基本都離不開 STM32CubeMX。這里我以最常見的 STM32F103C8T6 核心板為例把配置步驟列出來每一步都說清楚為什么。時(shí)鐘配置F103 最高主頻 72MHz一般直接在 Clock Configuration 里把 HSE 選為 Crystal/Ceramic Resonator然后把 HCLK 設(shè)為 72MHz軟件會(huì)自動(dòng)分配好 PLL 參數(shù)。定時(shí)器配置我們需要一路 PWM 輸出。TIM2、TIM3、TIM4 都可以。我選 TIM3配置 Channel 1 為 PWM Generation CH1輸出引腳通常是 PA6。如果你的板子上 PA6 剛好連著其它外設(shè)也可以換 CH2引腳作相應(yīng)調(diào)整。PWM 頻率我設(shè)置在 1kHz也就是 Period 設(shè)置為 999Prescaler 設(shè)置為 71這樣 72MHz / 72 / 1000 1kHz占空比的最小調(diào)節(jié)精度是 0.1%。按鍵引腳配置按鍵接 PA0GPIO 模式選 Input mode with Pull-up/Pull-down然后在上拉下拉選擇里勾選 Pull-up。這樣按鍵就是高電平空閑按下變低。生成代碼的時(shí)候我習(xí)慣勾選 “Generate peripheral initialization as a pair of .c/.h files per peripheral”這樣每個(gè)外設(shè)的初始化代碼獨(dú)立成文件看起來清爽后期維護(hù)也方便。3.2 核心邏輯代碼狀態(tài)機(jī) 占空比調(diào)節(jié)工程生成之后就要寫主邏輯了。我的思路是這樣的用全局變量 g_target_duty 記錄當(dāng)前目標(biāo)占空比初始值是 20。每次檢測(cè)到按鍵短按就把目標(biāo)占空比加 10達(dá)到 100 后回到 0。檢測(cè)按鍵安排在 TIM 更新中斷里做每 10ms 進(jìn)入一次掃描 PA0 電平并做軟件消抖。主循環(huán)里只需要做一件事調(diào)用 __HAL_TIM_SET_COMPARE 把目標(biāo)占空比寫入定時(shí)器比較寄存器LED 亮度立刻改變。核心代碼長(zhǎng)這樣/* main.h 或者 main.c 里定義 */ #define LED_PWM_TIM htim3 #define LED_PWM_CH TIM_CHANNEL_1 #define KEY_GPIO_PORT GPIOA #define KEY_PIN GPIO_PIN_0 uint8_t g_target_duty 20; /* 目標(biāo)占空比單位 % */ uint8_t g_key_state 0; /* 當(dāng)前按鍵狀態(tài)1 表示按下且已確認(rèn) */ uint8_t g_key_prev 0; /* 上一次采樣電平用于消抖判斷 */ uint8_t g_key_debounce_cnt 0; /* 在定時(shí)器更新中斷回調(diào)里調(diào)用 */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim htim3) /* 這里用的是同一個(gè)定時(shí)器復(fù)用做按鍵掃描 */ { KeyScan_10ms(); } } void KeyScan_10ms(void) { uint8_t level HAL_GPIO_ReadPin(KEY_GPIO_PORT, KEY_PIN); /* 軟件消抖確認(rèn)翻轉(zhuǎn)后需要連續(xù) 2 次采樣有效 */ if (level ! g_key_prev) { g_key_debounce_cnt; if (g_key_debounce_cnt 2) { g_key_debounce_cnt 0; g_key_prev level; if (level 0) /* 確認(rèn)按下 */ { g_key_state 1; /* 置按鍵事件標(biāo)志 */ } } } else { g_key_debounce_cnt 0; } } int main(void) { /* ... 初始化代碼由 CubeMX 生成 ... */ HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1); /* 啟動(dòng) PWM 輸出 */ __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, 200); /* 初次設(shè)為 20% 占空比 */ while (1) { if (g_key_state) { g_key_state 0; /* 調(diào)節(jié)目標(biāo)占空比 */ g_target_duty 10; if (g_target_duty 100) { g_target_duty 0; } /* 寫入比較寄存器 */ uint32_t compare (uint32_t)g_target_duty * (TIM3-ARR 1) / 100; __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, compare); /* 用串口或者調(diào)試器觀察當(dāng)前值便于調(diào)試 */ printf(Duty: %d%%\r\n, g_target_duty); } } }這里有一個(gè)重點(diǎn)要說計(jì)算比較寄存器值。很多人直接把占空比數(shù)字當(dāng)比較值用結(jié)果 LED 亮度要么永遠(yuǎn)不亮要么永遠(yuǎn)全亮。原因很簡(jiǎn)單——比較值和占空比不是一回事。如果你的定時(shí)器 ARR 是 999那么 50% 占空比對(duì)應(yīng)比較值應(yīng)該是 499 或 500而不是 50。我用compare g_target_duty * (ARR 1) / 100這一步就是把百分?jǐn)?shù)映射成定時(shí)器的實(shí)際比較值這樣占空比變量可以一直用 0-100 的整數(shù)來表示邏輯更清晰。3.3 我踩過的坑引腳重映射和 PWM 頻率選擇第一次做這個(gè)項(xiàng)目的時(shí)候我在 TIM3 的 PWM 引腳上栽了跟頭。F103 上 TIM3_CH1 默認(rèn)映射在 PA6但如果你在 CubeMX 里不小心勾了 Remap引腳就變成了 PB4。板子上 PA6 和 PB4 旁邊都有焊盤插線的時(shí)候很容易插錯(cuò)結(jié)果 LED 怎么都不亮。解決的辦法很簡(jiǎn)單回到 CubeMX 的 Pinout 視圖波形圖標(biāo)上點(diǎn)擊一下看它提示的實(shí)際引腳號(hào)對(duì)照原理圖確認(rèn)最終位置。這是我在新手時(shí)期養(yǎng)成的最重要的習(xí)慣之一。PWM 頻率的選擇也有講究。人眼對(duì) 100Hz 以下的光源閃爍比較敏感所以 PWM 頻率至少要選 500Hz 以上1kHz 是很穩(wěn)妥的選擇。如果結(jié)構(gòu)上有振動(dòng)比如電機(jī)帶動(dòng) LED頻率太低會(huì)出現(xiàn)“頻閃”現(xiàn)象看起來 LED 沒抖動(dòng)但拍照或高速運(yùn)動(dòng)時(shí)能看到明顯的條紋?,F(xiàn)在很多 LED 驅(qū)動(dòng)做到 20kHz 以上一是避開人耳聽覺范圍二是讓手機(jī)攝像頭也拍不出條紋不過 1kHz 對(duì)我們這個(gè)項(xiàng)目足夠用了。4. 常見問題與排查技巧實(shí)錄做這個(gè)項(xiàng)目每個(gè)人幾乎都會(huì)遇到下面幾個(gè)問題我按出現(xiàn)頻率從高到低排個(gè)序把排查思路寫出來?,F(xiàn)象可能原因排查方法按鍵按下去沒反應(yīng)GPIO 模式配錯(cuò)浮空輸入、引腳搞錯(cuò)、按鍵另一端沒接 GND用萬用表量按鍵引腳對(duì)地電壓按下時(shí)應(yīng)變低LED 永遠(yuǎn)不亮PWM 沒啟動(dòng)、比較值一直是 0、引腳復(fù)用配置錯(cuò)先查 GPIO 電平用示波器看 PWM 波形亮度只能跳兩檔按鍵消抖沒做好連續(xù)觸發(fā)縮短消抖時(shí)間檢查按鍵采樣邏輯輸出占空比正確但亮度不對(duì)LED 接錯(cuò)限流電阻、電流方向反了確認(rèn) LED 正負(fù)極確認(rèn)電阻阻值編譯下載時(shí)報(bào)錯(cuò)調(diào)試器連接問題檢查 ST-Link 接線和驅(qū)動(dòng)4.1 按鍵按下去沒反應(yīng)最先查什么八成是硬件接線問題。我之前見過一個(gè)同學(xué)把按鍵接在 GND 和 PB1 上代碼里卻是讀 PA0查了半小時(shí)才發(fā)現(xiàn)。所以我的習(xí)慣是先量引腳空閑電平和按下電平確認(rèn)引腳編號(hào)和代碼一致再看軟件邏輯。還有個(gè)常見問題用 CubeMX 配置時(shí)按鍵引腳沒有勾選 Pull-up 上拉。此時(shí)引腳懸空STM32 內(nèi)部既沒有上拉也沒有下拉讀到的電平完全不確定。這樣按鍵按下去可能偶爾有反應(yīng)松手之后也可能誤觸發(fā)。排查方式是按下前先打印引腳電平看是不是穩(wěn)定在高電平。4.2 LED 亮度調(diào)節(jié)不線性中間檔位跳變明顯如果你把占空比從 10 調(diào)到 20肉眼幾乎看不出變化但從 80 調(diào)到 90變化卻很明顯這是因?yàn)槿搜鄣牧炼雀兄皇蔷€性的而是近似對(duì)數(shù)的關(guān)系。也就是說占空比從 20% 變到 40%看起來像是在“變亮初期”而 80% 到 90% 的變化就很刺激。實(shí)際操作中如果你發(fā)現(xiàn)檔位之間跳躍太明顯可以考慮做一個(gè)非線性映射表讓低亮度區(qū)間的調(diào)節(jié)步長(zhǎng)小一些。但說實(shí)話業(yè)余項(xiàng)目用線性調(diào)節(jié)也完全能用只是“體驗(yàn)細(xì)節(jié)”有差別。這里也引出一個(gè)思考為什么那么多燈具控制器都支持“無極調(diào)光”本質(zhì)上就是調(diào)整 PWM 占空比的映射曲線讓人眼覺得亮度的變化是均勻的。4.3 編譯下載報(bào)錯(cuò)error: no stm32 target found這個(gè)錯(cuò)誤我剛開始玩 ST-Link 的時(shí)候經(jīng)常遇到問題基本都出在調(diào)試器連接上。檢查步驟我總結(jié)了四步看 ST-Link 的 SWDIO、SWCLK、GND、3.3V 四個(gè)引腳有沒有接對(duì)SWDIO 和 SWCLK 交叉接錯(cuò)的概率特別高確認(rèn)板子供電是否正常很多 ST-Link 和板子共用一個(gè) USB 供電如果 USB 口供電不足或者接觸不良就找不到 target在 Keil 或 STM32CubeProgrammer 里把調(diào)試接口選成 SW 模式不要選 JTAG如果還是不行按住板子上的復(fù)位鍵不放點(diǎn)擊下載等進(jìn)度條出現(xiàn)后再松開復(fù)位這個(gè)方法能救回很多“鎖死”的芯片。4.4 PWM 波形輸出異常有示波器的話這個(gè)問題特別好排查。沒有示波器就把 LED 串一個(gè)低阻值電阻用萬用表的交流電壓檔去測(cè)一端如果電壓波動(dòng)明顯不正常那大概率是——定時(shí)器沒啟動(dòng)。很多人初始化完之后忘了調(diào)用HAL_TIM_PWM_StartGPIO 電平是有的但就是沒有方波LED 一直不亮。GPIO 復(fù)用沒配置。CubeMX 里生成了代碼但你在手寫工程里忘了把 GPIO 配置成復(fù)用推挽模式。重映射搞錯(cuò)。F103 系列 TIM 引腳有重映射功能配置不對(duì)會(huì)輸出到錯(cuò)誤引腳上。5. 從按鍵調(diào)光到更多有趣玩法5.1 改成呼吸燈按鍵控制亮度做熟之后把項(xiàng)目擴(kuò)展成呼吸燈特別有意思。邏輯非常簡(jiǎn)單主循環(huán)里用一個(gè)變量讓占空比從 0 緩慢加到 100再從 100 減到 0周而復(fù)始。每次調(diào)節(jié)的時(shí)候不要太快建議每 5ms 改變 1-2 個(gè)百分點(diǎn)的占空比這樣視覺上才像呼吸。int dir 1; uint16_t duty 0; while (1) { __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, duty * 10); HAL_Delay(5); duty dir; if (duty 100) dir -1; if (duty 0) dir 1; }這段代碼我在開發(fā)板上實(shí)測(cè)下來效果很自然不會(huì)感覺到跳變推薦有興趣的同學(xué)試試。5.2 長(zhǎng)短按和檔位記憶按鍵連續(xù)循環(huán)調(diào)光最大的問題是從最暗調(diào)回最亮中間要按十幾次。一個(gè)更實(shí)用的做法是支持長(zhǎng)按連續(xù)調(diào)節(jié)按下超過 500ms 后每隔 100ms 自動(dòng)加一檔松手就停在當(dāng)前檔位。實(shí)現(xiàn)起來也不復(fù)雜掃描按鍵時(shí)記錄按下時(shí)間戳在主循環(huán)中判斷如果當(dāng)前處于“按下狀態(tài)”且時(shí)間差超過閾值就自動(dòng)執(zhí)行一次檔位遞增。這樣從最暗調(diào)到最亮只需要按住兩三秒體驗(yàn)好很多。檔位記憶也不難利用 STM32 內(nèi)部 Flash 的一個(gè)扇區(qū)每次檔位改變后把值寫進(jìn)去下次上電直接讀出來初始化。不過寫 Flash 要注意擦寫壽命頻繁寫入會(huì)磨損 Flash實(shí)際產(chǎn)品里通常把“最后保存的檔位”延遲 5 秒再寫入避免每次微調(diào)都觸發(fā)擦寫。5.3 使用中斷讀取按鍵的注意事項(xiàng)如果你確實(shí)要用外部中斷我建議的方式是中斷服務(wù)函數(shù)里只設(shè)置標(biāo)志位不做消抖延時(shí)也不改變 PWM 占空比退出中斷后在主循環(huán)里檢測(cè)到標(biāo)志位再執(zhí)行 10ms 的軟件消抖和后續(xù)邏輯。這樣既保證了按鍵響應(yīng)的及時(shí)性又不會(huì)阻塞主循環(huán)。我自己測(cè)試過用外部中斷做按鍵調(diào)節(jié)亮度體驗(yàn)上和輪詢掃描幾乎沒有差別。這個(gè)方案在低功耗設(shè)備里會(huì)更重要因?yàn)橹餮h(huán)可能大部分時(shí)間都處于休眠狀態(tài)依靠中斷喚醒。最后這個(gè)項(xiàng)目做完之后我最大的感受是嵌入式入門不要追求“高大上”把按鍵、PWM、定時(shí)器這幾塊基本功吃透后面很多項(xiàng)目都是這幾個(gè)知識(shí)點(diǎn)換著花樣組合。控制 LED 亮度只是 PWM 的一個(gè)應(yīng)用同樣的思路放到直流電機(jī)調(diào)速、舵機(jī)角度控制、逆變器輸出上只是驅(qū)動(dòng)電路和參數(shù)不一樣核心邏輯完全通用。如果條件允許建議你買一個(gè)邏輯分析儀或者二手示波器把按鍵按下瞬間的波形抓出來看看親眼看一下“抖動(dòng)”是什么樣子觀察 PWM 占空比變化時(shí)波形寬度的變化這個(gè)體驗(yàn)比看十篇文章都有用。調(diào)好看一眼波形你心里就特別有底了。本文還有配套的精品資源點(diǎn)擊獲取