本文目录
- 1. 1. API
- 2. 2. 接收处理的现实:分包与粘包
- 3. 3. 案例:LED 灯控协议
- 4. 4. LUA 实现(FSM 状态机)
把串口协议完全交给 LUA 脚本处理(自定义帧协议),用于对接私有协议设备。这篇是被动模式:设备主动发帧,HMI 接收并应答。
1. API
| 函数 | 说明 |
|---|---|
| uart_setup(ch, baudrate, databits, stopbit, parity) | 串口参数初始化:ch 0=主串口/1=副串口(副串口必须 setup,否则关闭);databits 8;stopbit 1 或 2;parity 0=无/1=奇/2=偶。主串口只用此函数配数据位,波特率在工程属性里设 |
| on_uart_recv(ch, packet) | 串口接收回调:packet 为下标从 1 的字节表。可能分包/粘包,必须自行缓冲解析 |
| uart_send(ch, packet) | 发送字节表 |
| set_free_protocol(en) | 1=交脚本接管主串口,0=恢复工程协议 |

2. 接收处理的现实:分包与粘包
on_uart_recv 收到的不保证是一帧完整协议数据:可能是半帧(分包)、可能多帧拼一起(粘包)。必须自己维护接收缓冲 + 帧解析状态机:帧头识别、长度校验、CRC 验证、数据提取。
另外:回调在系统任务轮询中执行,不是中断,别做耗时操作;DCBUS/XGUS 串口参数固定 1 位停止位、无校验。
3. 案例:LED 灯控协议
帧格式:帧头 1B + 长度 1B + 功能码 2B + 数据 nB + 帧尾 2B。
| 方向 | 帧头 | 帧尾 |
|---|---|---|
| 设备→屏 | 0x5A | 0xFC 0xFF |
| 屏→设备 | 0xA9 | 0xFC 0xFF |
长度字段 = 功能码 2 + 数据 n + 帧尾 2 = n+4,大端。功能码:0x0001 开关、0x0002 亮度、0x0003 改名称。

4. LUA 实现(FSM 状态机)
local rxBuf = {}
function on_init()
uart_setup(0, 0, 8, 1, 0) -- 主串口: 8位数据1停止位无校验
set_free_protocol(1) -- 交脚本接管
end
function on_uart_recv(ch, packet)
for i = 1, #packet do
table.insert(rxBuf, packet[i])
end
parse_rx()
end
function parse_rx()
-- 找帧头 0x5A
while #rxBuf >= 3 do
if rxBuf[1] == 0x5A then
local len = rxBuf[2] -- 长度 = n+4
if #rxBuf >= len + 1 then -- 帧头+长度+len
-- 校验帧尾
if rxBuf[len+1] == 0xFC and rxBuf[len+2] == 0xFF then
local func = rxBuf[3]*256 + rxBuf[4]
handle_cmd(func, rxBuf)
-- 移除已处理帧
for i = 1, len+2 do table.remove(rxBuf, 1) end
else
table.remove(rxBuf, 1) -- 帧尾不对, 丢弃帧头
end
else
break -- 等更多数据
end
else
table.remove(rxBuf, 1) -- 不是帧头, 丢弃
end
end
end
function handle_cmd(func, buf)
if func == 0x0001 then -- 开关
set_uint16(VT_LW, 0x1000, buf[5])
elseif func == 0x0002 then -- 亮度
set_uint16(VT_LW, 0x1001, buf[5])
elseif func == 0x0003 then -- 改名称
-- buf[5] 起是名称字节
end
-- 应答
local resp = {0xA9, 0x04, 0x00, 0x01, 0xFC, 0xFF}
uart_send(0, resp)
end
被动协议最坑的是分包粘包:必须做缓冲 + 状态机解析,别假设一次回调就是一帧。帧头同步用 while 循环逐字节丢弃非帧头数据,长度字段判完整帧。
整理自 广州大彩科技 VisualHMI 开发文档(hmi-doc.gz-dc.com)LUA 教程「自由串口协议(被动)」,版权归大彩科技所有。
发表回复