--
RS485
雙線、半雙工,如果使用的是 TTL, UART 這類的訊號轉換器,需要自己控制傳送、接收切換,切換的好不好是關鍵
--
Modbus
RS485 大多使用 Modbus RTU,格式如下
Name | Length (bits) | Function |
---|---|---|
Start | 28 | At least 3½ character times of silence (mark condition) |
Address | 8 | Station address |
Function | 8 | Indicates the function code; e.g., read coils/holding registers |
Data | n × 8 | Data + length will be filled depending on the message type |
CRC | 16 | Cyclic redundancy check |
End | 28 | At least 3½ character times of silence between frames |
--
CRC 參考資源
Modbus 通訊第一個程式門檻就是計算 CRC,這裡有大大寫好的可以直接套用
--
原始範例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
from binascii import * from crcmod import * # CRC16-MODBUS def crc16Add(read): crc16 =crcmod.mkCrcFun(0x18005,rev=True,initCrc=0xFFFF,xorOut=0x0000) data = read.replace(" ","") readcrcout=hex(crc16(unhexlify(data))).upper() str_list = list(readcrcout) if len(str_list) < 6: str_list.insert(2, '0'*(6-len(str_list))) # 位数不足补0 crc_data = "".join(str_list) print(crc_data) read = read.strip()+' '+crc_data[4:]+' '+crc_data[2:4] print('CRC16校验:',crc_data[4:]+' '+crc_data[2:4]) print('增加Modbus CRC16校验:>>>',read) return read if __name__ == '__main__': crc16Add("01 03 08 00 01 00 01 00 01 00 01") crc16Add("ff") |
--
list 陣列版
Python 使用 list 資料型態程式會比較靈活,因此實務上使用都是直接使用 list
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
from crcmod import * def crc16_maxim(read): r = bytes(bytearray(read)) crc16 = crcmod.mkCrcFun(0x18005, rev=True, initCrc=0xFFFF, xorOut=0x0000) crc_hex = hex(crc16(r)) crc = divmod(int(crc_hex, 16), 0x100) return [crc[1], crc[0]] if __name__ == '__main__': r = [1, 3, 0, 0, 0, 2] rs_crc = crc16_maxim(r) full_rs = r + rs_crc print(full_rs) # [1, 3, 0, 0, 0, 2, 196, 11] |
--
完整的 Modbus 範例
RS485 為半雙工通訊,讀取、寫入必須自己根據字元數計算後控制模式,時序控制非常重要,不能使用 .flush(),那是全雙工用的功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
from crcmod import * import gpio import serial import time rs_control = 6 gpio.setup(rs_control, 'out') # TR Pin def crc16_maxim(read): r = bytes(bytearray(read)) crc16 = crcmod.mkCrcFun(0x18005, rev=True, initCrc=0xFFFF, xorOut=0x0000) crc_hex = hex(crc16(r)) crc = divmod(int(crc_hex, 16), 0x100) return [crc[1], crc[0]] if __name__ == '__main__': r = [31, 3, 0, 0, 0, 2] rs_crc = crc16_maxim(r) full_rs = r + rs_crc print(full_rs) # [31, 3, 0, 0, 0, 2, 199, 181] py_serial = serial.Serial("/dev/ttyS1", baudrate=9600, timeout=0.5, writeTimeout=0.5, bytesize=8, parity='N', stopbits=1, rtscts=True, dsrdtr=True) read_count = full_rs[5] * 2 + 5 # 計算接收字數 gpio.set(rs_control, 1) # RS485 切換成 out 送出 py_serial.write(full_rs) # 寫入資料 # py_serial.flush() # flush() 為全雙工使用功能,RS485 使用會有太晚切換掉資料問題 time.sleep(0.001 * (len(full_rs))) # 計算寫入等待時間 字數*鮑率 gpio.set(rs_control, 0) # RS485 切換成 in 接收 read_data = b'' while 1: r = py_serial.read() read_data += r if len(r) == 0: break else: py_serial.timeout = 0.001 * 10 print(list(read_data)) # [31, 3, 4, 0, 25, 0, 25, 20, 63] |
--
3,442 total views, 1 views today