사용자 정의 프로토콜(被动):VisualHMI 内置 DCBUS、XGUS、Modbus(RTU/TCP 主从)、三菱 FX2N、台达 DVP、西门子 PPI 等프로토콜,但实际现场总有私有프로토콜要接。走 LUA 对시리얼 포트做底层控制,实现自定义帧的收发분석。本篇是被动모드:收到데이터触发回调,你只管분석。
1. API 总表
| 函数 | 설명 |
|---|---|
uart_setup(ch, baudrate, 데이터bits, stopbit, parity) | 설정시리얼 포트:stopbit 0=1位/1=1.5位;parity 0=无/1=奇/2=偶。副시리얼 포트必须调用才활성화,主시리얼 포트(ch=0)仅配데이터 |
on_uart_recv(ch, packet) | 被动수신回调,packet 为下标从 1 的바이트表。可能分包/粘包,必须自己缓冲분석 |
uart_send(ch, packet) | 보내기바이트表 |
set_free_protocol(en) | 1=主시리얼 포트交스크립트接管(自由프로토콜);0=恢复엔지니어링설정的原始프로토콜 |
主시리얼 포트(ch=0)触发 on_uart_recv 的条件:엔지니어링설정프로토콜设「自定义」,或스크립트里 set_free_protocol(1)。主시리얼 포트被 Modbus 等内置프로토콜占用时,on_uart_recv 不会被调用。DCBUS/XGUS 시리얼 포트 매개변수固定:1 位정지 비트、패리티 없음。
2. 프로토콜举例:LED 灯控
帧结构:
| 方向 | 帧头(1B) | 길이(1B) | 기능 코드(2B) | 데이터(nB) | 帧尾(2B) |
|---|---|---|---|---|---|
| 设备→屏幕 | 0x5A | 기능 코드到帧尾的바이트数 | 0x0001~0xFFFF | 可变 | 0xFC 0xFF |
| 屏幕→设备 | 0xA9 | 同上 | 同上 | 可变 | 0xFC 0xFF |
길이 = 기능 코드 2B + 데이터 nB + 帧尾 2B = n+4,大端。기능 코드:0x0001=开关、0x0002=亮度、0x0003=改名称。
3. 状态机분석(处理分包/粘包)
recvBuf = {} -- 수신缓冲
STATE_IDLE, STATE_LENGTH, STATE_데이터 = 0, 1, 2
state = STATE_IDLE
frameLen = 0
function on_uart_recv(ch, packet)
for i = 1, #packet do
local b = packet[i]
if state == STATE_IDLE then
if b == 0x5A then
state = STATE_LENGTH
recvBuf = {}
end
elseif state == STATE_LENGTH then
frameLen = b -- 第2바이트:길이
state = STATE_데이터
recvBuf = {b}
elseif state == STATE_데이터 then
table.insert(recvBuf, b)
if #recvBuf >= frameLen then
-- 收满一帧,분석
parse_frame(recvBuf)
state = STATE_IDLE
end
end
end
end
function parse_frame(buf)
local func = (buf[2] <> 8) & 0xFF, func & 0xFF}
for i = 1, #payload do table.insert(pkt, payload[i]) end
table.insert(pkt, 0xFC); table.insert(pkt, 0xFF)
uart_send(0, pkt)
end回调是系统任务轮询触发,不是硬件中断——别在回调里做耗时操作(문자串大拼接、文件读写),会拖慢系统。
四个坑:①副시리얼 포트不调 uart_setup 就是닫기的,on_init 里初始化所有要用的副시리얼 포트;②packet 不保证是一整帧——分包粘包必须自己缓冲状态机분석,这是被动프로토콜 90% 的 bug 来源;③主시리얼 포트被内置프로토콜占用时收不到回调,要么엔지니어링里设「自定义」要么 set_free_protocol(1);④길이필드算清楚(本例=기능 코드2+데이터n+帧尾2),算错一帧都解不出来。
整理自 广州大彩科技 VisualHMI 개발문서(hmi-doc.gz-dc.com)LUA 튜토리얼「사용자 정의 프로토콜-被动수신」,版权归大彩科技所有。
답글 작성