15 LUA 自由串口协议

大彩串口屏支持的协议有以下几类,但同时只能兼容一种:

  • 大彩协议:报文格式EE ... FF FC FF FF。另外,用户可以自定义大彩协议,使用EE B5 ... FF FC FF FF格式,也会触发LUA脚本的串口接收函数:on_uart_recv_data(packet),且packet是完整的一帧数据。
  • MODBUS-RTU:标准MODBUS RTU协议,主机、从机均可以实现
  • 三菱协议FX2N:标准Mitsubishi FX2N协议
  • XGUS:自定义帧头,通过寻址方式实现
  • 自由串口协议:LUA脚本处理,需要对系统变量uart_free_protocol 赋值为1,触发on_uart_recv_data(packet),用户对数据进行解析处理

如果用户设备是第三方,且不支持二次开发,需要屏幕来处理交互报文时,可以通过LUA脚本实现自由串口协议,本文档通过介绍常见的几种报文结构,来说明LUA如何开发自由串口协议。

15.1 说明

LUA脚本开启自由串口协议时(uart_free_protocol = 1),屏幕接收数据会自动触发on_uart_recv_data(packet)函数,需要注意以下点:

  1. packet(数据类型为数组):不一定是完整一帧报文,存在存在分包,且帧和帧之间的也可能存在粘包的情况
  2. 串口不属于中断回调,需要在初始化on_init(),设置超时为0,即uart_set_timeout(0, 0)
  3. 在LUA中需要声明一个全局数组,每次触发LUA接收数据时,将报文的每一个字节存在数组中。通过报文中的标识(帧头、帧尾或固定长度),把报文一帧一帧提取处理

本例程用灯的的开关和亮度控制做说明。并举例说明常见的报文格式的解析,常见的报文格如下所示:

  • 帧头 + ...+ crc16 + 帧尾
  • 帧头 + 数据长度
  • 报文定长

适用范围:M系列、W系列、X系列、F系列(固件版本 >= V4.2.401.0)

例程下载链接:《LUA - 自由串口协议》(点击跳转)

15.2 帧头+...+CRC16校验+帧尾

以帧头:0x5A、帧尾:0xA5 0x5A 0xA5 0xA5为例,且带CRC16校验,屏幕对该帧结构做以下解析示例

基本思路:

  1. 触发on_uart_recv_data(packet),检索是否有帧头0x5A;当检索到帧头且标志cmd_head_tag=0时,将帧头标志位置位cmd_head_tag = 1
  2. 填充缓存buff
  3. 检测到帧尾cmd_head_tag = 0XA55AA5A5,表示接收到完整一帧数据
  4. 调用add_crc16(start, n, data),进行CRC16检验
  5. 检验成功,调用my_processmessage(msg)执行相关操作
  6. 最后清除相关的标记和缓冲,完成一帧的读取,继续获取下一帧数据代码清单如程序清单 1所示:
local cmd_end      = 0xA55AA5A5     --帧尾
local buff          = {}            --缓冲区
local cmd_length  = 0               --帧长度
local cmd_head_tag = 0              --帧头标识
local cmd_end_tag = 0               --帧尾标识

--calculate CRC16
--@data : t, data to be verified
--@n    : number of verified
--@return : check result
function add_crc16(start, n, data)

    local carry_flag, a = 0
    local result = 0xffff
    local i = start

    while(true )
    do
        result = result ~ data[i]
        for j = 0, 7
        do
            a = result
            carry_flag = a & 0x0001
            result = result >> 1
            if carry_flag == 1
            then
                 result = result ~ 0xa001
            end
        end

        i = i + 1
        if i == start + n
        then
            break
        end
    end
    return result
end

--Instruction analysis
--@msg:data table
function my_processmessage(msg)

    local funccode = msg[1]
    if funccode == Func_lampState
    then

        local xth_lamp = msg[2]
        local xth_lamp_state = msg[3]
        my_set_lamp_state(xth_lamp, xth_lamp_state)

    elseif funccode == Func_lampLight
    then
        local xth_lamp = msg[2]
        local xth_lamp_light = msg[3]
        my_set_lamp_light(xth_lamp, xth_lamp_light)
    end
end

-- 系统函数: 初始化
function on_init()
    uart_set_timeout(0, 0)
end

-- 系统函数: 串口接收函数
function on_uart_recv_data(packet)

    local recv_packet_size = (#(packet))
    local check16          = 0

    for i = 0, recv_packet_size 
    do
        if packet[i] == cmd_head and cmd_head_tag == 0
        then
            cmd_head_tag = 1
        end

        if cmd_head_tag == 1
        then
            buff[cmd_length] = packet[i]
            cmd_length = cmd_length + 1
            cmd_end_tag = (cmd_end_tag << 8) | (packet[i])

            if (cmd_end_tag & cmd_end)== cmd_end
            then

                check16 = ((buff[cmd_length - 6] << 8) | 
                              buff[cmd_length - 5]) & 0xFFFF

                if check16 == add_crc16(1, cmd_length - 7, buff)
                then
                    my_processmessage(buff) --提取到完整的数据
                    buff         = {}
                    cmd_length   = 0
                    cmd_end_tag  = 0
                    cmd_head_tag = 0
                else
                    buff         = {}
                    cmd_length   = 0
                    cmd_end_tag  = 0
                    cmd_head_tag = 0
                end
            end
        end
    end
end

15.3 帧头+数据长度

以帧头0x5A + 长度LEN + DATA0...DATAn (LEN 表示DATA的字节数)为例,屏幕对该帧结构做以下解析示例

基本思路:

  1. 触发on_uart_recv_data(packet),检索是否有帧头0x5A;当检索到帧头且标志cmd_head_tag=0时,将帧头标志位置位cmd_head_tag = 1
  2. 填充缓存buff
  3. 记录data的长度data_length = packet[1]
  4. 检测到data_length + 2 = cmd_lenght,表示接收到完整一帧数据
  5. 调用my_processmessage(msg)执行相关操作
  6. 最后清除相关的标记和缓冲,完成一帧的读取,继续获取下一帧数据代码清下所示:
-- 系统函数: 初始化
function on_init()
    uart_set_timeout(0, 0)
end

local cmd_head     = 0x5A           --帧头
local buff         = {}            --缓冲区
local cmd_length   = 0             --帧长度
local data_length  = 0
local cmd_head_tag = 0

--Instruction analysis
--@msg:data table
function my_processmessage(msg)

    local funccode = msg[2]
    print('my_processmessage')

    print('msg len = '..(#(msg)))
    if funccode == Func_lampState
    then

        local xth_lamp = msg[3]
        local xth_lamp_state = msg[4]
        my_set_lamp_state(xth_lamp, xth_lamp_state) -- 设置灯开关

    elseif funccode == Func_lampLight
    then
        local xth_lamp = msg[3]
        local xth_lamp_light = (msg[4] << 8) |  msg[5]
        my_set_lamp_light(xth_lamp, xth_lamp_light) --设置灯亮度
    end
end

-- 系统函数: 串口接收函数
function on_uart_recv_data(packet)

    local recv_packet_size = (#(packet))
    local check16          = 0

    for i = 0, recv_packet_size 
    do
        if packet[i] == cmd_head and cmd_head_tag == 0
        then
            cmd_head_tag = 1
        end

        if cmd_head_tag == 1
        then
            buff[cmd_length] = packet[i]
            cmd_length = cmd_length + 1

            if cmd_length == 2
            then 
                data_length = buff[cmd_length]
            end

            if (data_length + 2) == cmd_length 
            then
                my_processmessage(buff)

                buff         = {}
                cmd_length   = 0
                data_length  = 0
                cmd_head_tag = 0
            end
        end
    end
end

15.4 报文定长

当没有特殊的帧头或帧尾标识,且每一帧的报文都是固定长度的,本章节假设指令报文长度就3个字节,进行解析

基本思路:

  1. 触发on_uart_recv_data(packet),开始填充缓存buff
  2. 检测到cmd_lenght = 3,表示接收到完整一帧数据
  3. 调用my_processmessage(msg)执行相关操作
  4. 最后清除相关的标记和缓冲,完成一帧的读取,继续获取下一帧数据
--Instruction analysis
--@msg:data table
function my_processmessage(msg)

    local funccode = msg[0]

    if funccode == Func_lampState
    then

        local xth_lamp = msg[1]
        local xth_lamp_state = msg[2]
        my_set_lamp_state(xth_lamp, xth_lamp_state)--设置灯状态

    elseif funccode == Func_lampLight
    then
        local xth_lamp = msg[1]
        local xth_lamp_light =  msg[2]
        my_set_lamp_light(xth_lamp, xth_lamp_light)--设置灯亮度
    end
end

-- 系统函数: 初始化
function on_init()
    uart_set_timeout(0, 0)
end

local buff         = {}            --缓冲区
local cmd_length   = 0             --帧长度

-- 系统函数: 串口接收函数
function on_uart_recv_data(packet)

    local recv_packet_size = (#(packet))
    local check16          = 0

    for i = 0, recv_packet_size 
    do
        buff[cmd_length] = packet[i]
        cmd_length = cmd_length + 1

        if cmd_length == 3 
        then
            my_processmessage(buff)

            buff         = {}
            cmd_length   = 0
        end
    end
end

15.5 LUA串口发送

LUA中,串口发送函数为uart_send_data(packet)

形参:packet,下表从0开始,表示发送的字节数组

以程序清单 4为例,用户发送报文时,建议封装一个函数,将变量设置成形参传递,给对应数组元素赋值。

--send notify of lamp status
--@xth_lamp: which lamp
--@state: state of lamp, 0-colse,1-open
function my_uartsend_lampstate_notify(xth_lamp,state)

    local lamp_state_notify = {}
    local send_crc16        = 0

    lamp_state_notify[0]  = 0xA5
    lamp_state_notify[1]  = Func_lampState
    lamp_state_notify[2]  = xth_lamp
    lamp_state_notify[3]  = state

    send_crc16 = add_crc16(1, 3, lamp_state_notify)
    lamp_state_notify[4] = (send_crc16 >> 8) & 0xFF
    lamp_state_notify[5] = (send_crc16 >> 0) & 0xFF

    lamp_state_notify[6] = 0x5A
    lamp_state_notify[7] = 0xA5
    lamp_state_notify[8] = 0x5A
    lamp_state_notify[9] = 0x5A

    uart_send_data(lamp_state_notify)
end
Copyright ©Dacai all right reserved,powered by Gitbook该文件修订时间: 2023-03-30 11:47:01

results matching ""

    No results matching ""