matlab 串口通信、显示及简易GUI页面设计自学笔记
写在前面
最近做了一个康复辅助设备的小项目,其中一部分设计串口通信及其对应知识,也是整个项目挺重要的一部分,故单独摘出来写成笔记以加强记忆。如有错误之处望诸君指正。
串口通讯初始化
串口通讯的第一步便是初始化,即配置串口参数与打开串口。
在matlab中,我们可以通过调用几个简单的指令来实现这一步骤。
首先是配置串口参数:
一般来讲,串口参数包括:串口名、波特率、奇偶校验位(起始位)、数据位、停止位这几项。
串口名是指:串口在系统中的名称,一般以COM加数字命名。如:COM2、COM11。
波特率是指:上位机与下位机通讯时的传输速度。常见的波特率有9600、14400、115200等,其单位为波特/秒,即b/s。
需要注意区分的一点是波特率不是比特率,比特率的单位是比特/s,即bit per second-bps/s;波特率中的波特与比特存在这样的关系,如果发送的内容有1位起始位、1位终止位、8位数据位,那么1波特便等于10比特。
如果下位机的采样频率过快(数据小于等于8位),比如达到了10kHZ,那么就要求在1秒以内上传10000个数,需要10000个波特,在这种情况下9600的波特率就不满足要求了,我们便需要更大的波特率。
另外顺便区分一下比特bit与字节Byte,在一般情况下1Byte=2^8bit,即一个字节由256个比特组成。在串口通讯中,如果设置数据位为8,那么我们一个波特所能携带的数最大便为11111111,因此对超过8位的数据我们需要将其拆分成高低位发送。
奇偶校验位(起始位)、数据位、停止位是指:每一个波特中所含的这些有不同含义的数的位数。一般奇偶校验位为0,数据位为8,停止位为1。
以上配置串口参数的过程可以用简单的matlab语句实现:
s=serial("COM8");%设置串口的句柄
set(s,'BaudRate',14400,'DataBits',8,'StopBits',1,'Parity','none');
%配置串口参数 波特率14400 无起始位 1终止位 8数据位
其次是打开串口:
在matlab中可用简单语句实现:
fopen(s);%配置并打开串口
这样,便完成了串口通讯的初始化,接下来便是发送数据和接收数据了。
串口发送数据
在matlab中,发送数据的函数有fprintf、fwrite、fscanf等,由于这次我使用的是fwrite,故下文只介绍fwrite。
先上代码:
fwrite(s, D, 'uint8'); %对定义的串口s发送该数据
fwrite一般以上述形式使用,第一位是串口名的句柄,第二位是所要发送的数据,第三位位发送数据的数据类型。
因为做的是上下位机之间的指令型通讯,所以一般采用的数据类型是uint8。而设置指令D的方法如下:
Str ='20';%字符串定义需要发送的十六进制内容
D = sscanf(Str,'%2x'); %将字符串转换成十六进制数据 其中x是16进制的意思
可将字符串中的数字转化为16进制数字(数字本身不改变)并由fwrite发送。
串口读取数据
串口读取数据的方式有很多种,本质上都是通过fread函数接受数据并进一步处理。但由于下位机发送的方式不同,接收的方式也有所不同。
有些下位机是以一个数据一个数据的形式发送的,这种发送方法在低速通信的时候没有什么问题;但如果发送的速度较快,上位机的代码运行时间较长,很容易发生丢失数据的现象,这对于需要将第八位与高八位拼接的读取方式来说是致命的。
因此,我推荐下位机将一次需要发送的数据以一个数组一个数组的方式发送。这样,上位机可一个一个数组的读取,在一定程度上可以有效避免上述问题。
这里略加一句,在Labview中,由于串口读取是以字符串的形式进行的,那么就会存在一个问题。平常情况下读取的字符串是一个字母一个字节,但是由于Labview直接将下位机发送的8位16进制数识别成了字符串,那么对于这个字符串来说,就变成了2个字母占1个字节;这就造成了普通的字符串转数字控件无法工作,需要将其转换为1个字母占1个字节的形式才可以工作。不过,好在matlab并不存在这个问题。
对于我这次的项目,由于下位机发送数据的方式是这样的:
所以根据下位机发送数据的方式,我使用了一种兼顾显示和采集的读取方式。下面给出我这次项目串口读取部分使用的代码:
%-------读取串口数据
for i=1:20
data=fread(s,4,'uint8');
data2=fread(s2,4,'uint8');%提取串口数据
if(data(1)==32&&data2(1)==32)
wave(1,:)=data(2)+data(3)*256;
wave(2,:)=data2(2)+data2(3)*256;
waveform=[waveform,wave];
end
end
%------------------
这个for循环是在一个大的for循环或者while循环下的。首先,使用fread语句读取串口数据(注意的是每次读取了4个字节,与下位机发送的数组字节数相符);之后用if循环寻找起始标志,再根据顺序判断出高低位并将其合成一个数;最后将每次读取到的数动态依次合成到同一个数组中。
另外,由于考虑到波形显示问题,我并没有每次都把波形数组输出。这是因为虽然这么做能让图像显示看起来更流畅,但是由于plot函数耗时较长,会造成上位机运行跟不上下位机发送,造成图像显示迟滞现象。因此,我选择了让它循环20次即获取20个点后再刷新图像。
串口波形的图像显示
本质上是使用plot函数将同一数组中的数据以两通道的形式显示出来。
不过需要注意的一点是,plot函数只会机械的将你需要显示的数组显示出来,并不具有类似Labview波形图标控件那样自动调整横坐标,使波形只有最近的一段显示,给人波形在滚动的感觉,也就是我们在示波器中看到的那样(如果不加干涉,波形只会越来越长,越来越小)。
因此我们需要调节plot函数显示的波形数组范围。下面给出使用的代码:
%-------显示图像
number=number+1;
waveform_size=size(waveform,2);
if(waveform_size<200)
plot(waveform(1,:));
hold on
plot(waveform(2,:));
hold off
drawnow %立刻画出
else
plot(waveform(1,waveform_size-199:waveform_size));
hold on
plot(waveform(2,waveform_size-199:waveform_size));%使滚动显示
hold off
drawnow
end
%--------------
hold on、hold off、drawnow这几个函数的使用是为了画出双通道的波形并动态显示。而整个if else结构是为了动态选取数组显示的范围,给使用者滚动显示的观感。
GUI部分
GUI如何构建和使用网上已有详尽的教程,这里不多赘述,只说几个点。
首先要注意set函数的使用,这个函数可以让页面上的控件获取运算中的变量值。如:
set(handles.edit1, 'string', A);
这段代码的意思就是将变量A的值放置到edit1这个控件的string里,handles是控件储存空间的句柄。
然后就是全局变量global的使用,因为如果要是想设计flag作为布尔管理循环是否break的话,作为判别布尔的变量flag需要被声明为全局变量。
最后就是图像显示的话直接在GUI中添加坐标轴控件就可以了。
总结
根据上文所写的方法,我们便可以在matlab上构建一套带GUI的串口通讯上位机了。
整体工程的源码正在审核,审核通过后我会放出链接。