大家好,我用VB的MSComm控件做的上位机,下位机是一片单片机,PC串口上接一个232转485的转换器与单片机通讯,单片机端用的MAX487芯片。用二进制收发。
1、现在PC给单片机发送数据后,单片机内必须延迟100多ms,再向PC回送数据,PC才能将数据收全,否则接收数据错误。
2、在PC的串口上,将串口的收发线直接短接,就是说用串口自发自收,没问题,串口上加上485 转换器后,就必须在单片机内做延时了
3、如果单片机向PC回送几十个数据,前面的数据错误,从开始正确的数据 开始,往其后的数据都是正确的。比如单片机延时50ms,PC收到的数据前10个不正确,后面的几十个数据都正确
如延时70ms,PC收到的数据前7个不正确,后面的都正确。
如不延时,会有近20个数据不正确。
4、用串口调试助手,则不存在需延时的问题。
请教一下这问题怎么解决,先谢了
1、现在PC给单片机发送数据后,单片机内必须延迟100多ms,再向PC回送数据,PC才能将数据收全,否则接收数据错误。
2、在PC的串口上,将串口的收发线直接短接,就是说用串口自发自收,没问题,串口上加上485 转换器后,就必须在单片机内做延时了
3、如果单片机向PC回送几十个数据,前面的数据错误,从开始正确的数据 开始,往其后的数据都是正确的。比如单片机延时50ms,PC收到的数据前10个不正确,后面的几十个数据都正确
如延时70ms,PC收到的数据前7个不正确,后面的都正确。
如不延时,会有近20个数据不正确。
4、用串口调试助手,则不存在需延时的问题。
请教一下这问题怎么解决,先谢了
RS485口通常是单工的,收和发需分开进行.
PC机侧的VB程序及COM口是双工的.收和发可同时进行.PC机侧的命令间需有适当的时间间隔,以空出等待单片机返回
命令.PC机侧完全可利用MsComm1_OnComm事件触发来实现接收,设置MSComm1.RThreshold = 1或返回命令的字节
长度来控制接收.PC机侧的命令间适当的时间间隔可根据波特率,接收字节长计算由TIMER计时器控制.
我现在是另建了一个项目,只是串口收发的,共有一个窗口,一个发送用按键。
接收用两种方法试过:1、发送完毕开启一个定时器,延时后,在定时器事件里查询接收
用: Do
DoEvents
Loop Until MSComm1.InBufferCount >= 1
2、在MSComm_OnComm事件里接收
Select Case MSComm1.CommEvent Case comEvReceive
ReDim out(57) As Byte
Do
DoEvents
Loop Until MSComm1.InBufferCount >= 1 '循环等待接收缓冲区>=1个字节
Var = MSComm1.Input
buffer = Var
out = buffer
End Select
这是接收事件里的全部代码,做这个只为测试用 窗口中全部代码如下:
MSComm1.CommPort = 1 '利用串口COM1进行通讯MSComm1.InputLen = 0 '每次读取接收缓冲区的1个字节
MSComm1.OutBufferSize = 512 '设置发送缓冲区为512字节
MSComm1.InBufferSize = 512 '设置接收缓冲区为2048字节
MSComm1.OutBufferCount = 0 '清除发送缓冲区
MSComm1.InBufferCount = 0 '清除接收缓冲区
MSComm1.InputMode = comInputModeBinary '数据传输设置为二进制格式
MSComm1.RThreshold = 1 '一次性接收起始码1个字节'可触发On_CommMSComm1.Settings = "2400,N,8,1" '设置波特率、校验位(1)、数据位、停止位
MSComm1.PortOpen = True '打开通信口COM1 按键事件中所有代码如下:
Private Sub Command1_Click()
'手动发送键Dim DatTempBuf(30) As ByteDim OutDatV As VariantMSComm1.OutBufferCount = 0 '清除发送缓冲区MSComm1.OutBufferCount = 0 '清除发送缓冲区MSComm1.OutBufferCount = 0
If MSComm1.PortOpen = False Then
MsgBox "串口未打开", 52, "提示"
Exit Sub
End If
'DatTempBuf(0) = &HFE
'DatTempBuf(1) = &HFE
'DatTempBuf(2) = &HFE
' DatTempBuf(3) = &HFE
' DatTempBuf(4) = &HFE
' DatTempBuf(5) = &HFE
' DatTempBuf(6) = &HFE
' DatTempBuf(7) = &HE
' DatTempBuf(8) = &HE
' DatTempBuf(9) = &HE
' DatTempBuf(10) = &HE
'DatTempBuf(0) = &HFE
' DatTempBuf(0) = &HFE
DatTempBuf(0) = &HFF
DatTempBuf(1) = &H55
DatTempBuf(2) = &HAA
DatTempBuf(3) = &H4
DatTempBuf(4) = &H0
DatTempBuf(5) = &H3
DatTempBuf(6) = &H7
MSComm1.Output = DatTempBuf
Do
DoEvents '转交控制权
Loop Until MSComm1.OutBufferCount = 0 '发送缓冲区清空
MSComm1.InBufferCount = 0 '清除接收缓冲区
MSComm1.RThreshold = 1 '所要接收的数据长度
End Sub
加上232-485转换器,并下位机用单片机后,就不行,
用串口调试助手接单片机,单片机发送数据前也不用延时,也能很好接收单片机发来的数据。
照现象看,很像MSComm控件和485转换器的配合问题,但转换器我用了两个试过都一样,其中一个是波士电子的,说是零延时且不用任何控制端的。
但我测试是发现,发送前加上 MSComm1.RTSEnable = False ,发送后就是在Do-Loop语句后加上 MSComm1.RTSEnable = True 会使接收正确的数据多一个字节。望指教
Option Explicit
'Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Dim BytReceived() As Byte
Dim strData As String
Private Sub Form_Load()
MSComm1.CommPort = 1 '利用串口COM1进行通讯
MSComm1.InputLen = 0 '每次读取接收缓冲区的1个字节
MSComm1.OutBufferSize = 512 '设置发送缓冲区为512字节
MSComm1.InBufferSize = 512 '设置接收缓冲区为2048字节
MSComm1.OutBufferCount = 0 '清除发送缓冲区
MSComm1.InBufferCount = 0 '清除接收缓冲区
MSComm1.InputMode = comInputModeBinary '数据传输设置为二进制格式
MSComm1.RThreshold = 1 '一次性接收起始码1个字节'可触发On_Comm
MSComm1.Settings = "2400,N,8,1" '设置波特率、校验位(1)、数据位、停止位
MSComm1.PortOpen = True '打开通信口COM1
Text1 = ""
End SubPrivate Sub Command1_Click()
'手动发送键
Dim DatTempBuf(30) As Byte '发送字节长31
Dim OutDatV As Variant
MSComm1.OutBufferCount = 0 '清除发送缓冲区
MSComm1.OutBufferCount = 0 '清除发送缓冲区MSComm1.OutBufferCount = 0
If MSComm1.PortOpen = False Then
MsgBox "串口未打开", 52, "提示"
Exit Sub
End If
DatTempBuf(0) = &HFF
DatTempBuf(1) = &H55
DatTempBuf(2) = &HAA
DatTempBuf(3) = &H4
DatTempBuf(4) = &H0
DatTempBuf(5) = &H3
DatTempBuf(6) = &H7
MSComm1.Output = DatTempBuf
MSComm1.InBufferCount = 0 '清除接收缓冲区
MSComm1.RThreshold = 1 '所要接收的数据长度
End SubPrivate Sub MSComm1_OnComm() '接收数据
Dim strBuff As String
Dim i As Integer
Select Case MSComm1.CommEvent
Case 2
MSComm1.InputLen = 0
strBuff = MSComm1.Input
BytReceived() = strBuff
For i = 0 To UBound(BytReceived) '接收数据处理为16进制
If Len(Hex(BytReceived(i))) = 1 Then
strData = strData & "0" & Hex(BytReceived(i))
Else
strData = strData & Hex(BytReceived(i))
End If
Next
If Len(strData) = 60 Then
Text1 = strData '数据处理
strData = ""
End If
End Select
End Sub
主要看你的单片机是如何与计算机通讯的,而且485为半双工,则计算机要在单片机回码后再发送,故计算机发码要有间隔或者判断,否则会导致数据错误.不清楚你的单片机如何工作的,所以不好给什么建议了.
现在的方式就是一次性读取,问题是前面的数据错误。调试时我是看的变量里的数据,所以应不是重复读取的问题
TO zdingyun:
刚将您的代码试过,效果与之前的相同,感觉不是读取方式的问题,很象是MSComm控件控制485转换器没有控制好,希望能继续提供解决方法,我在这边试试TO Arnold_fly:
我这边单片机也是被动回码的方式,PC发数据,单片机接收到之后,再根据收到的命令,将一些数据回传给PC.
现在测试的时候是PC单击按键发送一次,单片机回传数据一次,这样的情况都出错,必须要在单片机接收完PC数据后,延时100多ms再向PC上传数据才行。
单片机程序现在只有接收中断,和发送函数,发送是用的查询发送标志位来做的。 你的150ms是PC端向下发命令的间隔吧。
你用MSComm控件时,单片机收到数据立即回传可不可以呢?
请问你单片机的通信口是RS485还是RS232?
前贴已说明RS485口通常是单工的,收和发需分开进行.
你经RS232转RS485口接单片机,单片机需接收到完整命令后,并确认无误后才能返回数据,单片机需接收到完整命令的过程本身需时间,约125MS。
我贴的代码中Sleep函数或许能起作用:
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
你可在MSComm1_OnComm事件代码中的
Case 2句后加一句:
Sleep (150)
再试整个代码,看看有改观吗?
采用两个定时器Timer1和Timer2
Private Sub Timer1_timer()
函数A '利用定时器循环调用函数A
.... '根据函数返回的数据进行处理或者显示
End SubPrivate Sub Timer2_timer()
OverTime = true
End SubPrivate Function 函数A(ByRef 返回值)
'发送报警查询命令
FlagIn = False
Timer2.Interval = 150
Do
If OverTime Then
Exit Do
End If
DoEvents
Loop Until FlagIn
Timer2.Interval = 0
If FlagIn = True Then
'对接受到的数据进行校验,若通过则返回正确的数据
End If
End FunctionPrivate Sub MSComm1_OnComm() Select Case Me.MSComm1.CommEvent
Case comEvReceive '发生Oncomm事件
DataInB = MSComm1.Input
'判断帧头,正确则
FlagIn = True
End Sub
用这种方式,比起你的代码少用了一次DOEVENTS,但是当有多个定时器时,这种方式也会出问题,因为必须响应一个ONCOMM事件,这样就必须跳出循环,从而造成控制权移交,难以控制.
如果你的和我的一样,只被动回码,建议用API.
单片机上是485接口
我说的100多ms,是指单片机收到完整的命令后,要发送的时候,之前要另加100ms的延时。现在单片机程序只有接收和发送部分,接收用中断,发送用函数,查询发送标志的方式,发送时是连续的。单片机端由接收切换到发送的代码,放在100ms之前还是之后都对结果无影响。之前也试过,基本排除单片机切换收发所造成的影响。刚试过,加Sleep(150) 甚至200后,现象依旧。
Public FlagIn As Boolean '标识接收到数据了
Public OverTime As Boolean '标识到达间隔时间,可以发送下一条命令
刚试过,效果与之前的相同。你是否也用的232转485的转换器呢?
现在分不清是哪部分的问题,PC串口接485转换器,下位机用单片机,单片机内不用延时,收到完整命令后,直接回传数据即可,用串口调试助手返回的数据是正确的。说明485转换器到单片机部分一般没问题
串口短接后,用MSComm控件也没问题,完全可以立即自发自收,说明控件方面的程序应无问题。
就用控件和485这块配合不好,必须要延时才行,真有些搞不明白。
PC串口上接一个232转485的转换器与单片机通讯你上述的做法,将PC侧的COM口变成单工的RS485标准,是否回导致不能及时转换收发状态.我的浅显理解单片机上是485接口 ,应使用RS485转RS232转换器与PC连接(硬件不太了解).
问题找到,呵呵
向两位反馈一下结果:
原因是上述程序发送的数据我用固定数组做的,所以预留了些位置,数组长31个字节,呵呵。
我在所用的程序中也是类似的情况,我是做了个整理数据的子函数,存放数据的数组也是用的固定数组,长度20,所以造成这个现象。这次调试过程中对有些调试的表现太疏忽,也造成了我描述的不清,这个子函数是我以前做的,能想到这方面也是受了二位的启发,再次谢谢两位了,呵呵 现在还有个现象,单片机回送的数据少第一个字节,而波士电子的资料他们的232485 zhu an sad
刚打不了字,接上:
现在还有个现象,单片机回送的数据少第一个字节,而波士电子的资料说他们的RS232转RS485的转换器是零延时的,
PC机往下发的数据单片机全部能收到。这种现象你们是怎么处理的呢?TO zdingyun:
我原来也是把注意力放在这方面,看现象确实象这方面问题,呵呵,我的PC串口出来是外接一个RS232转RS485的转换器的,成品的那种。单片机端是用的MAX487芯片做转换。
TO Arnold_fly:
刚刷新后才看到您的帖子,呵呵。
PC接收的字节是不定长的,短的不到10个字节,长的300个字节左右。
我现在用的方法是MSComm1.RThreshold = 1,发生事件后,在事件内将MSComm1.RThreshold = 0 ‘,再循环查询的方式。每次读取是全部读取PC缓冲区的内容,再把这些数据按你说的进行判断帧头和效验。 现在看效果还不错,等再仔细调试一下,看看是否有问题。:)
windows xp + vb6.0我要发送的 数据是 0a 03 00 64 00 01 c4 ae'Dim DatTempBuf(30) As Byte '不能接收到正确的数据Dim DatTempBuf(7) As Byte '改为 DatTempBuf(7) 就能接收到正确的数据 请教原因!
代码如下:'手动发送键'Dim DatTempBuf(30) As Byte '不能接收到正确的数据Dim DatTempBuf(7) As Byte '改为 DatTempBuf(7) 能接收到正确的数据 高兴Dim OutDatV As VariantMSComm1.OutBufferCount = 0 '清除发送缓冲区MSComm1.OutBufferCount = 0 '清除发送缓冲区MSComm1.OutBufferCount = 0
If MSComm1.PortOpen = False Then
MsgBox "串口未打开", 52, "提示"
Exit Sub
End If '0A 03 F0 01 00 01 e7 b1 向下位机发送 数据一
DatTempBuf(0) = &HA
DatTempBuf(1) = &H3
DatTempBuf(2) = &HF0
DatTempBuf(3) = &H1
DatTempBuf(4) = &H0
DatTempBuf(5) = &H1
DatTempBuf(6) = &HE7
DatTempBuf(7) = &HB1
Do
DoEvents '转交控制权
Loop Until MSComm1.OutBufferCount = 0 '发送缓冲区清空
MSComm1.InBufferCount = 0 '清除接收缓冲区
MSComm1.RThreshold = 1 '所要接收的数据长度
Sleep (1000)
'不能用上面的代码实现手动发送2个数据包 接收到2个数据包
''''''''''''''''''''''''''''''''''''''''
'0A 03 00 01 00 05 d5 72 向下位机发送 数据二
DatTempBuf(0) = &HA
DatTempBuf(1) = &H3
DatTempBuf(2) = &H0
DatTempBuf(3) = &H1
DatTempBuf(4) = &H0
DatTempBuf(5) = &H5
DatTempBuf(6) = &HD5
DatTempBuf(7) = &H72
MSComm1.Output = DatTempBuf
Do
DoEvents '转交控制权
Loop Until MSComm1.OutBufferCount = 0 '发送缓冲区清空
MSComm1.InBufferCount = 0 '清除接收缓冲区
MSComm1.RThreshold = 1 '所要接收的数据长度''在 Private Sub MSComm1_OnComm() '接收数据
''不能接收到 下位机 对 0A 03 F0 01 00 01 e7 b1 数据一 返回值;只能接收到 '0A 03 00 01 00 05 d5 72 向下位机发送 数据二 用上面的代码实现手动发送2个数据包 接收到2个数据包
在
DatTempBuf(6) = &HE7
DatTempBuf(7) = &HB1
后加
MSComm1.Output = DatTempBuf 能接收到 下位机 对 0A 03 F0 01 00 01 e7 b1 数据一 返回值;能接收到 0A 03 00 01 00 05 d5 72 向下位机发送 数据二 用上面的代码实现手动发送2个数据包 接收到2个数据包