公司裡最近開始投入STM32的行列
特別是在馬達控制這一段
原先站長就已經蒐集了很多的相關資料
從SDK3.0,SDK4.3,SDK5.0直到現在的SDK5.4
然而從來沒有真正使用板子去完成一次馬達調適的功能
週三(9/25)是第一次使用 B-G431B-ESC1 的開發板
這幾天試下來很有心得
過程中還好幾次跟 Boss 發生超級激烈的討論
所幸結果是甘甜的
接下來把測試過程詳實記錄在此
希望可以減少新手使用上的盲點
尤其是只靠自己摸索而沒有機會去上培訓課程的人
=============================================================
B-G431B-ESC1 是一片為無人機(Drone)開發的馬達驅動板
大小只有半張名片不到
上面密密麻麻都是SMD的零件
信號用電阻電容基本上都是0402的規格
總之是一片零件密度非常高的板子
但這片小小的板子卻存在非常強大且完整的功能
它可以用 FOC 驅動馬達
也可以用六步方波去驅動
可以用hall sensor或encoder去偵測角度
也可以 sensorless 驅動就好
由 PLL 或 Cordic 去推算角度
供應電壓從 DC8~28V
驅動電流可以高達40A
不過功能這麼強大的板子
使用手冊卻很陽春
如果沒有之前使用 STM32 其他系列 MC Workbench 的經驗
要自己摸索出正確的使用方式是有點門檻的
=============================================================
第一次進行測試先要準備幾樣東西:
0. B-G431B-ESC1 with Vcc/Gnd/U/V/W cables
1. USB_mini cable
2. BLDC motor (Any type)
3. Power Supply (12V/24V)
4. 安裝 x-cube-mcsdk
(預載編譯環境 MDK5 及 STM32CubeMX)
分別說明一下這幾樣東西要怎麼準備
首先要把 B-G431B-ESC1 板子上的 Vcc/Gnd/U/V/W 接點拉線出來
可以在 UM2516 這份文件上找到接點的位置
並準備一條 mini type 的 USB cable
這種 USB cable 是在 USB3 出來之前手機最常用的充電下載線
也就是如果你有過去淘汰不用的手機所留下的充電線
直接拿來用就可以了
最好是有帶磁環(磁珠)的電纜
不然就是長度在 30cm~50cm 之間的 USB cable 比較恰當
馬達可以隨意一個你正在使用的 BLDC motor
不管高壓/低壓/電流大小
只要有 BLDC motor 就可以
電源供應器(Power Supply)最好用可以調整電壓電流的
建議可以輸出0~30V/0~5A以上的較為合適 (150W以上)
前面的工作都準備完成就可以開始安裝 x-cube-mcsdk (v5.4.1)
檔案會是zip壓縮檔,大小約112MB
由於站長是 Keil C 的愛用者
因此這裡用 MDK5 的環境來做介紹
而腳位定義與 MCU 周邊功能會使用到 STM32CubeMX
所以也要先安裝好才開始進行測試
=============================================================
第一次操作 Motor Profiler 與 MC Workbench
安裝好 x-cube-mcsdk 之後
桌面會有 Motor Profiler 與 MC workbench 兩個捷徑
如果要調適馬達參數
我們要先使用 Motor Profiler 這個工具
使用前請先將 USB_mini cable 連接到 B-G431B-ESC1 開發板
將馬達三條出力線任意接上 B-G431B-ESC1 拉線出來的 U/V/W
最後將電源供應器的輸出調節在 DC12V/2A 這個條件上
並接上 Vcc/Gnd 的線
執行 Motor Profiler 會出現板子的選用
第一個出現的就是 B-G431B-ESC1
點選板子後要輸入馬達基本參數
極對數在 Motor Profiler 有提供教學
在此不多做說明
看不懂的可以留言問我
如果你完全不曉得你所使用的馬達參數
有幾個方向你可以參考看看
首先就是馬達的大小
如果比拳頭小的通常轉速很快
設定轉速條件可以 5000RPM 以上
如果比拳頭大但比兩個拳頭小
轉速條件可以設在 3000RPM 進行測試
更大的馬達通常不容易拿到
如果拿得到也通常會提供完整參數
電壓電流暫定12V/2A
這是一個最保險且不容易損壞板子與電源供應器的條件
不過如果你的馬達比大拇指還小
建議還是要調低一下電流
對低壓馬達來說,1A應該是最低限度
電流太小其實不好調適馬達
當這些條件都輸入完成後
就點選 connect 讓 Motor Profiler 與 B-G431B-ESC1 連線
連線成功就直接按 Start 進行馬達參數的調適
如果你的馬達在調適過程中完全沒有動
可以先重複調適 3~5 次
有時候是因為 USB 通訊的問題
也有可能是電腦作業系統的問題
並不是每一次連線都可以很順利的
因為馬達本來就是信號干擾源
假如你調適幾次後發現馬達都完全沒有動
可以考慮加大你的輸出電流
或是提高轉速上限
不同的轉速上限與電流峰值
會改變馬達調適的初始化開迴路驅動信號
假如動了又停住
沒辦法連續一直轉
就建議調低一下電流量
大多數的馬達在 1~2A 的電流範圍內應該都是可以調適轉起來的
只是這並不一定是馬達真正適用的參數
我們只是要先確認馬達會動的方法
如果馬達有成功連續運轉
並有慢慢減速到停止
那代表這組參數是可以用的
用 SAVE 鍵把馬達參數存起來
記住這個馬達的名字 (這裡先取名為TEST_MOTOR)
接下來在 MC workbench 中會用到
如果您想試試看馬達運轉的情況
用 PLAY 鍵進入簡易測試環境
這裡可以設定馬達緩啟動的條件
也可以測試轉速的穩定度
如果測試起來不滿意可以一直重新調適
直到跑出一組覺得還蠻順的馬達參數
但原則上只要能轉就算OK了
因為進入Workbench後還有很多細節可以調整
這裡只是先抓個馬達的基本參數
讓程式有調整的方向而已
=============================================================
進入Workbench後要點選New Project
在Inverter選單中會出現B-G431B-ESC1這片板子
稍微說明一下
官方的板子有幾種類型
第一種是 All in One 的類型 (主控與驅動做在同一片板子上)
這一類會放置在 Inverter 中
第二種是組合套件且有附馬達的類型 (主控板與驅動板分開)
這一類會放置在 MC kit 中
第三種是組合套件沒有附馬達的類型 (主控板與驅動板分開)
這一類會放置在 Power & Control 中
而我們所使用的B-G431B-ESC1是All in One的板子
所以在Inverter中可以找到
不管你選用哪一種類型的板子
在頁面的最下方要記得選擇剛剛你所調適好的馬達
也就是選 TEST_MOTOR (使用你剛剛存起來的名字)
再來就讓 Workbench 自動幫你建立專案了
=============================================================
專案建立之後要做的事很簡單
如果你想先試試看馬達的動作
而你的 B-G431B-ESC1板子從剛剛調適之後就一直沒有拔除USB cable的話
可以 open Monitor 進入通訊調適介面
選擇 B-G431B-ESC1 的VCP (例如 COM 3)
執行Connect動作後便可以對板子連線操作
在Basic畫面中可以啟動馬達、停止馬達、改變馬達轉速、馬達正反轉
在Advanced畫面中可以調整馬達的PID參數、緩啟動設定
在Registers畫面中更可以看到所有馬達在運行中的參數變化
如果現在馬達的運轉方式是你所滿意的條件
便可以回到前一個畫面將程式碼 Generation
=============================================================
使用 Generation 功能時需要先建立專案名稱 (副檔名會是.stmcx)
然後會要求指定編譯軟體
除了常見的 Keil C 、IAR C
也支援 SW4 以及 CubeIDE
假如你跟我一樣都習慣使用 Keil C 或 IAR C
也不用刻意要改用官方提供的 SW4 或 CubeIDE
因為實測從Keil C轉出來的Code與CubeIDE所轉出來的Code
程式空間(Code size)差了將近一倍
如果Keil C轉出來是35KB好了
CubeIDE轉出來差不多是 68KB
但奇怪的是
兩個檔案資料夾是Keil C的專案夾比較占空間
而且編譯時間比CubeIDE長了不只一倍
我猜是FOC運算函式庫在Keil C有用特別的硬體加速函式
讓程式碼大幅縮小
可是因為不是標準庫的編譯方式
導致時間上變長了
如果知道這一段怎麼處理的
應該也可以在CubeIDE上做到同樣的效果
不過那就是以後才要研究的新目標
當下還是先能夠調好馬達動作比較重要
=============================================================
產生程式碼之後還會跳進CubeMX再確認一次所有的IO設定
如果你有個人想做的額外動作
比方說把預留的Hall Sensor Port拿來當做Display的控制線
因為Sensorless驅動時不需要用 Hall Sensor Port
所以在此可以先做一個小小的變化
然後再GenCode一次
轉出來的專案可以直接下載到 B-G431B-ESC1
然後如果你按照預設的程式都沒有改變
下載後按一下板子上的按鍵
馬達就自己轉起來了
當然也可以用VCP跟他連線
只要打開Workbench中剛剛建立好的專案.stmcx
進入Monitor中就可以連線操作馬達了
不過這時候的連線跟剛剛調適時的連線有點不太一樣唷!
剛剛調適時的連線
是Motor Profile預載一個空馬達參數的架構
透過Motor Profile的調適流程將馬達參數計算出來
所有的參數都是可以即時修正的
但是只要斷電重來
所有的參數就遺失了
必須重新調適
然而經過燒錄後的程式
是將調適好的參數當成預設參數填入MCU中
每次重新上電就會自動執行這組參數
某些板子還支援兩顆馬達同時運作
此時就是放入兩組不同的馬達參數
以同樣的FOC架構去運行
假如你想要一片板子可以同時讓很多不一樣的馬達使用
當然也可以自己將各組馬達參數寫在程式中
以IO切換或通訊更新的方式來切換馬達參數
這樣就可以讓一片板子運行很多不一樣的馬達
但前提要板子的驅動能力是可以應付的
也就是瓦數相近且電壓範圍也相近的馬達才能這樣用
畢竟一顆馬達會用到的參數很多
如有任一參數引用錯誤的話
是非常有可能發生災難的
=============================================================
上面的調適流程如果夠熟悉
而板子的驅動能力又跟馬達有匹配的話
基本上只要10分鐘不到就可以讓一個馬達順利運轉起來
不過調適馬達並不是只要會轉
還要加載測試及各種意外狀況的模擬
這些就要額外多花時間去調整了
但這套工具真的大幅縮短了馬達前期調適的時間
讓馬達驅動可以快速進入應用階段的測試
對電機工程師來說真的是一個很大的福音
重點是價格還不貴
軟體又通通在官網就可以下載得到
強烈推薦給大家試用看看唷~~
2019年9月27日 星期五
2019年9月1日 星期日
STM32CubeMX之串列中斷--STM32F205RBT6在MDK5的應用範例
站長在過去的文章中
有大量提及串列通訊的概念
不論是在機械手臂的控制篇或是 Modbus 的介紹中
而在 STM32 環境裡只有寫法變成了函式庫的呼叫用法
其他的概念都一樣
只要瞭解函式庫所代表的動作含意
寫起來就跟以往的串列中斷沒甚麼兩樣
延續上一個程式
我們開啟 Hello.ioc 將 Connectivity -> USART3 中
Configuration -> NVIC Settings 的 Enable 勾選
重新按下右上角的 GENERATE CODE
此時串列中斷的功能就被開啟了
而且在 GENERATE CODE 之後
我們可以發現原來寫在 USER CODE 裡的程式碼都被保留下來
並不會被新產生的程式碼所覆蓋清除
如此我們可以放心地將板子上的功能一一慢慢驗證
當問題發生時也容易釐清是哪一個新加入的功能導致誤動作
儘量不要囫圇吞棗一下子把所有的功能都打開
尤其在面對不熟悉的介面或很多周邊整合應用時
常常會發生某個功能設定好就導致另一個功能異常的情況
特別是周邊功能有優先權衝突的時候
慢慢加入功能可以快速且有效地釐清問題點
建議大家可以用這樣的方式進行程式撰寫
==========================================================
/* USER CODE BEGIN PV */
uint8_t Enter[] = { 0x0d, 0x0a };
uint8_t Hello[] = { "Hello World !!!" };
uint8_t uart3_read_buffers[10];
uint8_t uart3_conv;
uint8_t uart3_read_cnt=0;
/* USER CODE END PV */
/* USER CODE BEGIN 2 */
HAL_UART_Transmit (&huart3, Enter, sizeof(Enter), 0xffff);
HAL_UART_Transmit (&huart3, Hello, sizeof(Hello)-1, 0xffff);
HAL_UART_Transmit (&huart3, Enter, sizeof(Enter), 0xffff);
HAL_Delay(1000);
HAL_UART_Receive_IT (&huart3,&uart3_conv,1);
/* USER CODE END 2 */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
UNUSED(huart);
uart3_read_buffers[uart3_read_cnt]=uart3_conv;
uart3_read_cnt++;
HAL_UART_Receive_IT (&huart3,&uart3_conv,1);
if (uart3_read_cnt>=10 || uart3_conv==0x0a)
{
HAL_UART_Transmit_IT (&huart3,uart3_read_buffers,uart3_read_cnt);
uart3_read_cnt=0;
}
}
/* USER CODE END 4 */
上一篇文章利用 while 迴圈的輪詢
讓 UART3 不斷送出 Hello World !!! 的資料
這一次我們將 Hello World !!! 搬到 /* USER CODE BEGIN 2 */ 的位置
清空原來 while 迴圈裡的程式 (/* USER CODE BEGIN 3 */的位置)
並加入串列接收的中斷功能 HAL_UART_Receive_IT (&huart3,&uart3_conv,1)
這是啟動從 UART3_RX 收到一個 Byte 的資料後會發生中斷的功能
收到的資料會放在 uart3_conv 這個變數上
請注意這個函式所接受的資料參數是指標型態
因此呼叫的時候我寫成 &uart3_conv
就是把收到的資料放在 uart3_conv 的位址上
任何變數前面加上 & 的符號就是代表該變數在程式裡的位址 (指標的概念)
當中斷發生時,系統會自動執行
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) 這個函式
我把函式放在 /* USER CODE BEGIN 4 */ 的位置
接下來的程式裡要傳達幾個概念:
第一個是 UNUSED(huart)
這是避免編譯器出現未使用警告
詳細的發生原因可以參考這篇文章的說明
第二個是接收資料並重啟中斷的寫法
uart3_conv這個變數會儲存最新一個 Byte 的串列資料
在下一個 Byte 進來之前
我們必須將裡面的資料搬移到資料陣列中 uart3_read_buffers[]
等待接收到滿足條件的資料後才進行資料分析
並且重新啟動串列接收中斷
才不會漏掉即將進來的下一個 Byte 資料
第三個是判斷資料與回傳資料的時機
這個範例是將儲存在資料陣列中的資料回傳
當作是滿足指令條件的回應動作
而指令就是收到換行符號
然而資料陣列的長度只有 10 Bytes
超過陣列長度的資料除非覆蓋掉原來的資料繼續儲存
不然就會產生誤動作 (更改到其他變數用記憶體的資料)
因此程式才會在滿足陣列長度時先回傳資料
並將計數歸零以重新接收新一輪的資料
直到有新的換行符號出現為止
而且回傳的函式也用了中斷寫法
當傳送完成後系統會先進到
HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) 這個函式中
如果你有需要判斷傳送是否完成
可以在 HAL_UART_TxCpltCallback 中加入判斷式及對應動作
==========================================================
使用中斷寫法的好處
就是系統不會一直等待串列動作完成
而把收發資料的動作交給串列周邊自己完成
等收發完成後系統再接手
而等待收發完成的這段時間
系統可以繼續做其他的事
比方說繼續量測電壓
繼續驅動馬達運轉......
諸如此類
不過
即便以中斷寫法來寫
進入中斷函式到離開還是需要一些時間
這段時間還是算占用中斷資源
如果在這段時間有其他中斷進來
就會排在後面或直接被捨棄掉
利用 AccessPort 做了一下實驗
將六組帶有換行符號的字串不間斷地連續送給 MCU
並不是每一次都可以六組完整地回傳給 AccessPort
最差的情況只回傳了三組
換句話說
串列還是要考慮進入中斷後重新啟動所花費的時間
收與發還是要有一定的時間間隔
才能確保資料的收發不會漏掉
==========================================================
為了證明收與發互搶中斷資源的問題
特別將資料陣列的長度改成 100
並且讓收與發的字串長度增加到了 70~80 Bytes
再用 AccessPort 連續發送六組資料
這次發現不管重複發送多少次
六組資料都完整地被回傳
當回傳的中斷執行間隔被拉長
收發的穩定度就提高了
因為發送資料的頻率降低
占用中斷的時間也相對變少
也不容易發生重複排程而被捨棄的問題
實驗後才發現
原來不是資料送得少才穩定
反而是送得多才穩定
送得多代表大部分的動作交給串列周邊處理
真正占用到 MCU 的時間變少了
系統才有充裕的時間處理更多的事
這也才是使用中斷的精華所在
希望這篇文章能對您有些幫助
有任何問題或是我有說錯的地方
也請不吝指教
接下來要引入 DMA 的用法
也請繼續期待!
有大量提及串列通訊的概念
不論是在機械手臂的控制篇或是 Modbus 的介紹中
而在 STM32 環境裡只有寫法變成了函式庫的呼叫用法
其他的概念都一樣
只要瞭解函式庫所代表的動作含意
寫起來就跟以往的串列中斷沒甚麼兩樣
延續上一個程式
我們開啟 Hello.ioc 將 Connectivity -> USART3 中
Configuration -> NVIC Settings 的 Enable 勾選
重新按下右上角的 GENERATE CODE
此時串列中斷的功能就被開啟了
而且在 GENERATE CODE 之後
我們可以發現原來寫在 USER CODE 裡的程式碼都被保留下來
並不會被新產生的程式碼所覆蓋清除
如此我們可以放心地將板子上的功能一一慢慢驗證
當問題發生時也容易釐清是哪一個新加入的功能導致誤動作
儘量不要囫圇吞棗一下子把所有的功能都打開
尤其在面對不熟悉的介面或很多周邊整合應用時
常常會發生某個功能設定好就導致另一個功能異常的情況
特別是周邊功能有優先權衝突的時候
慢慢加入功能可以快速且有效地釐清問題點
建議大家可以用這樣的方式進行程式撰寫
==========================================================
/* USER CODE BEGIN PV */
uint8_t Enter[] = { 0x0d, 0x0a };
uint8_t Hello[] = { "Hello World !!!" };
uint8_t uart3_read_buffers[10];
uint8_t uart3_conv;
uint8_t uart3_read_cnt=0;
/* USER CODE END PV */
/* USER CODE BEGIN 2 */
HAL_UART_Transmit (&huart3, Enter, sizeof(Enter), 0xffff);
HAL_UART_Transmit (&huart3, Hello, sizeof(Hello)-1, 0xffff);
HAL_UART_Transmit (&huart3, Enter, sizeof(Enter), 0xffff);
HAL_Delay(1000);
HAL_UART_Receive_IT (&huart3,&uart3_conv,1);
/* USER CODE END 2 */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
UNUSED(huart);
uart3_read_buffers[uart3_read_cnt]=uart3_conv;
uart3_read_cnt++;
HAL_UART_Receive_IT (&huart3,&uart3_conv,1);
if (uart3_read_cnt>=10 || uart3_conv==0x0a)
{
HAL_UART_Transmit_IT (&huart3,uart3_read_buffers,uart3_read_cnt);
uart3_read_cnt=0;
}
}
/* USER CODE END 4 */
上一篇文章利用 while 迴圈的輪詢
讓 UART3 不斷送出 Hello World !!! 的資料
這一次我們將 Hello World !!! 搬到 /* USER CODE BEGIN 2 */ 的位置
清空原來 while 迴圈裡的程式 (/* USER CODE BEGIN 3 */的位置)
並加入串列接收的中斷功能 HAL_UART_Receive_IT (&huart3,&uart3_conv,1)
這是啟動從 UART3_RX 收到一個 Byte 的資料後會發生中斷的功能
收到的資料會放在 uart3_conv 這個變數上
請注意這個函式所接受的資料參數是指標型態
因此呼叫的時候我寫成 &uart3_conv
就是把收到的資料放在 uart3_conv 的位址上
任何變數前面加上 & 的符號就是代表該變數在程式裡的位址 (指標的概念)
當中斷發生時,系統會自動執行
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) 這個函式
我把函式放在 /* USER CODE BEGIN 4 */ 的位置
接下來的程式裡要傳達幾個概念:
第一個是 UNUSED(huart)
這是避免編譯器出現未使用警告
詳細的發生原因可以參考這篇文章的說明
第二個是接收資料並重啟中斷的寫法
uart3_conv這個變數會儲存最新一個 Byte 的串列資料
在下一個 Byte 進來之前
我們必須將裡面的資料搬移到資料陣列中 uart3_read_buffers[]
等待接收到滿足條件的資料後才進行資料分析
並且重新啟動串列接收中斷
才不會漏掉即將進來的下一個 Byte 資料
第三個是判斷資料與回傳資料的時機
這個範例是將儲存在資料陣列中的資料回傳
當作是滿足指令條件的回應動作
而指令就是收到換行符號
然而資料陣列的長度只有 10 Bytes
超過陣列長度的資料除非覆蓋掉原來的資料繼續儲存
不然就會產生誤動作 (更改到其他變數用記憶體的資料)
因此程式才會在滿足陣列長度時先回傳資料
並將計數歸零以重新接收新一輪的資料
直到有新的換行符號出現為止
而且回傳的函式也用了中斷寫法
當傳送完成後系統會先進到
HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) 這個函式中
如果你有需要判斷傳送是否完成
可以在 HAL_UART_TxCpltCallback 中加入判斷式及對應動作
==========================================================
使用中斷寫法的好處
就是系統不會一直等待串列動作完成
而把收發資料的動作交給串列周邊自己完成
等收發完成後系統再接手
而等待收發完成的這段時間
系統可以繼續做其他的事
比方說繼續量測電壓
繼續驅動馬達運轉......
諸如此類
不過
即便以中斷寫法來寫
進入中斷函式到離開還是需要一些時間
這段時間還是算占用中斷資源
如果在這段時間有其他中斷進來
就會排在後面或直接被捨棄掉
利用 AccessPort 做了一下實驗
將六組帶有換行符號的字串不間斷地連續送給 MCU
並不是每一次都可以六組完整地回傳給 AccessPort
最差的情況只回傳了三組
換句話說
串列還是要考慮進入中斷後重新啟動所花費的時間
收與發還是要有一定的時間間隔
才能確保資料的收發不會漏掉
==========================================================
為了證明收與發互搶中斷資源的問題
特別將資料陣列的長度改成 100
並且讓收與發的字串長度增加到了 70~80 Bytes
再用 AccessPort 連續發送六組資料
這次發現不管重複發送多少次
六組資料都完整地被回傳
當回傳的中斷執行間隔被拉長
收發的穩定度就提高了
因為發送資料的頻率降低
占用中斷的時間也相對變少
也不容易發生重複排程而被捨棄的問題
實驗後才發現
原來不是資料送得少才穩定
反而是送得多才穩定
送得多代表大部分的動作交給串列周邊處理
真正占用到 MCU 的時間變少了
系統才有充裕的時間處理更多的事
這也才是使用中斷的精華所在
希望這篇文章能對您有些幫助
有任何問題或是我有說錯的地方
也請不吝指教
接下來要引入 DMA 的用法
也請繼續期待!