MSCOMM如何高速采集几十个仪表的温度数据
现用编vb mscomm控件写一采集温度(共20个仪表)的工控程序,现有思路如下:
用一Timer控件定时(3000)依次采集温度数据,发现Timer的间隔如果再减少,会造成采集数据出错。请问高手如何达到200ms的温度数据采集速度?
主要代码机构如下:
Private Sub Timer1_Timer()
'intAddress 为仪表地址1-20
’依次发读取命令,GetData函数获取MSCOMM串口数据
intAddress = 1
‘bytsend()数组存放读命令
MSComm1.InBufferCount = 0 '清空接收缓冲区
MSComm1.OutBufferCount = 0 '清空接收缓冲区
MSComm1.Output = bytSend
GetData
......
intAddress = 20
MSComm1.InBufferCount = 0 '清空接收缓冲区
MSComm1.OutBufferCount = 0 '清空接收缓冲区
MSComm1.Output = bytSend
GetData
End Sub
Private Function GetData() As Boolean
Dim bytGet() As Byte '定义字节数组
Do '额外增加的时间延时
DoEvents
intC = intC + 1
Loop Until intC = 500000
If MSComm1.InBufferCount <> 10 Then GoTo ErrorCode
bytGet() = MSComm1.Input '从接收队列中读入字符串
txtTemp(intAddress - 1).Text = strPV '显示测量值
txtSVTemp(intAddress - 1).Text = strSV '显示设定值
MSComm1.InBufferCount = 0 '清空接收缓冲区
GetData = True
End Function
如上代码实际运行时差不多每隔6秒刷新一次时间,无法达到200ms采集速度。需要如果改进思路?
[最优解释]
Private Sub Timer1_Timer()
Timer1.enable=false
'intAddress 为仪表地址1-20
’依次发读取命令,GetData函数获取MSCOMM串口数据
intAddress +=1
select case intAddress
case 1
'intAddress = 1
‘bytsend()数组存放读命令
MSComm1.InBufferCount = 0 '清空接收缓冲区
MSComm1.OutBufferCount = 0 '清空接收缓冲区
MSComm1.Output = bytSend
GetData
......
case 20
'intAddress = 20
MSComm1.InBufferCount = 0 '清空接收缓冲区
MSComm1.OutBufferCount = 0 '清空接收缓冲区
MSComm1.Output = bytSend
GetData
case else
intAddress=0
end select
Timer1.enable=true
End Sub
[其他解释]
重点在于通讯的时序,至于时间作为参考参数就可以了。
因为VB的串口通讯效率很低的,而且属于异步通讯,采用定时通讯的方式稳定性很差。
这么说吧,以 9600 波特率来说明吧:
9600 波特率代表串口以每秒 9600 个二进制位的速度进行传输,而根据串口通讯设置
如:9600,N,8,1 // 9600波特率、没有校检、8个二进制位一个包、一个停止位
一个串口数据包大小=8+1+2个下降沿=11个二进制位,理论上
9600/11 = 872 字节/秒
实际上,如果你用VB的控件传输或接收一个固定为872个字节的串,在时间上肯定不是稳定在1秒内的。
时常会超过很多时间,根据你电脑的配置,有时候甚至会有卡死的现象。
这是因为VB的串口通讯不是单纯的进行硬件层的通讯,他还要经过驱动、系统的任务队列、堆栈的转换、内存和缓存的重载与分配,一层层的系统概念套下来,在VB里目标代码的执行的效率大幅度下降,所以才会有这样的问题存在。而且VB的发送数据不是直接将数据发往串口,而是把数据写入缓存,再由系统根据系统资源使用情况慢慢将缓存的数据发往串口,而这种发网串口的过程,就不一定是连续的串口包发过去这么计算了,可能是连续发了五六个字节,然后被系统暂停了任务去执行别的东西,过几十毫秒或更久时间再回来继续发数据(接收也一样),就这样的工作、暂停、工作的方式一直把缓冲区的数据发送完。如果是配置高的电脑,同时运行的程序不多,有大量的资源去处理这种任务的交换,可能可以按照你的定时完成发送或接收任务,但如果计算机配置不高或运行了比较多的任务,可能你提交到缓冲区的数据还没有发送完就到了定时就收发送命令返回的时间了。所以这种做法很难保障通讯步骤的稳定(时序不对),如果下位机没有较高的容错处理,甚至会影响下位机的正常工作状态。所以建议不要用定时器来处理过程,如果想处理过程与时间挂钩,就在处理过程里计算时间或读取相关的参数来辨别,否者因为时序问题导致通讯过程失败,比误差时间所带来的问题更为严重。
[其他解释]
串口传输,是极低速的通讯方式。
你可以试试:
1 提高波特率,例如到 115200;
2 不要用固定延时:
Do
DoEvents
If MSComm1.InBufferCount = 10 Then Exit Do
intC = intC + 1
Loop Until intC = 500000 '超时
If intC = 500000 Then GoTo ErrorCode
[其他解释]
建议你采用轮询的方式读取每个温控仪的数据。
通常温度控制是有温控仪完成的,上位机并不参与控制。所以你完全没有必要很密集的读取温控仪数据,实际上,在VB中串口通信也做不到1秒内读取几十个温控仪数据。读一次温控仪所开销掉的时间大约在20~100ms,如果有20个温控仪,你可以算一算。读一圈要花费:400~2000ms,这个速度对于高速数据采集而言,是达不到要求的。
实际上由于Windows是多任务操作系统,系统还有其它的任务要执行,那么如果你的通信占用太多的CPU资源,就会导致其它的任务被阻塞。
[其他解释]
楼上所做的改变对于改善数据采集速度没有帮助啊。
[其他解释]
如果换个思路,按顺序判断发给每个温度仪表的读数据命令已经获得完整的,有效的温度数据(即发读取命令-获取数据过程),是否可行?
那问题在于MSCOMM控件怎么判断发出去的读命令,已经获取了完整有效的数据呢?
[其他解释]
If MSComm1.InBufferCount = 10 Then Exit Do
现场试验该语句不能保证读取得数据是有效的温度测量值,仪表设定值。
[其他解释]
受益了,跟着学点
[其他解释]
同意楼上。
温度是不容易突变的物理量,没有必要如此密集采样。
[其他解释]
我看到厂商提供的温控软件刷新速度很快,但因为是通用版本(单机版),不符合远程数据存储,远程数据显示(网络版),所有需要改造。虽然已经解决了温度采集(但有缺陷),远程显示,存储,曲线查看。本人现在还没琢磨出怎么做到快速显示,已经保证读取的数据是有效的仪表设定值,温度测量值,请大家指教。温控仪表宇电A518。
[其他解释]
现在还没琢磨出怎么做到快速显示,以及如何保证读取的数据是有效的仪表设定值,温度测量值,请大家指教。采用温控仪表是宇电A518。
[其他解释]
厦门宇电的表?多通道的吗?
[其他解释]
是否多通道我也不清楚,所用仪表也是刚接触,我只是看了它的通信协议来写的程序。
TO: Veron_04
建议你采用轮询的方式读取每个温控仪的数据。 你所说轮询的方式跟以下程序步骤是否类似?
不妨点拨,谢谢!
Private Sub Timer1_Timer()
'intAddress 为仪表地址1-20
’依次发读取命令,GetData函数获取MSCOMM串口数据
intAddress = 1
‘bytsend()数组存放读命令
MSComm1.InBufferCount = 0 '清空接收缓冲区
MSComm1.OutBufferCount = 0 '清空接收缓冲区
MSComm1.Output = bytSend
GetData
......
intAddress = 20
MSComm1.InBufferCount = 0 '清空接收缓冲区
MSComm1.OutBufferCount = 0 '清空接收缓冲区
MSComm1.Output = bytSend
GetData
End Sub
[其他解释]
我通常是这样做的
Dim intCommCount as Integer
Private Sub Timer1_Timer()
Select case intCommCount
case 0
'读第一个温控仪
case 1
'读第二个温控仪
case 2
.
.
.
case 29
'读第三十个温控仪
End Select
intCommCount=(intCommCount+1) Mod 30 '更新下次要读的温控仪下位机地址
End Sub
[其他解释]
现在遇到一个问题轮询20个仪表时,总有2个仪表读取数据时始终不能正常读出数据。
电话咨询厂商,说是接受区缓存数据处理可能有问题。我百思不得其解,请大家看看如何处理:
TO:twohorses
终于看出您的思路确实不错,当时没仔细看透其中的区别,谢谢。
Private Sub Form_Load()
MSComm1.CommPort = 1 '设置串口为com1
MSComm1.Settings = "9600,n,8,1" '通讯参数 波特率 奇偶校验 数据位 停止位
MSComm1.InputMode = comInputModeBinary '二进制接收
MSComm1.SThreshold = 10 '设置并返回传输缓冲区中允许的最小字符数
MSComm1.InBufferSize = 10
MSComm1.PortOpen = True '打开串口
MSComm1.InputLen = 10
MSComm1.InBufferCount = 0 '清空接收缓冲区
MSComm1.OutBufferCount = 0
MSComm1.RThreshold = 10
End Sub
Private Sub Timer1_Timer()
intAddress = 1
‘bytsend()数组存放读命令
MSComm1.InBufferCount = 0 '清空接收缓冲区
MSComm1.OutBufferCount = 0 '清空接收缓冲区
MSComm1.Output = bytSend
GetData
End Sub
Private Function GetData() As Boolean
Do
DoEvents
intC = intC + 1
If MSComm1.InBufferCount = 10 Then
MSComm1.OutBufferCount = 0
Exit Do
End If
Loop Until intC = 50000
If MSComm1.InBufferCount <> 10 Then GoTo ErrorCode
bytGet() = MSComm1.Input '从接收队列中读入字符串
txtTemp(intAddress - 1).Text = strPV '显示测量值
txtSVTemp(intAddress - 1).Text = strSV '显示设定值
MSComm1.InBufferCount = 0 '清空接收缓冲区
GetData = True
End Function
通讯协议中说,读取的数据包含10个字节,因此我把缓存区大小设置为10,一次读取的数据大小也是10,读取数据前清空接受,发送区缓存数据。以上处理还是不能保证能正确读取数据,为什么?