找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 8103|回复: 0
打印 上一主题 下一主题
收起左侧

excel vba 获取rs232数据

[复制链接]
跳转到指定楼层
楼主
ID:104835 发表于 2016-2-4 03:52 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
本文介绍VB6.0利用MSComm通信控件,开发微机通过串口对工业仪表进行实时数据采集的编程技术。给出的程序代码具有通用性,并有详细的注释,可以直接或稍加改动后用于其他数据采集或实时控制程序中。

----一台工业专用实时检测仪表,接高精度位移传感器,用于测量微小形变或微量位移,仪表测量精度为0.01毫米,测量范围最大值为50毫米。该仪表带有一个9针的RS-232C 串口,能与微机进行串口数据通信,实时传送检测数据,通过微机软件处理可实现工业实时监控。

----该仪表的串口数据通信协议是:数据传输速率为 9600bps,1位开始位,8位数据位,1位停止位,无奇偶校验位。仪表每秒发送50帧检测数据,每帧数据由4个字节组成。第一个字节定义为二进制常数0F0H,是每帧数据开始的标志字节;后面连续2个字节为数据字节,采用压缩的BCD码编码方式,高位在前,低位在后,即一个字节表示两位十进制数,则两个字节表示四位十进制数,小数点采用固定形式,定义在两字节中间;第四个字节为符号字节,该字节第八位为1,即:
1xxxxxxx


----则为负数;第八位为0,即:
0xxxxxxx


----则为正数。

----例如:0F0H 26H 87H 80H 0F0H 34H 62H 00H 表示 -26.87 34.62。

----通信传输速率为9600bps,则最快速度1.04ms发送一个字节,仪表每秒发送50帧数据,每帧数据有4个字节,即每秒发送200个字节,平均5.0ms 发送一个字节,连续读取串口数据时要在程序中添加循环等待程序。

----为了实现实时监测功能,接收数据的读取要尽可能的快速,则设置MSComm1的属性如下:
RThreshold = 1
接收缓冲区收到一个字节产生OnComm事件
InputLen = 1 每次读取一个字节

----仪表每秒发送50帧数据,微机收到一帧完整数据至少需要20 ms时间,然后再进行数据处理。如果微机在下一帧数据接收前即20ms内能将数据计算处理完毕,则接收缓冲区内只会保存有一帧数据,不会存有两帧以上数据,接收缓冲区的大小不会影响实时监测效果(接收缓冲区>4字节),这时完全可以实现实时监测或实时控制;如果微机在20ms内不能将数据计算处理完毕,接收缓冲区设置得又很大,在数据计算处理完毕前,接收缓冲区内就会保存有两帧以上数据,而且一次工作时间越长,缓冲区内滞留数据帧就越多,数据采集和数据处理之间产生逐渐增大的额外时间差,当接收缓冲区充满后,时间差不再增大,固定在某一值,部分数据因不能及时采集到接收缓冲区中,数据产生丢失现象,真实工作情况就会和微机处理结果产生较大的时间差,对实时监测和实时控制很不利,这种情况下接收缓冲区的大小就会影响实时监测效果,所以接收缓冲区设置不能过大,以保证数据处理的实时性。

----设置接收数据模式采用二进制形式,即 InputMode=comInputModeBinary,但用Input属性读取数据时,不能直接赋值给 Byte 类型变量,只能通过先赋值给一个 Variant 类型变量,返回一个二进制数据的数组,再转换保存到Byte类型数变量中。

----VB中有Byte类型变量,但没有字节的位处理语句,符号字节的位处理要判断符号字节的值是否大于 127,大于127则为负数;压缩的BCD码存入 Byte类型变量,VB系统只按十进制数处理,这要通过一个简单算法换算,解压BCD码才能还原成十进制表示数值。假如a是Byte类型变量,D是Single类型变量,将一个压缩的BCD 码存入a中,则算法是:
D=(a\16)*10 + a-(a\16)*16
则D=a-(a\16)*6

----程序清单:

----在通用声明中定义程序所用变量:
Dim ab(4) As Byte
‘字节数据类型数组,用来存储接收到的一组字节数据
Dim av As Variant ‘用来从接收缓冲区读取数据
Dim i As Integer
Dim j As Integer
Dim w As Integer ‘接收数据个数计数器
Dim b1 As Single
Dim b2 As Single
Dim WW As Single ‘十进制检测值
Dim MaxW As Single ‘最大值
Dim MinW As Single ‘最小值

----在窗体中添加名为Command1的[开始]按钮和名为 MSComm1的MSComm控件。

---- [开始]按钮的Click事件处理程序主要是对MSComm1控制的参数初始化设置,程序中大部分参数在设计时可在MSComm1控制的属性窗口中设置:
Private Sub Command1_Click()
‘开始按钮
With MSComm1
.CommPort=2 ‘使用COM2
.Setting=“9600,N,8,1" ‘设置通信口参数
.InBufferSize=40
‘设置MSComm1接收缓冲区为40字节
.OutBufferSize=2
‘设置MSComm1发送缓冲区为2字节
.InputMode = comInputModeBinary
‘设置接收数据模式为二进制形式
.InputLen = 1
‘设置Input 一次从接收缓冲读取字节数为1
.SThreshold = 1
‘设置Output 一次从发送缓冲读取字节数为1
.InBufferCount = 0 ‘清除接收缓冲区
.OutBufferCount = 0 ‘清除发送缓冲区
MaxW = -99
‘最大值赋初值
MinW = 99 ‘最小值赋初值
w = 0
‘数据个数计数器清零
.RThreshold = 1
‘设置接收一个字节产生OnComm事件
If .PortOpen = False Then
‘判断通信口是否打开
.PortOpen = True ‘打开通信口
If Err Then ‘错误处理
MsgBox “串口通信无效"
Exit Sub
End If
End If
End With
End Sub

----为了达到实时数据采集目的,实时数据采集处理程序采用MSComm事件驱动方式。

----MSComm1_OnComm的事件处理程序只处理 comEvReceive事件,首先判断帧数据的开始字节,关闭OnComm接收事件,然后接收数据字节,将压缩BCD进行还原转换,再接收符号字节,判断数据符号,判断数据最大最小值,最后打开OnComm接收事件,等待下一次OnComm事件产生:
Private Sub MSComm1_OnComm()
With MSComm1
Select Case .CommEvent
‘判断MSComm1通信事件
Case comEvReceive
‘收到Rthreshold个字节产生的接收事件
av = .Input
‘读取一个接收字节
ab(1) = av(0)
‘转换保存到字节数据类型数组
If ab(1) = &HF0 Then
‘判断是否为数据开始标志
RThreshold = 0
‘关闭OnComm事件接收
Do
DoEvents
Loop Until .InBufferCount >= 3
‘循环等待MSComm1接收缓冲区>=3个字节
w = w + 1 ‘计数器累加计数
av = .Input
‘读取第二个数据字节(BCD码高位字节)
ab(2) = av(0)
‘转换保存到字节数据类型数组
av = .Input
‘读取第三个数据字节(BCD码低位字节)
ab(3) = av(0)
‘转换保存到字节数据类型数组
av = .Input
‘读取第四个数据字节(符号位字节)
ab(4) = av(0)
‘转换保存到字节数据类型数组
b1 = ab(2) - 6 * (ab(2)\16)
‘高位字节压缩BCD码转换为实数
b2 = ab(3) - 6 * (ab(3)\16)
‘低位字节压缩BCD码转换为实数
WW = b1 + b2 / 100
‘数值组合,标定小数点
If ab(4) > 127 Then WW=?WW
‘判断数据符号位
Label1(0) = Format(WW, “0.00")
‘显示毫米单位数值,2位小数
Label1(1) =Format(WW /25.4, “0.000")
‘显示英寸单位数值,3位小数
If WW > MaxW And WW < 51 Then

----‘判断最大值,仪表在刚开始工作时有干扰,会传导一些乱码,位移传感器有参数偏差,最大值一般都略大于50毫米,所以取51为极限最大值,取-51为极限最小值。
MaxW = WW
Label1(2) = Format(MaxW, “0.00")
‘显示毫米单位最大值,2位小数
Label1(3) = Format(MaxW/25.4,“0.000")
‘显示英寸单位最大值,3位小数
End If
If WW < MinW And WW > -51 Then
‘判断最小值
MinW = WW
Label1(4) = Format(MinW, “0.00")
‘显示毫米单位最小值,2位小数
Label1(5) = Format(MinW/25.4,“0.000")
   ‘显示英寸单位最小值,3位小数
End If
.RThreshold = 1
‘打开MSComm1事件接收
End If
Case Else
End Select
End With
End Sub

1992年Crescent Software Inc.公司专为VB设计了MSCOMM.VBX用户通信控件,它提供了通过串口发送和接收数据的串行通信能力,不但包括了全部Windows API中关于串行通信的16个函数所完成的功能,而且开拓了更多的使用户设计方便的对象属性来满足不同用户不同业务的需求。目前国内众多的VB资料中很少涉及该通信控件的介绍,本文将详细介绍MSCOMM.VBX通信控件及编程方法,并结合工作中的一个实例给出基本通信程序。

1 MSCOMM.VBX通信控件描述

MSCOMM.VBX通信控件可直接从VB的ToolBox中加入窗体Form,即可用其进行通信。若ToolBox中无此控件,则用Tools的Custom Controls 将MSCOMM.VBX从Windows的System子目录中加入VB的ToolBox中。

1.1 通信方式

MSCOMM.VBX有2种不同的方式来处理和解决各类通信软件的开发和设计问题

1、事件驱动。它与C/C++写Windows 软件时的窗口回调函数类似,是1种功能强大的处理问题的方法。在实际工作中,往往要处理许多通信中的相关事件,例如:当线路数据到达本端或CD线和RTS信号线状态发生变化时,要求我们使用相应的事件来跟踪和处理,该控件是使用OnComm事件来实现的,它也包括检测和处理通信错误等方面的问题,CommEvent 值返回最近的通信事件或错误的数字代码。通信控件详细的错误和事件举例有:

MSCOMM-ER-BREAK 收到1个Break Signal

MSCOMM-ER-CDTO CD 信号超时

……

MSCOMM-EV-CD CD信号改变

……

2、查询方式。由程序设计者负责读取CommEvent的值并处理所发生的错误或事件。通常简单的应用程序设计可采用这种办法。

1.2 通信控件的属性

利用通信控件编制通信程序,关键是准确理解设置通信控件的属性。MSCOMM.VBX提供了27个关于通信控件方面的属性,例如:

CommPort:设置或返回通信口编号。

Settings:设置或返回以字符串形式出现的数据通信格式:波特率、校验、数据位和停 止位。

PortOpen:设置或返回通信口状态(包括打开和关闭1个通信口)

……

3、 实例

本程序应用背景为DCC95型静电除尘器自动监控系统软件,解决1个PC工控机(主站)与32个单片机(子站)之间的通信问题。主站与子站之间这总线式网络结构,采用RS-485通信标准,以问答方式进行数据通信。由于32个子站与主站发送通信命令(下行命令),主站在接收子站发回的相应回答命令(上行命令)后继续发送下行命令的通信形式。根据系统功能的要求,主站需发送2种类型的命令:(1)同期命令,它由定时器触发引起,每隔ls周期发送1次;(2)非周期性命令,它由操作者按动相应命令按钮引起,非周期性发送。自动监控系统软件安装在主站上,而通信程序作为自动监控系统软件的一部分也安装在主站上。

本文仅列出调试通信程序时进行试验用的基本演示程序清单。试验时,用1台PC机作为主站,另一台PC机模拟32个子站的工作,两台PC机之间采用RS232c串口通信。往主站的通信演示程序窗体(form)中加入1个通信控件、2个定时器控件和1个命令按钮控件,通信控件(Mscomm1)用于访问串口,发送和接收数据;Periodic定时器控件(Periodic)用于控制每秒由主站向各子站发送周期性命令;命令按钮控件(NonPeriodic-Command)与NonPeriodic定时器控件(NonPeriodic)用于发送非周期性命令。数据传送采用事件驱动的通信方式,根据不同的发送命令设置RTreshlod属性,从而引起OnComm事件以接收数据。

2.1 窗体各控件初始化程序

设置通信串口工作参数,设置Periodic定时器的在断间隔为ls, NonPeriodic定时器的中断间隔为0.5s。

Sub Form-Load ()

Mscomm1.CommPort=2 ’选用COM2串行口

Mscomm1.Settings="9600,N8,1" ’波特率9600,无奇偶校验位,8位数据位1位停止位

Mscomm1.InputLen=0 ’Input将读取接收缓冲区的全部内容

Mscomm1.InBufferSize=1024 ’设置接收缓冲区的字节长度

Mscomm1.PortOpen=True ’打开通信口

Mscomm1.InBufferCount=0 ’清除发送缓冲区数据

Mscomm1.OutBufferCount=0 ’清除接收缓冲区数据

Periodic.inteval=100 ’设置ls定时间隔,使遥测命令每隔ls发送1次

NonPeriodic.inteval=500 ’设置0.5s定时间隔,查询命令按钮是否处于激活状态以确定是否发送周期性命令

Command-Pressed=False ’命令按钮为未激活状态

During- Periodic=False ’周期性命令数据传输尚未开始

During- NonPeriodic=False ’非周期性命令数据传输尚未开始

End Sub

2.2 非周期性命令发送程序

根据命令按钮状态及周期性命令数据传输状态,在NonPeriodic定时器的中断程序中发送非周期性命令。

Sub NonPeriodic-Command-Click ()

Command-Pressed=True ’命令按钮激活

End Sub

Sub NonPeriodic-Timer ()

if During- Periodic=True OR Command-Pressed=False

Then Exit Sub ’若周期性命令数据传输尚未结束或命令按钮处于激活状态,则退出发送非周期性命令程序。

Command-Pressed=False ’命令按钮恢复为未激活状态

Call SendData (NONPERIODIC-COMMAND) ’发送非周期性命令

Mscomm1.RThreshold=R-NONPERIODIC-BYTE’发送非周期性命令后,设置Rthreshold属性,使主站接收所设定的字节数后引发OnComm事件

End Sub

2.3 Periodic定时器程序

在Periodic定时器的中断程序中发送周期性命令:

Sub Periodic-Timer ()

if During- NonPeriodic=True Then Exit Sub ’若非周期性命令数据传输尚未结束,则退出发送非周期性命令程序。

During-Periodic=True ’设置周期性命令数据传输状态为正在进行中

Call SendData (PERIODIC-COMMAND) ’发送周期性命令

Mscomm1.RThreshold=R-PERIODIC-BYTE ’发送周期性命令后,主站接收R-REMOT- EDATA-BYTE个字节,可引发OnComm 事件

End Sub

2.4 OnComm事件程序

根据RThreshold属性设置值,当接收缓存区内接收到相应字节的字符时,引发OnComm事件,在中断程序中接收数据。

Sub Mscomm1-OnComm ()

Select Case Mscomm1.CommEvent ’在此可插入处理各种不同错误或事件的代码

Case MSCOMM-EV-RECEIVE

ReceiveString$=Mscomm1.Input

Select Case Mscomm1.RThreshold

Case R-PERIODIC-BYTE ’周期性命令的应答数据

Call DisposeData(PERIODIC-COMMAND) ’处理接收数据

During Periodic=False ’设置周期性命令数据传输状态为结束

Case R-NONPERIODIC-BYTE ’非周期性命令的应答数据

Call DisposeData(NONPERIODIC-COMMAND) ’处理接收数据

During-Nonperiodic=False ’设置非周期性命令数据传输状态为结束

End Select

End Select

End Sub

------------------------------------------------------------------------

随着VB版本的不断升级,VB将成为最快速、易用、强劲的应用开发工具,是企业级客户/服务器应用软件开发的首选工具之一。

VB有一个例子在Samples\CompTool\Mscomm中。你可以利用这个例子来学习串口编程。
     打开串口先设置CommPort属性(端口号),使用PortOpen=True就可以了。
     串口数据保存在MSComm的Input属性中。你可以使用下面的代码:
     Dim v As Variant
     v = MSComm1.Input
     For i = 0 To MSComm1.InBufferCount - 1
      Debug.Print v(i)
     Next
  
现有电子秤一台,使用串口与计算机进行通讯。编写VB程序来访问串口,达到读取电子秤上显示的数据。该电子秤为BE01型仪表,输出为RS-232C标准接口,波特率为300-9600、偶校验、7个数据位、2个停止位。所有字符均发送11位ASCII码,一个起始位。在VB中与串口通讯需要引入控件MSComm串口通讯控件(在Microsoft Comm Control 6.0中)。具体程序如下:控件简称:MSC

Dim Out(12) As Byte '接收var中的值
Dim var As Variant '接收MSC.input中的数值
Dim nRece As Integer '计算MSC.inputbuffer的个数
Dim i As Integer, j As Integer '随即变量,计算循环

****************************************************************************

Private Sub Form_Load()
 ClearText
 With MSC
  .CommPort = 1 '设置Com1为通信端口
  .Settings = "9600,E,7,2" '设置通信端口参数 9600赫兹、偶校验、7个数据位、1个停止位.(这里需要进一步说明的是:.Setting=”BBBB,P,D,S”。
  含义是:B:Baud Rate(波特率);P:Parity(奇偶);D:Data Bit;S:Stop Bit)

  .InBufferSize = 40 '设置缓冲区接收数据为40字节
  .InputLen = 1 '设置Input一次从接收缓冲读取字节数为1
  .RThreshold = 1 '设置接收一个字节就产生OnComm事件

 End With

End Sub

****************************************************************************

Private Sub ClearText()
 Text3.Text = ""
 Text2.Text = "5"
 Text1.Text = ""
End Sub

Private Sub Command1_Click()
 ClearText
 ' nRece = 0 '计数器清零
 With MSC
  .InputMode = comInputModeBinary '设置数据接收模式为二进制形式
  .InBufferCount = 0 '清除接收缓冲区
  If Not .PortOpen Then
   .PortOpen = True '打开通信端口
  End If
 End With
End Sub

Private Sub MSC_OnComm()
 DelayTime ‘用来延续时间
 ClearText
 With MSC
  Select Case .CommEvent '判断通信事件
  Case comEvReceive: '收到Rthreshold个字节产生的接收事件
   SwichVar 1
   If Out(1) = 2 Then '判断是否为数据的开始标志
    .RThreshold = 0 '关闭OnComm事件接收
   End If
   Do
    DoEvents
   Loop Until .InBufferCount >= 3 '循环等待接收缓冲区>=3个字节
   ' nRece = nRece + 1
   For i = 2 To 12
    SwichVar i
    Text1.Text = Text1.Text & Chr(Out(i))
   Next
   Text1.Text = LTrim(Text1.Text)
   Text2.Text = Text2.Text & CStr(nRece)
   .RThreshold = 1 '打开MSComm事件接收
  Case Else
   ' .PortOpen = False
  End Select
 End With

End Sub

****************************************************************************

Private Sub DelayTime()

 Dim bDT As Boolean
 Dim sPrevious As Single, sLast As Single

 bDT = True

 sPrevious = Timer (Timer可以计算从子夜到现在所经过的秒数,在Microsoft Windows中,Timer函数可以返回一秒的小数部分)

 Do While bDT
  If Timer - sPrevious >= 0.3 Then bDT = False
 Loop
 bDT = True

End Sub

(通信传输速率为9600bps,则最快速度1.04ms发送一个字节,仪表每秒发送50帧数据,每帧数据有4个字节,即每秒发送200个字节,平均5.0ms 发送一个字节,连续读取串口数据时要在程序中添加循环等待程序)

Private Sub SwichVar(ByVal nNum As Integer)

 DelayTime
 var = Null
 var = MSC.Input
 Out(nNum) = var(0)

End Sub

(设置接收数据模式采用二进制形式,即 InputMode=comInputModeBinary,但用Input属性读取数据时,不能直接赋值给 Byte 类型变量,只能通过先赋值给一个 Variant 类型变量,返回一个二进制数据的数组,再转换保存到Byte类型数变量中。)

Private Sub Text1_Change()

 Text3.Text = CText(Text1.Text) - CText(Text2.Text)

End Sub

****************************************************************************

Private Function CText(ByVal str As String) As Currency

 If str <> "" Then
  CText = CCur(Val(str))
 Else
  CText = 0
 End If

End Function

  (仪表每秒发送50帧数据,微机收到一帧完整数据至少需要20 ms时间,然后再进行数据处理。如果微机在下一帧数据接收前即20ms内能将数据计算处理完毕,则接收缓冲区内只会保存有一帧数据,不会存有两帧以上数据,接收缓冲区的大小不会影响实时监测效果(接收缓冲区>4字节),这时完全可以实现实时监测或实时控制;如果微机在20ms内不能将数据计算处理完毕,接收缓冲区设置得又很大,在数据计算处理完毕前,接收缓冲区内就会保存有两帧以上数据,而且一次工作时间越长,缓冲区内滞留数据帧就越多,数据采集和数据处理之间产生逐渐增大的额外时间差,当接收缓冲区充满后,时间差不再增大,固定在某一值,部分数据因不能及时采集到接收缓冲区中,数据产生丢失现象,真实工作情况就会和微机处理结果产生较大的时间差,对实时监测和实时控制很不利,这种情况下接收缓冲区的大小就会影响实时监测效果,所以接收缓冲区设置不能过大,以保证数据处理的实时性。) 小结:本文所用的仪表为梅特勒公司出产的BE01型电子秤,其输出的每个编码均为标准的ASCII码。其他的仪表存在发射的编码中含有BCD压缩码,而且分为高低位,需要接收后对其进行解码换算,之后还要将高位和低位数字进行相加,即可以将其BCD码换算成实数。另还存在误差的可能:判断最大值,仪表在刚开始工作时有干扰,会传导一些乱码,位移传感器有参数偏差,最大值一般都略大于50毫米,所以取51为极限最大值,取-51为极限最小值。暂时先写这些,当然其他的情况可以依此类推!

串口数据接收方式
如何处理不定长数据的接收
用字符方式收发码值大于127的字符数据
串口通讯问答2


串口数据接收方式

1、 在OnComm 事件中接收数据:
这种方式能充分MSCOMM控件的特性。OnComm 事件还可以检查和处理通讯错误;可以通过检查 CommEvent 属性的值来查询事件和错误;对于不定长数据以及对数据进行处理比较复杂的情况,此法不是很方便。

Private Sub MSComm_OnComm ()
Select Case MSComm1.CommEvent

' 错误
Case comEventBreak ' 收到 Break。
Case comEventCDTO ' CD (RLSD) 超时。
Case comEventCTSTO ' CTS Timeout。
Case comEventDSRTO ' DSR Timeout。
Case comEventFrame ' Framing Error
Case comEventOverrun '数据丢失。
Case comEventRxOver'接收缓冲区溢出。
Case comEventRxParity' Parity 错误。
Case comEventTxFull '传输缓冲区已满。
Case comEventDCB '获取 DCB] 时意外错误

' 事件
Case comEvCD ' CD 线状态变化。
Case comEvCTS ' CTS 线状态变化。
Case comEvDSR ' DSR 线状态变化。
Case comEvRing ' Ring Indicator 变化。
Case comEvReceive ' 收到 RThreshold # of chars.
Case comEvSend ' 传输缓冲区有 Sthreshold 个字符 '
Case comEvEof ' 输入数据流中发现 EOF 字符

End Select
End Sub

2.轮循法采集数据:
A、定时器轮循法
对于数据包方式收发数据以及不需即时响应情况,用轮循法更好些。实际上轮循法最大的好处在于集中处理数据而且不太占用CPU。轮循法要注意定时采集的时间片段大小;这里用二进制收发模式;使属性RThreshold、SThreshold为0,屏蔽ONCOMM事件。

InputMode = comInputModeBinary
RThreshold = 0
SThreshold = 0

Private Sub TmrComm_Timer()
'采用轮循法采集数据
Dim Rx_buff() As Byte
Dim okstring As String
Dim ReceivedLen As Integer

On Error GoTo ErrorHandler
TmrComm.Enabled = False '关闭定时器
If commport.InBufferCount > 0 Then
ReceivedLen = commport.InBufferCount
Rx_buff = commport.Input
okstring = StrConv(tempbyte, vbUnicode)
If ReceivedLen = 6 Then
If Chr(tempbyte(0)) = ":" And tempbyte(3) = &h0a Then
....
End If
If Instr(okstring ,":@END*",vbBinaryCompare) Then
....
End If
End If
TmrComm.Enabled = True '打开定时器
End Sub

B、直接轮循法
此法用于接收少量控制命令字;
' 保存输入子串的缓冲区
Dim Instring As String
' 使用 COM1。
MSComm1.CommPort = 1
' 9600 波特,无奇偶校验,8 位数据,一个停止位。
MSComm1.Settings = "9600,N,8,1"
' 当输入占用时,
' 告诉控件读入整个缓冲区。
MSComm1.InputLen = 0
' 打开端口。
MSComm1.PortOpen = True
' 将 attention 命令送到调制解调器。
MSComm1.Output = "ATV1Q0" & Chr$(13)
' 确保
' 调制解调器以"OK"响应。
' 等待数据返回到串行端口。
Do
DoEvents
Buffer$ = Buffer$ & MSComm1.Input
Loop Until InStr(Buffer$, "OK" & vbCRLF)
' 从串行端口读 "OK" 响应。
' 关闭串行端口。
MSComm1.PortOpen = False



如何处理不定长数据的接收

在处理串口通讯时,经常会遇到不定长数据的接收。由于通讯任务不同及编程要求的差异所以采用的方法也有所不同。本文就此问题进行探讨。不定长数据从数据格式上分,可分为有格式和无格式。

一、无格式不定长数据的接收
这种格式在实际串口通讯中用得不多,一般只用传送字符串数据。问题在于怎么判断接收结束。一般用时间延迟的方法解决。
A、对于非握手式通讯,可用一个定时器定时轮循接收,并假定每个轮循接收完成。用ONCOMM事件接收也可,只是不如定时器定时轮循接收简便。
B、对于握手方式通讯,可用直接轮循法提高接收的准确性。下面是实现此法的函数:

Function sComm(sCommand As String, comReceive As MSComm) As String
Dim nReceiveCount As Integer
If comReceive.PortOpen = False Then
comReceive.PortOpen = True
End If

comReceive.Output = sCommand

Do
nReceiveCount = comReceive.InBufferCount
sleep (2) 'API 函数,挂起当前进程一段时间
Loop Until comReceive.InBufferCount = nReceiveCount
If comReceive.PortOpen = True Then
sComm = comReceive.Input
End If
End Function
注:此函数参照了xth一文。
此法一般是能确保数据接收的正确,但由于WINDOWS是多任务操作系统,当有耗时的进程运行时会丢失数据。如果系统会出现这种情况,可增大函数sleep()的参数值。

二、不定长格式数据的接收
对于不定长数据接收最好的方法是制定通讯协议,比如定义开始字符和结束字符。由于单片机系统通讯一般不太复杂,没必要去制定一套象通用计算机间通讯的协议,而根据单片机系统的大小和性能要求制定通讯协议。实际上为便于交流、维护以及一致性,可制定一套可伸缩的通讯协议。定义了开始字符和结束字符就容易实现不定长格式数据通讯,但在实际通讯编程还是容易出现一些比较隐蔽的通讯错误。下面就常用方法分别进行分析。
A、定时器轮循法。
假定每个轮循期数据接收完毕,并在每个轮循期处理数据,由于有开始字符和结束字符很容易确定接收数据的完整性。好象合理设定轮循时间值就万无一失了,但被动接收数据时无论如何也找不合适的轮循时间值,因为启动定时器和数据到来基本不同步,这就会出现一次发送的数据被分在两个轮循期接收,所以被动接收数据时不能假定每个轮循期数据接收完毕。在接收到结束字符后才确定一次数据接收完毕就可解决此问题。
B、OnComm事件法。
方法和定时器轮循法基本相同,因为每次OnCommg事件也只能接收到一部分数据。在VB的在线帮助中这样注解“设置 Rthreshold 为 1,接收缓冲区收到每一个字符都会使 MSComm 控件产生 OnComm 事件。”。但实际上OnComm事件并不是每收到一个字符便触发一次 OnComm 事件。OnComm事件是在缓冲区收到几个甚至几十个字节数据后才被触发的。版主认为这是WINDOWS多任务使操作系统不能实时响应造成的。如果要在每次OnComm事件接收一个字符似乎可设INPUTLEN属性为1,但实际行不通。VB在线帮助中“有该属性在从输出格式为定长数据的机器读取数据时非常有用”的注解,好象在说对定长字符有效,但版主发现INPUTLEN设为16,接收16个字符定长数据时却被当作两次接收了,一次12个,一次4个。建议在OnComm事件中接收数据要定义通讯协议并检测数据的完整性。 对于不定长格式数据的接收程序员更喜欢定时器轮循法,也许OnComm事件不好控制吧。
对于不定长数据的接收,最佳方法可能是在OnComm事件中启动定时器轮循接收,并同时停止OnComm事件的触发,接收完毕后或超时开启OnComm事件。


用字符方式收发码值大于127的字符数据

VB的通讯控件友好、功能强大,编程速度快是众人皆知的。加上VB的易学、易用,快速开发等特点,数据通讯量不是很大时,在单片机通讯领域广泛地使用VB开发PC上层通讯软件。实际开发时会有不少问题,这里就用字符方式收发码值大127的字符数据进行讨论。
在实际开发中经常遇到通讯只是用来发送一些控制字符命令和少量数据。在VB的中文在线帮助中有“若数据只用 ANSI 字符集,则用 comInputModeText”的表述。 ANSI字符集是0-127这容易使人误解为&h88也可用“INPUTMIDE=comInputModeText”方式收发。我刚开始用VB编通讯模块时就为此迷惑过,网上不少网友也时常问及这种问题。实际上在VB中0-127是可以正常收发的,大于127即&H7F的只有&H80和&HFF能够收发,其余ANSI字符都被过滤为0。由于串口通讯是以字节收发的,数据如以comInputModeText模式收发则非字符串数据会被过滤。在VB中用“INPUTMIDE = comInputModeBinary” 就可以解决这个问题,只是收发都必须用动态数组来完成。用comInputModeBinary模式编程稍有点复杂,调试也不直观,对于初学者不易掌握。另外软件完成后,在实际应用时会增加工程维护难度,因为对于二进制代码不是易于理解的。比如下端机发送现场统计数据233,comInputModeBinary模式下串口监测到“:A &H233;",它代表A探针的温度。一般串口监测软件要么用ASCII方式显示,要么用二进制方式显示。用ASCII方式则不能看到&H233,而二进制方式则示不好理解,如果显示58 65 233 59,我想没有人喜欢这种方式(如果有更好的方式的话)。但如果显示“:A 2 3 3 ;”不就解决问题了!用comInputModeText方式就可完成任务了,只是多了一段数据分离程序。对于一般通讯要求这种方法不为是一种好方法。由于通讯任务是多种多样的,有时候这种方法就有点力不从心了,如传送较多的的数据时,这会显著地增加通讯量,通讯变得复杂了,对于单片机系统就不太合适了;还有一些特殊要求,如数据包的识别符也不适此法,但能确定传送数据码值范围也可用此法。下面介绍另一种方法,此法适用比较广,传送二进制数据通讯量增加也不大。
这种方法实际上很简单,实际运用中有不少采用此法。原理是一码分为二码。如设7E为临界字符,对于7E则分为7E和0两个ASCII码,依此类推,8F分为7E和11。接收合并时遇到7E则将7E和后一个ASCII码相加为下字符。下面给出C语言函数,VB转换一下便可。
由于C语言不能返回两个参数,所以用数组指针。


void Filt(char code[],char c)
{
if(c=='F')
{
if(code[0]>=0X7E)
{
code[1]=code[0]-0X7E;
code[0]=0X7E;
}
else
{
code[1]=0XFF; /*0XFF作为标记code[1]不可能产生0XFF*/
}
}
else if(c=='H')
{
if(code[0]!=0X7E)
{
code[1]=0xFE; /*转换完成标记*/
}
else
{
if(code[1]==0XFE)
{
code[1]=0XFF; /*接收下一个码的标记*/
}
else
{
code[0]=code[0]+code[1];
code[1]=0XFE;
}
}
}
发送时:
char SendChar[2]; /*存储发送的值*/
....
SendChar[0]=c; /*c为待发ASCII码*/
Filt(SendChar,'F');
if(SendChar(1)==0XFF)
{
..... /*发送SendChar[0]*/
}
else
{
...... /*发送SendChar[0],SendChar[1]*/
}
接收时:
char ReceiveChar[2]; /*存储接收的值*/
.....
ReceiveChar[0]=c0; /*c0接收的ASCII码*/
Filt(ReceiveChar,'H');
if(ReceiveChar[1]==0xFF)
{
ReceiveChar[1]=c1; /*c1为下一个*/
Filt(ReceiveChar,'H);
}
else if(ReceiveChar[1]==0xFE)
{
...... /*存储转换后的ReceiveChar[0]*/
}
以上代码仅提供一种思路,实际情况视编程需要而定。


串口通讯问答录
1、Q:各位vb高手:我有一个问题想请教一下。我从COM口用BIN方式接收到数据(一串汉字),存入一BYTE数组,但无法还原为一串汉字,我认为是ANSI和UNICODE的转换,请问如何转换。
例:字符串“我”,按BIN方式接收成一BYTE数组,其值为“206,210”,如用“CHR(206)+CHR(210)”却无法得到“我”,实际上“我”=CHR(-12860)请问如何能实现BYTE数组(206,210)与字符串“我”之间的转换?万分感谢!!!

JY
1999.10
A:经CHR(206)+CHR(210)转换后实际上变成了两个UNICODE字符,四个字节了。汉字的收发必须用BINARY方式。下面的程序能实现汉字收发。
发:
Dim ytemp() As Byte
Dim stemp As String
stemp = "你好!"
ytemp = StrConv(stemp, vbFromUnicode)
Debug.Print UBound(ytemp)
MSComm1.Output = ytemp
收:
Private Sub mscTest_OnComm()
'中文收发
Dim yTemp() As Byte
Dim stemp As String
Dim i As Integer
If mscTest.InBufferCount > 0 Then
i = mscTest.InBufferCount
yTemp = mscTest.Input
stemp = StrConv(yTemp, vbUnicode)
txtTest1.Text = stemp
End If
End Sub


分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表