驅(qū)動實(shí)戰(zhàn):L298N接線與PWM調(diào)速全解析)
簡介本資源是一套面向嵌入式初學(xué)者與STM32實(shí)踐者的C語言電機(jī)驅(qū)動工程專為正點(diǎn)原子STM32F103核心板適配L298N雙H橋驅(qū)動模塊設(shè)計(jì)解決直流電機(jī)正反轉(zhuǎn)、差速拐彎及PWM無級調(diào)速等典型控制需求適用于智能小車、機(jī)器人底盤等硬件項(xiàng)目開發(fā)。壓縮包共76個文件含33個頭文件.h定義外設(shè)配置與函數(shù)接口、32個源文件.c實(shí)現(xiàn)GPIO初始化、PWM輸出、電機(jī)狀態(tài)切換等核心邏輯另有Keil工程配置.uvprojx/.uvoptx、啟動文件、系統(tǒng)庫及LICENSE等完整開發(fā)支撐文件總大小312KB。已有125人下載學(xué)習(xí)。讀者可直接導(dǎo)入Keil MDK編譯運(yùn)行獲得可驗(yàn)證的電機(jī)控制功能代碼框架涵蓋GPIO推挽輸出配置、TIM定時器PWM生成、IN1–IN4電平組合邏輯封裝及左右輪獨(dú)立調(diào)速函數(shù)目錄結(jié)構(gòu)遵循正點(diǎn)原子標(biāo)準(zhǔn)模板CORE/SYSTEM/HARDWARE/USER便于理解STM32固件庫工程組織方式與電機(jī)驅(qū)動分層設(shè)計(jì)思想。 給單片機(jī)小車寫驅(qū)動繞不開的坎就是電機(jī)控制。我從一塊正點(diǎn)原子F103核心板和一塊L298N驅(qū)動模塊開始把正反轉(zhuǎn)、差速拐彎、PWM調(diào)速這幾個功能全部跑通前前后后踩了不少坑今天把這套驅(qū)動程序的完整設(shè)計(jì)思路、接線方案、代碼實(shí)現(xiàn)和排查經(jīng)驗(yàn)一次性整理出來。這篇東西寫給剛?cè)腴TSTM32、想玩小車但卡在電機(jī)驅(qū)動這一層的朋友也寫給那些已經(jīng)能點(diǎn)燈但一接電機(jī)就“冒煙”的同學(xué)。先說結(jié)論L298N驅(qū)動直流電機(jī)這件事本身不難難的是把GPIO方向控制、定時器PWM調(diào)速、按鍵交互邏輯這三塊整合在一起還要保證小車跑起來不抖、不偏、不燒板子。我的這套驅(qū)動用標(biāo)準(zhǔn)庫風(fēng)格實(shí)現(xiàn)代碼量不大但每一行后面都有原理支撐照著抄能跑理解了能改。1. 項(xiàng)目背景與整體設(shè)計(jì)思路1.1 做這個小車驅(qū)動要解決什么問題很多人第一次接觸STM32都是從點(diǎn)燈開始的可點(diǎn)完燈之后想做個能跑的小車突然發(fā)現(xiàn)電機(jī)驅(qū)動這塊的資料特別碎有的教程只講L298N怎么接不講代碼怎么寫有的代碼寫出來了但一上電電機(jī)嗡嗡叫就是不轉(zhuǎn)還有的轉(zhuǎn)向邏輯搞得特別復(fù)雜根本看不懂。我的核心訴求很簡單用正點(diǎn)原子F103核心板作為主控通過L298N驅(qū)動兩個直流減速電機(jī)實(shí)現(xiàn)三個最基本的功能——電機(jī)正反轉(zhuǎn)、小車拐彎、速度連續(xù)可調(diào)。這三個功能是任何輪式機(jī)器人的地基搞懂了之后后面做循跡、避障、遙控小車都是在這個基礎(chǔ)上加傳感器和邏輯而已。這個項(xiàng)目中真正值得研究的是兩個層面的問題硬件層面是STM32和L298N怎么正確匹配包括電平、電源、共地這些容易踩坑的地方軟件層面是如何把“方向”和“速度”這兩個控制維度分離——方向控制走GPIO電平速度控制走定時器PWM兩者互不干擾這樣的架構(gòu)寫出來的代碼才清晰后面加功能也容易。1.2 硬件選型為什么是F103加L298N的組合正點(diǎn)原子F103核心板用的是STM32F103系列芯片72MHz主頻內(nèi)置多個定時器每個定時器又能輸出多路PWM對于控制兩路直流電機(jī)這種場景來說性能和資源都綽綽有余。市面上很多小車套件用的就是這款芯片資料多、例程全、出了問題也好查。L298N則是經(jīng)典的電機(jī)驅(qū)動芯片內(nèi)部集成雙H橋一片就能獨(dú)立驅(qū)動兩個直流電機(jī)。它和單片機(jī)之間只需要四根方向控制引腳加兩根PWM使能引腳邏輯非常簡單。很多新手會糾結(jié)要不要換成TB6612或者DRV8833這類更“現(xiàn)代”的驅(qū)動芯片——確實(shí)它們體積更小、壓降更低但L298N勝在皮實(shí)耐操、引腳排列清晰、出錯容錯率高作為理解電機(jī)驅(qū)動原理的第一個芯片再合適不過了。選型還有一層考慮正點(diǎn)原子核心板已經(jīng)把最小系統(tǒng)、調(diào)試接口、按鍵和LED都做上去了省去了自己焊最小系統(tǒng)的麻煩調(diào)試時直接用板載按鍵模擬控制信號連JLINK或STLINK就能下載程序可以讓我把精力全部集中在電機(jī)驅(qū)動程序本身的邏輯上而不是浪費(fèi)在外圍電路排查上。1.3 軟件架構(gòu)把“方向控制”和“速度控制”拆開寫驅(qū)動最忌諱的就是把所有功能堆在一起。我一開始也犯過這個毛病在一個函數(shù)里既翻轉(zhuǎn)GPIO又修改PWM占空比結(jié)果想改個轉(zhuǎn)速還得在控制邏輯里翻來翻去。后來重構(gòu)了一把把軟件拆成三個層次。底層是硬件抽象層負(fù)責(zé)初始化GPIO和定時器提供“設(shè)置某個引腳高/低電平”“設(shè)置某路PWM占空比”這樣的原子操作。中間是電機(jī)控制層把底層操作組合成有意義的電機(jī)動作比如電機(jī)正轉(zhuǎn)、反轉(zhuǎn)、停止、左側(cè)減速、右側(cè)減速。最上層是業(yè)務(wù)邏輯層處理按鍵輸入、模式切換這類交互邏輯它只需要調(diào)用中間層提供的函數(shù)接口完全不用關(guān)心硬件細(xì)節(jié)。這樣分層的直接好處是調(diào)試方便小車不動了先查最底層GPIO和PWM有沒有波形輸出再查中間層控制函數(shù)有沒有被正確調(diào)用最后才查業(yè)務(wù)邏輯。每一層都可以獨(dú)立測試問題定位速度快很多。后面想加超聲波避障或者紅外循跡也只需要在業(yè)務(wù)邏輯層加判斷完全不碰底層驅(qū)動代碼。這套思路其實(shí)就是嵌入式開發(fā)里經(jīng)典的分層設(shè)計(jì)花小車的錢提前養(yǎng)成好習(xí)慣值。2. 硬件接線與模塊原理2.1 L298N內(nèi)部H橋到底是怎么轉(zhuǎn)的先不急著接線把L298N內(nèi)部的東西搞清楚。L298N內(nèi)部是兩個完整的H橋電路每個H橋由四個開關(guān)管組成電機(jī)接在H橋的中間橫臂上。所謂“正轉(zhuǎn)”就是讓對角線上的兩個開關(guān)管導(dǎo)通電流從電機(jī)一端流向另一端所謂“反轉(zhuǎn)”就是換另一條對角線導(dǎo)通電流方向反過來。對應(yīng)到L298N模塊的引腳上每個電機(jī)有三個控制輸入端IN1、IN2和ENA。其中IN1和IN2負(fù)責(zé)控制H橋四個開關(guān)管的組合狀態(tài)ENA是使能端。當(dāng)ENA為高電平時H橋電路才真正接通IN1和IN2的電平組合才有意義當(dāng)ENA為低電平時不管IN1和IN2是什么狀態(tài)電機(jī)都不會轉(zhuǎn)相當(dāng)于被“關(guān)斷”了。所以就有了最關(guān)鍵的設(shè)計(jì)思路用兩個GPIO引腳給IN1、IN2輸出高低電平?jīng)Q定電機(jī)轉(zhuǎn)動的方向用一路PWM信號接到ENA通過改變PWM的占空比來控制電機(jī)的平均電壓從而實(shí)現(xiàn)調(diào)速。方向控制是開關(guān)量速度控制是模擬量兩種不同性質(zhì)的信號各走各的通道互不干擾這就是整個驅(qū)動程序的底層邏輯。2.2 F103核心板引腳規(guī)劃與接線表引腳規(guī)劃上我遵循一個原則方向控制引腳隨便選普通GPIO就行但PWM引腳必須挑定時器的通道引腳。STM32F103的每個定時器輸出通道都有對應(yīng)的引腳映射比如TIM3的CH3在PB0上CH4在PB1上。我選PB0和PB1作為兩路PWM輸出用來接L298N的ENA和ENB。方向和使能引腳的分配是這樣的功能L298N引腳STM32引腳左電機(jī)方向1IN1PA4左電機(jī)方向2IN2PA5左電機(jī)使能/調(diào)速ENAPB0 (TIM3_CH3)右電機(jī)方向1IN3PA6右電機(jī)方向2IN4PA7右電機(jī)使能/調(diào)速ENBPB1 (TIM3_CH4)電源地GNDGND電機(jī)供電我用的是一塊12V鋰電池組直接接L298N的12V輸入端子。L298N模塊上的5V輸出引腳給F103核心板供電但前提是L298N的12V輸入足夠穩(wěn)定。這個方案接線少、使用簡單但后面我會專門講為什么實(shí)際跑車時我建議給單片機(jī)單獨(dú)供電別長期依賴L298N的5V輸出。2.3 電源與共地問題最容易燒板子的一步接線接錯最容易出的問題就是電源。L298N模塊上通常有VCC、GND和5V三個電源相關(guān)的端子。VCC接電機(jī)電源范圍通常是7V到12V我實(shí)測12V沒問題GND是電源地5V是模塊自帶的穩(wěn)壓輸出可以給外部邏輯電路供電。這里有個至關(guān)重要的細(xì)節(jié)單片機(jī)的GND必須和L298N的GND連接在一起。只有兩邊共地了單片機(jī)輸出的高電平對L298N來說才是真正的高電平。如果地不共單片機(jī)認(rèn)為的3.3V高電平在L298N參考地眼里可能只有2V甚至更低導(dǎo)致H橋邏輯判斷混亂電機(jī)要么不動要么亂抖。我見過很多新手接完線電機(jī)死活不轉(zhuǎn)最后發(fā)現(xiàn)就是一根GND跳線沒插。還要注意不要把L298N的5V輸出當(dāng)成核心板的唯一供電來源。如果你用的電機(jī)是12V大功率減速電機(jī)啟動瞬間電流可能到1A以上L298N的穩(wěn)壓芯片壓力很大輸出的5V可能被拉垮連帶單片機(jī)復(fù)位重啟。所以我實(shí)際跑車的時候給F103核心板單獨(dú)接了一路5V電源L298N的5V輸出只作為一個參考電源或干脆不接。3. 驅(qū)動核心實(shí)現(xiàn)正反轉(zhuǎn)與PWM調(diào)速3.1 GPIO初始化方向控制的基礎(chǔ)方向控制的本質(zhì)就是讓GPIO引腳輸出高電平或者低電平。STM32的GPIO在使用前必須初始化告訴芯片這個引腳是輸入還是輸出、輸出模式是什么、速度多快。對于L298N這種邏輯輸入引腳我全部配置成推挽輸出模式頻率選50MHz就夠用了。GPIO初始化代碼中有一個關(guān)鍵點(diǎn)是看引腳所在的端口總線。STM32的GPIO掛在APB2總線上所以初始化前必須開啟對應(yīng)端口的時鐘。我以前剛學(xué)的時候經(jīng)常忘了開時鐘結(jié)果引腳電平怎么都拉不上去后來才明白是時鐘沒打通寄存器操作根本沒生效。初始化完成后可以先用萬用表量一下引腳電壓來驗(yàn)證配置是否正確。程序里把IN1引腳拉高、IN2拉低用萬用表測PA4應(yīng)該能讀到大約3.3V電壓PA5應(yīng)該接近0V。這一步驗(yàn)證做完才說明GPIO層面的驅(qū)動是通的再去接L298N就不會一頭霧水了。3.2 定時器PWM配置轉(zhuǎn)速怎么調(diào)PWM調(diào)速的原理是改變一個周期內(nèi)高電平所占的比例這個比例就叫占空比。電機(jī)兩端看到的平均電壓 電源電壓 × 占空比占空比越高電機(jī)轉(zhuǎn)速越快。STM32的定時器產(chǎn)生PWM不需要CPU干預(yù)只要配置好定時器的工作模式和比較值硬件會自動翻轉(zhuǎn)引腳電平這大大減輕了CPU負(fù)擔(dān)。我使用TIM3的CH3和CH4輸出兩路PWM。定時器配置時有三個關(guān)鍵參數(shù)預(yù)分頻系數(shù)PSC、自動重裝載值A(chǔ)RR、比較值CCR。這三個值決定了PWM的周期和占空比。PSC用來把72MHz的系統(tǒng)時鐘分頻到一個合理的計(jì)數(shù)頻率ARR決定計(jì)數(shù)周期CCR則是比較值當(dāng)計(jì)數(shù)器計(jì)數(shù)到CCR時輸出電平翻轉(zhuǎn)。具體數(shù)值這樣算我想讓PWM頻率在1kHz左右。定時器輸入時鐘是72MHz先經(jīng)過預(yù)分頻PSC設(shè)為71實(shí)際上是72分頻計(jì)數(shù)頻率變成72MHz ÷ 72 1MHz也就是計(jì)數(shù)器每1微秒加1。然后ARR設(shè)為999計(jì)數(shù)器從0數(shù)到999剛好需要1000微秒所以PWM頻率就是1kHz。此時把CCR設(shè)為500占空比就是500/1000 50%電機(jī)得到一半的平均電壓。那為什么選1kHz而不是更高頻率L298N使用的續(xù)流二極管和開關(guān)管的開關(guān)速度有限頻率太高會導(dǎo)致開關(guān)損耗增大、管子發(fā)熱頻率太低又會讓電機(jī)產(chǎn)生“咔咔”的抖動聲。實(shí)測下來1kHz到10kHz之間都是可以接受的我選1kHz只是為了方便示波器觀察和計(jì)算你可以根據(jù)自己的電機(jī)特性調(diào)整。3.3 電機(jī)控制函數(shù)與拐彎邏輯方向控制函數(shù)本質(zhì)上是IN1、IN2引腳的邏輯組合。同一個電機(jī)IN1高、IN2低為正轉(zhuǎn)IN1低、IN2高為反轉(zhuǎn)兩個引腳電平相同則電機(jī)制動停止。我封裝了三個基礎(chǔ)函數(shù)分別對應(yīng)正轉(zhuǎn)、反轉(zhuǎn)和停止這樣業(yè)務(wù)邏輯里就不需要關(guān)心具體引腳了。對于兩個電機(jī)的小車底盤正轉(zhuǎn)前進(jìn)和反轉(zhuǎn)后退直接調(diào)用基礎(chǔ)函數(shù)就行。但拐彎就稍微復(fù)雜一點(diǎn)我用了差速轉(zhuǎn)向的方案左轉(zhuǎn)的時候左側(cè)電機(jī)速度降低右側(cè)電機(jī)速度保持不變這樣兩邊輪子轉(zhuǎn)速不同小車就向左拐彎右轉(zhuǎn)同理反過來。速度差越大轉(zhuǎn)彎半徑就越小。這種拐彎方式的優(yōu)點(diǎn)是不需要額外硬件完全靠軟件調(diào)節(jié)PWM占空比實(shí)現(xiàn)。我把差速比例抽成一個參數(shù)比如左轉(zhuǎn)時左側(cè)PWM占空比設(shè)為目標(biāo)速度的40%右側(cè)保持100%實(shí)測下來轉(zhuǎn)彎半徑大約在30厘米左右比較適合需要機(jī)器人原地轉(zhuǎn)彎的場景。如果想要更急的轉(zhuǎn)彎可以讓一側(cè)電機(jī)反轉(zhuǎn)兩個輪子朝相反方向轉(zhuǎn)動小車就能原地轉(zhuǎn)圈這個后續(xù)按需擴(kuò)展就行。4. 完整代碼與關(guān)鍵實(shí)現(xiàn)4.1 代碼結(jié)構(gòu)說明整個工程分成三個模塊main.c負(fù)責(zé)主循環(huán)和按鍵邏輯motor.c負(fù)責(zé)電機(jī)相關(guān)初始化和控制函數(shù)tim_pwm.c負(fù)責(zé)定時器PWM的初始化。這樣的分離讓每個模塊的職責(zé)非常單一代碼可讀性高也方便后面移植。我在正點(diǎn)原子F103核心板例程的工程模板基礎(chǔ)上修改復(fù)用了它的按鍵初始化和延時函數(shù)。時鐘頻率方面系統(tǒng)默認(rèn)是外部晶振倍頻到72MHz這也是正常開發(fā)時的標(biāo)準(zhǔn)配置。核心的電機(jī)和PWM代碼都是自己寫的不依賴?yán)谭奖隳憧疵靼酌恳徊皆诟墒裁础?.2 關(guān)鍵代碼逐段解讀先看定時器PWM初始化函數(shù)這個函數(shù)的作用是把PB0和PB1配置成TIM3的CH3和CH4輸出工作模式是PWM1模式。PWM1模式指的是計(jì)數(shù)器計(jì)數(shù)到CCR之前輸出高電平之后變低電平占空比越大平均電壓越高。void TIM3_PWM_Init(u16 arr, u16 psc) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // PB0和PB1復(fù)用為定時器輸出功能 GPIO_InitStructure.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOB, GPIO_InitStructure); // 定時器基礎(chǔ)配置計(jì)數(shù)器向上計(jì)數(shù)到arr后自動清零 TIM_TimeBaseStructure.TIM_Period arr; TIM_TimeBaseStructure.TIM_Prescaler psc; TIM_TimeBaseStructure.TIM_ClockDivision 0; TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, TIM_TimeBaseStructure); // CH3和CH4都配置為PWM1模式輸出極性高電平有效 TIM_OCInitStructure.TIM_OCMode TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse 500; TIM_OCInitStructure.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC1Init(TIM3, TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_OC2Init(TIM3, TIM_OCInitStructure); TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); TIM_Cmd(TIM3, ENABLE); }調(diào)用參數(shù)上我傳入了PSC71ARR999對應(yīng)前面說的1kHz頻率。要注意這里用的TIM_OC2Init對應(yīng)于CH3和CH4嗎不是OC1或OC2對應(yīng)的是TIM3的CH1/CH2。我實(shí)現(xiàn)里實(shí)際上是把TIM3的CH3和CH4分別對應(yīng)到PB0和PB1所以在配置OC時要選OC3和OC4。上面OC1只是示意實(shí)際工程里要寫OC3和OC4的初始化這里提醒你注意對應(yīng)關(guān)系。GPIO方向的初始化比較簡單就不貼完整代碼了核心就是把PA4到PA7都配置成推挽輸出初始狀態(tài)全部拉低保證上電瞬間電機(jī)不會偷跑。接下來是電機(jī)控制函數(shù)這是整個驅(qū)動最核心的部分。我實(shí)現(xiàn)了一個底層設(shè)置函數(shù)接收兩個方向的邏輯值和兩個PWM比較值然后一次性輸出到引腳和定時器void Motor_Set(int left_direction, int left_speed, int right_direction, int right_speed) { // left_direction和right_direction取1表示正轉(zhuǎn)0表示反轉(zhuǎn) if(left_direction 1) { GPIO_SetBits(GPIOA, GPIO_Pin_4); GPIO_ResetBits(GPIOA, GPIO_Pin_5); } else { GPIO_ResetBits(GPIOA, GPIO_Pin_4); GPIO_SetBits(GPIOA, GPIO_Pin_5); } if(right_direction 1) { GPIO_SetBits(GPIOA, GPIO_Pin_6); GPIO_ResetBits(GPIOA, GPIO_Pin_7); } else { GPIO_ResetBits(GPIOA, GPIO_Pin_6); GPIO_SetBits(GPIOA, GPIO_Pin_7); } // 通過修改TIM3的CCR寄存器來改變占空比 TIM_SetCompare3(TIM3, left_speed); TIM_SetCompare4(TIM3, right_speed); }在這個基礎(chǔ)上前進(jìn)后退拐彎的函數(shù)就是對這個底層設(shè)置函數(shù)的封裝。比如前進(jìn)就是左側(cè)正轉(zhuǎn)加右側(cè)正轉(zhuǎn)左轉(zhuǎn)就是左側(cè)速度減半右側(cè)全速。這樣封裝的好處是任何新動作都只需要一次函數(shù)調(diào)用業(yè)務(wù)邏輯層非常干凈。4.3 按鍵控制邏輯與實(shí)測效果我在主循環(huán)里用正點(diǎn)原子F103核心板上的三個按鍵做交互KEY1按下小車前進(jìn)KEY0按下小車后退KEY_UP每按一次速度加一檔。因?yàn)榘存I會抖動所以我復(fù)用了例程里的按鍵掃描函數(shù)檢測到有效電平變化后延時消抖再確認(rèn)。速度我用的是一個全局變量speed_level取值范圍從200到900對應(yīng)PWM比較值也就是占空比20%到90%。設(shè)置下限20%是有講究的直流減速電機(jī)存在靜摩擦力占空比太低時電機(jī)兩端電壓不足以克服靜摩擦?xí)霈F(xiàn)“叫喚但不轉(zhuǎn)”的現(xiàn)象。我給了一個200的初始值這樣上電默認(rèn)小車就能動而不是卡在0%的占空比上讓新手以為程序?qū)戝e了。實(shí)測效果上PWM占空比從200到900線性變化電機(jī)轉(zhuǎn)速能明顯感覺到從慢到快的平滑過渡沒有階梯感。轉(zhuǎn)向方面40%差速比實(shí)現(xiàn)的轉(zhuǎn)彎半徑適中在普通桌面上就能測試不會瞬間沖出桌面。整個系統(tǒng)連續(xù)跑了十分鐘電機(jī)微熱L298N芯片有發(fā)熱但手能按住屬于正常范圍。5. 常見問題與排查技巧5.1 電機(jī)不轉(zhuǎn)/抖動的排查順序小車接完線死活不轉(zhuǎn)這是每個做驅(qū)動的人都會遇到的事。我的排查順序是從電源開始往前查一步步縮小范圍。先看L298N上的電源指示燈是否點(diǎn)亮再量12V輸入和5V輸出是否正常接著檢查STM32和L298N是否共地然后用萬用表量IN1、IN2引腳在程序運(yùn)行后是否能正常翻轉(zhuǎn)電平。如果電機(jī)是“嗡嗡叫但轉(zhuǎn)子不動”大概率是PWM頻率設(shè)置不合理或者占空比太低。前者把頻率調(diào)到1kHz到10kHz之間即可后者把占空比抬高到20%以上。電機(jī)抖動還有一個隱蔽原因使能引腳ENA懸空。有些模塊的ENA上有跳線帽如果你用PWM控制調(diào)速必須把跳線帽拔掉讓PWM信號真正接入如果不拔模塊內(nèi)部把ENA拉到高電平你的PWM信號根本進(jìn)不去轉(zhuǎn)速永遠(yuǎn)是100%。5.2 PWM沒反應(yīng)或調(diào)速不線性PWM完全沒反應(yīng)先查定時器和引腳的對應(yīng)關(guān)系。很多STM32的引腳有多重復(fù)用功能你配置的是TIM3_CH3輸出但引腳的復(fù)用功能映射可能默認(rèn)不是這個。F103的引腳不需要額外的AFIO重映射時通常默認(rèn)就能用但如果你選錯引腳比如用了PA6卻以為它能輸出TIM3_CH3那肯定沒有波形。遇到這個問題我建議直接看一下芯片的數(shù)據(jù)手冊中定時器通道和引腳對應(yīng)的表格或者用示波器量一下引腳有沒有波形這個問題兩分鐘就能定位。調(diào)速不線性是另一個常見問題。PWM占空比和電機(jī)轉(zhuǎn)速并不是嚴(yán)格線性的關(guān)系因?yàn)殡姍C(jī)內(nèi)部的線圈電感、摩擦阻力、電源內(nèi)阻都會影響實(shí)際轉(zhuǎn)速。表現(xiàn)為占空比在30%到50%的時候轉(zhuǎn)速變化不明顯到了70%以上突然變快。這不是程序問題是電機(jī)本身的特性。要改善可以讓PWM比較值按指數(shù)曲線映射而不是線性映射或者直接忽略這種非線性畢竟小車控制精度要求沒那么高。5.3 L298N發(fā)熱和電源問題的避坑L298N發(fā)熱是很多人的心頭病但這其實(shí)是一個正?,F(xiàn)象。L298N用的是雙極型晶體管工藝導(dǎo)通壓降比MOSFET方案大得多電流越大發(fā)熱越厲害。實(shí)測兩路電機(jī)同時工作在1A電流下芯片表面溫度到60度以上不奇怪。解決辦法是加大散熱片、降低PWM頻率減少開關(guān)損耗或者干脆換MOSFET方案的驅(qū)動芯片。電源問題上面已經(jīng)強(qiáng)調(diào)過再補(bǔ)一個實(shí)操經(jīng)驗(yàn)不要用USB給F103核心板供電的同時指望核心板再通過GPIO輸出引腳給L298N的邏輯部分供電。雖然L298N邏輯部分電流不大但如果你把外部傳感器也接在核心板上總電流可能超過USB口的500mA極限導(dǎo)致核心板電壓被拖垮、程序跑飛。正確的做法是電機(jī)電源、主控電源、邏輯電源各管各的只在信號地線上共地。最后再分享一個我實(shí)測中發(fā)現(xiàn)的細(xì)節(jié)上電順序很關(guān)鍵。理想順序是先給L298N供電再給STM32供電這樣STM32初始化程序時能正確配置所有GPIO為低電平避免L298N在輸入引腳懸空時誤動作。如果反過來STM32先上電但程序還在跑初始化之前懸空引腳可能被內(nèi)部上拉電阻拉到高電平導(dǎo)致電機(jī)在上電瞬間猛地動一下對機(jī)械結(jié)構(gòu)不友好也容易嚇人一跳。6. 實(shí)操心得與后續(xù)擴(kuò)展整套驅(qū)動寫下來我最想強(qiáng)調(diào)的一個經(jīng)驗(yàn)就是先讓硬件在最小范圍內(nèi)動起來再去堆軟件功能。我第一次調(diào)試時直接寫了全部功能結(jié)果電機(jī)不轉(zhuǎn)排查了半天到最后發(fā)現(xiàn)是ENA跳線帽沒拔浪費(fèi)了一個晚上的時間。后來改成先把GPIO方向控制調(diào)通讓電機(jī)能正轉(zhuǎn)反轉(zhuǎn)再加PWM調(diào)速讓電機(jī)轉(zhuǎn)速可控最后才加按鍵和轉(zhuǎn)向邏輯。每加一個功能都驗(yàn)證一個問題出現(xiàn)時一下子就能定位到新加的環(huán)節(jié)。這套驅(qū)動寫完之后后面可以擴(kuò)展的方向很多。最簡單的擴(kuò)展是加一個藍(lán)牙串口模塊把按鍵控制改成手機(jī)控制代碼只需要在業(yè)務(wù)邏輯層加一個串口解析函數(shù)。稍微復(fù)雜一點(diǎn)可以加編碼器測速用TIM定時器的編碼器模式讀取電機(jī)轉(zhuǎn)速做一個閉環(huán)PID調(diào)速這樣小車走直線就不容易跑偏了。再往后加超聲波模塊做避障加紅外傳感器做循跡核心的電機(jī)驅(qū)動層代碼幾乎不用動這也是我為什么強(qiáng)調(diào)一開始就要把驅(qū)動層和控制邏輯層分離的原因。有同學(xué)問過我代碼是用標(biāo)準(zhǔn)庫還是HAL庫。正點(diǎn)原子的老教程大量用的是標(biāo)準(zhǔn)庫網(wǎng)上資料多、直接能搜到答案HAL庫則更貼近STM32CubeMX的圖形化配置流程代碼生成效率高。兩種庫本身的控制原理完全一樣都是操作寄存器。學(xué)的時候建議選一種主流庫吃透不要頻繁切換否則容易陷入工具選擇的糾結(jié)中反而忽略了真正重要的電機(jī)控制原理本身。這篇驅(qū)動最核心的收獲就是你親手搞懂了“方向靠電平、速度靠PWM”這個電機(jī)控制的基本功這套概念可以平移到幾乎所有電機(jī)驅(qū)動場景。哪怕以后用更高級的驅(qū)動芯片、更大功率的電機(jī)控制的本質(zhì)都不會變。我的建議是拿到代碼之后把每一部分和實(shí)物接線、芯片手冊對照著看一遍改一改速度參數(shù)試試效果然后試著從零自己寫一遍只有自己能寫出來才算真的掌握。本文還有配套的精品資源點(diǎn)擊獲取