項目背景及功能
在小型自動化調試或簡易設備控制場景中,無刷電機的速度調控與運行狀態(tài)監(jiān)看存在明顯不便:傳統方案下,電機轉速調節(jié)需依賴現場專用工具連接調試,無法遠程操作;設備運行畫面、電機工作狀態(tài)的查看也需人員到場,導致運維效率較低。睿擎派嵌入式開發(fā)板具備串口外設接口與網絡傳輸能力,可同時承載控制與監(jiān)看需求,因此本項目基于該硬件展開開發(fā)。項目通過串口實現電機速度環(huán)控制,精準調節(jié)轉速;同時集成視頻推流功能,將電機運行畫面或設備現場視頻遠程傳輸,實現狀態(tài)的實時遠程監(jiān)看。該方案有效解決了傳統方式中現場調試繁瑣和遠程監(jiān)看缺失的問題,適配了此類輕量場景下的實際需求。
RTT使用情況
本項目基于 RT-Thread開發(fā),充分依托 RTT 嵌入式操作系統的多線程調度、設備驅動框架、FinSH 調試終端、WebNet 組件四大核心能力,實現了 FOC 電機控制與 WEB 視頻推流的高效集成,大幅簡化開發(fā)流程并提升系統穩(wěn)定性。

1、UART 串口(電機控制)依托 RTT 的串口設備驅動,通過rt_device_find(“uart3”)查找電機連接的 UART3 設備,rt_device_open(serial, RT_DEVICE_FLAG_RDWR)打開設備并配置為 “讀寫模式”;后續(xù)通過rt_device_write()發(fā)送電機控制指令(含 CRC 校驗),rt_device_read()丟棄電機響應數據,全程無需操作寄存器,適配效率提升 80%。
2、UVC 攝像頭(視頻推流)項目中 UVC 攝像頭通過 RTT 的usbh_uvc驅動適配,直接調用rt_device_find(“uvc”)獲取設備句柄,rt_device_control()設置視頻幀回調函數(video_frame_callback),實現 “攝像頭采集→幀數據回調→WEB 推流” 的端到端流程,底層 USB 枚舉、數據傳輸等邏輯完全由 RTT 驅動封裝,開發(fā)者僅需關注應用層數據處理。
3、網絡設備(WEB 服務)無需手動配置網卡,RTT 通過sal(套接字抽象層)自動適配以太網 / 無線網卡,WebNet 組件直接基于lwIP協議棧創(chuàng)建 TCP 監(jiān)聽(80 端口),通過webnet_start()一鍵啟動 WEB 服務,簡化網絡編程復雜度。
項目的 WEB 視頻推流功能完全基于 RTT 官方webnet組件實現,無需從零開發(fā) HTTP 服務:
1、WEB 服務啟動:通過webnet_start(80, “/webroot”)啟動 80 端口 WEB 服務,指定網頁資源根目錄為/webroot(FTP 上傳的 HTML 頁面存于此目錄);
2、視頻推流 CGI 注冊:通過webnet_cgi_register(“mjpeg_stream”, cgi_mjpeg_stream_handler)注冊推流接口,當瀏覽器訪問mjpeg_stream時,自動觸發(fā)cgi_mjpeg_stream_handler回調;
3、流數據傳輸:在回調函數中,通過webnet_session_printf()設置 HTTP 響應頭(multipart/x-mixed-replace格式),webnet_session_write()發(fā)送 JPEG 視頻幀,實現 “一幀一響應” 的 MJPEG 推流,瀏覽器無需插件即可實時播放。
技術路徑
開箱上手
首先是到手的開箱,有一個電源和一個主板,經過驗證,便宜的DAP-Link無法使用,因此該DAP-Link用作串口調試,燒錄就使用USB進行下載。

接著安裝RTT的工業(yè)開發(fā)平臺,沒有什么需要注意的,直接一直下一步就好了。

新建項目就好了,但是要注意jtag和daplink的區(qū)分即可,接著就使用RKtool刷一下系統升級,這個太簡單了,就跳過。然后使用moba就可以進入系統了。

這個就是我們的RTT操作系統了,很容易上手。接著,輸入一下。
help
就可以看到所有支持的指令了。

接著把RTT的歷程燒寫進去,體驗一下多線程的系統效果。

可以看到打印了預期的結果。
開發(fā)過程
先去建立一個空的工程,我們先測試一下串口的功能,下面給出串口控制的代碼。
/*** RT-Thread RuiChing** COPYRIGHT (C) 2024-2025 Shanghai Real-Thread Electronic Technology Co., Ltd.* All rights reserved.** The license and distribution terms for this file may be* found in the file LICENSE in this distribution.*/#include#include#defineUART_NAME"uart3"staticrt_device_tserial;staticcharrx_buffer[64];staticrt_size_trx_len =0;staticrt_err_tuart_rx_ind(rt_device_tdev,rt_size_tsize){ charch; while(rt_device_read(dev,0, &ch,1) ==1) { if(rx_len
就是一個簡單的數據發(fā)送與接受,測試ok后進入下一個階段,進行FOC的開發(fā)。
首先,介紹一下次使用的電機:本末科技的m0603a
M0603A電機具有靜音、結構緊湊、安裝簡便、運行穩(wěn)定等特點,采用雙驅動輪設計提升智能操控體驗,已成功應用于追覓智能洗地機等產品,并成為家用機器人、AGV、驅動輪等領域的首選動力方案。
其次,給出我的硬件接線結構整體圖:

接著我們看一下電機的控制參數:

可看到是LIN轉到UART,38400的速度,接著我們來看協議


使用10幀格式和crc8的末尾校驗格式,為此我們可以在RT中進行編程CRC生成代碼。
staticrt_err_tmotor_send_speed(int16_tspeed){ // 若未初始化,先執(zhí)行初始化 if(motor_inited == RT_FALSE) { if(motor_init() != RT_EOK) { returnRT_ERROR; } } // 構造速度控制指令(對齊CH55x的mot_control_data) speed = speed *10; uint8_tcmd_speed[9] = { MOTOR_ADDR, // 第1字節(jié):設備地址 CMD_SPEED_CTRL, // 第2字節(jié):速度指令碼(0x64) (speed >>8) &0xFF, // 第3字節(jié):速度高8位 speed &0xFF, // 第4字節(jié):速度低8位 0x00,0x00,0x00,0x00,0x00 // 保留字節(jié) }; // 發(fā)送速度指令(復用通用指令發(fā)送函數) if(motor_send_cmd(cmd_speed) == RT_EOK) { rt_kprintf("當前速度:%d\n", speed); returnRT_EOK; } returnRT_ERROR;}
接著就是電機初始化系統指令控制的代碼:
// 功能:執(zhí)行CH55x中的初始化流程:發(fā)送使能指令 → 延時 → 發(fā)送速度模式指令staticrt_err_t motor_init(void){ if(motor_inited==RT_TRUE) { rt_kprintf("電機已初始化,無需重復執(zhí)行!\n"); returnRT_EOK; } // 1. 構造“電機使能指令”(對齊CH55x的enable數組:0x01,0xA0,0x08,...) uint8_t cmd_enable[9]={ MOTOR_ADDR, // 第1字節(jié):設備地址 CMD_ENABLE, // 第2字節(jié):控制指令碼(0xA0) SUB_CMD_ENABLE,// 第3字節(jié):子指令(使能:0x08) 0x00,0x00, // 第4-5字節(jié):保留 0x00,0x00, // 第6-7字節(jié):保留 0x00,0x00 // 第8-9字節(jié):保留 }; // 發(fā)送使能指令 if(motor_send_cmd(cmd_enable)!=RT_EOK) { rt_kprintf("電機使能失??!\n"); returnRT_ERROR; } rt_kprintf("電機使能成功!\n"); rt_thread_mdelay(INIT_DELAY); // 延時500ms(與CH55x一致,等待使能完成) // 2. 構造“速度模式指令”(對齊CH55x的position_mode數組:0x01,0xA0,0x02,...) uint8_t cmd_speed_mode[9]={ MOTOR_ADDR, // 第1字節(jié):設備地址 CMD_ENABLE, // 第2字節(jié):控制指令碼(0xA0) SUB_CMD_SPEED_MODE,// 第3字節(jié):子指令(速度模式:0x02) 0x00,0x00, // 第4-5字節(jié):保留 0x00,0x00, // 第6-7字節(jié):保留 0x00,0x00 // 第8-9字節(jié):保留 }; // 發(fā)送速度模式指令 if(motor_send_cmd(cmd_speed_mode)!=RT_EOK) { rt_kprintf("切換速度模式失??!\n"); returnRT_ERROR; } rt_kprintf("已切換到速度模式!\n"); rt_thread_mdelay(10); // 短延時,確保模式生效 // 3. 初始化標志置位(避免重復執(zhí)行) motor_inited=RT_TRUE; current_speed=0; // 初始化速度為0 returnRT_EOK;}
然后針對電機特性,創(chuàng)建一個DEMO線程
staticrt_err_tmotor_send_speed(int16_tspeed){ // 若未初始化,先執(zhí)行初始化 if(motor_inited == RT_FALSE) { if(motor_init() != RT_EOK) { returnRT_ERROR; } } // 構造速度控制指令(對齊CH55x的mot_control_data) speed = speed *10; uint8_tcmd_speed[9] = { MOTOR_ADDR, // 第1字節(jié):設備地址 CMD_SPEED_CTRL, // 第2字節(jié):速度指令碼(0x64) (speed >>8) &0xFF, // 第3字節(jié):速度高8位 speed &0xFF, // 第4字節(jié):速度低8位 0x00,0x00,0x00,0x00,0x00 // 保留字節(jié) }; // 發(fā)送速度指令(復用通用指令發(fā)送函數) if(motor_send_cmd(cmd_speed) == RT_EOK) { rt_kprintf("當前速度:%d\n", speed); returnRT_EOK; } returnRT_ERROR;}
這樣我們的電機就成功驅動了,速度環(huán)控制成功。

電機控制的完整代碼:
#include#include// -------------------------- 1. 硬件與協議配置(完全對齊CH55x代碼)--------------------------#defineMOTOR_UART_NAME "uart3" // 電機UART設備名#defineMOTOR_ADDR 0x01 // 電機從機地址(與CH55x一致:0x01)// 電機協議指令(從CH55x代碼提?。?defineCMD_ENABLE 0xA0 // 使能/模式控制指令碼(CH55x中0xA0)#defineSUB_CMD_ENABLE 0x08 // 子指令:電機使能(CH55x中0x08)#defineSUB_CMD_SPEED_MODE 0x02 // 子指令:速度模式(CH55x中0x02,對應sel_2_mode=1)#defineCMD_SPEED_CTRL 0x64 // 速度控制指令碼(原代碼不變)// 速度參數#defineSPEED_STEP 5 // 速度變化步長#defineSPEED_DELAY 100 // 速度變化間隔(ms)#defineINIT_DELAY 500 // 初始化指令間隔(ms,與CH55x一致)// -------------------------- 2. 全局變量 --------------------------staticrt_device_tmotor_uart = RT_NULL; // 電機UART句柄staticint16_tcurrent_speed =0; // 當前速度(-100~100)staticuint8_tspeed_dir =1; // 速度方向:1=0→100,2=100→-100,3=-100→100staticrt_bool_tmotor_inited = RT_FALSE;// 電機初始化標志(避免重復初始化)// -------------------------- 3. 基礎工具函數(位反轉+CRC8,原代碼不變)--------------------------staticuint8_treverse_bits(uint8_tbyte){ uint8_treversed =0; for(uint8_ti =0; i 8; i++)? ? {? ? ? ? reversed <<=?1;? ? ? ? reversed |= (byte &?0x01);? ? ? ? byte >>=1; } returnreversed;}staticuint8_tcrc8_maxim(constuint8_t* data,size_tlen){ uint8_tcrc =0x00; constuint8_tpoly =0x31; for(size_ti =0; i < len; i++)? ? {? ? ? ? uint8_t?reversed_byte =?reverse_bits(data[i]);? ? ? ? crc ^= reversed_byte;? ? ? ? for?(uint8_t?j =?0; j 8; j++)? ? ? ? {? ? ? ? ? ? crc = (crc &?0x80) ? ((crc <1) ^ poly) : (crc <1);? ? ? ? ? ? crc &=?0xFF;? ? ? ? }? ? }? ? crc =?reverse_bits(crc);? ? return?crc;}// -------------------------- 4. 新增:電機指令發(fā)送通用函數(復用邏輯)--------------------------// 功能:發(fā)送任意9字節(jié)電機指令(含CRC),并丟棄返回數據(對齊CH55x的“讀響應丟棄”邏輯)static?rt_err_t?motor_send_cmd(const?uint8_t* cmd){? ? if?(motor_uart == RT_NULL)? ? {? ? ? ? rt_kprintf("電機UART未初始化!\n");? ? ? ? return?RT_ERROR;? ? }? ? // 1. 計算CRC(9字節(jié)指令+1字節(jié)CRC)? ? uint8_t?crc =?crc8_maxim(cmd,?9);? ? // 2. 發(fā)送指令(9字節(jié)指令)? ? rt_size_t?send_len1 =?rt_device_write(motor_uart,?0, cmd,?9);? ? // 3. 發(fā)送CRC(1字節(jié))? ? rt_size_t?send_len2 =?rt_device_write(motor_uart,?0, &crc,?1);? ? if?(send_len1 !=?9?|| send_len2 !=?1)? ? {? ? ? ? rt_kprintf("指令發(fā)送失敗!\n");? ? ? ? return?RT_ERROR;? ? }? ? // 4. 丟棄電機返回的響應數據(對齊CH55x的while(Serial0_available()){Serial0_read()})? ? rt_thread_mdelay(10); ?// 等待響應數據返回? ? uint8_t?dummy;? ? while?(rt_device_read(motor_uart,?0, &dummy,?1) ==?1); ?// 讀取并丟棄所有響應? ? return?RT_EOK;}// -------------------------- 5. 新增:電機初始化(使能+速度模式設置)--------------------------// 功能:執(zhí)行CH55x中的初始化流程:發(fā)送使能指令 → 延時 → 發(fā)送速度模式指令static?rt_err_t?motor_init(void){? ? if?(motor_inited == RT_TRUE)? ? {? ? ? ? rt_kprintf("電機已初始化,無需重復執(zhí)行!\n");? ? ? ? return?RT_EOK;? ? }? ? // 1. 構造“電機使能指令”(對齊CH55x的enable數組:0x01,0xA0,0x08,...)? ? uint8_t?cmd_enable[9] = {? ? ? ? MOTOR_ADDR, ? ?// 第1字節(jié):設備地址? ? ? ? CMD_ENABLE, ? ?// 第2字節(jié):控制指令碼(0xA0)? ? ? ? SUB_CMD_ENABLE,// 第3字節(jié):子指令(使能:0x08)? ? ? ? 0x00,?0x00, ? ?// 第4-5字節(jié):保留? ? ? ? 0x00,?0x00, ? ?// 第6-7字節(jié):保留? ? ? ? 0x00,?0x00? ? ?// 第8-9字節(jié):保留? ? };? ? // 發(fā)送使能指令? ? if?(motor_send_cmd(cmd_enable) != RT_EOK)? ? {? ? ? ? rt_kprintf("電機使能失??!\n");? ? ? ? return?RT_ERROR;? ? }? ? rt_kprintf("電機使能成功!\n");? ? rt_thread_mdelay(INIT_DELAY); ?// 延時500ms(與CH55x一致,等待使能完成)? ? // 2. 構造“速度模式指令”(對齊CH55x的position_mode數組:0x01,0xA0,0x02,...)? ? uint8_t?cmd_speed_mode[9] = {? ? ? ? MOTOR_ADDR, ? ? ? ?// 第1字節(jié):設備地址? ? ? ? CMD_ENABLE, ? ? ? ?// 第2字節(jié):控制指令碼(0xA0)? ? ? ? SUB_CMD_SPEED_MODE,// 第3字節(jié):子指令(速度模式:0x02)? ? ? ? 0x00,?0x00, ? ? ? ?// 第4-5字節(jié):保留? ? ? ? 0x00,?0x00, ? ? ? ?// 第6-7字節(jié):保留? ? ? ? 0x00,?0x00? ? ? ? ?// 第8-9字節(jié):保留? ? };? ? // 發(fā)送速度模式指令? ? if?(motor_send_cmd(cmd_speed_mode) != RT_EOK)? ? {? ? ? ? rt_kprintf("切換速度模式失?。n");? ? ? ? return?RT_ERROR;? ? }? ? rt_kprintf("已切換到速度模式!\n");? ? rt_thread_mdelay(10); ?// 短延時,確保模式生效? ? // 3. 初始化標志置位(避免重復執(zhí)行)? ? motor_inited = RT_TRUE;? ? current_speed =?0; ?// 初始化速度為0? ? return?RT_EOK;}// -------------------------- 6. 速度發(fā)送函數(復用motor_send_cmd,邏輯更簡潔)--------------------------static?rt_err_t?motor_send_speed(int16_t?speed){? ? // 若未初始化,先執(zhí)行初始化? ? if?(motor_inited == RT_FALSE)? ? {? ? ? ? if?(motor_init() != RT_EOK)? ? ? ? {? ? ? ? ? ? return?RT_ERROR;? ? ? ? }? ? }? ? // 構造速度控制指令(對齊CH55x的mot_control_data)? ? speed = speed *?10;? ? uint8_t?cmd_speed[9] = {? ? ? ? MOTOR_ADDR, ? ? ? ?// 第1字節(jié):設備地址? ? ? ? CMD_SPEED_CTRL, ? ?// 第2字節(jié):速度指令碼(0x64)? ? ? ? (speed >>8) &0xFF, // 第3字節(jié):速度高8位 speed &0xFF, // 第4字節(jié):速度低8位 0x00,0x00,0x00,0x00,0x00 // 保留字節(jié) }; // 發(fā)送速度指令(復用通用指令發(fā)送函數) if(motor_send_cmd(cmd_speed) == RT_EOK) { rt_kprintf("當前速度:%d\n", speed); returnRT_EOK; } returnRT_ERROR;}// -------------------------- 7. 速度循環(huán)線程(原邏輯不變,新增初始化檢查)--------------------------staticvoidspeed_loop_thread(void*parameter){ // 線程啟動時先檢查初始化(雙重保障) if(motor_inited == RT_FALSE) { if(motor_init() != RT_EOK) { rt_kprintf("線程啟動失敗:電機初始化失?。n"); return; // 初始化失敗,線程退出 } } while(1) { // 速度方向邏輯(原代碼不變) switch(speed_dir) { case1: // 0→100(遞增) current_speed += SPEED_STEP; if(current_speed >=100) { current_speed =100; speed_dir =2; } break; case2: // 100→-100(遞減) current_speed -= SPEED_STEP; if(current_speed <=?-100)? ? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ? current_speed =?-100;? ? ? ? ? ? ? ? ? ? speed_dir =?3;? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? break;? ? ? ? ? ? case?3: ?// -100→100(遞增)? ? ? ? ? ? ? ? current_speed += SPEED_STEP;? ? ? ? ? ? ? ? if?(current_speed >=100) { current_speed =100; speed_dir =2; } break; default: speed_dir =1; current_speed =0; break; } // 發(fā)送速度(此時電機已初始化完成) motor_send_speed(current_speed); rt_thread_mdelay(SPEED_DELAY); }}// -------------------------- 8. 主初始化函數(UART配置+線程創(chuàng)建)--------------------------staticintmotor_speed_loop_init(void){ // 1. 配置電機UART(原邏輯不變) motor_uart =rt_device_find(MOTOR_UART_NAME); if(motor_uart == RT_NULL) { rt_kprintf("未找到UART設備:%s\n", MOTOR_UART_NAME); returnRT_ERROR; } // 打開UART(讀+寫:需讀取電機響應并丟棄,所以用RDWR模式) if(rt_device_open(motor_uart, RT_DEVICE_FLAG_RDWR) != RT_EOK) { rt_kprintf("打開UART失?。?s\n", MOTOR_UART_NAME); returnRT_ERROR; } // 配置UART參數(波特率38400,8N1,與CH55x一致) structserial_configureconfig = RT_SERIAL_CONFIG_DEFAULT; config.baud_rate = BAUD_RATE_38400; config.data_bits = DATA_BITS_8; config.stop_bits = STOP_BITS_1; config.parity = PARITY_NONE; rt_device_control(motor_uart, RT_DEVICE_CTRL_CONFIG, &config); // 2. 先執(zhí)行電機初始化(使能+速度模式) if(motor_init() != RT_EOK) { rt_kprintf("電機初始化失敗,無法啟動線程!\n"); rt_device_close(motor_uart); // 初始化失敗,關閉UART returnRT_ERROR; } // 3. 創(chuàng)建并啟動速度循環(huán)線程 rt_thread_ttid =rt_thread_create( "speed_loop", speed_loop_thread, RT_NULL, 4096, 25, 10 ); if(tid != RT_NULL) { rt_thread_startup(tid); rt_kprintf("電機速度環(huán)線程啟動成功!\n"); } else { rt_kprintf("線程創(chuàng)建失??!\n"); returnRT_ERROR; } returnRT_EOK;}// -------------------------- 9. 導出Msh命令(啟動+停止)--------------------------MSH_CMD_EXPORT(motor_speed_loop_init, 啟動電機速度環(huán)(含初始化+速度模式切換));// 新增:一鍵停止(發(fā)送0速度+刪除線程)staticintmotor_speed_loop_stop(void){ // 1. 發(fā)送0速度停電機 if(motor_uart != RT_NULL && motor_inited == RT_TRUE) { motor_send_speed(0); rt_kprintf("電機已停轉(速度0)\n"); } // 2. 查找并刪除速度環(huán)線程 rt_thread_ttid =rt_thread_find("speed_loop"); if(tid != RT_NULL) { rt_thread_delete(tid); rt_kprintf("速度環(huán)線程已刪除\n"); } // 3. 重置狀態(tài)(下次啟動可重新初始化) motor_inited = RT_FALSE; if(motor_uart != RT_NULL) { rt_device_close(motor_uart); motor_uart = RT_NULL; } returnRT_EOK;}MSH_CMD_EXPORT(motor_speed_loop_stop, 停止電機速度環(huán)(停電機+刪線程))
下面進行推流的設置

配一下ip地址,然后使用FTP傳輸web代碼到RT睿擎派上面
下面給出web代碼:
html>
<2;title>RT-Thread 視頻流控制中心title>
基于睿擎派的工業(yè)FOC無刷電機控制系統與WEB推流監(jiān)看系統| 技術集結