用STM32F401和nRF24L01做無線調速小車
硬體配置
在做這個小專案前, 深入考察過STM32F103C8T6, STM32F401CCU6和STC89C52這三個MCU, 並實際跑了一些用例
- STC89C52在程式碼上要簡單得多, 它的問題是沒有ADC功能, 所以無法用於遙控器部分, 只能用於小車部分, 而且PWM輸出是軟輸出, 通過主迴圈實現的.
- STM32F103C8T6的功能應對這個專案沒問題, 在網上這個型號的程式碼特別豐富, 問題在於現在價格太貴了, 一個原裝晶片的最小系統板價格為38RMB, CH版也要25RMB, 相對於高一代且系統板價格僅為15RMB的STM32F401CCU6沒有優勢.
- STM32F401CCU6的劣勢是程式碼, STM32F4系列的程式碼例子在網上很少, 並且很多程式碼是針對F407這些高階型號的, 如果用在F401上需要額外的調整.
最終選擇的是STM32F401CCU6, 程式碼上的問題都不是問題, 對嗎?
遙控器部分
- 電源: 3.7V 18650鋰電
- 3.3V穩壓: 一個1N4148二極體
- MCU: STM32F401CCU6最小系統板
- 輸入: 雙軸搖桿模組
- 無線: nRF24L01模組
硬體部分的說明
- 1N4148能產生0.6V的壓降, 對於3.7V的鋰電來說是足夠的, 實際測試18650兩端電壓約4.0V, 通過1N4148後輸出的電壓是3.2V. STM32F401CCU6, nRF24L01和雙軸搖桿耗電量都非常小, 1N4148應付這個沒問題.
- 因為單節18650電池盒輸出線是裸的多芯軟線, 不方便直接連到模組上, 並且因為這部分兩個模組nRF24L01和雙軸搖桿都需要用到3.3V電壓, 所以另外用萬能板做了一箇中轉, PCB接線端子用來連線電源線, 3V3通過一個1N4148後接到3V3的排針上, 另一個排針是地線.
小車部分
- 電源: 7.4V (18650鋰電x2)
- 6V穩壓: 串聯兩個IN4007
- MCU: STM32F401CCU6
- 電機驅動: L9110雙路x2
- 小車底盤
- 48:1減速電機x4
硬體部分的說明
- 電機用的是普通的48:1減速電機, 工作電壓為6V, 另外L9110s雖然標稱能到12V, 市面上的L9110s大部分到不了這麼高, 安全電壓在7V左右, 所以這裡需要將電壓降到6V附近, 通過兩個1N4007能產生1.2V的壓降
- 執行時每個電機的電流為0.15A, 合計0.6A, STM32F401,nRF24L01的耗電可以忽略不計, 1N4007的工作電流為1A, 應該是夠的
- 因為有4個電機, 所以需要兩個雙通道L9110s模組
- 電池盒引出的是裸的多芯軟線, 不方便直接連到模組上, 因此另外用萬能板做了箇中間板, 加了接線端子連線電池盒, 正極串聯兩個1N4007輸出到正極排針, 另外還有地線排針, 以及四組排針用於L9110s的輸入(各側的兩個通道共用一組PWM)
接線
遙控器部分
MCU需要的介面如下
- UART: 方便除錯
- PA9 => USB2TTL的RX
- PA10 => USB2TTL的TX
- SPI: 連線nRF24L01
- PA5,PA6,PA7, PB13,PB14,PB15
- ADC: 兩個pin, 連線雙軸搖桿
- PA0 => 搖桿AXIS X
- PA1 => 搖桿AXIS Y
- VCC
- GND
與nRF24L01的接線
STM32 | nRF24L01 |
---|---|
PA4 SPI1_NSS | N/A |
PA5 SPI1_SCK | SCK |
PA6 SPI1_MISO | MISO |
PA7 SPI1_MOSI | MOSI |
PB13 | IRQ |
PB14 | CE |
PB15 | CSN |
小車部分
MCU需要的介面如下
- UART: 方便除錯
- PA9,PA10, 同上
- SPI: 連線nRF24L01
- PA5,PA6,PA7, PB13,PB14,PB15
- PWM: 4個pin, 輸出4組PWM, 分別對應左右側的兩組 L9110
- PA0,PA2: 左側電機
- PA1,PA3: 右側電機
nRF24L01接線同上.
功能實現
遙控器部分
這一塊主要是通過兩個ADC通道採集搖桿電壓, ADC採集使用的DMA的模式, 在主迴圈中定時(幾十到幾百毫秒)去讀取電壓, 並轉換到[0, FF]區間, 通過nRF24L01發射出去. ADC採集電壓時, 每個通道使用4個u16做快取, 輸出的數值是對這個4個數值做平均, 抑制抖動.
涉及的技術名詞: UART, ADC, DMA, TIMER, SPI
小車部分
小車部分的功能有幾部分:
- nRF24L01的中斷接收. 需要將nRF24L01的接收配置為中斷模式, 這樣只有在遙控端發出指令時, 小車才做相應的動作, 相比在迴圈中檢測接收資訊並調整輸出的實現方式更及時高效.
- PWM輸出控制小車的速度和方向.
- 將接收得到的X軸Y軸向量, 對映到兩路電機的方向和強度.
涉及的技術名詞: UART, TIMER, EXTI, SPI, PWM
以下具體說明
nRF24L01的中斷接收
這部分需要在STM32上新增一個EXTI中斷源, 對映到nRF24L01的IRQ PIN腳. 這個中斷是低電平觸發, 注意在處理完中斷後, 需要清空接收緩衝, 不然下一次還會讀到舊值.
中斷處理的方法內根據接收到的數值調整PWM輸出, 實現遙控功能.
這裡還有一個定時器TIM3, 當前設定的定時時間為0.5秒, 每次中斷處理時會將定時器初始化, 在定時器經過0.5秒後觸發時, 會將PWM輸出歸零, 歸零後電機都會停止. 通過這個機制, 在遙控器發出指令後小車會在當前指令下輸出PWM 0.5秒, 如果持續收到指令則持續輸出, 如果未收到指令, 則在0.5秒後停止輸出, 體現在小車運動上, 就是每次指令下小車會移動0.5秒.
PWM輸出控制速度和方向
PWM的頻率選擇: 48:1減速電機是淘寶上最便宜常見的減速電機, 最佳PWM頻率是25Hz-50Hz. 這個頻率的來源在這裡, 裡面有很詳細的說明和實驗測試結果. 我把我關心的部分內容翻譯了一下, 可以看這裡. 我在實際使用中觀察到的結果是符合這篇文章的結論的.
這裡多說幾句. 關於電機的PWM頻率選擇, 在網上查了很久, 得到的結果大部分是錯誤的, 很多人文章裡寫的頻率是6-20KHz. 這裡需要注意區分一下, 如果你用的是直流有刷電機, 那麼用這麼高的PWM頻率是會出問題的, 建議在幾十到幾百Hz的範圍去測試.
PWM控制速度比較好理解, 但是控制方向的具體實現需要通過兩個PWM配合. 嘗試過通過1路PWM+1路GPIO進行方向切換, 但是無法正常工作, 最後還是要通過兩路PWM. 根據方向, 設定其中一路PWM輸出為0. 這裡為了避免出現雙高電平(網上有很多人提到雙高導致L9110s燒燬), 在程式中先設定輸出為0的一路PWM, 再輸出另一路不為0的PWM.
X軸Y軸向量對映到左右兩路電機
這一塊花了我一些時間. 在網路上找到的資料看, 實現方式更多是通過Y軸計算出左右電機整體的前進後退佔空比, 然後通過X軸計算左右電機佔空比差值, 再將這兩個結果疊加, 得到最後的左右電機佔空比. 這個計算方式的問題是當工作點在Y軸區間兩端的時候, 此時疊加的差值會使Y軸的值超出區間, 但是實際上這個數值是不可能的, 所以要麼將兩個通道的數值都往回拉, 要麼就忽略Y軸超出區間的部分, 都不是很合理.
我使用的計算方式, 是先規定搖桿圓周4個方向上對應LR通道的值:
- 0° => L:FF, R:-FF
- 90° => L:FF, R:FF
- 180° => L:-FF, R:FF
- 270° => L-FF, R:-FF
將搖桿得到的XY軸的值做成向量, 將這個向量投影為圓周上某一點, 再根據圓周上這個點兩端的值計算當前點的LR值.
因為搖桿得到的XY軸空間, 實際上是一個正方形, 將其對映到圓上時, 有一個有趣的現象, 當角度位於0°到45°時, 向量的長度等於X軸的值, 而在45°到90°時, 向量長度等於Y軸的值, 這個使得計算簡便了許多.
遇到的問題
L9110s發熱燒燬
電源為兩節18650, 電壓為3.7x2=7.4V, 兩路pwm輸出, 當從0,0 -> 0,全速時, 電機無動作, L9110s發燙然後冒煙燒燬. 這個直接導致兩個模組各燒了一片L9110s. 於是上網查相關的資料
相關的討論
- Is this the reason for burning my h-bridge? https://www.eevblog.com/forum/beginners/is-this-the-reason-for-burning-my-h-bridge/
- L9110 IC goes up in smoke https://forum.arduino.cc/t/l9110-ic-goes-up-in-smoke/367873
- L9110 up in smoke https://forum.arduino.cc/t/l9110-up-in-smoke/381488
- https://forum.arduino.cc/t/fried-my-mega-and-multiple-dc-motor-control-boards-how-can-i-prevent-this/664303
- AB全高狀態,靜態加電時AB=HH沒有發熱,但是給1kHz脈衝,9110就冒煙.正常驅動A=H,B=L時,B變H感覺有短時間剎車現象,因為如果A=0,電機停得慢,當A=H,B由L變H就快速停.按理AB=HH是可以的,只是實際結果,靜態沒問題,動態就燒了 https://www.amobbs.com/thread-4986052-1-1.html
- https://www.zhihu.com/question/52548517
- https://www.icxbk.com/ask/detail?tid=30203
- 對直流電機選擇最優的PWM頻率 https://learn.adafruit.com/improve-brushed-dc-motor-performance?view=all
因為模組已經帶了輸出電容和上拉電阻, 所以
可能的原因是
- 電機啟動電流過大導致模組燒燬. 電機靜態電阻為6.5Ω, 電壓7.4V時電流超過1A,
應對方案: 串聯一個5Ω的限流電阻, 可以將電流降到7.4/(6.5+5)=0.64A, 避免超出L9110s的最大電流, 運轉中的電機阻抗為40Ω - 45Ω, 此時電阻上的分壓不到1V, 影響不大. - L9110s耐壓超限. 有人說最高到6.5V.
應對方案: 在L9110s輸入電壓前串聯2個1N4007, 將電壓降到7.4-1.4=6V, 串聯2個時,啟動電流1A,正向電阻0.7Ω, 空轉時0.15A,正向電阻5Ω - PWM頻率過高. 過高的PWM頻率會導致電機在低佔空比時無法啟動,
- PWM同時輸出高電平
最終解決方案
- 串聯兩個1N4007將電壓降到6.2V
- PWM頻率降到100Hz
有些佔空比下電機不動
在逐漸增大佔空比的過程中, 有些值下電機不轉, 能聽到吱吱聲, 如果手摸著L9110s晶片, 能感覺到此時有一陣發燙, 所以此時電流到位了, 但是沒能驅動電機.
這個原因和前一個問題是一樣的, 因為PWM頻率過高(17.5KHz), 無法驅動電機, 在將頻率降到100Hz後這個問題就沒再出現.
小車在執行一段時間後中斷燈常亮, 失去響應
經過檢查, 是因為在處理nRF24L01接收中斷時, 加入了一個延時1ms的處理, 會卡在這個延時函式上, 將這個延時處理刪除後就未再出現這個情況