扇系統(tǒng)設(shè)計與實現(xiàn))
1. 為什么“風(fēng)扇”值得用STM32重新做一遍夏天實驗室里的桌面風(fēng)扇有幾種打開淘寶搜索你會發(fā)現(xiàn)絕大多數(shù)所謂“智能風(fēng)扇”其實只有三檔調(diào)速、定時關(guān)機(jī)、香薰功能稱不上“智能”。再拆開看內(nèi)部主控芯片往往是專用風(fēng)扇IC或者一顆普通的8位單片機(jī)人機(jī)交互只有幾個輕觸按鍵。但當(dāng)你開始準(zhǔn)備單片機(jī)畢業(yè)設(shè)計把“風(fēng)扇”作為課題對象時會發(fā)現(xiàn)它是一個非常適合做系統(tǒng)設(shè)計訓(xùn)練的載體。這個題目不算難但麻雀雖小五臟俱全。它同時涉及輸入檢測、輸出控制、電機(jī)驅(qū)動、顯示、電源管理、異常保護(hù)和低功耗策略幾乎可以把STM32單片機(jī)應(yīng)用的核心知識點都串起來。從技術(shù)選型來看用STM32做智能風(fēng)扇有一個明顯的優(yōu)勢它不需要像驅(qū)動伺服電機(jī)那樣處理復(fù)雜的運動控制也不像做物聯(lián)網(wǎng)網(wǎng)關(guān)那樣需要接觸大量通信協(xié)議但它能讓你把 GPIO、ADC、定時器、PWM、外部中斷、I2C/SPI 等外設(shè)全部實際用一遍。更重要的是風(fēng)扇這類設(shè)備天然適合“綠色節(jié)能”主題你可以順理成章地引入溫度檢測、人體感應(yīng)、自動調(diào)速、睡眠模式、低功耗設(shè)計等機(jī)制。一句話判斷這是一個性價比極高的畢業(yè)設(shè)計選題。比單純做個FM收音機(jī)或電子時鐘能展示的知識點更多又比做無人機(jī)飛控、平衡小車這類難度波動大的項目更可控做出來還方便現(xiàn)場演示。這篇文章會從系統(tǒng)總體方案開始逐步拆解硬件選型、主要電路設(shè)計、STM32的軟件架構(gòu)和核心代碼實現(xiàn)、溫控策略、調(diào)試方法和實測驗證思路。如果你正在準(zhǔn)備單片機(jī)課程設(shè)計或畢業(yè)設(shè)計可以直接把本文當(dāng)作一套從零開始的實現(xiàn)參考。需要提醒的是文中的代碼以STM32F103系列Cortex-M3內(nèi)核為例但整體思路遷移到STM32F4、G0、L0系列也同樣適用寄存器級別的寫法在不同系列上略有差異我會在相關(guān)地方單獨說明。2. 智能綠色風(fēng)扇系統(tǒng)的總體方案設(shè)計2.1 這個項目要解決哪些問題做任何單片機(jī)系統(tǒng)前先回答一個問題系統(tǒng)需要感知哪些信息執(zhí)行哪些動作如何被人操作如何應(yīng)對異常。智能綠色風(fēng)扇拆解下來核心需求如下自動調(diào)速根據(jù)環(huán)境溫度自動調(diào)節(jié)風(fēng)扇轉(zhuǎn)速溫度高時轉(zhuǎn)速快溫度低時轉(zhuǎn)速慢。人體感應(yīng)有人靠近時風(fēng)扇正常工作人離開一段時間后風(fēng)扇自動進(jìn)入待機(jī)或停機(jī)狀態(tài)避免浪費電。手動控制用戶不想要自動模式時可以手動切換風(fēng)速和模式這需要按鍵輸入。狀態(tài)顯示至少能顯示當(dāng)前模式、風(fēng)速檔位、環(huán)境溫度等信息通常用LED、數(shù)碼管或OLED屏。節(jié)能處理在持續(xù)無人狀態(tài)下進(jìn)入低功耗模式或在夜間自動降低最大轉(zhuǎn)速限制體現(xiàn)“綠色”主題。安全保護(hù)電機(jī)堵轉(zhuǎn)保護(hù)、驅(qū)動電路過流保護(hù)、軟件看門狗。整理成系統(tǒng)框圖大致是這樣的傳感器部分溫度傳感器讀取環(huán)境溫度人體紅外傳感器PIR檢測人體存在。輸入部分按鍵提供模式切換、風(fēng)速加減、開關(guān)機(jī)操作??刂坪诵腟TM32負(fù)責(zé)數(shù)據(jù)采集、模式判斷和控制信號輸出。執(zhí)行部分通過PWM控制MOS管或電機(jī)驅(qū)動芯片進(jìn)而控制直流無刷風(fēng)扇或普通直流電機(jī)風(fēng)扇。顯示部分OLED或數(shù)碼管顯示運行狀態(tài)。電源部分適配器供電或USB供電前端經(jīng)過降壓穩(wěn)壓給單片機(jī)和外設(shè)供電。2.2 核心設(shè)計判斷選直流無刷還是普通直流電機(jī)這是整個項目里第一個值得講清楚的關(guān)鍵選擇也是很多人容易糾結(jié)的地方。市面上的智能風(fēng)扇多使用直流無刷電機(jī)BLDC因為這類電機(jī)效率高、噪聲小、壽命長而且能通過PWM調(diào)速。但如果你把它拿到本科畢業(yè)設(shè)計的語境里看使用無刷電機(jī)意味著你需要增加無刷驅(qū)動電路或者購買帶驅(qū)動板的無刷電機(jī)模塊這會引入換相控制、反電動勢檢測等額外復(fù)雜點。如果你的目標(biāo)是體驗完整的“單片機(jī)系統(tǒng)設(shè)計”其實使用普通直流電機(jī)加帶PWM調(diào)速的驅(qū)動方案就夠了。普通直流電機(jī)成本低、控制簡單只需要調(diào)節(jié)PWM占空比改變平均電壓就可以實現(xiàn)轉(zhuǎn)速變化配合霍爾傳感器或紅外對管測速還可以做一個簡單的閉環(huán)調(diào)速。但從“綠色節(jié)能”的角度無刷電機(jī)更契合題目立意。如果預(yù)算充足或者實驗室有現(xiàn)成模塊可以在樣機(jī)階段先使用普通直流電機(jī)驗證全部邏輯再把執(zhí)行機(jī)構(gòu)替換為帶驅(qū)動板的無刷風(fēng)扇模塊。軟件層面只需調(diào)整PWM頻率或控制引腳極性主邏輯不用大改。這個“先驗證邏輯再替換執(zhí)行機(jī)構(gòu)”的開發(fā)思路是實際工程項目中很常用的做法也能在畢業(yè)設(shè)計的答辯中體現(xiàn)工程思維。2.3 STM32型號怎么選針對這個題目首選STM32F103C8T6。原因很簡單資料最豐富開發(fā)板最便宜例程最全面Keil MDK環(huán)境下的支持非常成熟。雖然它的主頻只有72MHzFlash只有64KB但對于風(fēng)扇控制這種任務(wù)完全夠用。如果你后續(xù)想加藍(lán)牙、WIFI模塊做手機(jī)APP控制也能通過串口擴(kuò)展不需要換芯片。需要的外設(shè)資源清單如下PWM輸出至少1路用來控制電機(jī)調(diào)速。最好使用定時器的PWM輸出通道比如TIM2_CH1或TIM3_CH2。ADC采集至少1路如果使用NTC熱敏電阻測溫需要ADC讀取分壓電壓如果使用數(shù)字溫度傳感器如DS18B20則用GPIO模擬單總線協(xié)議不需要ADC。GPIO輸入按鍵至少2到4個PIR人體感應(yīng)1路。I2C接口如果使用0.96寸OLED顯示屏通常走I2C占用兩個引腳。定時器輸入捕獲如果做測速閉環(huán)還需要一個定時器通道配合編碼器或霍爾脈沖測速。選擇F103C8T6還有一個好處引腳數(shù)量48pin在最小系統(tǒng)板上就能把上述外設(shè)全部引出來合適。2.4 操作模式和狀態(tài)機(jī)設(shè)計為了不讓主程序變成一團(tuán)亂麻做單片機(jī)軟件時一定要先定義狀態(tài)和遷移關(guān)系。本設(shè)計建議定義以下狀態(tài)開機(jī)態(tài)系統(tǒng)上電完成初始化顯示歡迎信息默認(rèn)進(jìn)入自動模式。手動模式用戶可以通過按鍵選擇低、中、高三檔風(fēng)速或關(guān)閉風(fēng)扇。自動模式系統(tǒng)根據(jù)實時溫度自動設(shè)定目標(biāo)風(fēng)速檔位。待機(jī)態(tài)人體傳感器檢測到無人持續(xù)超過設(shè)定時間系統(tǒng)進(jìn)入低功耗待機(jī)等待人體感應(yīng)信號喚醒。異常態(tài)電機(jī)堵轉(zhuǎn)或驅(qū)動電路異常時關(guān)閉PWM輸出進(jìn)入保護(hù)狀態(tài)等待按鍵復(fù)位。這些狀態(tài)不一定都對應(yīng)代碼里的一個switch-case枚舉但狀態(tài)機(jī)思維可以顯著降低邏輯混亂。如果所有判斷都堆在主循環(huán)的if-else里后期加新功能時很容易出問題。3. 硬件電路與模塊選型3.1 整體硬件清單從零搭建這個系統(tǒng)建議按下面的清單準(zhǔn)備硬件。這些模塊在大多數(shù)單片機(jī)實驗室都能找到網(wǎng)上采購也很方便。模塊推薦型號/方案說明主控STM32F103C8T6最小系統(tǒng)板藍(lán)板或自制最小系統(tǒng)電機(jī)普通直流電機(jī)或12V直流無刷風(fēng)扇優(yōu)先帶驅(qū)動方案電機(jī)驅(qū)動L9110S / TB6612 / MOS管 續(xù)流二極管小功率電機(jī)用L9110S即可溫度采集DS18B20防水型或NTC熱敏電阻注意上拉電阻人體感應(yīng)HC-SR501 PIR模塊可調(diào)延時和靈敏度顯示0.96寸I2C OLED (SSD1306)顯示信息豐富接線簡單按鍵輕觸按鍵 x4需軟件消抖電源USB 5V 或 12V適配器取決于電機(jī)額定電壓測速可選紅外對管 碼盤用于閉環(huán)調(diào)速擴(kuò)展3.2 電源設(shè)計要點電源是實際調(diào)試中第一個容易踩坑的環(huán)節(jié)。STM32和傳感器一般需要3.3V供電可以直接從最小系統(tǒng)板的穩(wěn)壓器獲取電機(jī)驅(qū)動電源要單獨考慮。如果使用5V直流電機(jī)可以共用USB的5V但必須注意電流。小功率玩具電機(jī)空載電流可能只有幾百毫安堵轉(zhuǎn)時可能上升到1A甚至更高如果直接從開發(fā)板的5V引腳取電很容易把板載穩(wěn)壓器燒掉。更穩(wěn)妥的做法是電機(jī)電源和單片機(jī)電源分開比如使用12V適配器給電機(jī)驅(qū)動模塊供電再通過降壓模塊轉(zhuǎn)5V給單片機(jī)。驅(qū)動模塊的電源輸入端并聯(lián)一個大電容例如470uF/25V可以有效抑制電機(jī)啟動瞬間的電壓跌落和換向噪聲。L9110S或TB6612這類驅(qū)動模塊通常帶邏輯電源引腳需要接單片機(jī)的3.3V或5V作為邏輯參考使用前務(wù)必查看模塊的接線說明。驅(qū)動電源沒接、邏輯地沒共地、電機(jī)啟動導(dǎo)致單片機(jī)復(fù)位這三件事占了電機(jī)驅(qū)動調(diào)試故障的一大半。3.3 電機(jī)驅(qū)動電路與GPIO控制如果使用L9110S模塊控制邏輯很簡單模塊有兩個輸入引腳通常標(biāo)記為IA和IB。給IA高電平、IB低電平電機(jī)正轉(zhuǎn)反過來反轉(zhuǎn)兩者都低電平則停止。PWM調(diào)速時把其中一個引腳接STM32的PWM輸出另一個引腳固定拉低即可通過占空比調(diào)速。這里有一個新手容易不懂的細(xì)節(jié)不要直接用一個GPIO接PWM另一個也接PWM以為可以“差速”。對于單電機(jī)只需要調(diào)速的場景一個PWM引腳加一個方向引腳就夠了。電機(jī)是感性負(fù)載無論使用模塊還是自己搭MOS管驅(qū)動都要確保有續(xù)流二極管。如果沒有續(xù)流二極管電機(jī)斷電瞬間產(chǎn)生的反向電動勢可能擊穿MOS管或干擾單片機(jī)供電。L9110S、TB6612模塊內(nèi)部已經(jīng)包含續(xù)流二極管因此更推薦直接用模塊減少硬件調(diào)試難度。3.4 PIR人體感應(yīng)模塊的接線與注意點HC-S501是最常見的PIR模塊輸出數(shù)字信號檢測到人體運動時輸出高電平。模塊上有兩個電位器一個調(diào)節(jié)靈敏度一個調(diào)節(jié)輸出高電平保持時間通常可以調(diào)到幾秒到幾百秒。把這個模塊接到STM32的一個GPIO輸入引腳即可。需要注意的是PIR檢測的是“運動”而不是“存在”。如果人長時間靜坐不動PIR可能檢測不到導(dǎo)致系統(tǒng)誤判為無人。解決方式有兩種在PIR輸出高電平后軟件開啟一個較長的“占用計時器”只要期間再次檢測到運動就刷新計時。結(jié)合另一路傳感器比如微波雷達(dá)模塊或熱釋電陣列但會增加成本。對于課程設(shè)計來說使用PIR加軟件計時延長邏輯就夠用了。軟件里把“無人持續(xù)檢測時長閾值”默認(rèn)配置為3到5分鐘避免測試時風(fēng)扇頻繁開關(guān)造成體驗不好。3.5 顯示模塊的選型顯示方案我建議用0.96寸I2C OLEDSSD1306而不是數(shù)碼管。原因有幾個OLED顯示內(nèi)容靈活能同時顯示溫度、模式、風(fēng)速檔位、人體感應(yīng)狀態(tài)演示效果直觀。I2C接口只占用兩個引腳接線簡單。網(wǎng)上SSD1306的驅(qū)動代碼非常多移植成本低。功耗低不影響“綠色節(jié)能”主題。如果實驗室條件有限也可以使用LCD1602。但LCD1602需要8個或4個數(shù)據(jù)引腳而且無法顯示中文OLED在同等尺寸下視覺效果更好。部分ST7789屏幕需要SPI接口且驅(qū)動時序更復(fù)雜不建議在這個項目上占用過多精力。4. STM32環(huán)境搭建與工程初始化4.1 開發(fā)環(huán)境與工具鏈如果你的開發(fā)環(huán)境還沒有準(zhǔn)備好下面是當(dāng)前比較主流的組合Keil MDK-ARMSTM32開發(fā)最常用的IDE資料多調(diào)試方便。STM32CubeMX用于圖形化配置時鐘、GPIO、定時器等外設(shè)自動生成初始化代碼。STM32CubeProgrammer或ST-Link Utility用于下載程序。調(diào)試器ST-Link V2便宜、穩(wěn)定兼容性好適合F103系列。在Keil安裝和Pack版本選擇上有一個常見坑STM32F103屬于Cortex-M3內(nèi)核需要安裝對應(yīng)的Device Pack。如果打開工程提示找不到芯片多半是Pack沒有安裝或版本不匹配。建議從Keil官網(wǎng)Pack Installer中安裝“Keil.STM32F1xx_DFP”最新穩(wěn)定版。要注意兼容C51和STM32的Keil軟件安裝方式可能會遇到許可證、Pack路徑等問題建議單獨安裝不要混用。STM32CubeMX并非必須但對工程項目幫助很大。你可以在CubeMX里完成引腳功能分配、時鐘頻率設(shè)置再生成MDK工程。這樣至少不會在引腳配置代碼上出錯也方便后期切換引腳。4.2 時鐘樹配置STM32F103的外部高速晶振常見為8MHz經(jīng)過PLL倍頻后系統(tǒng)時鐘為72MHz。如果你用的最小系統(tǒng)板沒有外部晶振也可以配置為內(nèi)部HSI 8MHz但USB功能不可用定時器時間基準(zhǔn)計算也要重新?lián)Q算。很多初學(xué)者在這步容易出錯直接用系統(tǒng)默認(rèn)的HSI然后在延時函數(shù)里按72MHz計算導(dǎo)致延時不準(zhǔn)。更常見的錯誤是延時函數(shù)里沒有重新調(diào)用SystemInit或時鐘配置不對結(jié)果串口波特率錯亂、PWM頻率異常。在STM32CubeMX中配置RCC時選擇Crystal/Ceramic Resonator并把HCLK設(shè)為72MHzCubeMX會自動計算PLL參數(shù)。如果使用寄存器或標(biāo)準(zhǔn)庫開發(fā)則必須確認(rèn)SystemInit函數(shù)是否正確設(shè)置時鐘。4.3 GPIO和外部中斷配置按鍵和PIR信號適合用外部中斷方式檢測而不是在主循環(huán)里輪詢至少按鍵應(yīng)該優(yōu)先考慮中斷加消抖。GPIO配置要點按鍵引腳輸入上拉模式。PIR引腳輸入浮空或下拉模式取決于模塊輸出邏輯。OLED I2C引腳復(fù)用開漏輸出。PWM輸出引腳復(fù)用推挽輸出。配置完成后用萬用表或示波器確認(rèn)引腳電平是否符合預(yù)期。這一步很多人忽略。比如PIR模塊上電后有大約30到60秒的初始化穩(wěn)定期期間可能隨機(jī)輸出高電平。如果在穩(wěn)定期之前就去讀引腳程序可能誤判為人體出現(xiàn)把本來正常的功能破壞掉。解決辦法是在代碼里做上電延時等PIR模塊穩(wěn)定后再進(jìn)入主邏輯。5. 核心軟件邏輯與代碼實現(xiàn)5.1 軟件總體框架這個系統(tǒng)的主循環(huán)可以采用“前后臺”架構(gòu)后臺是中斷服務(wù)函數(shù)前臺是主循環(huán)輪詢。中斷負(fù)責(zé)時敏任務(wù)TIM定時中斷定時采集溫度、刷新顯示。外部中斷按鍵動作、PIR觸發(fā)事件。定時器PWM輸出電機(jī)調(diào)速不需要CPU干預(yù)。主循環(huán)負(fù)責(zé)慢速任務(wù)讀取當(dāng)前系統(tǒng)狀態(tài)。根據(jù)模式自動計算目標(biāo)風(fēng)速。執(zhí)行狀態(tài)切換邏輯。處理按鍵事件隊列。不建議在主循環(huán)里使用軟件延時函數(shù)去做長時間等待否則按鍵響應(yīng)和PIR檢測會被堵塞。如果某個函數(shù)需要等待應(yīng)該改為“標(biāo)志位加超時”的結(jié)構(gòu)化寫法。5.2 狀態(tài)機(jī)及數(shù)據(jù)定義先在頭文件里定義系統(tǒng)狀態(tài)、運行模式等枚舉// 文件路徑Src/fan_app.h #ifndef FAN_APP_H #define FAN_APP_H #include stm32f1xx_hal.h #include stdint.h typedef enum { FAN_STATE_BOOT 0, // 開機(jī)初始化態(tài) FAN_STATE_AUTO, // 自動溫控模式 FAN_STATE_MANUAL, // 手動模式 FAN_STATE_STANDBY, // 無人待機(jī)態(tài) FAN_STATE_FAULT // 故障保護(hù)態(tài) } FanState_t; typedef enum { WIND_OFF 0, WIND_LOW, WIND_MID, WIND_HIGH } WindLevel_t; typedef struct { FanState_t state; // 當(dāng)前狀態(tài) WindLevel_t windLevel; // 當(dāng)前風(fēng)速檔位 float temperature; // 環(huán)境溫度單位℃ uint8_t personDetected; // 是否檢測到人體 uint16_t noPersonTime; // 無人持續(xù)秒數(shù) uint8_t fanRunning; // 電機(jī)是否運轉(zhuǎn) } FanContext_t; extern FanContext_t g_fan; void FAN_App_Init(void); void FAN_App_Task(void); void FAN_ProcessKeyEvent(uint8_t keyId); void FAN_SetWindLevel(WindLevel_t level); #endif這個結(jié)構(gòu)體把整個系統(tǒng)的公共狀態(tài)集中起來便于主任務(wù)和其他模塊讀寫。5.3 溫度采樣邏輯溫度傳感器有很多選型DS18B20是最常見的數(shù)字方案。它使用單總線協(xié)議一根數(shù)據(jù)線既能供電又能傳數(shù)據(jù)。如果使用STM32的GPIO模擬單總線時序需要注意DS18B20對時序要求比較嚴(yán)格。一個簡化但可靠的方案是每1秒采集一次溫度。DS18B20的轉(zhuǎn)換時間默認(rèn)最長750ms如果你在發(fā)出轉(zhuǎn)換命令后立即讀取溫度值讀到的可能是上一次殘留的數(shù)據(jù)導(dǎo)致溫度跳變。正確做法是發(fā)送轉(zhuǎn)換命令后等待至少750ms再發(fā)送讀暫存器命令讀取溫度。如果使用NTC熱敏電阻配ADC則思路不同。NTC溫度-電阻特性不是線性的需要通過公式計算// 文件路徑Src/ntc_temperature.c #include adc.h #include math.h // 分壓電阻阻值單位歐姆 #define NTC_FIXED_RESISTOR 10000.0f // NTC B值例如3950 #define NTC_B_VALUE 3950.0f // 25攝氏度時的額定阻值 #define NTC_NOMINAL_RES 10000.0f #define NTC_NOMINAL_TEMP 25.0f // ADC參考電壓 #define ADC_REF_VOLTAGE 3.3f #define ADC_RESOLUTION 4095.0f float NTC_GetTemperature(uint32_t adcValue) { float voltage ((float)adcValue / ADC_RESOLUTION) * ADC_REF_VOLTAGE; float resistance NTC_FIXED_RESISTOR * (ADC_REF_VOLTAGE - voltage) / voltage; float steinhart; steinhart logf(resistance / NTC_NOMINAL_RES); steinhart / NTC_B_VALUE; steinhart 1.0f / (NTC_NOMINAL_TEMP 273.15f); steinhart 1.0f / steinhart; steinhart - 273.15f; return steinhart; }NTC方案的成本更低但需要自己處理分壓電阻選擇、ADC采樣精度和溫漂。對于課程設(shè)計DS18B20能減少不少數(shù)學(xué)公式和電路調(diào)試時間但我個人更推薦NTC因為你在做的是智能硬件了解ADC采樣與非線性標(biāo)定是單片機(jī)開發(fā)中很重要的一環(huán)。在實際工程中對ADC采樣的數(shù)據(jù)不能直接拿去算溫度需要做多次采樣取平均值并且增加中值濾波或滑動平均濾波否則讀數(shù)可能會跳動一兩度導(dǎo)致風(fēng)扇轉(zhuǎn)速來回變。這個現(xiàn)象在實驗室電源紋波較大或者引線較長時特別明顯。5.4 溫度到風(fēng)速的映射策略這里有一個邏輯層的設(shè)計判斷。溫度控制風(fēng)扇不能搞成簡單的一對一映射溫度超過30度就最高轉(zhuǎn)速低于25度就關(guān)閉。因為這樣會導(dǎo)致風(fēng)扇在臨界點附近反復(fù)橫跳實際體驗很差。更合理的做法是分段設(shè)置遲滯區(qū)間當(dāng)溫度 24℃時風(fēng)扇停止。當(dāng)24℃ 溫度 28℃時低速運行。當(dāng)28℃ 溫度 32℃時中速運行。當(dāng)溫度 32℃時高速運行。當(dāng)溫度從高回落時必須降到不同的閾值才會切換檔位。這個“遲滯”或“滯回比較”的概念和你在電力電子課上學(xué)過的施密特觸發(fā)器、空調(diào)溫控器里的邏輯是同一種思路。它能避免系統(tǒng)在臨界點反復(fù)切換。代碼實現(xiàn)可以這樣設(shè)計// 文件路徑Src/fan_temp_control.c #include fan_temp_control.h #define TEMP_HYST_UP_LOW_TO_MID 27.0f #define TEMP_HYST_DOWN_MID_TO_LOW 25.0f #define TEMP_HYST_UP_MID_TO_HIGH 30.0f #define TEMP_HYST_DOWN_HIGH_TO_MID 28.5f #define TEMP_STOP_BELOW 23.0f #define TEMP_RESTART_ABOVE 24.5f WindLevel_t FAN_ComputeTargetLevel(WindLevel_t current, float temperature) { switch (current) { case WIND_OFF: if (temperature TEMP_RESTART_ABOVE) return WIND_LOW; break; case WIND_LOW: if (temperature TEMP_STOP_BELOW) return WIND_OFF; if (temperature TEMP_HYST_UP_LOW_TO_MID) return WIND_MID; break; case WIND_MID: if (temperature TEMP_HYST_DOWN_MID_TO_LOW) return WIND_LOW; if (temperature TEMP_HYST_UP_MID_TO_HIGH) return WIND_HIGH; break; case WIND_HIGH: if (temperature TEMP_HYST_DOWN_HIGH_TO_MID) return WIND_MID; break; default: break; } return current; }用當(dāng)前的運行檔位參與判斷只有當(dāng)溫度跨越了對應(yīng)區(qū)間的閾值時才切換這樣系統(tǒng)天然就具備抗抖動能力。5.5 PWM調(diào)速與電機(jī)控制PWM頻率的選擇有一定講究。普通直流電機(jī)使用的PWM頻率一般在1kHz到20kHz之間。頻率太低電機(jī)會發(fā)出可聞的嘯叫頻率太高驅(qū)動電路開關(guān)損耗變大。對于L9110S這類模塊推薦先使用10kHz到20kHz聽不到明顯噪聲即可。如果使用STM32的TIM定時器需要根據(jù)定時器時鐘和預(yù)分頻計算最終PWM頻率。假設(shè)系統(tǒng)時鐘為72MHz使用TIM2輸出PWM配置PSC為71ARR為999那么PWM頻率為72MHz / (711) / (9991) 1kHz如果希望頻率為10kHz可以設(shè)置PSC為71ARR為99即 72MHz / 72 / 100 10kHz。要注意ARR不能小于PWM比較值。實際項目中可以用示波器直接測量引腳波形來確認(rèn)。設(shè)置占空比最簡單的做法是調(diào)用HAL庫函數(shù)// 文件路徑Src/motor_control.c #include motor_control.h #include tim.h void MOTOR_SetSpeed(uint8_t percent) { uint32_t pulse; if (percent 100) { percent 100; } pulse (uint32_t)((TIM_PERIOD_BASE 1) * percent / 100); __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, pulse); } void MOTOR_Start(void) { HAL_TIM_PWM_Start(htim2, TIM_CHANNEL_1); } void MOTOR_Stop(void) { __HAL_TIM_SET_COMPARE(htim2, TIM_CHANNEL_1, 0); }如果你的工程是用標(biāo)準(zhǔn)庫寫的邏輯也是一樣的只是接口變成了TIM_SetCompare2等函數(shù)。設(shè)定“低檔30%、中檔60%、高檔100%”之前最好先手動測試最低啟動占空比。因為每臺電機(jī)的啟動電壓不同有的電機(jī)在20%占空比下轉(zhuǎn)不起來反而會嗡嗡響并可能過流。如果出現(xiàn)電機(jī)啟動困難的情況可以給電機(jī)加一個短暫的滿占空比“啟動脈沖”比如啟動前50ms到100ms全速跑一下再回落到目標(biāo)占空比。這個技巧在直流電機(jī)控制中很常見能顯著改善低轉(zhuǎn)速啟動問題。5.6 按鍵處理與外部中斷按鍵消抖有兩種方式硬件消抖和軟件消抖。最簡單的軟件消抖是在檢測到按鍵按下后延時10ms到20ms再讀一次如果仍然是按下狀態(tài)才確認(rèn)有效。但前面說過主循環(huán)中不要用阻塞延時去做消抖。更推薦的做法在GPIO外部中斷服務(wù)函數(shù)里只置一個按鍵標(biāo)志位不執(zhí)行消抖主循環(huán)檢測到標(biāo)志后使用一個非阻塞的計時函數(shù)進(jìn)行消抖。這樣可以避免在中斷里做太多事。下面是一個簡化示例// 文件路徑Src/key_driver.c #include key_driver.h #include main.h #include fan_app.h #define KEY_DEBOUNCE_MS 20 static uint8_t keyPressedFlag 0; static uint32_t lastKeyTick 0; void Key_GpioExtiCallback(uint16_t pin) { if (pin KEY_MODE_PIN) { if (HAL_GetTick() - lastKeyTick 100) { // 簡單的連續(xù)按鍵間隔過濾 lastKeyTick HAL_GetTick(); keyPressedFlag | (1 0); } } } void Key_Process(void) { if (keyPressedFlag (1 0)) { keyPressedFlag ~(1 0); FAN_ProcessKeyEvent(KEY_ID_MODE); } }實際使用中還可以增加長按實現(xiàn)關(guān)機(jī)、短按切換模式等操作。長按檢測也需要基于HAL_GetTick或系統(tǒng)節(jié)拍實現(xiàn)禁止用純循環(huán)阻塞延時。5.7 無人自動待機(jī)與低功耗設(shè)計綠色節(jié)能最重要的體現(xiàn)是無人管理能力。PIR檢測到無人持續(xù)一段時間后程序要自動停機(jī)并進(jìn)入低功耗狀態(tài)。低功耗方案有兩種級別第一種是簡單停機(jī)。風(fēng)扇停止PWM輸出OLED進(jìn)入休眠或關(guān)閉顯示主循環(huán)進(jìn)入低頻率輪詢。這種做法實現(xiàn)簡單但STM32本身仍處于運行模式功耗大約在幾毫安到十幾毫安級別。第二種是真正的低功耗模式使用STM32的睡眠模式Sleep Mode或停機(jī)模式Stop Mode。在Sleep Mode下CPU停止執(zhí)行但外設(shè)時鐘仍然工作電流可降到毫安級別在Stop Mode下大部分外設(shè)時鐘關(guān)閉電流可以降到微安級別需要外部中斷喚醒。這個項目更推薦使用睡眠模式加RTC鬧鐘或外部中斷喚醒的方式。人不在的時候死等PIR并不環(huán)保人來了PIR引腳跳變作為外部中斷喚醒MCU系統(tǒng)迅速恢復(fù)到正常工作狀態(tài)。代碼思路void FAN_EnterStandby(void) { OLED_Clear(); OLED_ShowString(0, 0, No person, sleep); MOTOR_SetSpeed(0); OLED_Dim(0); HAL_SuspendTick(); // 配置PIR引腳為外部中斷喚醒源 HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); // 被喚醒后恢復(fù) HAL_ResumeTick(); OLED_Dim(1); FAN_SetState(FAN_STATE_AUTO); }需要注意如果系統(tǒng)使用了SysTick作為HAL_GetTick的時基進(jìn)入Stop模式前必須調(diào)用HAL_SuspendTick否則喚醒后HAL_GetTick可能不準(zhǔn)確。這個坑在中文社區(qū)里經(jīng)常被提到。OLED進(jìn)入省電狀態(tài)時SSD1306通常有dim函數(shù)可以把屏幕調(diào)暗也可以直接關(guān)閉顯示。因為OLED每個像素都是自發(fā)光顯示全亮/全暗的功耗差異非常大。5.8 電機(jī)堵轉(zhuǎn)保護(hù)與看門狗電機(jī)在運行過程中可能被異物卡住或者用戶不小心用手握住扇葉。當(dāng)堵轉(zhuǎn)發(fā)生時電機(jī)電流上升如果驅(qū)動模塊或電源端沒有保險絲可能發(fā)熱冒煙。軟件上可以通過電流采樣電阻加ADC檢測電流來判斷堵轉(zhuǎn)但簡單的課程設(shè)計不一定需要額外增加電流傳感器。如果你的驅(qū)動模塊帶電流檢測引腳比如TB6612的可選電流采樣電阻可以接ADC檢測電壓如果沒有可以退而求其次在PWM開啟一段時間后檢測電機(jī)轉(zhuǎn)速反饋如果轉(zhuǎn)速為零或異常則判定堵轉(zhuǎn)。如果連測速反饋也沒有防護(hù)只能依賴電源限流或者驅(qū)動模塊的過熱保護(hù)??撮T狗IWDG建議開啟。在主循環(huán)中周期喂狗如果程序進(jìn)入死循環(huán)或異常跑飛看門狗可以復(fù)位系統(tǒng)。特別是在無人值守演示的時候看門狗是保證系統(tǒng)不“死機(jī)”的最后一道防線。6. 整機(jī)聯(lián)調(diào)與效果驗證6.1 分模塊調(diào)試順序拿到硬件后不建議立刻把所有模塊接在一起調(diào)試。我的建議是分模塊點亮逐層驗證先點亮LED驗證單片機(jī)最小系統(tǒng)、Keil下載鏈路和調(diào)試器是否正常。輸出一路PWM用示波器測量波形確認(rèn)頻率和占空比都能調(diào)整。接上電機(jī)驅(qū)動模塊先用固定占空比測試電機(jī)旋轉(zhuǎn)方向確認(rèn)邏輯引腳和電源接線無誤。調(diào)試OLED顯示能正確輸出溫度數(shù)值和狀態(tài)信息。調(diào)試DS18B20或NTC溫度采集對比水銀溫度計或已知溫度源。接入PIR模塊用手遮擋和移動驗證檢測結(jié)果。最后把自動溫控邏輯、按鍵模式和低功耗待機(jī)全部集成測試。每步通過后再進(jìn)入下一步能極大縮短排查范圍。防止一次接入十幾個模塊后出現(xiàn)問題不知道是哪一塊的故障。6.2 實測驗證方法溫度控制效果驗證可以用電吹風(fēng)或熱風(fēng)槍靠近溫度傳感器觀察OLED顯示溫度變化和風(fēng)扇轉(zhuǎn)速變化。也可以通過空調(diào)調(diào)節(jié)實驗室溫度看風(fēng)扇是否按預(yù)期啟動或停機(jī)。人體感應(yīng)驗證需要記住PIR有30秒左右的啟動穩(wěn)定期。剛上電時不要立刻靠近傳感器測試否則結(jié)果可能不準(zhǔn)。同時要調(diào)整PIR的靈敏度和延時旋鈕找到適合實驗室桌面場景的位置。功耗驗證如果條件允許可以在電源輸入端串入萬用表電流檔分別測量以下狀態(tài)的電流正常運轉(zhuǎn)低速檔電流。正常運轉(zhuǎn)高速檔電流。人體感應(yīng)無人后系統(tǒng)進(jìn)入待機(jī)態(tài)電流。進(jìn)入睡眠模式后電流。如果待機(jī)電流比正常工作時只低一點點說明電機(jī)雖然停了但其他外圍電路仍然在耗電需要檢查OLED是否關(guān)閉、LED是否常亮、電源模塊空載損耗是否偏大。6.3 常見問題排查表問題現(xiàn)象可能原因排查方式解決方案程序燒錄后開發(fā)板沒有任何反應(yīng)啟動模式引腳接錯 / 時鐘配置失敗 / 電源接觸不良檢查BOOT0跳線確認(rèn)ST-Link連接狀態(tài)BOOT0置0從Flash啟動重新下載并復(fù)位電機(jī)不轉(zhuǎn)但程序運行正常驅(qū)動模塊電源沒接 / PWM引腳復(fù)用配置錯 / 使能引腳狀態(tài)錯誤用萬用表測驅(qū)動電源示波器測PWM引腳按驅(qū)動模塊邏輯要求接線重新配置GPIO復(fù)用電機(jī)轉(zhuǎn)速不受PWM控制只有全速/停止使用普通GPIO輸出高低電平?jīng)]有啟動PWM外設(shè)示波器檢查是否有PWM波形調(diào)用HAL_TIM_PWM_Start啟動PWM通道PIR檢測不穩(wěn)定風(fēng)扇頻繁開關(guān)PIR延時旋鈕調(diào)得過短 / 傳感器安裝方向不合適觀察PIR輸出LED調(diào)節(jié)模塊電位器增大模塊輸出延時軟件延長無人判定時長溫度值跳動明顯ADC采樣未濾波 / 傳感器接觸不良 / 電源紋波大串口打印原始ADC值多次采樣取平均值外加滑動濾波OLED花屏或無顯示I2C地址錯誤 / SCL SDA接反 / 電平不匹配用I2C掃描例程檢查設(shè)備地址核對OLED模塊地址0x3C或0x3D交換I2C引腳風(fēng)扇運轉(zhuǎn)時單片機(jī)頻繁復(fù)位電機(jī)啟動電流過大導(dǎo)致電壓跌落用示波器或萬用表監(jiān)測5V/3.3V波形獨立電源供電增大電源電容軟啟動看門狗開啟后系統(tǒng)反復(fù)復(fù)位喂狗位置不合理主循環(huán)某處卡住注釋掉看門狗后測試增加調(diào)試日志調(diào)整喂狗時機(jī)檢查阻塞延時代碼待機(jī)功耗遲遲降不下來外設(shè)未完全關(guān)閉 / 進(jìn)入低功耗模式前未禁用不必要的時鐘逐模塊斷電測量電流關(guān)閉OLED顯示、LED和未使用外設(shè)時鐘6.4 代碼調(diào)試技巧調(diào)試STM32項目時最常用的工具是串口。建議從一開始就預(yù)留一個串口打印函數(shù)把系統(tǒng)狀態(tài)周期性輸出到串口助手例如[temp] 26.5C [wind] MID [person] 1 [state] AUTO [time] 123s串口輸出的關(guān)鍵點在故障復(fù)現(xiàn)時非常有用。如果按鍵有問題、溫控邏輯判斷不對看串口日志比盯代碼效率高很多。printf重定向時記得確認(rèn)串口波特率與實際配置一致。使用STM32CubeMX生成工程時串口波特率經(jīng)常會默認(rèn)設(shè)置為115200調(diào)試助手同樣需要設(shè)置成115200。如果串口輸出亂碼大概率是波特率不匹配或時鐘配置改變了。7. 從設(shè)計到論文如何擴(kuò)展和表達(dá)這個項目7.1 加入藍(lán)牙或WiFi遠(yuǎn)程控制如果你的畢業(yè)設(shè)計需要體現(xiàn)更強(qiáng)的前沿性或者學(xué)校對通信模塊有要求可以在現(xiàn)有系統(tǒng)上通過串口外接藍(lán)牙模塊或WiFi模塊。單片機(jī)通過USART與藍(lán)牙模塊通信可使用AT指令集進(jìn)行配對與數(shù)據(jù)傳輸。手機(jī)端可以編寫一個簡單APP或者直接使用現(xiàn)有的串口藍(lán)牙調(diào)試助手發(fā)送指令??刂茀f(xié)議可自定義AT的功能 發(fā)送 M 進(jìn)入手動模式 發(fā)送 A 進(jìn)入自動模式 發(fā)送 1 低速 發(fā)送 2 中速 發(fā)送 3 高速 發(fā)送 S 停止STM32串口接收中斷解析字符更新狀態(tài)機(jī)。這部分代碼量不大但對通信協(xié)議設(shè)計、串口中斷處理、數(shù)據(jù)幀解析都是很好的練習(xí)。如果你采用ESP8266模塊連接WiFi則可以選擇MQTT協(xié)議接入本地服務(wù)器或云平臺。需要說明的是涉及互聯(lián)網(wǎng)對設(shè)備的遠(yuǎn)程訪問時務(wù)必注意設(shè)備安全邊界。課程設(shè)計演示中建議只在本地局域網(wǎng)內(nèi)驗證不要輕易把設(shè)備暴露到公網(wǎng)。配置任何路由器端口映射和云服務(wù)器前先明確安全責(zé)任遵循最小權(quán)限原則。這部分內(nèi)容在論文中可以展開但不要做任何冒險嘗試。7.2 加入測速閉環(huán)如果你想體現(xiàn)自動控制方面的能力可以為風(fēng)扇加入測速功能。方法是使用紅外對管和碼盤電機(jī)旋轉(zhuǎn)時碼盤周期性遮擋紅外光線GPIO外部中斷檢測脈沖邊沿利用定時器測量單位時間內(nèi)的脈沖數(shù)換算成轉(zhuǎn)速RPM。然后使用增量式PID控制算法讓實際轉(zhuǎn)速逼近目標(biāo)轉(zhuǎn)速。這是一個很大的加分項因為你的系統(tǒng)從“開環(huán)PWM調(diào)速”升級成了“閉環(huán)調(diào)速”能抵御電源電壓波動和負(fù)載變化對轉(zhuǎn)速的影響。不過閉環(huán)控制會讓調(diào)試復(fù)雜性成倍增加PID參數(shù)需要根據(jù)實際系統(tǒng)調(diào)整。建議在基礎(chǔ)功能穩(wěn)定后再作為擴(kuò)展功能逐步加入。7.3 如何把“綠色”和“節(jié)能”寫清楚論文中“綠色”兩個字不能只是標(biāo)題噱頭你需要能說出具體在哪些環(huán)節(jié)實現(xiàn)了節(jié)能溫度自動控制避免風(fēng)扇長時間全速運轉(zhuǎn)。PIR人體感應(yīng)無人時自動停機(jī)和進(jìn)入低功耗模式。OLED顯示和外圍電路的功耗管理。使用PWM調(diào)速代替串聯(lián)電阻調(diào)速減少能量損耗。如果使用直流無刷電機(jī)可以強(qiáng)調(diào)其效率高于傳統(tǒng)交流電機(jī)和普通直流電機(jī)。建議在論文中加一節(jié)“節(jié)能效果測試”用數(shù)據(jù)說話。用電流表記錄自動模式與手動模式全速運行一周的等效功耗差異或記錄一個典型使用場景下每天累計工作時間估算節(jié)約的電能比例。這會讓項目的邏輯閉合答辯時很有說服力。7.4 展望部分的寫法畢業(yè)設(shè)計的展望不要寫“為未來智能家居提供了有力支撐”這類空話而是要提可落地的方向加入光敏電阻實現(xiàn)環(huán)境光亮度檢測夜間自動降低最高轉(zhuǎn)速避免影響睡眠。加入溫濕度傳感器DHT11當(dāng)溫度高且濕度高時優(yōu)先提高風(fēng)速改善體感。使用電機(jī)電流檢測實現(xiàn)堵轉(zhuǎn)保護(hù)增強(qiáng)安全性能。加入小型鋰電池和充電管理電路做成便攜式USB風(fēng)扇。實現(xiàn)FreeRTOS操作系統(tǒng)把任務(wù)調(diào)度從前后臺架構(gòu)升級為多任務(wù)調(diào)度機(jī)制處理按鍵、溫控、無線通信等任務(wù)時會更清晰。這些方向不用展開但每一條都能體現(xiàn)你對系統(tǒng)的理解深度。8. 常見誤區(qū)與開發(fā)建議8.1 不要直接復(fù)制例程而不理解引腳沖突從網(wǎng)上找一個OLED例程、一個PWM例程、一個溫度傳感器例程合在一起很可能會遇到引腳沖突。比如PIR接了PA1OLED的SCL也接了PA1你單獨跑每個例程時都正常合在一起后就有一個模塊不工作。建議從一開始就做一張引腳分配表把每個外設(shè)使用的引腳列出來做成Excel表格貼在電腦前。引腳分配要綜合考慮定時器通道映射關(guān)系不是所有引腳都能作為PWM輸出。使用STM32CubeMX的圖形界面當(dāng)你給某個引腳分配了復(fù)用功能后它會自動提示沖突能有效避免這個問題。8.2 不要在中斷服務(wù)函數(shù)中寫復(fù)雜邏輯外部中斷里做延時消抖、LED取反、OLED刷新、PWM占空比調(diào)整這些操作都不應(yīng)該放在中斷函數(shù)里。中斷服務(wù)函數(shù)應(yīng)該盡量短只負(fù)責(zé)置標(biāo)志位、記錄觸發(fā)時間其余交給主循環(huán)處理。如果不遵循這個原則你會遇到非常難排查的故障按鍵按下去后功能時靈時不靈有些操作會莫名卡死PIR信號到來后OLED刷新出現(xiàn)殘影。這些問題的根源往往都是中斷優(yōu)先級和主循環(huán)任務(wù)搶占了CPU時間。8.3 上下拉電阻和電平匹配按鍵引腳如果不設(shè)置上下拉懸空輸入時電平不確定可能頻繁觸發(fā)按鍵事件。使用STM32內(nèi)部上拉電阻就能解決大部分按鍵問題不需要額外在PCB上加外部電阻。如果使用5V供電的傳感器模塊要確認(rèn)模塊輸出信號能不能直接接到3.3V的STM32引腳上。很多模塊標(biāo)注“兼容3.3V和5V”但實際輸出高電平接近5V長此以往可能損壞STM32引腳。更安全的做法是加電平轉(zhuǎn)換模塊或分壓電阻。這個點體現(xiàn)了你對硬件可靠性的理解審閱老師會比較關(guān)注。8.4 調(diào)試時留足打印信息最后再優(yōu)化在功能還沒有完全調(diào)通時不要急著精簡代碼、刪除串口打印或增加各種優(yōu)化。先保證功能可跑再考慮代碼整潔性和功耗優(yōu)化。刪除打印前要考慮后續(xù)排障是否需要。程序的開發(fā)效率不一定體現(xiàn)在寫得快而是體現(xiàn)在出問題時能不能快速定位到問題。8.5 OLED和溫度傳感器放置位置如果OLED顯示屏和溫度傳感器放在一起OLED自身工作時會產(chǎn)生一定熱量長時間運行后會導(dǎo)致溫度傳感器讀數(shù)偏高進(jìn)而讓風(fēng)扇一直工作在一個“虛高”的溫度下。散熱最好的方式是把溫度傳感器放在靠近風(fēng)扇進(jìn)風(fēng)路徑的位置而不是貼在主控板或OLED背面。同理如果你把NTC放在陽光直射或靠近電機(jī)的位置測出來的溫度也不具備環(huán)境代表性。這是硬件布局在系統(tǒng)設(shè)計中最容易被忽略的問題。9. 寫在后面智能綠色風(fēng)扇這個題目的上限不低下限也很明確它很適合做一個完整的單片機(jī)畢業(yè)設(shè)計或課程設(shè)計。你可以用最基礎(chǔ)的開環(huán)PWM加按鍵控制交差也可以不斷往上面疊加人體感應(yīng)、閉環(huán)調(diào)速、低功耗、無線控制、物聯(lián)網(wǎng)告警這些模塊把它做成一個非常有工程味道的項目。真正的價值不完全在于“風(fēng)扇”本身而在于你在設(shè)計過程中走通了一條從需求分析到原理圖、再到軟硬件調(diào)試的完整路徑。以后你面對一個更復(fù)雜的系統(tǒng)時往往會發(fā)現(xiàn)核心的思維方式還是相同的外設(shè)不會用就去查參考手冊現(xiàn)象不對就用示波器、串口、萬用表一層層定位邏輯不清楚就用狀態(tài)機(jī)把它畫清楚。如果你現(xiàn)在手里的平臺正好是STM32F103C8T6建議先把電機(jī)轉(zhuǎn)起來再點亮OLED然后每增加一個功能模塊就做一次小驗證。按這個節(jié)奏推進(jìn)整個項目在兩到三周內(nèi)完成是可行的而且會有比較多的余量去擴(kuò)展新功能。希望這份基于STM32設(shè)計智能綠色風(fēng)扇的實現(xiàn)思路對你有所幫助。如果你正在做類似的單片機(jī)畢業(yè)設(shè)計建議先收藏本文等到搭硬件和調(diào)試邏輯時再對照參考。有問題時優(yōu)先懷疑電源、地線和GPIO復(fù)用配置這三個地方占了這個題目80%以上的故障原因。