Matlab 实时读取串口并绘图

Matlab 实时读取串口并绘图

Vofa+

Vofa+ 是一个很好的跨平台上位机软件,但是它无法保存数据,而且作者也并没有要继续更新的意思,保存数据功能应该是遥遥无期了。因此本文使用 Matlab 实时读取串口数据,并使用 plot 函数绘制。

vofa+ 里发送 float 型数据的协议是 justfloat,其具体规则如下:
在这里插入图片描述

下位机发送数据

下位机发送程序各有不同,下面是一个例子,使用的是 DSP28027 单片机,定义了全局变量 gTime 做时间向量:

void myPutVariableData_vofa(void)
{
    int   num = 6; // number of float variables to be sent
    float data[6]      = {0}; // float variables to be sent 
    uint16_t txBuf[14] = {0}; 
    float testData = 114514.0;
    float testW    = 10.0;

    txBuf[2 * num]     = 0x0000;
    txBuf[2 * num + 1] = 0x7f80;
    
    data[0] = testData;
    data[1] = testData;
    data[2] = testData;
    data[3] = testData;
    data[4] = testData;
    data[5] = sin(2 * 3.1415 * testW * gTime);

    memcpy(txBuf, (uint16_t *)data, num * sizeof(float));
    mySCI_sendDataBlocking(txBuf, 2 * num + 2);
}

Matlab 代码

使用 matlab 读取串口主要分为接收数据和协议解析,用的方法比较粗暴,直接读取串口然后判断帧尾。

Step 1. 设置串口

Matlab 中使用 serialportlist 命令查找电脑连接的串口,找到对应设备名称后在设置相关参数

devicelist = serialportlist' % This command shows the port connected.

baudrate        = 115200;
device          = "/dev/cu.usbserial-A700eG4x";
myPort          = serialport(device,baudrate); 
myPort.DataBits = 8;
myPort.StopBits = 1;
myPort.Parity   = "none";

Step 2. 设置数据缓冲区

设置绘图数据的个数和时间窗口大小,本文设置 6 个数据, 2000 个采样点的窗口,每一帧传输时间使用 d t = ( n ∗ 4 + 4 ) ∗ 8 BIT Baudrate dt = \frac{(n*4+4)*8 \text{BIT}}{\text{Baudrate}} dt=Baudrate(n4+4)8BIT 计算, n n n 为传输数据个数。然后将数据缓冲区都初始化为0:

varNum = 6;
buf    = 2000;
dt     = (varNum * 4 + 4) * 8 / baudrate;
time   = -dt*(buf/2):dt:dt*(buf/2-1);

dataBuf.var0 = zeros(buf,1);
dataBuf.var1 = zeros(buf,1);
dataBuf.var2 = zeros(buf,1);
dataBuf.var3 = zeros(buf,1);
dataBuf.var4 = zeros(buf,1);
dataBuf.var5 = zeros(buf,1);

这里也可以使用动态命名变量来初始化:

varNum = 6;
buf    = 2000;
dt     = (varNum * 4 + 4) * 8 / baudrate;
time   = -dt*(buf/2):dt:dt*(buf/2-1);


for i = 1:varNum
	namelist{i} = ['var',num2str(i-1)];
	dataBuf.(namelist{i}) = zeros(buf,1);
end

Step 3. 协议解析

协议解析主要是找到帧尾标志,使用 read() 读取4字节串口数据并判断是否是帧尾,找到帧尾就可以按照对应位置解析数据。转换 00 00 80 7F 为十进制数组 0 0 128 127 再进行判断,如果找到帧尾,就依次读取数据位(这里读取的是下一帧的数据,差一帧)。然后使用 circshift() 循环存储数据,让缓冲区的最后一位始终存储最新的数据。

因为数据是小端浮点数组(先发低位再发高位),所以使用 flip() 函数重新排序,然后将读取到的数据转换为 uint32,再使用 typecast() 函数转换为单精度浮点型数据。

dec2uint32 = @(dec)uint32(dec * [2^24, 2^16, 2^8, 2^0]');
temp = read(myPort,4,'uint8');
if prod(temp == [0 0 128 127])
    temp = read(myPort, varNum * 4, 'uint8');
    dataBuf.var0 = circshift(dataBuf.var0);
    dataBuf.var0(end) = typecast(dec2uint32(flip(temp(1:4))),'single');
    dataBuf.var1 = circshift(dataBuf.var1);
    dataBuf.var1(end) = typecast(dec2uint32(flip(temp(5:8))),'single');
    dataBuf.var2 = circshift(dataBuf.var2);
    dataBuf.var2(end) = typecast(dec2uint32(flip(temp(9:12))),'single');
    dataBuf.var3 = circshift(dataBuf.var3);
    dataBuf.var3(end) = typecast(dec2uint32(flip(temp(13:16))),'single');
    dataBuf.var4 = circshift(dataBuf.var4);
    dataBuf.var4(end) = typecast(dec2uint32(flip(temp(17:20))),'single');
    dataBuf.var5 = circshift(dataBuf.var5);
    dataBuf.var5(end) = typecast(dec2uint32(flip(temp(21:24))),'single');
end

同样这里可以使用动态变量命名简化代码:

temp = read(myPort,4,'uint8');
if prod(temp == [0 0 128 127])
    temp = read(myPort, varNum * 4, 'uint8');
    for i = 1:varNum
        dataBuf.(namelist{i})      = circshift(dataBuf.(namelist{i}),-1);
        dataBuf.(namelist{i})(end) = typecast(dec2uint32(flip(temp((i-1) * 4 + 1: i * 4))),'single');
    end
end

Step 4. 绘制数据

先建立 figure 对象,使用 while 判断窗体存在条件,持续绘制数据

h   = figure;
plotenabled = 0;

%%
while(ishandle(h))
    temp = read(myPort,4,'uint8');
    if prod(temp == [0 0 128 127])
        temp = read(myPort, varNum * 4, 'uint8');
        for i = 1:varNum
            dataBuf.(namelist{i})      = circshift(dataBuf.(namelist{i}),-1);
            dataBuf.(namelist{i})(end) = typecast(dec2uint32(flip(temp((i-1) * 4 + 1: i * 4))),'single');
        end
        plot(time, dataBuf.var5,'b-','LineWidth',1.5);
        if plotenabled == 0
            plotenabled = 1;
            grid on
            title('Matlab Serial Port Read','FontSize',14,'FontName','Times New Roman');
            xlabel('Time','FontSize',14,'FontName','Times New Roman');
            ylabel('Value','FontSize',14,'FontName','Times New Roman');
        end
    end
    plotenabled = 0;
end
  • 变量 plotenabled 用于首次绘图设置绘图参数,只有第一次绘图的时候会执行设置 title, xlabel, ylabel 等参数。

完整的 Matlab 代码如下:

%---------------------------------------
% This is the series port read script
%
% hu 2023-07-07 Created
% hu 2024-04-10 Test vofa+ protocol
%---------------------------------------

clc,clear,close all
devicelist = serialportlist' % This command shows the port connected.

baudrate        = 115200;
device          = "/dev/cu.usbserial-A700eG4x";
myPort          = serialport(device,baudrate); 
myPort.DataBits = 8;
myPort.StopBits = 1;
myPort.Parity   = "none";

varNum   = 6;
buf      = 2000;
dt       = (varNum * 4 + 4) * 8 / baudrate;
time     = -dt*(buf/2):dt:dt*(buf/2 - 1);

for i = 1:varNum
    namelist{i} = ['var',num2str(i-1)];
    dataBuf.(namelist{i}) = zeros(buf,1);
end
dec2uint32 = @(dec)uint32(dec * [2^24, 2^16, 2^8, 2^0]');

h   = figure;
plotenabled = 0;

%%
while(ishandle(h))
    temp = read(myPort,4,'uint8');
    if prod(temp == [0 0 128 127])
        temp = read(myPort, varNum * 4, 'uint8');
        for i = 1:varNum
            dataBuf.(namelist{i})      = circshift(dataBuf.(namelist{i}),-1);
            dataBuf.(namelist{i})(end) = typecast(dec2uint32(flip(temp((i-1) * 4 + 1: i * 4))),'single');
        end
        plot(time, dataBuf.var5,'b-','LineWidth',1.5);
        if plotenabled == 0
            plotenabled = 1;
            grid on
            title('Matlab Serial Port Read','FontSize',14,'FontName','Times New Roman');
            xlabel('Time','FontSize',14,'FontName','Times New Roman');
            ylabel('Value','FontSize',14,'FontName','Times New Roman');
        end
    end
    plotenabled = 0;
end
clc,clear,close all

绘制数据可以通过 fig 窗口保存到本地,或者在数据转换后保存到工作区,这里不再赘述。

  • 24
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值