MATLAB实现串口输入数据的实时绘图
最近做了一个基于单片机的心率计,其中需要用计算机将波形数据以实时绘图的方式呈现出来。参考了一些网上的资料,写了一个简单的matlab程序,记录一下。
0 说明
- 基本原理:通过手指指尖的血流变化反映脉搏变化。传感器(KY-039,淘宝上可以搜到)的一端是红外二极管,一端是光敏三极管,脉搏引起的血流变化引发光强变化,进而导致电压变化。
- 脉搏信号被一个红外传感器采集后,经过放大滤波电路输入单片机,再经ADC,从串口输出。
- 输出在0-1023范围内,需要用两个字节表示。
1 创建串口对象
创建串口对象s,并对其部分参数进行设置。下面只设置了一部分,当然,可以根据自己的需要进一步设置,比如波特率之类,可以运行这段代码后在工作区点开s查看:
s=serial('com3'); % COM
s.BytesAvailableFcnMode='byte';
s.InputBufferSize=4096;
s.OutputBufferSize=1024;
s.BytesAvailableFcnCount=100;
s.ReadAsyncMode='continuous';
s.Terminator='CR';
2 数据读取与转化
打开串口,同时打开一个文件保存数据。
fopen(s); % 打开串口
fid=fopen('serial_data.txt','wt');
寻找读取起点:
init = 1;
while(init~='#')
init = fread(s,1,'uint8');
end
out = fread(s,2,'uint8'); % 8bit读两次
这里 ‘#’ 是一个标志字符(这是单片机代码中设置的),因为需要用两个字节表示一个点的值,#的作用就是让程序知道,从这里开始往后两个字节拼起来是一个数。这组数据是不用的(其实要用也可以,但不差这一个点的数据),之后再读取就只需循环:
out = fread(s,3,'uint8');
注意out是一个列向量,out(1)是#,out(2)是数值的高8位,out(3)是低8位。
接下来是对数据的转换,将两个8位二进制数位拼接转成一个十进制数:
num = out(2)*256+out(3);
或者也可以将这个数存在out(1)中,反正 ‘#’ 已经达成使命,现在它反而变成了一个累赘。
3 绘图
设置一个向量t作为横坐标,长度是InputBufferSize,纵坐标上下留出margin的空间。
hold on
grid on
InputBufferSize = 100;
margin = 50;
t = 1:InputBufferSize;
用InputBuffer存放当前需要绘制区域内的所有数值,即它的长度也是InputBufferSize。那么画“一帧”的图像就是:
plot([1:i],InputBuffer(1,1:i),'-r');
axis([t(1) t(InputBufferSize) min(InputBuffer)-margin max(InputBuffer)+margin]);
4 绘制动态图像
怎么让上述图像动起来呢?其实很简单,只要将每次新读进来的数据放到InputBuffer的最后,InputBuffer中所有元素向前移动一格,再把坐标轴右移1,就得到了下一帧图像的全部信息。让这个过程循环往复,图线就动起来了。
for i = 1:1000
t=t+1;
for j = 2:InputBufferSize % 循环左移一格
InputBuffer(j-1) = InputBuffer(j);
end
out = fread(s,3,'uint8'); % 一次读出3个字符
out = out'; % 其实没什么必要
num = out(2)*256+out(3);
fprintf(fid,'%g\n',num); % 存入文件
InputBuffer(InputBufferSize) = num;
plot(t,InputBuffer(1,:),'-r');
axis([t(1) t(InputBufferSize) min(InputBuffer)-margin max(InputBuffer)+margin]);
pause(0.001);
end
如果想做得再好一点,能呈现最开始从0慢慢长到InputBufferSize过程中的图线,那就在这段之前加一段:
for i = 1:InputBufferSize
out = fread(s,3,'uint8'); % 一次读出3个字符
out = out';
num = out(2)*256+out(3);
InputBuffer(i) = num;
fprintf(fid,'%g\n',num);
plot([1:i],InputBuffer(1,1:i),'-r');
axis([t(1) t(InputBufferSize) min(InputBuffer)-margin max(InputBuffer)+margin]);
pause(0.001);
end
与上面一段类似,只是这里固定坐标轴,且新数据只加在InputBuffer的最后。
最后别忘了关掉串口和文件~
fclose(s);
fclose(fid);
5 完整代码
clear;
s=serial('com4');
s.BytesAvailableFcnMode='byte'; % 串口设置
s.InputBufferSize=4096;
s.OutputBufferSize=1024;
s.BytesAvailableFcnCount=100;
s.ReadAsyncMode='continuous';
s.Terminator='CR';
fopen(s); % 打开串口
fid=fopen('serial_data.txt','wt');
init = 1;
while(init~='#')
init = fread(s,1,'uint8');
end
out = fread(s,2,'uint8');
hold on
grid on
InputBufferSize = 100;
margin = 50;
t = 1:InputBufferSize;
% InputBuffer = zeros(1,InputBufferSize);
for i = 1:InputBufferSize
out = fread(s,3,'uint8'); % 一次读出3个字符
out = out';
num = out(2)*256+out(3);
InputBuffer(i) = num;
fprintf(fid,'%g\n',num);
plot([1:i],InputBuffer(1,1:i),'-r');
axis([t(1) t(InputBufferSize) min(InputBuffer)-margin max(InputBuffer)+margin]);
pause(0.001);
end
for i = 1:1000
% hold on
t=t+1;
for j = 2:InputBufferSize
InputBuffer(j-1) = InputBuffer(j);
end
out = fread(s,3,'uint8'); % 一次读出3个字符
out = out';
num = out(2)*256+out(3);
fprintf(fid,'%g\n',num);
InputBuffer(InputBufferSize) = num;
plot(t,InputBuffer(1,:),'-r');
axis([t(1) t(InputBufferSize) min(InputBuffer)-margin max(InputBuffer)+margin]);
pause(0.001);
end
fclose(s);
fclose(fid);
6 补充
- 如果需要中断运行,可以在命令行中Ctrl + C,但记得随后在命令行中输入fclose(s)以关闭串口,否则下次启动会报错。无论何种方式导致程序没有执行到fclose(s),都应在下次运行前手动关闭串口。
- 若开始运行时报错,先检查串口是否匹配,若匹配,再检查串口当前是否被其他程序占用,若没有,尝试重启MATLAB。(再不行,重启电脑,再不行我也不知道了2333
- 放几张运行结果图(更新了一张动图)
版权声明:本文为CSDN博主「Volumtuous」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_48510320/article/details/118945642
MATLAB实现串口输入数据的实时绘图
最近做了一个基于单片机的心率计,其中需要用计算机将波形数据以实时绘图的方式呈现出来。参考了一些网上的资料,写了一个简单的matlab程序,记录一下。
0 说明
- 基本原理:通过手指指尖的血流变化反映脉搏变化。传感器(KY-039,淘宝上可以搜到)的一端是红外二极管,一端是光敏三极管,脉搏引起的血流变化引发光强变化,进而导致电压变化。
- 脉搏信号被一个红外传感器采集后,经过放大滤波电路输入单片机,再经ADC,从串口输出。
- 输出在0-1023范围内,需要用两个字节表示。
1 创建串口对象
创建串口对象s,并对其部分参数进行设置。下面只设置了一部分,当然,可以根据自己的需要进一步设置,比如波特率之类,可以运行这段代码后在工作区点开s查看:
s=serial('com3'); % COM
s.BytesAvailableFcnMode='byte';
s.InputBufferSize=4096;
s.OutputBufferSize=1024;
s.BytesAvailableFcnCount=100;
s.ReadAsyncMode='continuous';
s.Terminator='CR';
2 数据读取与转化
打开串口,同时打开一个文件保存数据。
fopen(s); % 打开串口
fid=fopen('serial_data.txt','wt');
寻找读取起点:
init = 1;
while(init~='#')
init = fread(s,1,'uint8');
end
out = fread(s,2,'uint8'); % 8bit读两次
这里 ‘#’ 是一个标志字符(这是单片机代码中设置的),因为需要用两个字节表示一个点的值,#的作用就是让程序知道,从这里开始往后两个字节拼起来是一个数。这组数据是不用的(其实要用也可以,但不差这一个点的数据),之后再读取就只需循环:
out = fread(s,3,'uint8');
注意out是一个列向量,out(1)是#,out(2)是数值的高8位,out(3)是低8位。
接下来是对数据的转换,将两个8位二进制数位拼接转成一个十进制数:
num = out(2)*256+out(3);
或者也可以将这个数存在out(1)中,反正 ‘#’ 已经达成使命,现在它反而变成了一个累赘。
3 绘图
设置一个向量t作为横坐标,长度是InputBufferSize,纵坐标上下留出margin的空间。
hold on
grid on
InputBufferSize = 100;
margin = 50;
t = 1:InputBufferSize;
用InputBuffer存放当前需要绘制区域内的所有数值,即它的长度也是InputBufferSize。那么画“一帧”的图像就是:
plot([1:i],InputBuffer(1,1:i),'-r');
axis([t(1) t(InputBufferSize) min(InputBuffer)-margin max(InputBuffer)+margin]);
4 绘制动态图像
怎么让上述图像动起来呢?其实很简单,只要将每次新读进来的数据放到InputBuffer的最后,InputBuffer中所有元素向前移动一格,再把坐标轴右移1,就得到了下一帧图像的全部信息。让这个过程循环往复,图线就动起来了。
for i = 1:1000
t=t+1;
for j = 2:InputBufferSize % 循环左移一格
InputBuffer(j-1) = InputBuffer(j);
end
out = fread(s,3,'uint8'); % 一次读出3个字符
out = out'; % 其实没什么必要
num = out(2)*256+out(3);
fprintf(fid,'%g\n',num); % 存入文件
InputBuffer(InputBufferSize) = num;
plot(t,InputBuffer(1,:),'-r');
axis([t(1) t(InputBufferSize) min(InputBuffer)-margin max(InputBuffer)+margin]);
pause(0.001);
end
如果想做得再好一点,能呈现最开始从0慢慢长到InputBufferSize过程中的图线,那就在这段之前加一段:
for i = 1:InputBufferSize
out = fread(s,3,'uint8'); % 一次读出3个字符
out = out';
num = out(2)*256+out(3);
InputBuffer(i) = num;
fprintf(fid,'%g\n',num);
plot([1:i],InputBuffer(1,1:i),'-r');
axis([t(1) t(InputBufferSize) min(InputBuffer)-margin max(InputBuffer)+margin]);
pause(0.001);
end
与上面一段类似,只是这里固定坐标轴,且新数据只加在InputBuffer的最后。
最后别忘了关掉串口和文件~
fclose(s);
fclose(fid);
5 完整代码
clear;
s=serial('com4');
s.BytesAvailableFcnMode='byte'; % 串口设置
s.InputBufferSize=4096;
s.OutputBufferSize=1024;
s.BytesAvailableFcnCount=100;
s.ReadAsyncMode='continuous';
s.Terminator='CR';
fopen(s); % 打开串口
fid=fopen('serial_data.txt','wt');
init = 1;
while(init~='#')
init = fread(s,1,'uint8');
end
out = fread(s,2,'uint8');
hold on
grid on
InputBufferSize = 100;
margin = 50;
t = 1:InputBufferSize;
% InputBuffer = zeros(1,InputBufferSize);
for i = 1:InputBufferSize
out = fread(s,3,'uint8'); % 一次读出3个字符
out = out';
num = out(2)*256+out(3);
InputBuffer(i) = num;
fprintf(fid,'%g\n',num);
plot([1:i],InputBuffer(1,1:i),'-r');
axis([t(1) t(InputBufferSize) min(InputBuffer)-margin max(InputBuffer)+margin]);
pause(0.001);
end
for i = 1:1000
% hold on
t=t+1;
for j = 2:InputBufferSize
InputBuffer(j-1) = InputBuffer(j);
end
out = fread(s,3,'uint8'); % 一次读出3个字符
out = out';
num = out(2)*256+out(3);
fprintf(fid,'%g\n',num);
InputBuffer(InputBufferSize) = num;
plot(t,InputBuffer(1,:),'-r');
axis([t(1) t(InputBufferSize) min(InputBuffer)-margin max(InputBuffer)+margin]);
pause(0.001);
end
fclose(s);
fclose(fid);
6 补充
- 如果需要中断运行,可以在命令行中Ctrl + C,但记得随后在命令行中输入fclose(s)以关闭串口,否则下次启动会报错。无论何种方式导致程序没有执行到fclose(s),都应在下次运行前手动关闭串口。
- 若开始运行时报错,先检查串口是否匹配,若匹配,再检查串口当前是否被其他程序占用,若没有,尝试重启MATLAB。(再不行,重启电脑,再不行我也不知道了2333
- 放几张运行结果图(更新了一张动图)
版权声明:本文为CSDN博主「Volumtuous」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_48510320/article/details/118945642
暂无评论