本文在遵循Modbus協(xié)議的基礎(chǔ)上,闡述了Modbus的兩種傳輸模式和串口通訊程序的設(shè)計(jì)實(shí)例,并給出了VB語言的程序清單。
基于Modbus協(xié)議的串口通訊程序----Modbus協(xié)議簡介
MODBUS協(xié)議支持傳統(tǒng)的RS-232、RS-422、RS-485和以太網(wǎng)設(shè)備。許多工業(yè)設(shè)備,包括PLC,DCS,智能儀表等都在使用Modbus協(xié)議作為他們之間的通訊標(biāo)準(zhǔn)。
Modbus 協(xié)議是應(yīng)用于電子控制器上的一種通用語言。通過此協(xié)議,控制器相互之間、控制器經(jīng)由網(wǎng)絡(luò)(例如以太網(wǎng))和其它設(shè)備之間可以通信。Modbus 協(xié)議定義了一個(gè)控制器能認(rèn)識使用的消息結(jié)構(gòu),而不管它們是經(jīng)過何種網(wǎng)絡(luò)進(jìn)行通信的。它描述了一控制器請求訪問其它設(shè)備的過程,如果回應(yīng)來自其它設(shè)備的請求,以及怎樣偵測錯(cuò)誤并記錄。它制定了消息域格局和內(nèi)容的公共格式。
基于Modbus協(xié)議的串口通訊程序----RS485總線簡介
rs-485采用半雙工工作方式,支持多點(diǎn)數(shù)據(jù)通信。rs-485總線網(wǎng)絡(luò)拓?fù)湟话悴捎媒K端匹配的總線型結(jié)構(gòu)。即采用一條總線將各個(gè)節(jié)點(diǎn)串接起來,不支持環(huán)形或星型網(wǎng)絡(luò)。
rs-485采用平衡發(fā)送和差分接收,因此具有抑制共模干擾的能力。加上總線收發(fā)器具有高靈敏度,能檢測低至200mv的電壓,故傳輸信號能在千米以外得到恢復(fù)。 有些rs-485收發(fā)器修改輸入阻抗以便允許將多達(dá)8倍以上的節(jié)點(diǎn)數(shù)連接到相同總線。rs-485最常見的應(yīng)用是在工業(yè)環(huán)境下可編程邏輯控制器內(nèi)部之間的通信。
串口通訊程序設(shè)計(jì)實(shí)例
為了便于理解,下面列舉一種采用RTU模式通訊的應(yīng)用實(shí)例。這個(gè)實(shí)例的硬件由一臺計(jì)算機(jī)和分布在10個(gè)房間的10塊溫濕度表組成RS485網(wǎng)絡(luò)架構(gòu)。溫濕度表的地址分別設(shè)定為01H至0AH。計(jì)算機(jī)讀各溫濕度表數(shù)據(jù)的命令消息幀包含8個(gè)字節(jié):
被點(diǎn)名的溫濕度表接收到上述命令消息后,向計(jì)算機(jī)發(fā)送溫濕度數(shù)據(jù),該消息幀包含11個(gè)字節(jié):
VB語言設(shè)計(jì)的上述實(shí)例的串口通訊程序清單
Private Declare Function timeGetTime Lib “winmm.dll” () As Long
Public btLoCRC As Byte, btHiCRC As Byte, t0 As Long, t1 As Long, t2 As Long, t3 As Long
Public Rnumber As Integer, ii As Integer, i As Integer, j As Integer, k As Integer, ReadT, Crc
Dim TemperatureData(10), HumidityData(10)
Me.Height = 6660
Rnumber = 10 ‘房間數(shù)量(每個(gè)房間裝1塊溫濕度表)
ReadT = 10 ’每10秒讀一輪溫濕度表數(shù)據(jù)
If MSComm1.PortOpen = True Then MSComm1.PortOpen = False ‘如果串口1是打開狀態(tài)則關(guān)閉它
With MSComm1 ’設(shè)置串口參數(shù)
.CommPort = 1 ‘指定使用串口1
.Settings = “9600,N,8,1” ’波特率9600bit/s,無校驗(yàn),8個(gè)數(shù)據(jù)位,1個(gè)停止位
.InputMode = comInputModeBinary ‘發(fā)送二進(jìn)制數(shù)值(=comInputModeText為發(fā)送字符)
.InputLen = 50 ’從接收緩沖區(qū)中可一次性讀取的數(shù)據(jù)個(gè)數(shù)
.InBufferCount = 0 ‘清空接收緩沖區(qū)
.OutBufferCount = 0 ’清空發(fā)送緩沖區(qū)
.RThreshold = 5 + 2 * 2 ‘設(shè)置成接收9個(gè)字節(jié)就產(chǎn)生OnComm事件
.InBufferSize = 512 ’設(shè)置接收緩存區(qū)容量
.OutBufferSize = 512 ‘設(shè)置發(fā)送緩存區(qū)容量
MSComm1.PortOpen = True ’打開串口1
End With
Timer1.Interval = 100 ‘定時(shí)器1定時(shí)100毫秒
Timer1.Enabled = True ’定時(shí)器1開始計(jì)時(shí)
End Sub
Private Sub Timer1_Timer() ‘定時(shí)發(fā)送(讀數(shù)據(jù)的)命令
Timer1.Enabled = False ’定時(shí)器1停止計(jì)時(shí)
t0 = timeGetTime ‘從系統(tǒng)取得當(dāng)前 (開始讀溫濕度表) 時(shí)刻
Dim tbisend(7) As Byte ’定義發(fā)送數(shù)據(jù)的數(shù)組
If MSComm1.PortOpen = True Then
For k = 1 To Rnumber ‘依次向各個(gè)房間的溫濕度表發(fā)送讀命令
ii = k
tbisend(0) = “&h” + Hex(k) ’被呼叫子機(jī)的地址碼
tbisend(1) = “&h” + Hex(4) ‘4是讀寄存器的功能碼
tbisend(2) = “&h” + Hex(0) ’被讀寄存器的起始地址高字節(jié)
tbisend(3) = “&h” + Hex(0) ‘被讀寄存器的起始地址低字節(jié)
tbisend(4) = “&h” + Hex(0) ’一次讀寄存器的個(gè)數(shù)的高字節(jié)
tbisend(5) = “&h” + Hex(2) ‘一次讀寄存器的個(gè)數(shù)的低字節(jié)
Crc = CRC16(tbisend, 6, btLoCRC, btHiCRC) ’計(jì)算tbisend(0)~tbisend(5)的CRC校驗(yàn)值
tbisend(6) = “&h” + Hex(btLoCRC) ‘CRC低位
tbisend(7) = “&h” + Hex(btHiCRC) ’CRC高位
If MSComm1.PortOpen = False Then MSComm1.PortOpen = True
MSComm1.Output = tbisend ‘發(fā)送數(shù)據(jù)
t1 = timeGetTime
While timeGetTime 《 t1 + 100 ’延時(shí)等待100毫秒,以便有足夠時(shí)間接收從機(jī)發(fā)來的數(shù)據(jù)
DoEvents
Wend
Text1(k - 1).Value = TemperatureData(k) ‘顯示溫度值
Text2(k - 1).Value = HumidityData(k) ’顯示濕度值
Next k
End If
t2 = timeGetTime ‘從系統(tǒng)取得當(dāng)前 (結(jié)束讀溫濕度表) 時(shí)刻
t3 = t2 - t0 ’算出讀溫濕度表的耗時(shí)
Timer1.Interval = ReadT * 1000 - t3 ‘定時(shí)器1定時(shí),如果不減去T3,會(huì)使讀周期變長
Timer1.Enabled = True ’定時(shí)器1開始計(jì)時(shí)
End Sub
Private Sub MSComm1_OnComm() ‘接收數(shù)據(jù)
Dim TemperatureData6 As String, HumidityData6 As String
Dim INByte() As Byte
If MSComm1.CommEvent = comEvReceive Then ’如有接收事件發(fā)生,則響應(yīng)并作計(jì)算
INByte = MSComm1.Input ‘接收數(shù)據(jù)
If INByte(0) = ii And INByte(1) = 4 Then ’如收到的地址碼=被叫從機(jī)地址并且功能碼=讀寄存器,
‘則將收到的CRC碼與收到的數(shù)據(jù)計(jì)算出的CRC碼比較
Crc = CRC16(INByte, UBound(INByte) - LBound(INByte) - 1, btLoCRC, btHiCRC) ’計(jì)算收到數(shù)據(jù)的CRC校驗(yàn)值
If INByte(UBound(INByte) - 1) = btLoCRC And INByte(UBound(INByte)) = btHiCRC Then ‘如校驗(yàn)正確則計(jì)算
TemperatureData6 = Hex(INByte(3)) & Format(Hex(INByte(4)), “00”) ’將溫度轉(zhuǎn)換成十六進(jìn)制
HumidityData6 = Hex(INByte(5)) & Format(Hex(INByte(6)), “00”) ‘將濕度轉(zhuǎn)換成十六進(jìn)制
TemperatureData(ii) = Format(Val(“&H” & TemperatureData6) / 10, “##0.0”) ’將溫度轉(zhuǎn)換為十進(jìn)制
HumidityData(ii) = Format(Val(“&H” & HumidityData6) / 10, “##0.0”) ‘將濕度轉(zhuǎn)換為十進(jìn)制
End If
End If
MSComm1.InBufferCount = 0 ’清接收緩存
End If
End Sub
Function CRC16(Data() As Byte, No As Integer, CRC16Lo As Byte, CRC16Hi As Byte) As String
Dim CL As Byte, CH As Byte, SaveLo As Byte, SaveHi As Byte
CRC16Hi = &HFF ‘為16位CRC校驗(yàn)寄存器賦初始值 FFFFH
CRC16Lo = &HFF
CH = &HA0 ’為16位CRC校驗(yàn)多項(xiàng)式賦初始值 A001H
CL = &H1
For i = 1 To No
CRC16Lo = CRC16Lo Xor Data(i - 1) ‘將被校驗(yàn)的每個(gè)字節(jié)數(shù)據(jù)依次與CRC校驗(yàn)寄存器進(jìn)行異或
For j = 1 To 8 ’8次移位
SaveHi = CRC16Hi
SaveLo = CRC16Lo
CRC16Hi = CRC16Hi \ 2 ‘高位右移一位
CRC16Lo = CRC16Lo \ 2 ’低位右移一位
If ((SaveHi And &H1) = &H1) Then ‘如果高位字節(jié)最右一位為1,則低位字節(jié)最左位補(bǔ)1,否則補(bǔ)0
CRC16Lo = CRC16Lo Or &H80
End If
If ((SaveLo And &H1) = &H1) Then ’如低位字節(jié)最右一位為1,則與多項(xiàng)式值異或
CRC16Hi = CRC16Hi Xor CH
CRC16Lo = CRC16Lo Xor CL
End If
Next j
Next i
End Function
評論