本文目录
- 1. 1. API 总表
- 2. 2. 协议举例:LED 灯控
- 3. 3. 状态机解析(处理分包/粘包)
自定义协议(被动):VisualHMI 内置 DCBUS、XGUS、Modbus(RTU/TCP 主从)、三菱 FX2N、台达 DVP、西门子 PPI 等协议,但实际现场总有私有协议要接。走 LUA 对串口做底层控制,实现自定义帧的收发解析。本篇是被动模式:收到数据触发回调,你只管解析。
1. API 总表
| 函数 | 说明 |
|---|---|
uart_setup(ch, baudrate, databits, 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_DATA = 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_DATA
recvBuf = {b}
elseif state == STATE_DATA 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 教程「自定义协议-被动接收」,版权归大彩科技所有。
发表回复