MATLAB数据通信与测控应用编程实践
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.3 MATLAB串口通信

1.3.1 MATLAB串口简介

在MATLAB中,用户可以通过计算机的串行接口和外界设备,如调制解调器、打印机、示波器等进行通信,甚至还可以把计算机作为中介在两台外部设备之间进行通信。通过建立一个串行接口对象,可以使用MATLAB命令直接和外部设备进行通信。这可以把外部设备的数据反馈到MATLAB中,再利用MATLAB强大的功能对它们进行处理。这对于科技工作者来说,无疑多了一位得力的助手。

本节主要有以下几部分内容:串口的功能,支持的接口和操作平台;串口接口标准,针分配、串数据格式;创建串口对象;在设备和串口对象间进行连接;配置通信参数;对设备进行读写操作;事件和回调;保存和装载串口对象;断开与设备的连接和清空工作空间。

1.MATLAB串口接口

通过MATLAB串行接口,用户可以直接对已连接到计算机串口上的外部设备进行访问,如调制解调器、打印机和科学仪器等。该接口是通过一个串口对象建立的,串口对象提供了一些函数和性质,通过它们用户可以做到:

  • • 形成串口通信;
  • • 使用串口控制针;
  • • 读写数据;
  • • 使用事件和回调;
  • • 保存信息到硬盘。

若用户希望和PC兼容数据获得硬件通信,如多功能I/O键盘等,则需使用数据获得工具箱(Data Acquisition Toolbox);若要和与GPIB或VISA兼容的仪器通信,则需使用仪器控制工具箱(Instrument Control Toolbox),注意该工具箱也包括附加的串口工具函数,这些函数对于对象的创建和形成、仪器的通信等有很大的帮助。

2.支持的串口接口标准

在过去的几年内,相继制定了一系列串口接口标准,这些标准包括RS-232、RS-422和RS-485。MATLAB支持所有的接口标准,不过在计算机与外设的连接中最常用的是RS-232标准。

3.所支持的操作平台

MATLAB串口接口可被Microsoft Windows、Linux和Sun Solaris操作平台支持。

4.根据自己的设备使用例子

本节中的许多例子反映的是连接到计算机串口上特定的外部设备,如一台连接到COM1端口的TektonixTDS210双通道示波器,因而,许多字符串命令都是对应于一个特定设备的。若用户的设备连接到不同的端口,或接收到不同的命令,都应对例子进行相应的修改。

1.3.2 开始使用串口I/O流

1.一个简单的例子

在对串口通信有了一定初步了解后,便可以运用它了,下面来看一个简单的例子。

若用户有一台设备连接到COM1通信端口,且每秒传递的位数为4800,便可以执行下面这个完整的例子。

s=serial('COM1');
set(s,'BaudRate',4800);
fopen(s);
fprintf(s,'*IDN?')
out=fscanf(s);
fclose(s)
delete(s)
clear s

其中,*IDN?命令表示向设备询问认证信息,使用fscanf将该命令执行结果返回到out变量中。若用户的设备不支持该命令或者连接的不是COM1端口,则应对上面的例子进行相应的修改。

2.串口对象使用步骤

当用户要和一个已经连接到串口上的设备进行通信时,所采取的步骤大致如下。

1)建立一个串口对象

使用serial串口对象创建函数,用户可以为一个特定的串口创建一个串口对象,在串口对象创建时,还可以设置通信属性,特别是与串口通信相关的一些属性,如波特率、数据位等,用户可以在串口对象创建时就进行设置。

2)连接到设备

使用函数fopen可以将串口对象连接到设备,当串口对象连接好后,用户就可以将设备的一些设置参数转换对象的特性值,并进行数据的读写操作。

3)构建通信属性值

要建立预期的串口对象,用户应使用函数set来为串口对象属性赋值。事实上,用户可以在任何时间(对象创建期间或对象创建后)对串口对象的属性进行赋值。相反地,用户可以忽略这步而采用默认的属性值,这取决于用户的设备的参数设置和是否需要串口应用程序。

4)读写数据

在做完这几步后,便可以进行数据的读写操作了。使用函数fprintf或fwrite可以将数据写入设备,使用函数fgetl、fgets、fread、fscanf或readasync可以从设备中读入数据。串口对象的行为特点取决于前面对它属性所赋的值或默认值。

5)断开连接清空工作空间

当不再使用串口对象时,就需要将其与设备断开,这时可使用函数fclose,它的作用是使用clear命令将MATLAB工作空间中的变量清除掉。

3.构建和返回属性值

在构建了串口对象的通信属性值后,用户才能建立所期望的串口对象。若要显示和设置属性值,可以使用函数set、函数get或者圆点标记。

1)显示属性名称和值

一旦串口对象被创建,用户便可以使用函数set来将所有的可被设置的属性值和名称显示到命令行中,另外,若串口对象的属性值为一组有限的字符串,那么函数set将会显示这些值。

s=serial('COM1');
set(s)
ByteOrder: [{littleEndian} | bigEndian ]
BytesAvailableFcn
BytesAvailableFcnCount
BytesAvailableFcnMode: [ {terminator} | byte ]
ErrorFcn
InputBufferSize
Name
OutputBufferSize
OutputEmptyFcn
RecordDetail: [ {compact} | verbose ]
RecordMode: [ {overwrite} | append | index ]
RecordName
Tag
Timeout
TimerFcn
TimeerPeriod
UserData
SERIAL specific properties:
BaudRate
BreakInterruptFcn
DataBits
DataTerminalReady: [ {on} | off ]
FlowControl: [ {none} | hardware | software ] Parity: [ {none} | odd | even |mArk | space ] PinStatusFcn
Port
ReadAsyncMode: [ { continuous } |mAnual ]
RequestToSend: [ {on} | off ]
StopBits
Terminator

用户还可以使用函数get来显示一个或多个属性名以及它们的当前值,下面的代码用来显示所有的属性名称和它们的当前值。

get(s)
ByteOrder=1ittleEndian
BytesAvailable=0
BytesAvailableFcn=
BytesAvailableFcnCount=48
BytesAvailableFcnMode=terminator
BytesToOutput=0
ErrorFcn=
InputBufferSize=512
Name=Serial-COMl
OutputBufferSize=512
OutputEmptyFcn=
RecordDetail=compact
RecordMode=overwrite
RecordName=record.txt
RecordStatus=off
Status=closed
Tag=
Timeout=10
TimerFcn=
TimerPeriod=1
TransferStatus=idle
Type=serial
UserData=[ ]
ValuesReceived=0
ValuesSent=0
SERIAL specific properties: BaudRate=9600
BreakInterruptFcn=
DataBits=8
DataTerminalReady=on
FlowControl=none
Parity=none
PinStatus=[lxl struct]
PinStatusFcn=
Port=COMl
ReadAsyncMode=continuous
RequestToSend=on
StopBits=1
Terminator=LF

若要显示一个属性的值,可以将该属性的名称作为参数传递给函数get,如下所示。

get(s,'OutputBufferSize')
ans=
   512

若要显示多个属性的值,则应将一个包含这些属性名称的单元阵列作为一个参数传递给函数get,如下所示。

get(s,{'Parity','TransferStatus'})
ans=
   'none' 'idle'

也可以用圆点标记来显示单个属性的值,如下所示。

s.Parity
ans=
   none
2)配置属性值

用户可以通过使用函数set来配置属性值。

set(s,'BaudRate',4800);

或使用圆点标记,即

s.BaudRate=4800;

若要配置多个属性,可以采用多个属性名称-值配对的方式来配置。

get(s,'DataBits',7,'Name','Test1-serial')

但使用圆点标记来配置属性值时,一次只能配置一个属性值。

实际上,在串口对象存在期间,用户可以在任何时候配置多个属性值,但一些属性值在串口对象连接到设备后或将信息保存到硬盘时,它们的属性值是不可配置的。

3)配置属性的名称

对于串口属性名的表达,可以采用多种表达方式来表达同一个属性名,这样使得属性名更易读,当设定了属性名后可以采用任一种方式来表达,这样就可以简写多数的属性名。当然,必须使用具有足够长度的名称字符串来识别各个属性。

例如,可以采用下面几种方法来构建BaudRate的属性值。

set(s,'BaudRate',4800)
set(s,'baudRate',4800)
set(s,'BAUD',4800)

当在一个M-file中使用到串口对象的属性时,必须使用该对象的属性名全称,这样做可以避免将来由于新属性的增加而导致和原来属性名相同。

4)默认属性值

只要没有对一个属性明确定义它的值,那么该属性就采用默认的属性值,所有能被配置的属性值都有默认值。

虽然操作系统为所有的串口都提供了诸如每秒传输位数等属性值,但MATLAB代码可以使这些设置无效,它们也不会对串口应用程序产生影响。

若某个属性的值为一组有限的字符串,那么默认值就会用{}括起来。如奇偶位的默认属性值是没有的,下面语句可以显示它的值。

set(s,'Parity')
[{none}| odd | even |mArk | space]

1.3.3 串口对象的建立

1.建立一个串口对象

可以使用函数serial来建立一个串口对象。函数serial需要用连接到设备的串口名来作为它的输入参数,另外还可以在对象的创建过程中配置对象的属性值。例如,要创建一个与串口COM1连接的串口对象,可以使用下面的语句。

s=serial('COM1')

现在,串口对象就存在于MATLAB的工作空间中了,用whos命令可查看该类信息。

whos s
Name Size Bytes Class
s 1x1 512 serial object
Grand total is 11 elements using 512 bytes

一旦串口对象被建立,表1-8列出的这些属性将会被自动赋值。这些通用的属性值为串口对象提供了描述性的信息,这些信息则是根据对象类型和串口来确定的。

表1-8 串口对象的通用属性

使用函数get可以显示s的这些属性值,如下所示。

get (s,{'Name','Port','Type'})
ans=
   'Serial-COM1' 'COM1' 'serial'
1)在对象创建时配置属性值

可以在串口对象创建时配置它的属性值,函数serial可以使用和函数set相同的格式来接收属性名称和值,例如,可以像下面这样设定一对属性名称和参数值。

s=serial ('COM1','BaudRate',4800,'Parity','even');

若设定的属性名是无效的,那么该串口对象就不会被建立。但有时若设定的值是无效的,对象仍然会被建立,但若不使用函数fopen将该对象连接到设备上,用户不会被告知该特征值无效。

2)串口对象的显示

串口对象为用户提供了一个方便的显示方法,通过它可以得出一些重要的配置和状态信息,可以采用下面三种方法来调用显示功能。

  • • 在命令行输入串口对象变量名;
  • • 在创建一个串口对象时不使用分号;
  • • 在使用圆点标志配置属性值时不使用分号。
3)创建一个串口对象阵列

在MATLAB中,可以通过把现有变量连接在一起,来创建一个阵列。该方法对于串口对象仍然适用,例如,假定创建了串口对象s1和s2,如下所示。

s1=serial('COM1');
s2=serial('COM2');

现在使用一般的MATLAB语法来创建一个包含对象s1和s2的串口对象阵列。例如,要创建行阵列x,命令如下。

x=[s1s2]
Instrument Object Array
Index: Type: Status: Name:
1serial closed Serial-COM1
2 serial closed Serial-COM2

若要创建列阵列,命令为:

y=[s1:s2];

不能创建一个串口对象矩阵,如下面语句所示。

z=[s1s2;s1s2]
??? Error using==> serial/vertcat
Only a row or column vector of instrument objects can be created.

用户可以根据自己的应用程序来把一个串口对象阵列作为参数传递给一个函数。如要通过一次调用函数set来配置s1和s2的baudrate和parity值,可以使用以下语句。

set (x,'BaudRate',19200,'Parity','even')

2.连接到设备

在使用串口对象传递数据之前,必须通过在函数serial中指定串口将它连接到设备上,这时就用到函数fopen,使用格式为

fopen(s)

当串口对象被连接上时,它的一些属性值是只读的,因而在使用函数fopen之前必须先配置它们的值。

用户可以创建任意各串口对象,对于某一个串口,每次只能连接一个串口对象。

可以通过检测对象的Status属性值,查看该串口对象是否己连接到设备上。

s.Status
ans=
open

当串口对象与设备正确连接后便可以使用它来获得数据了。

3.配置通信参数

在读写数据之前,必须确定串口对象和设备具有一致的通信参数,设定串口通信参数主要是为那些控制波特率(BaudRate)和数据格式的属性赋值。这些属性名如表1-9所示。

表1-9 串口对象属性

1)BaudRate

BaudRate指信息在通信频道中每秒传输的波特数,又称为波特率。传输的位数包括:起始位、数据位、奇偶校验位(若使用)和终止位,但只有数据位被存储起来。在串口中“9600 baud”意味着串口每秒能够最大传输9600位,若信息单位为lbit,那么比特率和波特率就是一致的;若l baud包含10bit(如8个数据位和2个结构位),那么比特率就是9600,但波特率却是9600/10,即960,但用户一般将波特率当成每秒传输的比特数。

波特率的默认值为9600。

要成功读写数据,必须将计算机和外设的波特率配置为相同数值。

2)DataBits

DataBits用于设定传输的数据位数,可以将它设为5、6、7或8数值,也就是说数据一次传递5、6、7或8位。在传输ASCII码时,需要至少7位数据位;传递二进制数据时,需要至少8位数据位;5位和6位数据位用于特殊通信设备的数据传输。

DataBits的默认值为8。

计算机和外设的数据位数必须配置为相同值。

3)Parity

Parity用于设定奇偶校验类型,可以将它设定为none、odd、even、mark或space。若设定为none,表明不必进行奇偶校验,不传输奇偶位。

若串口对象和设备的通信参数不一致,便不能成功地读写数据。建议参阅设备的用户文档,参考它对通信参数的设置,这样有助于正确编写程序。

1.3.4 数据的读写

对于多数串口应用程序而言,在进行数据的读写时,需考虑以下3个重要的问题。

  • • 读或写函数模块能否访问MATLAB命令行?
  • • 将被传输的数据是二进制格式(数值)或文本格式?
  • • 在怎样的条件下读写操作才算完成?

下面将对这些问题做出回答,先看一个简单的例子。

1.一个简单例子

假定有一台TektronixTDS双通道示波器,已连接到串口COM1,用户想得到它的验证信息需要使用函数fprintf将*IDN?命令写入到仪器中,然后使用函数fscanf将该命令的结果读回。程序代码如下。

s=serial ('COM1');
fopen (s)
fprintf (s,'*IDN?')
out=fscanf (s)

信息验证的结果如下。

out=
TEKTRONIX,TDS 210,0,CF: 91.1CT FV: v1.16 TDS2CM: CMV: v1.04

要结束串口通信期,使用如下代码。

fclose (s)
delete (s)
clear s

2.控制对MATLAB命令行的访问

通过把读写操作设定为同步通信或异步通信,用户便可以对MATLAB命令行进行访问。一个同步通信的操作模块可以访问MATLAB命令行,直到读写函数执行完毕;而一个异步通信的操作是不会访问MATLAB命令行的,此时用户可以使用一个附加的命令而使读写操作转入后台执行。

术语“同步通信”和“异步通信”通常用来描述在硬件水平上的串口操作。RS-232标准支持异步通信协定,使用该协定,每台设备都使用自己的时钟。使用字节的开始位和一个或多个终止位,这样便保持数据传输同步。RS-232标准也支持同步通信模式,其中所有的位传输都是在一个时钟信号下进行。

在硬件水平上,大多数的串口通信操作都是异步通信,但对于大多数的读写函数来说,在默认条件下采用的是同步通信方式,此时用户也可以模拟一个同步通信串口的操作。

采用异步通信的方式来读写数据,主要有以下两个好处:在读写函数执行时可以执行其他命令;可以使用回调特性。因为串口有分开的读写针,故可以同时读写数据。

3.向串口设备写数据

用于向设备写入数据的函数如表1-10所示。

表1-10 数据写入函数

与写入数据相关的属性如表1-11所示。

表1-11 与写入数据相关的属性

下面分三部分来介绍怎样向串口设备写入数据。

1)输出缓冲和数据流

输出缓冲是被串口对象分配的一部分内存,用于存储将被写入到设备的数据。当将数据写入设备时,数据流按以下步骤进行。

① 将写函数指定的数据送到输出缓冲区。

② 将输出缓冲区的数据送到设备。

参数OutputBufferSize用于显示输出缓冲中能够存储数据的最大字节数,参数BytesToOutput用于显示当前输出缓冲的字节数,它们的默认值如下。

s=serial('COM1');
get(s,{'OutputBufferSize','BytesToOutput'})
ans=
 [512] [0]

若把比缓冲区容量更大的数据写入设备,那么将会返回错误,且不会写入任何数据。假如使用函数fprintf将字符串命令*IDN?写入到TDS210示波器中,字符串首先被写入输出缓冲中。当字符串被写入输出缓冲后,它再通过串口被写入到设备中。

2)写入文本数据

向设备写入文本数据可以使用函数fprintf。对于多数设备来讲,写入文本数据也就是写入字符串命令,该函数可以用来改变设备的属性值或获取设备的状态信息。

如命令Display: Contrast可以改变示波器的显示比。

s=serial('COM1');
fopen(s)
fprintf (s,'Display:Contrast 45')

在默认情况下,fprintf使用%s

格式来写数据,因为多数串口设备只接收字符串命令。可以使用属性ValuesSent来验证传输到设备的字节数。

s.ValuesSent
ans=
20

在默认情况下,函数fprintf采用同步通信方式,因而在函数执行期间将会占用整个MATLAB命令行,直到函数执行结束。若要使用异步通信的方式来写入文本数据,应把函数fprintf的最后一个输入参数设定为“async”标志,如下面的语句所示。

fprintf(s,'Display:Contrast 45','async')

异步通信操作不会占用MATLAB命令行,也就是说,在执行写入数据操作时,MATLAB还可以执行其他命令。此外,使用异步通信还有以下两个优点:因为串口上读和写的针是分开的,故可执行异步通信的读操作;充分利用回调属性。

3)写入二进制数据

向设备写入二进制数据可以使用函数fwrite,写入二进制数据也就意味着写入数值数据。写入二进制数据的典型应用程序包括将如同波形发生器之类的标定数据写入仪器。

与fprintf一样,在默认情况下,该操作采用同步通信,若要采用异步通信,也应把函数最后一个输入参数设定为“async”标志。

一些串口设备只接收文本格式的命令,这些命令使用SCPI语言或其他仪器厂家指定的语言。因而,可能需要用函数fwrite来完成所有的写入操作。

4.从设备读入数据

用于从设备读入数据的函数有以下几个,如表1-12所示。

表1-12 读入数据函数

与读入数据相关的属性如表1-13所示。

表1-13 读入数据的相关属性

下面分三部分来介绍怎样从设备读入数据。

1)输入缓冲和数据流

输入缓冲是计算机中为串口对象分配的一片内存区,用于存储将从设备读入的数据,当进行数据读入时,数据的流动有如下两步。

① 将从设备读入的数据存储在输入缓冲区。

② 将输入缓冲区的数据返回到MATLAB中被读函数指定的变量中。

属性InputBufferSize指定了在输入缓冲中能够存储的最大字节数,而属性BytesAvailable指定了输入缓冲中能被用来读入数据的可用字节数,它们的默认值可用函数get查看。

s=serial('COM1');
get(s,{'IntputBufferSize','BytesAvarilable'})
ans=
[512] [0]

若要读入比输入缓冲容量还大的数据,将会返回错误,且不会读入任何数据。

2)读入文本数据

可以使用函数fgetl、fgets和fscanf来从设备中读入数据,并将数据转换为文本格式。比如,假定用户想返回示波器的验证信息,这需要将*IDN?命令写入仪器,然后将该命令的结果读回来,代码和返回结果如下。

s=serial('COM1');
fopen(s)
fprintf (s,'*IND?')
out=fscanf (s)
out=
TEKTRONIX,TDS 210,0,CF: 91.1CT FV: v1.16 TDS2CM: CMV: v1.04

默认时,函数fscanf使用%c格式读入数据,这是因为许多串口设备返回的数据是基于文本格式的。

在读入操作时,不管该操作是同步通信方式还是异步通信方式,用户都可以设定读入函数的ReadAsyncMode属性值,该属性值有两个:continuous和manual。

若ReadAsyncMode取值为continuous(默认值),那么串口对象将会连续不断地询问设备是否数据可读,若数据可读,就会采取异步通信的方式将数据存储在输入缓冲中;而将输入缓冲中的数据传输到MATLAB中,必须使用一个同步通信的读函数,如fgetl和fscanf,这样才能保证MATLAB在读入数据时不去执行其他命令。若缓冲区数据可用,则该函数迅速返回数据。

S.readasyncmode='continuous';
Fprintf (s,'*IDN?')
S.bytesavalable
Ans=
   56
Out=fscanf (s);

若ReadAsyncMode值取manual,那么串口对象将不会连续询问设备是否数据可读,要采用异步通信方式来读入数据,需使用函数readasync,然后在使用一个同步通信函数将缓冲区的数据读入到MATLAB中,如下所示。

s.ReadAsyncMode='manual';
fprintf (s,'*IDN?')
s.BytesAvailable
ans=
   0
rdadasync(s)
s.BytesAvailable
ans=
   56
out=fscanf(s):

同样在读入数据中采用异步通信方式也有很多优点,如同写入数据中采用的异步通信方式一样,这里就不再赘述。

3)读入二进制数据

从设备中读入数据可以使用函数fread,读二进制数据也就是将数值读入到MATLAB。如想返回示波器的游标和显示它的参数设置,可以向示波器传输“CURSOR?and DISPLAY?”命令,然后将命令结果读回MATLAB中,代码如下所示。

s=serial ('COM1');
fopen (s)
fprintf (s,'CURSOR?')
fprintf (,'DISPLAY?')

因为参数ReadAsyncMode的默认值为continuous,故只要可以从设备中获得数据,那么数据就会以异步通信的方式返回到输入缓冲中,还可以使用属性BytesAvailable来验证缓冲区中可用的字节数。

s.BytesAvailable
ans=
69

虽然可以使用任何一个同步通信的读函数将缓冲区的数据读入到MATLAB,但如果使用函数fgetl、fgets或fscanf,则必须调用它们两次,因为输入缓冲中存储着两个终端。若使用函数fread,则一次调用就能将所有数据返回到MATLAB中。

out=freas(s,69)

在默认条件下,函数fread返回的是双精度的数值阵列,当然也可以设定为其他精度,甚至可以使用MATLAB字符转换函数将数值数据转化为文本数据,如下面的代码所示。

val=char (out)
val=
   HBARS; CH1; SECONS; -1.0E-3; 1.0E-3; VOLTS; -6.56E-1;1.34E-1
YT; DOTS; 0; 45

5.数据读写例程

1)文本数据读写

下面示范怎样对串口仪器进行文本数据的读写操作。仪器为连接到COM1端口上的一台TektronixTDS 210双通道示波器,下面的一些命令仅针对该设备而言。一个正弦波输入到示波器的第二通道中,现在的任务是测量输入信号波峰间的电压。该过程的完整步骤如下。

① 为串口COM1创建一个对应的串口对象s。

s=serial('COM1');

② 连接到设备——示波器。因为属性ReadAsyncMode的默认值为continuous,故只要仪器的数据可以获得,将会采用异步通信的方式将数据返回到输入缓冲区中。

fopen (s)

③ 读写数据,使用函数fprintf将*IDN?命令传输到仪器中,然后使用函数fscanf将该命令的结果读回。

fprintf (s,'*IDN?')
idn=fscanf(s)
idn=
TEKTRONIX,TDS 210,0,CF: 91.1CT FV: v1.16 TDS2CM: CMV: v1.04

此外,还需确定测量源,可能的测量源包括示波器的通道1和通道2,通过下述代码可确定测量源。

fprintf(s,'MEASUREMENT: IMMED: SOURCE?')
source=fscanf(s)
source=
    CH1

表明仪器是从通道1来返回量测值,因为输入信号连接到通道2,还必须确保仪器从该通道来返回量测值。现重新配置量测值返回通道,代码如下所示。

fprintf (s,'MEASUREMENT: IMMED: SOURCE CH2')
fprintf (s,'MEASUREMENT: IMMED: SOURCE?')
source=fscanf (s)
source=
    CH2

现在确保示波器可以返回两波峰间的电压了,然后就可以访问量测值。

fprintf(s,'MEASUREMENT: MEAS1: TYPE PK2PK')
fprintf(s,'MEASUREMENT: MEAS1: VALUE?',)

最后使用fscanf将输入缓冲的数据返回到MATLAB。

ptop=fscanf(s,'%g')
ptop=
  2.0199999809E0

④ 断开连接并清空工作空间。当不再使用串口对象s时,应当将它和仪器断开,并将它从内存中和MATLAB工作空间中删除,代码如下。

fclose(s)
delete(s)
clear s
2)使用函数strread解析输入数据

下面介绍怎样使用函数strread来解析和格式化从设备中读来的数据。当把一个字符串解析为一个或多个变量,且每个变量都有自己特定的格式时,函数strread就显得特别有用。

下面例程中的仪器与上面例程相同。

① 建立串口对象。

s=serial('COM1');

② 连接到设备。

fopen(s)

③ 读写数据。使用函数fprintf将RS232?命令传输到仪器中,然后使用函数fscanf将该命令的结果读回。命令RS232?将查询RS-232的各种参数,然后返回波特率、软件流控制参数、硬件流控制参数、奇偶校验类型以及终端等数据,代码和返回结果如下。

Fprintf(s,'RS232?')
Data=fscanf(s)
Data=
  6900; 0; 0; NONE; LF

使用函数strread来把数据变量解析和格式化为五个新变量。

[br,sfc,hfc,par,tm]=strread (data,'%d%d%d%s%s','delimiter',';')
br=
9600
sfc=
0
hfc=
0
par=
'NONE'
tm=
'LF'

④ 断开连接并清空工作空间。

fclose (s)
delete (s)
clear s
3)二进制数据的读入

下面介绍怎样将示波器屏幕上的显示传送到MATLAB中,屏幕显示数据采用Windows位图格式传输并保存到硬盘中。由于需要传输的数据量可能非常大,故采用异步通信的方式将仪器中的数据返回到输入缓冲中,这样在数据传输过程中可以允许用户进行其他的操作。另外,仪器应使用它的最高数据传输率为19200bps。

① 建立串口对象。

s=serial('COM1');

② 配置属性值。合理设定输入缓冲区大小,将波特率设定为仪器支持的最高值。

s.InputBufferSize=50000;
s.BaudRate=19200

③ 连接到设备。

fopen (s)

④ 读写数据。将示波器设定为用于传输位图格式的屏幕显示数据。

fprintf (s,'HARDCOPY: PORT RS232')
printf (s,'HARDCOPY: FORMAT BMP')
printf (s,'HARDCOPY START')

当数据全部传输到输入缓冲后,再把它以unsigned8位整型格式返回到MATLAB工作空间中。

out=freas (s,s.BytsAvailable,'uint8');

⑤ 断开连接并清空工作空间。

fclose(s)
delete (s)
clear s
4)查看位图数据

要查看位图数据,应做以下几步。

① 打开磁盘文件。

② 将数据写入磁盘文件。

③ 关闭磁盘文件。

④ 使用函数imread将数据输入MATLAB。

⑤ 使用函数imagesc来标定和显示数据。

注意:还要使用用于处理文件I/O流的函数fopen、fwrite和fclose。

fid=fopen('testl.bmp','w');
fwrite (fid,Out,'uint8');
fclose (fid)
a=imread ('testl.bmp','bmp');
imagesc (a)

因为示波器返回的屏幕显示数据为两色的,故还需选择恰当的位图颜色。

mymap=[0 0 0; 1 1 1];
colormap (mymap)

1.3.5 事件和函数回调

用户通过事件可以使串口应用程序具有更大的伸缩性和更强的功能,在遇到一个可以产生事件的条件时,就会产生一个事件,同时产生一个或多个回调。当串口对象连接到设备后,便可以使用事件来显示消息、显示数据、分析数据等;回调则是通过回调属性和回调函数来控制的,所有的事件类型都有相应的回调属性值,回调函数则是用户根据特定应用程序需要建立的M文件。

通过把相应的回调属性值设定为M文件回调函数名,当一个特定的事件发生时,用户就执行一个回调。

1.一个简单的例子

该例中使用回调函数instrcallback把一条消息显示到命令行中,当终端读取数据时,便产生与该函数对应的事件。

s=serial('COM1')
fopen (s)
s.BytesAvailableFcnMode='terminator';
s:BytesAvailableFcn=@instrcallback;
fprintf (s.'*IDN?')
out=fscanf (s)

回调instrcallback显示的结果如下。

BytesAvailable event occurred at 17: 01: 29 for the object:
Serial-COM1

结束串口通信期。

fclose(s)
delete (s)
clear s

也可以在命令行使用type命令来显示instrcallback回调。

2.事件类型和回调属性

串口事件类型和回调属性如表1-14所示。

表1-14 串口事件类型和回调属性

1)Break interrupt事件

当串口发出一个中断信号后,随即就产生一个中断事件。当接收到的数据处于停止状态的时间长于一个字符的传输时间时,串口就会发出一个中断。该中断事件就会执行一个由BreakInterruptFcn属性指定的回调函数,该回调函数可由同步通信或异步通信的读写操作产生。

2)Bytes available事件

当输入缓冲区中预先确定的字节数可用,或者一个终端在进行读操作时,正如BytesAvailableFcnMode属性确定得那样,就会发生一个Bytes available事件;若属性BytesAvailableFcnMode取值为字节(byte),那么只要属性BytesAvailableFcnCount指定的字节数存储在输入缓冲中,Bytes available事件就会执行BytesAvailableFcn属性指定的回调函数;若BytesAvailableFcnMode为终端设备,那么只要由终端属性指定的字符被读入,回调函数就会执行。该事件只能在一个异步通信的读事件中发生。

3)Error事件

当一个错误出现时,一个错误事件就会发生。该事件执行由ErrorFcn属性指定的回调函数,它只能在一个异步通信的读写操作中发生。当出现一个处理间歇时,就会引发一个错误事件,而处理间歇是由于在Timeout属性指定的时间之内一个读或写操作没有成功完成,像设定一个无效的属性值之类的配置错误是不会产生错误事件的。

4)Output empty事件

当输出缓冲为空时就会产生一个空输出事件,该事件执行由OutputEmptyFcn属性指定的回调函数。该事件只能在一个异步通信的写操作中出现。

5)Pin status事件

当端口中CD、CTS、DSR或RI针状态发生改变时就会产生一个Pin status事件,该事件会执行由PinStatusFcn属性指定的回调函数。该事件无论在同步通信或异步通信的读写操作中都会发生。

6)Timer事件

当超过由TimerPeriod属性指定的时间后,就会发生一个Timer事件。时间是相对于串口对象连接到设备的起始时间而测得的。该事件执行由TimerFcn属性指定的回调函数。

若系统的处理速度太慢或TimerPeriod属性值太低,一些Timer事件就不会被处理

3.存储事件信息

用户可以在一个回调函数或一个记录文件中存储事件信息。若在回调函数中存储事件信息,将会使用两个域:类型域和数据域。类型域包含了事件的类型,数据域则包含了特定事件的信息。有关事件的信息如表1-15所示。

表1-15 事件信息

关于数据域的描述如下。

1)AbsTime域

AbsTime被所有事件定义,显示了事件发生的绝对时间,返回的绝对时间,采用时钟格为式:day-month-year hour:minute:second。

2)Pin域

当显示CD、CTS、DSR或RI针是否改变状态时,Pin status事件就会使用Pin域。

3)PinValue域

PinValue域被Pin status事件用来显示CD、CTS、DSR或RI针的状态,取值为on或off。

4)Message域

当一个错误发生时,Message域被错误事件用来存储描述信息。

4.建立和执行一个回调函数

若把M文件的名称包含在相关的回调属性里面,当一个特定的事件类型发生时,用户就可执行指定的回调函数。用户还可以把回调函数设定为一个函数句柄或一个字符串单元阵列元素。

如在每次终端从设备读数据时执行回调函数mycallback。

s .BytesAvailableFcnMode='terminator';
s. BytesAvailableFcn=@mycallback;

也可以将回调函数设定为一个单元阵列。

s.BytesAvailableFcn={'mycallbaek'};

M文件的回调函数需要至少两个输入参数:第一个参数为串口对象,第二个参数为一个捕捉事件信息的变量。该事件信息仅和引起回调函数执行的事件有关。回调函数mycallback的函数头如下。

function mycallback(obj,event)

可以把回调函数和参数作为单元阵列元素,将它们作为附加参数传递给回调函数。

time=datestr (now,0);
s.BytesAvailableFcnMode='terminator';
s.BytesAvailableFcn={@mycallbaek,time};

也可以将回调函数设定为一个单元阵列的回调函数。

s.BytesAvailableFcn={'mycallback',time};

对应的函数头为:

function mycallback(obj,event,time)

若将一个附加的参数传递给回调函数,这些附加参数包含在函数头中两个必需的参数之后。

也可以把回调函数设定为一个字符串,在这种情况下,回调函数在MATLAB工作空间中被计算,而且不需要对回调函数的输入变量做任何要求。

5.使用事件和回调示例

该例使用M文件回调函数把一个Bytes available事件或一个Output empty事件出现时的信息显示到MATLAB命令行中。下面给出该过程的完整步骤。

① 创建一个串口对象。假定设备与串口COM 1相连接,建立串口对象s。

s=serial('COM1');

② 连接到设备。设备为TektronixTDS210示波器,因为ReadAsyncMode属性的默认值为continuous,故只要数据能从仪器中获得,就会以异步通信的方式将数据返回到输入缓冲中。连接到设备的语句如下。

fopen(s)

③ 配置各种属性值。配置串口对象s的各种属性,使它在一个Bytes available,事件或一个Output empty事件出现时能够执行回调函数。因为instrcallback需要将串口对象和事件信息作为输入参数,故将回调函数设定为一个函数句柄。

s.BytesAvailableFcnMode='terminator';
s.BytesAvailableFcn=@instrcallback;
s.OutputEmptyFcn=@instrcallback;

④ 读写数据。将命令RS232?采用异步通信的方式写入到示波器中,该命令将会查询RS-232的各种参数设置,并返回波特率、软件流控制参数、硬件流控制参数、奇偶校验类型和终端参数。

fprintf (s,'RS232?','async')

当命令RS232?被送出后或终端进行读操作时,instrcallback就会被调用,执行结果显示如下。

OutputEmpty event occurred at 17: 37: 21 for the object:
Serial-COM1.
BytesAvailable event occurred at 17: 37: 21 for the object:
Serial-COM1.

然后从输入缓冲区中将数据读入。

Out=fscanf(s)
Out=
9600; 0; 0; NONE; LF

⑤ 断开连接和清空工作空间。当不再使用对象s后,需要将它和设备断开,并从内存和MATLAB工作空间中除去。

fclose(s)
delete(s)
clear s

1.3.6 使用控制针

正如前面章节所描述的,一个9针串口中有6个控制针,这些控制针有以下用途:

  • • 发出连接的设备己就位的信号;
  • • 控制数据流。

与串口控制针相关的属性如表1-16所示。

表1-16 控制针属性

1.发出设备就位信号

DTE设备和DCE设备经常使用CD、DSR、RI和DTR针来显示串口和设备之间的连接是否被建立起来,一旦建立了它们之间的连接,就可以进行数据的读写操作了。用户可以使用PinStatus属性来侦察CD、DSR和RI的状态,也可以使用DataTerminalReady属性来设定或侦察DTR针的状态。

下面这个例子演示了当两台调制解调器连接在一起时,这些针的使用情况。

两台调制解调器通过同一台计算机连接在一起,第一台调制解调器连接在COM1上,第二台连接在COM2上,下面将演示用户怎样来侦察计算机-调制解调器之间的通信状态以及两个调制解调器之间的连接状态。

1)建立串口对象

在两个调制解调器(Modem)的电源打开后,为第一个Modem建立串口对象s1,为第二个Modem建立串口对象s2。

s1=serial ('COM1');
s2=serial ('COM2');
2)连接到设备

将串口对象s1和s2分别连接到两个Modem。由于ReadAsyncMode属性的默认值为continuous,故只要数据能从Modem中获得,就会以异步通信的方式将数据返回到输入缓冲中。连接到设备的语句如下。

fopen(s1)
fopen(s2)

因为DataTerminalReady属性的默认值为on,故计算机(数据终端)现在就可以使用两个Modem来交换数据了。

用户可以利用PinStatus属性来检查Data Set Ready针的值,通过该值就可以验证Modem(数据集)是否能和计算机通信。

s1.Pinstatus
ans=
CarrierDetect: 'off'
DataSetReady: 'on'
RingIndicator: 'off'

域DataSetReady的值为on,因为在两台Modem连接到串口对象之前它们的电源就已经被打开了。

3)配置属性值

将两台调制解调器的波特率都设为2 400 bps,将它们的终端属性都设为CR,如下所示。

s1.BaudRate=2400;
s1.Terminator='CR';
s2.BaudRate=2400;
s2.Terminator='CR';
4)读写数据

将atd命令写入到第一个Modem中,该命令将会使Modem关闭挂起状态,该状态就相当于人们举起话筒时电话所处的状态。

fprintf (s1,'atd')

将ata命令写入到第二个Modem,该命令将Modem置于回答模式,这样就可以使它与第一台Modem相连接。

fprintf (s2,'ata')

当两台Modem间协调好后,用户就可以通过使用PinStatus属性值来检查CarrierDetect针的值,并用它来验证连接状态。

s1.Pinstatus
ans=
CarrierDetect:'on'
ClearToSend:'on'
DataSetReady:'on'
Ringindicator: 'off'

也可以通过读取第二个Modem返回的描述信息来验证两台Modem的连接。

s2.BytesAvailable
ans=
25
out=fread(s2,25);
char(out)
ans=
ata
CONNECT 2400/NONE

通过把DataTerminalReady属性的值设为off来断开两台Modem之间的连接,也可以通过检查CarrierDetect针的值来验证Modem间的连接是否已被断开。

s1.DataTerminalReay='off';
s1.PinStatus
ans=
CarrierDetect:'off'
ClearToSend:'on'
DataSetReady:'on'
RingIndicator:'off'
5)断开连接和清空工作空间

将串口对象和Modem断开,并将它们从内存和MATLAB工作空间中删除。

fclose([s1s2])
delete([s1s2])
clear sl s2

2.数据流的控制

在DCE和DTE之间通信时需要对数据流进行控制,以防止传输期间的数据损失,控制数据流通常使用同步交换方法。举例来说,假如计算机在数据被处理之前只能接收有限量的数据,若到达这一界限,一个同步交换信号将被传送到DCE,使其停止传送数据。当计算机能接收更多的数据时,另外的一个同步交换信号又被传送到DCE,使其重新开始传送数据。

若设备支持的话,用户还可以使用硬件同步交换或软件同步交换来控制数据流。

虽然可以为设备同时配置硬件同步交换和软件同步交换,但MATLAB不支持这一行为。可以使用FlowControl属性来设定数据流的控制方法,如果FlowControl属性值是硬件,则硬件同步交换会被用于控制数据流;如果FlowControl属性值是软件,则软件同步交换将用于控制数据流;如果没有设定FlowControl属性值,则就不会使用同步交换。

1)硬件同步交换

硬件同步交换使用特定的串口针来控制数据流,在大部分情形下,这些针是RTS针和CTS针。如果FlowControl属性值是硬件,那么RTS和CTS针自动被DTE和DCE管理。用户可以使用PinStatus属性来返回CTS针值,也可以用RequestToSend属性来配置或返回RTS针值。

一些装置也使用DTR和DSR针作为同步交换。然而,这些针一般是被用来显示系统已经为通信做好准备,它们并不被用于控制数据传输。在MATLAB中,硬件同步交换总是使用RTS和CTS针。

如果用户的装置没有使用标准方式的硬件同步交换,那么就可能需用手工配置RequestToSend属性。在这情况下,应该不为FlowControl属性配置任何值。如果FlowControl属性值是硬件,那么用户设定的RequestToSend值可能不会起作用。

2)软件同步交换

软件同步交换使用特定的ASCII字符来控制数据流。这些字符是Xon和Xoff(XON和XOFF),如表1-17所示。

表1-17 软件同步交换字符

当使用软件同步交换时,控制字符以与规则数据一样的方式通过传输线传输,因而只需要使用TD、RD和GND针。

使用软件同步交换最主要的缺点是当数值数据被写入设备时不能写Xon和Xoff。这是因为数值数据可能也包含17和19两个数,这就使得无法辨别控制字符和数据。但当数据以异步通信的方式从设备读入时,可以写入Xon和Xoff字符,因为此时都使用了TD针和RD针。

3)软件同步交换例子

以前面读二进制数据例子为例,假定使用软件流控制,要做到这点,首先必须为软件流控制配置示波器和串口对象。

fprintf (s,'RS232: SOFTF ON')
s.FlowControl='software';

要暂停数据传输,需将数值19写入设备。

fwrite(s,19)

要继续数据传输,需将数值17写入设备。

fwrite(s,17)

1.3.7 记录数据到磁盘

当串口对象被连接到设备的时候,可以把下面的信息记录到一个磁盘文件中。

  • • 被写入设备的数据数目、从设备读出的数据数目,以及它们的数据类型;
  • • 写入设备和从设备读出数据的内容;
  • • 事件信息。

将信息保存到磁盘中,该方法为串口通信提供了一种永久记录数据的方法,也是一种易于调试应用程序的方法。

用户可以使用记录函数将信息保存到磁盘,与之相关的些属性如表1-18所示。

表1-18 记录的属性

1.一个简单例子

该例子中记录了写入设备和从设备读入的数值数目,并将这此信息以文本格式存储到文件myfile.txt中。

s=serial('COM1');
fopen(s)
s.RecordName='myfile.txt';
record(s)
fprintf(s,'*IDN?')
idn=fscanf(s);
fprintf(s,'RS232?')
rs232=fscanf(s);

结束串口通信期。

fclose(s)
delete(s)
clear s

使用type命令可以将myfile.txt的内容显示到MATLAB命令行中。

2.创建多个记录文件

当用户使用函数record时,也就完成了对保存数据操作的初始化,RecordMode属性值决定了是否创建一个新的记录文件或是否将新信息添加到一个已经存在的记录文件中。

用户可以将记录模式RecordMode属性值设为overwrite、append或index,分别表示以覆盖、追加和索引的方式来建立或打开文件。若将记录模式设定为overwrite,那么每当记录操作被初始化时,以前的记录文件内容就会被覆盖:若记录模式设定为append,那么新的信息将会被添加到由RecordName指定的文件中;若记录模式设定为index,那么每当记录操作被初始化时,将会建立一个新的磁盘文件。设定一个记录文件名的规则将在下面讨论。

3.设定一个文件名

设定一个记录文件的名称需使用RecordName属性为RecordName设定一个值,该值包含文件的目录路径,该值即为设定的文件名,当然前提是该文件名格式被操作系统支持。另外若RecordMode值为index,那么文件名必须遵循以下规则:

  • • 若记录模式采用index,则文件名由一个数字来识别,该数字在文件扩展名之前;
  • • 若成功记录文件,则该数据增1;
  • • 若初始文件名中不包括数字这一部分,那么第一个记录文件便没有相应的数字标志;
  • • 若记录文件名为myfile.txt,那么myfile.txt就是第一个记录文件的名称,myfile0l.txt就是第二个记录文件的名称,其余以此类推;
  • • 当记录文件被关闭后,记录名称就会被更新,若设定的文件名已经存在,那么它的内容就会被覆盖。

4.记录文件的格式

记录文件是一个包含一个或多个串口通信期的ASCII码文件。使用RecordDetail属性用户可以设定存储到记录文件的信息量,RecordDetail属性值可以取compact(紧凑模式)或verbose(冗长模式)。

采用紧凑模式的记录文件包含要被写入设备的数值数目、从设备读入数值的数目、数值的数据类型和事件信息;采用冗长模式记录的文件不仅包括读入、读出设备的数据信息,还包括前面数据的信息。

二进制数据的精度由类型决定,这些类型有uchar、schar、int8、intl6或(u)int32。二进制数据采用十六进制格式保存。举例来说,如果整数数值255是从仪器读入的一个16位的整数,在记录文件中对应的十六进制数值为00FF。单精度和双精度的浮点数据则使用%g格式以十进制格式存储,或存储为十六进制数值,该数值使用二进制浮点数的IEEE 754—1985标准指定的格式。

浮点格式组成和相关的单精度和双精度位数如表1-19所示。

表1-19 浮点精度和相应的格式

5.示例

该例演示了怎样记录一个串口对象和一台Tektronix TDS210示波器之间传递的信息,另外也给出了记录文件格式。

1)建立串口对象

与前面的例子一样,第一步也是建立一个串口对象,假定连接的串口位COM1。

s=serial('COMl');
2)连接到设备

由于ReadAsyncMode属性的默认值为continuous,故只要数据能从Modem中获得,就会以异步通信的方式将数据返回到输入缓冲中,连接到设备的语句如下。

fopen(s)
3)配置属性值

将串口对象s配置为能使用冗长格式将记录信息保存到多个磁盘文件。记录操作由第一个文件WaveForml.txt初始化。

s.RecordMode='index';
s.RecordDetail='verbose';
s.RecordName='WaveForml.Txt';
record(s)
4)读写数据

首先将命令写入仪器,然后将其结果读出,并存储在磁盘记录文件中。

fprintf(s,'*IDN?')
idn=fscanf(s);
fprintf(s,'MEASUREMENT:IMMED:SOURCE CH2')
fprintf(s,'MEASUREMENT:IMMED:SOURCE?')
source=fscanf(s);

使用函数fread读取两个波峰之间的数据,注意保存函数fread返回的数据采用的是十六进制格式。

fprintf(s,'MEASUREMENT:MEAS1:TYPE PK2PK')
fprintf(s,'MEASUREMENT: MEAS1:VALUE?')
ptop=fread(s,s.BytesAvailable);
Convert the peak-to-peak voltage to a character array. char (ptop)
ans=
  2.0199999809E0

记录状态由on变为off,因为RecordMode值为index,记录文件名被自动更新。

record(s)
s.RecordStatus
ans=
 off
s.RecrdName
ans=
 WabeForm2.txt
5)断开设备连接和清空工作空间
fclose(s)
delete(s)
clear s

记录文件的内容,因为RecordDetail属性值为冗长模式,故保存了数值数目、命令和数据,注意函数fread返回的数据为十六进制格式。使用命令type可以查看文件内容。

type WaveForm1.tex

1.3.8 保存和装载数据

1.一个简单例子

用户可以将串口对象存储到一个MAT文件中,如同使用save命令操作MATLAB工作空间中的变量一样,如下面的例子所示。假定创建了一个与串口COM1相连的对象s,对一些属性值进行了设置,并进行了一个读和写的操作。

s=serial('COM1');
s.BaudRate=19200;
s.Tag='My serial object';
fopen (s)
fprintf (s,'*IND?')
out=fscanf (s);

要将串口对象和从设备读出的数据保存到MAT文件中,只需使用下面语句。

save myserial s out

可以使用函数record将数据和数据信息以文本格式存储到磁盘文件中。使用load命令可以在MATLAB工作空间中重新建立对象s和out,如下面语句所示。

load myserial

2.断开连接和清空工作空间

当不再需要串口对象时,应该将它从连接的设备中断开,并把它从计算机内存和MATLAB工作空间中除去。这是一次串口通信期的最后一步。

1)断开串口对象和设备的连接

当不再需要和设备通信时,应当使用函数fclose将设备和串口对象断开。

fclose (s)

可以使用Status属性来验证串口对象是否已经和设备断开。

s.Status
ans=
closed

当使用函数fclose后,s所对应的串口便可用了,即可以使用函数fopen将该串口连接到另一串口对象上。

2)清空工作空间

当不再使用串口对象时,应使用函数delete将它从内存中删除。

delete(s)

在使用函数delete之前,必须使用函数fclose将串口对象从设备上断开。一个删除的对象是无效的,这也就意味着不能将它再连接到设备。在这样的情况下,应当使用命令clear将它从MATLAB工作空间中除去。

clear s

若对一个仍然还连接在设备上的串口对象使用clear命令,对象被从工作空间中除去但还连接到设备上。此时可以使用函数instrfind来还原被清除的对象。

1.3.9 串口对象属性参考

下面对串口对象的属性进行简要的归纳,并根据它们的用处进行分类,为大家提供参考。关于这些属性更详细的描述,请读者参阅帮助文件。

1.串口对象的通信属性

串口对象的通信属性如表1-20所示。

表1-20 通信属性

2.写入操作属性

串口对象的写入操作属性如表1-21所示。

表1-21 写入操作属性

3.读入操作属性

串口对象的读入操作属性如表1-22所示。

表1-22 读入操作属性

4.回调属性

串口对象的回调属性如表1-23所示。

表1-23 回调属性

5.控制针属性

串口对象的控制针属性如表1-24所示。

表1-24 控制针属性

6.记录操作属性

串口对象的记录操作属性如表1-25所示。

表1-25 记录操作属性

7.通用属性

串口对象的通用属性如表1-26所示。

表1-26 串口对象的通用属性