完整版教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=94547
第9章 Matlab的串口通信實現
本章節主要為大家講解Matlab的串口方式波形數據傳輸和后期數據分析功能,非常實用。
9.1 初學者重要提示
9.2 程序設計框架
9.3 下位機STM32H7程序設計
9.4 上位機Matlab程序設計
9.5 Matlab上位機程序運行
9.6 實驗例程說明(MDK)
9.7 實驗例程說明(IAR)
9.8 總結
9.1 初學者重要提示
1、 測試本章節例程注意事項。
- 請優先運行開發板,然后運行matlab。
- 調試matlab串口數據發送前,請務必關閉串口助手。
2、 函數delete(instrfindall);
如果不用matlab了,請在matlab的命令輸入窗口調用此函數,防止matlab一直占用串口。
9.2 程序設計框架
上位機和下位機的程序設計框架如下:

上位機和下位機做了一個簡單的同步,保證數據通信不出錯。
9.3 下位機STM32F429程序設計
STM32F429端的程序設計思路。
9.3.1 第1步,發送的數據格式
為了方便數據發送,專門設計了一個數據格式:
__packed typedef struct { uint16_t data1; uint16_t data2; uint16_t data3; uint8_t data4; uint8_t data5; uint8_t data6; uint8_t data7; } SENDPARAM_T; SENDPARAM_T g_SendData;
為了保證數據的連續存儲,特地在前面加了一個關鍵詞__packed。關於結構體變量占用多少字節問題,此貼進行了詳細說明:http://www.armbbs.cn/forum.php?mod=viewthread&tid=89103 。
9.3.2 第2步,接收同步信號$
Matlab發送同步信號$(ASCII編碼值是13)給開發板。
int main(void) { /* 省略未寫,僅留下關鍵代碼 */ /* 進入主程序循環體 */ while (1) { /* 判斷定時器超時時間 */ if (bsp_CheckTimer(0)) { /* 每隔100ms 進來一次 */ bsp_LedToggle(2); } if (comGetChar(COM1, &read)) { /* 接收到同步幀'$'*/ if(read == 13) { bsp_LedToggle(4); bsp_DelayMS(10); Serial_sendDataMATLAB(); } } } }
通過函數comGetChar獲取串口接收到的數據,如果數值是13,說明接收到Matlab發送過來的同步信號了。
這里要注意一點,程序這里接收到同步信號后,延遲了10ms再發數據給matlab,主要是因為matlab的波形刷新有點快,程序這里每發送給matlab一次數據,matlab就會刷新一次,10ms就相當於100Hz的刷新率,也會有一定的閃爍感。
9.3.3 第3步,發送數據給Matlab
下面是給Matlab回復同步信號和相應數據的實現:
/* ********************************************************************************************************* * 函 數 名: Serial_sendDataMATLAB * 功能說明: 發送串口數據給matlab * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void Serial_sendDataMATLAB(void) { /* 先發同步信號'$' */ comSendChar(COM1, 13); /* 發送數據,一共10個字節 */ g_SendData.data1 = rand()%65536; g_SendData.data2 = rand()%65536; g_SendData.data3 = rand()%65536; g_SendData.data4 = rand()%256; g_SendData.data5 = rand()%256; g_SendData.data6 = rand()%256; g_SendData.data7 = rand()%256; comSendBuf(COM1, (uint8_t *)&g_SendData, 10); }
開發板接收到同步信號后,會回應一個同步信號,然后將10個字節的數據發送給matlab。通過這三步就完成了STM32H7端的程序設計。
9.4 上位機Matlab程序設計
Matlab端的程序設計要略復雜些,需要大家理解matlab端的API。具體說明可以看如下地址:
https://ww2.mathworks.cn/help/matlab/serial-port-devices.html 。
9.4.1 第1步,配置並打開串口
下面操作是配置並打開串口:
close all clear all %刪除所有已經打開的串口,這條很重要,防止之前運行沒有關閉串口 delete(instrfindall); %打開串口COM1,波特率115200,8位數據位,1位停止位,無奇偶校驗,無流控制 s = serial('COM1', 'BaudRate', 115200, 'DataBits', 8, 'StopBits', 1, 'Parity', 'none', 'FlowControl', 'none'); s.ReadAsyncMode = 'continuous'; fopen(s); fig = figure(1);
這里有以下幾點需要大家了解:
- 函數delete(instrfindall)
這個函數比較重要,防止上次matlab操作串口,結束時沒有關閉串口。通過這個函數會將其關閉。
- 函數serial
大家要特別注意打開的COM序號,務必要根據實際使用的COM號進行設置。
- 函數fopen
通過函數fopen打開串口。
9.4.2 第2步,相關變量設置
程序里面對這些變量的注釋已經比較詳細:
AxisMax = 65536; %坐標軸最大值 AxisMin = -65536; %坐標軸最小值 window_width = 800; %窗口寬度 g_Count =0; %接收到的數據計數 SOF = 0; %同步幀標志 AxisValue = 1; %坐標值 RecDataDisp = zeros(1,100000); %開辟100000個數據單元,用於存儲接收到的數據。 RecData = zeros(1,100); %開辟100個數據單元,用於數據處理。 Axis = zeros(1,100000); %開辟100000個數據單元,用於X軸。 window = window_width * (-0.9); %窗口X軸起始坐標 axis([window, window + window_width, AxisMin, AxisMax]); %設置窗口坐標范圍 %子圖1顯示串口上傳的數據 subplot(2,1,1); grid on; title('串口數據接收'); xlabel('時間'); ylabel('數據'); %子圖2顯示波形的幅頻響應 subplot(2,1,2); grid on; title( 'FFT'); xlabel( '頻率'); ylabel( '幅度'); Fs = 100; % 采樣率 N = 50; % 采樣點數 n = 0:N-1; % 采樣序列 f = n * Fs / N; %真實的頻率
這里有以下幾點需要大家了解:
- 變量RecDataDisp,RecData和Axis
這幾個變量專門開辟好了數據空間,防止matlab警告和刷新波形慢的問題,大家根據需要可以進行加大。
- 采樣率Fs = 100和采樣點數N = 50
這個地方要根據實際的情況進行設置。
9.4.3 第3步,數據同步部分
這部分代碼比較關鍵,matlab先發送同步信號$出去,然后等待開發板回復同步信號$,並讀取本次通信的數據。
%設置同步信號標志, = 1表示接收到下位機發送的同步幀 SOF = 0; %發送同步幀 fwrite(s, 13); %獲取是否有數據 bytes = get(s, 'BytesAvailable'); if bytes == 0 bytes = 1; end %讀取下位機返回的所有數據 RecData = fread(s, bytes, 'uint8'); %檢索下位機返回的數據中是否有字符$ StartData = find(RecData == 13); %如果檢索到$,讀取10個字節的數據,也就是5個uint16的數據 if(StartData >= 1) RecData = fread(s, 5, 'uint16'); SOF =1; StartData = 0; end
這里有以下幾點需要大家了解:
- 函數fwrite(s, 13)
用於發送同步信號$(ASCII值是13)。
- 函數get(s, 'BytesAvailable')
用於獲取串口緩沖中的字節數。
- 函數fread(s, bytes, 'uint8')
將串口緩沖的數據讀取輸出。
- 函數find(RecData == 13)
檢索接收到串口數據中是否有同步信號$。
- 函數fread(s, 5, 'uint16')
如果檢索到$,繼續讀取10個字節的數據,也就是5個uint16的數據。
9.4.4 第4步,顯示串口上傳的數據
下面matlab的數據顯示波形
%更新接收到的數據波形 if(SOF == 1) %更新數據 RecDataDisp(AxisValue) = RecData(1); RecDataDisp(AxisValue + 1) = RecData(2); RecDataDisp(AxisValue + 2) = RecData(3); RecDataDisp(AxisValue + 3) = RecData(4); RecDataDisp(AxisValue + 4) = RecData(5); %更新X軸 Axis(AxisValue) = AxisValue; Axis(AxisValue + 1) = AxisValue + 1; Axis(AxisValue + 2) = AxisValue + 2; Axis(AxisValue + 3) = AxisValue + 3; Axis(AxisValue + 4) = AxisValue + 4; %更新變量 AxisValue = AxisValue + 5; g_Count = g_Count + 5; %繪制波形 subplot(2,1,1); plot(Axis(1:AxisValue-1), RecDataDisp(1:AxisValue-1), 'r'); window = window + 5; axis([window, window + window_width, AxisMin, AxisMax]); grid on; title('串口數據接收'); xlabel('時間'); ylabel('數據'); drawnow end
這里有以下幾點需要大家了解:
- 數組RecDataDisp,RecData和Axis
這里要尤其注意,matlab的數組索引是從1開始的,也是開頭直接定義AxisValue = 1的原因。
- 函數plot
這里plot的實現尤其重要,務必要注意坐標點和數值個數要匹配。
9.4.5 第5步,FFT數據展示
FFT部分會在在后面章節為大家詳細講解,這里也做個說明,這里是每接收夠50個數據,做一次FFT:
if(g_Count== 50) subplot(2,1,2); %對原始信號做 FFT 變換 y = fft(RecDataDisp(AxisValue-50:AxisValue-1), 50); %求 FFT 轉換結果的模值 Mag = abs(y)*2/N; %繪制幅頻相應曲線 plot(f, Mag, 'r'); grid on; title( 'FFT'); xlabel( '頻率'); ylabel( '幅度'); g_Count = 0; drawnow end
9.5 Matlab上位機程序運行
M文件的程序代碼在例子V7-203_Matlab串口波形刷新和數據分析m文件里面。M文件的運行方法在第4章的4.2小節有詳細說明。
9.6 實驗例程說明(MDK)
配套例子:
V6-202_Matlab的串口通信實現
實驗目的:
- 學習matlab的串口數據通信。
實驗內容:
- 啟動一個自動重裝軟件定時器,每100ms翻轉一次LED2。
- 請優先運行開發板,然后運行matlab。
- 調試matlab串口數據發送前,請務必關閉串口助手。
使用AC6注意事項
特別注意附件章節C的問題
上電后串口打印的信息:
波特率 115200,數據位 8,奇偶校驗位無,停止位 1
Matlab的上位機效果:
程序設計:
系統棧大小分配:
硬件外設初始化
硬件外設的初始化是在 bsp.c 文件實現:
/* ********************************************************************************************************* * 函 數 名: bsp_Init * 功能說明: 初始化所有的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只需要調用一次 * 形 參:無 * 返 回 值: 無 ********************************************************************************************************* */ void bsp_Init(void) { /* STM32H429 HAL 庫初始化,此時系統用的還是F429自帶的16MHz,HSI時鍾: - 調用函數HAL_InitTick,初始化滴答時鍾中斷1ms。 - 設置NVIV優先級分組為4。 */ HAL_Init(); /* 配置系統時鍾到168MHz - 切換使用HSE。 - 此函數會更新全局變量SystemCoreClock,並重新配置HAL_InitTick。 */ SystemClock_Config(); /* Event Recorder: - 可用於代碼執行時間測量,MDK5.25及其以上版本才支持,IAR不支持。 - 默認不開啟,如果要使能此選項,務必看V5開發板用戶手冊第8章 */ #if Enable_EventRecorder == 1 /* 初始化EventRecorder並開啟 */ EventRecorderInitialize(EventRecordAll, 1U); EventRecorderStart(); #endif bsp_InitKey(); /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */ bsp_InitTimer(); /* 初始化滴答定時器 */ bsp_InitUart(); /* 初始化串口 */ bsp_InitExtIO(); /* 初始化擴展IO */ bsp_InitLed(); /* 初始化LED */ }
主功能:
主程序實現如下操作:
- 接收matlab發送過來的同步信號,並回一個同步信號后,傳輸相應的數據過去
/* ********************************************************************************************************* * 函 數 名: main * 功能說明: c程序入口 * 形 參: 無 * 返 回 值: 錯誤代碼(無需處理) ********************************************************************************************************* */ int main(void) { uint8_t ucKeyCode; /* 按鍵代碼 */ uint8_t read; bsp_Init(); /* 硬件初始化 */ PrintfLogo(); /* 打印例程名稱和版本等信息 */ PrintfHelp(); /* 打印操作提示 */ bsp_StartAutoTimer(0, 50); /* 啟動1個100ms的自動重裝的定時器 */ /* 進入主程序循環體 */ while (1) { bsp_Idle(); /* 這個函數在bsp.c文件。用戶可以修改這個函數實現CPU休眠和喂狗 */ /* 判斷定時器超時時間 */ if (bsp_CheckTimer(0)) { /* 每隔100ms 進來一次 */ bsp_LedToggle(2); } if (comGetChar(COM1, &read)) { /* 接收到同步幀'$'*/ if(read == 13) { bsp_LedToggle(4); bsp_DelayMS(100); Serial_sendDataMATLAB(); } } /* 按鍵濾波和檢測由后台systick中斷服務程序實現,我們只需要調用bsp_GetKey讀取鍵值即可。 */ ucKeyCode = bsp_GetKey(); /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */ if (ucKeyCode != KEY_NONE) { switch (ucKeyCode) { case KEY_DOWN_K1: /* K1鍵按下 */ printf("K1鍵按下\r\n"); break; case KEY_UP_K1: /* K1鍵彈起 */ break; case KEY_DOWN_K2: /* K2鍵按下 */ printf("K2鍵按下\r\n"); break; case KEY_UP_K2: /* K2鍵彈起 */ printf("K2鍵彈起\r\n"); break; case KEY_DOWN_K3: /* K3鍵按下 */ printf("K3鍵按下\r\n"); break; case KEY_UP_K3: /* K3鍵彈起 */ printf("K3鍵彈起\r\n"); break; default: /* 其它的鍵值不處理 */ break; } } } } /* ********************************************************************************************************* * 函 數 名: Serial_sendDataMATLAB * 功能說明: 發送串口數據給matlab * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void Serial_sendDataMATLAB(void) { /* 先發同步信號'$' */ comSendChar(COM1, 13); /* 發送數據,一共10個字節 */ g_SendData.data1 = rand()%65536; g_SendData.data2 = rand()%65536; g_SendData.data3 = rand()%65536; g_SendData.data4 = rand()%256; g_SendData.data5 = rand()%256; g_SendData.data6 = rand()%256; g_SendData.data7 = rand()%256; comSendBuf(COM1, (uint8_t *)&g_SendData, 10); }
9.7 實驗例程說明(IAR)
配套例子:
V6-202_Matlab的串口通信實現
實驗目的:
- 學習matlab的串口數據通信。
實驗內容:
- 啟動一個自動重裝軟件定時器,每100ms翻轉一次LED2。
- 請優先運行開發板,然后運行matlab。
- 調試matlab串口數據發送前,請務必關閉串口助手。
上電后串口打印的信息:
波特率 115200,數據位 8,奇偶校驗位無,停止位 1
Matlab的上位機效果:
程序設計:
系統棧大小分配:
硬件外設初始化
硬件外設的初始化是在 bsp.c 文件實現:
/* ********************************************************************************************************* * 函 數 名: bsp_Init * 功能說明: 初始化所有的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只需要調用一次 * 形 參:無 * 返 回 值: 無 ********************************************************************************************************* */ void bsp_Init(void) { /* STM32H429 HAL 庫初始化,此時系統用的還是F429自帶的16MHz,HSI時鍾: - 調用函數HAL_InitTick,初始化滴答時鍾中斷1ms。 - 設置NVIV優先級分組為4。 */ HAL_Init(); /* 配置系統時鍾到168MHz - 切換使用HSE。 - 此函數會更新全局變量SystemCoreClock,並重新配置HAL_InitTick。 */ SystemClock_Config(); /* Event Recorder: - 可用於代碼執行時間測量,MDK5.25及其以上版本才支持,IAR不支持。 - 默認不開啟,如果要使能此選項,務必看V5開發板用戶手冊第8章 */ #if Enable_EventRecorder == 1 /* 初始化EventRecorder並開啟 */ EventRecorderInitialize(EventRecordAll, 1U); EventRecorderStart(); #endif bsp_InitKey(); /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */ bsp_InitTimer(); /* 初始化滴答定時器 */ bsp_InitUart(); /* 初始化串口 */ bsp_InitExtIO(); /* 初始化擴展IO */ bsp_InitLed(); /* 初始化LED */ }
主功能:
主程序實現如下操作:
- 接收matlab發送過來的同步信號,並回一個同步信號后,傳輸相應的數據過去
/* ********************************************************************************************************* * 函 數 名: main * 功能說明: c程序入口 * 形 參: 無 * 返 回 值: 錯誤代碼(無需處理) ********************************************************************************************************* */ int main(void) { uint8_t ucKeyCode; /* 按鍵代碼 */ uint8_t read; bsp_Init(); /* 硬件初始化 */ PrintfLogo(); /* 打印例程名稱和版本等信息 */ PrintfHelp(); /* 打印操作提示 */ bsp_StartAutoTimer(0, 50); /* 啟動1個100ms的自動重裝的定時器 */ /* 進入主程序循環體 */ while (1) { bsp_Idle(); /* 這個函數在bsp.c文件。用戶可以修改這個函數實現CPU休眠和喂狗 */ /* 判斷定時器超時時間 */ if (bsp_CheckTimer(0)) { /* 每隔100ms 進來一次 */ bsp_LedToggle(2); } if (comGetChar(COM1, &read)) { /* 接收到同步幀'$'*/ if(read == 13) { bsp_LedToggle(4); bsp_DelayMS(100); Serial_sendDataMATLAB(); } } /* 按鍵濾波和檢測由后台systick中斷服務程序實現,我們只需要調用bsp_GetKey讀取鍵值即可。 */ ucKeyCode = bsp_GetKey(); /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */ if (ucKeyCode != KEY_NONE) { switch (ucKeyCode) { case KEY_DOWN_K1: /* K1鍵按下 */ printf("K1鍵按下\r\n"); break; case KEY_UP_K1: /* K1鍵彈起 */ break; case KEY_DOWN_K2: /* K2鍵按下 */ printf("K2鍵按下\r\n"); break; case KEY_UP_K2: /* K2鍵彈起 */ printf("K2鍵彈起\r\n"); break; case KEY_DOWN_K3: /* K3鍵按下 */ printf("K3鍵按下\r\n"); break; case KEY_UP_K3: /* K3鍵彈起 */ printf("K3鍵彈起\r\n"); break; default: /* 其它的鍵值不處理 */ break; } } } } /* ********************************************************************************************************* * 函 數 名: Serial_sendDataMATLAB * 功能說明: 發送串口數據給matlab * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ static void Serial_sendDataMATLAB(void) { /* 先發同步信號'$' */ comSendChar(COM1, 13); /* 發送數據,一共10個字節 */ g_SendData.data1 = rand()%65536; g_SendData.data2 = rand()%65536; g_SendData.data3 = rand()%65536; g_SendData.data4 = rand()%256; g_SendData.data5 = rand()%256; g_SendData.data6 = rand()%256; g_SendData.data7 = rand()%256; comSendBuf(COM1, (uint8_t *)&g_SendData, 10); }
9.8 總結
本章講解的例程非常實用,需要大家熟練掌握。
