跳转至

can - can操作库

作者:王棚嶙

一、概述

CAN(Controller Area Network)是一种被广泛应用于汽车和工业控制领域的串行通信协议。它支持多主节点通信,具有高可靠性、实时性以及错误检测能力。LuatOS支持CAN 2.0A/B标准,允许最高达1Mbps的通信速率。

1.1 CAN协议简介

  • 支持协议:CAN 2.0A/B标准,兼容标准帧(11位ID)和扩展帧(29位ID)
  • 通信速率:最高支持1Mbps
  • 物理层依赖:需外接CAN收发器(如川土微的CA-IF1051S/VS)
  • 错误检测:具备完善的错误检测和处理机制

1.2 硬件接口特性

  • GPIO映射
  • CAN_TXD(发送):默认映射到GPIO26
  • CAN_RXD(接收):默认映射到GPIO25
  • CAN_STB(待机模式控制):默认映射到GPIO28

1.3 错误码说明

错误码由4byte组成小端格式的uint32:

--[[
错误码格式:
byte3:预留无意义
byte2:方向,0-TX 1-RX  
byte1:类型,0-bit 1-form 2-stuff
byte0:位置:
0x03 - start of frame
0x02 - extended: ID bits 28-21, standard: 10-3
0x06 - extended: ID bits 20-18, standard: 2-0
0x04 - extended: substitute RTR, standard: RTR
0x05 - identifier extension
0x07 - extended: ID bits 17-13
0x0f - extended: ID bits 12-5
0x0e - extended: ID bits 4-0
0x0C - RTR
0x0D - reserved bit 1
0x09 - reserved bit 0
0x0b - data length code
0x0a - data section
0x08 - CRC sequence
0x18 - CRC delimiter
0x19 - ACK slot
0x1b - ACK delimiter
0x1a - end of frame
0x12 - intermission
0x00 - unspecified
]]

二、核心示例

1、核心示例是指:使用本库文件提供的核心 API,开发的基础业务逻辑的演示代码;

2、核心示例的作用是:帮助开发者快速理解如何使用本库,所以核心示例的逻辑都比较简单;

3、更加完整和详细的 demo,请参考 LuatOS 仓库 中各个产品目录下的 demo/can;

-- 基础发送接收示例
-- CAN总线初始化
local result = can.init(0, 128)
if not result then
    log.error("can", "初始化失败")
    return
end

-- 配置时序(500Kbps)
result = can.timing(0, 500000, 6, 6, 3, 2)
if not result then
    log.error("can", "时序配置失败")
    return
end

-- 设置工作模式
result = can.mode(0, can.MODE_NORMAL)
if not result then
    log.error("can", "模式设置失败")
    return
end

-- 设置接收过滤(接收ID为0x123的标准帧)
result = can.node(0, 0x123, can.STD)
if not result then
    log.error("can", "节点设置失败")
    return
end

-- 注册事件回调
can.on(0, function(id, type, param)
    if type == can.CB_MSG then
        log.info("can", "收到新消息")
        -- 读取消息
        local succ, msg_id, msg_type, rtr, data = can.rx(0)
        if succ then
            log.info("can", "消息ID:", string.format("0x%X", msg_id))
            log.info("can", "消息类型:", msg_type == can.EXT and "扩展帧" or "标准帧")
            log.info("can", "数据:", data:toHex())
        end
    elseif type == can.CB_TX then
        if param == 0 then
            log.info("can", "发送成功")
        else
            log.error("can", "发送失败", param)
        end
    elseif type == can.CB_ERR then
        log.error("can", "总线错误", param)
    end
end)

-- 发送测试消息
sys.timerLoopStart(function()
    local data = "\x00\x01\x02\x03\x04\x05\x06\x07"
    local result = can.tx(0, 0x123, can.STD, false, true, data)
    if result == 0 then
        log.info("can", "发送请求成功")
    else
        log.error("can", "发送请求失败", result)
    end
end, 1000)
-- 多节点通信示例
-- 节点1:发送节点
local result = can.init(0, 256)
if result then
    can.timing(0, 250000, 6, 6, 4, 2)
    can.mode(0, can.MODE_NORMAL)
end

-- 节点2:接收节点(监听模式)
result = can.init(1, 256)
if result then
    can.timing(1, 250000, 6, 6, 4, 2)
    can.mode(1, can.MODE_LISTEN)  -- 监听模式,只接收不发送
end

-- 节点1发送配置
can.node(0, 0x200, can.STD)  -- 发送ID为0x200的消息

-- 节点2接收配置
can.node(1, 0x200, can.STD)  -- 接收ID为0x200的消息

-- 节点2的事件处理
can.on(1, function(id, type, param)
    if type == can.CB_MSG then
        local succ, msg_id, msg_type, rtr, data = can.rx(1)
        if succ and msg_id == 0x200 then
            log.info("节点2", "收到节点1的消息", data:toHex())
        end
    end
end)

-- 节点1定时发送
sys.timerLoopStart(function()
    can.tx(0, 0x200, can.STD, false, true, "HelloCAN")
end, 500)
-- 错误处理和状态监控
-- 初始化CAN
local result = can.init(0, 128)
if result then
    can.timing(0, 125000, 6, 6, 4, 2)
    can.mode(0, can.MODE_NORMAL)
end

-- 状态监控定时器
sys.timerLoopStart(function()
    local state = can.state(0)
    local state_name = "未知"
    if state == can.STATE_STOP then
        state_name = "停止"
    elseif state == can.STATE_ACTIVE then
        state_name = "正常"
    elseif state == can.STATE_PASSIVE then
        state_name = "被动错误"
        log.warn("CAN", "进入被动错误状态")
    elseif state == can.STATE_BUSOFF then
        state_name = "总线离线"
        log.error("CAN", "总线离线,尝试恢复")
        can.reset(0)  -- 尝试恢复
    end
    log.info("CAN状态", state_name)
end, 1000)

-- 错误事件处理
can.on(0, function(id, type, param)
    if type == can.CB_ERR then
        -- 解析错误码
        local direction = (param >> 16) & 0xFF
        local error_type = (param >> 8) & 0xFF
        local position = param & 0xFF
        log.error("CAN错误",
            "方向:", direction == 0 and "TX" or "RX",
            "类型:", error_type,
            "位置:", string.format("0x%02X", position))
    elseif type == can.CB_STATE then
        log.info("CAN", "状态变更", param)
    end
end)
-- 时钟特性分析和波特率配置
-- 获取时钟特性
local result, clk, div_min, div_max, div_step = can.capacity(0)
if result then
    log.info("can.capacity", "基础时钟:", clk, "Hz")
    log.info("can.capacity", "分频范围:", div_min, "-", div_max, "步进:", div_step)
    -- 常用波特率验证
    local baud_rates = {125000, 250000, 500000, 1000000}
    for _, baud in ipairs(baud_rates) do
        -- 计算所需分频系数(假设标准时序参数)
        local div = clk / baud / (1 + 6 + 6 + 3)
        if div >= div_min and div <= div_max then
            log.info("can.capacity", "波特率", baud, "可行,分频系数", div)
        else
            log.warn("can.capacity", "波特率", baud, "不可行,需要分频系数", div)
        end
    end
end

-- 配置验证过的波特率
local target_baud = 500000
result = can.timing(0, target_baud, 6, 6, 3, 2)
if result then
    log.info("can.timing", "波特率配置成功")
else
    log.error("can.timing", "波特率配置失败")
end

三、常量详解

核心库常量,顾名思义是由合宙 LuatOS 内核固件中定义的、不可重新赋值或修改的固定值,在脚本代码中不需要声明,可直接调用。

3.1 CAN工作模式常量

3.1.1 can.MODE_NORMAL(正常工作模式)

常量含义:CAN控制器正常工作模式,可以正常发送和接收消息;
数据类型:number
取值范围:暂无;
注意事项:1. 默认工作模式;
         2. 适用于正常的CAN通信场景
示例代码:-- 设置CAN为正常工作模式
         can.mode(0, can.MODE_NORMAL)

3.1.2 can.MODE_LISTEN(监听模式)

常量含义:CAN控制器监听模式,仅接收消息,不发送消息;
数据类型:number
取值范围:暂无;
注意事项:1. 可用于总线监控;
         2. 不会发送任何消息,包括ACK
示例代码:-- 设置CAN为监听模式
         can.mode(0, can.MODE_LISTEN)

3.1.3 can.MODE_TEST(自测模式)

常量含义:CAN控制器自测模式,自收自发,用于硬件自检;
数据类型:number
取值范围:暂无;
注意事项:1. 发送的消息会被自己接收;
         2. 不会发送到总线上;
示例代码:-- 设置CAN为自测模式
         can.mode(0, can.MODE_TEST)

3.1.4 can.MODE_SLEEP(休眠模式)

常量含义:CAN控制器休眠模式,降低功耗;
数据类型:number
取值范围:暂无;
注意事项:1. 总线活动可唤醒;
         2. 唤醒后需要重新配置;
示例代码:-- 设置CAN为休眠模式
         can.mode(0, can.MODE_SLEEP)

3.2 CAN状态常量

3.2.1 can.STATE_STOP(停止状态)

常量含义:CAN控制器停止工作状态
数据类型:number
取值范围:暂无;
注意事项:1. 控制器未工作;
         2. 需要初始化后才能进入其他状态;
示例代码:
local state = can.state(0)
if state == can.STATE_STOP then
    log.info("CAN", "控制器停止")
end

3.2.2 can.STATE_ACTIVE(主动错误状态)

常量含义:CAN控制器主动错误状态,正常通信状态;
数据类型:number
取值范围:暂无;
注意事项:1. 正常通信时的状态;
         2. 错误计数器值较低;
示例代码:
local state = can.state(0)
if state == can.STATE_ACTIVE then
    log.info("CAN", "总线正常")
end

3.2.3 can.STATE_PASSIVE(被动错误状态)

常量含义:CAN控制器被动错误状态,错误计数器值较高;
数据类型:number
取值范围:暂无;
注意事项:1. 总线错误较多时进入;
         2. 仍可正常收发消息;
示例代码:local state = can.state(0)
         if state == can.STATE_PASSIVE then
            log.warn("CAN", "被动错误状态")
          end

3.2.4 can.STATE_BUSOFF(总线关闭状态)

常量含义:CAN控制器总线关闭状态,错误过多导致离线;
数据类型:number
取值范围:暂无;
注意事项:1. 错误计数器溢出时进入;
         2. 不能收发消息,需要手动恢复;
示例代码:local state = can.state(0)
         if state == can.STATE_BUSOFF then
             log.error("CAN", "总线离线")
             -- 尝试恢复
             can.reset(0)  
          end

3.2.5 can.STATE_LISTEN(监听状态)

常量含义:CAN控制器监听状态,选择监听模式时进入这个状态;
数据类型:number
取值范围:暂无;
注意事项:1. 仅在使用监听模式时出现;
         2. 表示控制器处于监听模式;
示例代码:local state = can.state(0)
         if state == can.STATE_LISTEN then
             log.info("CAN", "监听模式状态")
         end

3.2.6 can.STATE_TEST(自收自发状态)

常量含义:CAN控制器自收自发状态,选择自测模式时进入这个状态;
数据类型:number
取值范围:暂无;
注意事项:1. 仅在使用自测模式时出现;
         2. 表示控制器处于自测模式;
示例代码:local state = can.state(0)
         if state == can.STATE_TEST then
             log.info("CAN", "自测模式状态")
         end

3.2.7 can.STATE_SLEEP(休眠状态)

常量含义:CAN控制器休眠状态,选择休眠模式时进入这个状态;
数据类型:number
取值范围:暂无;
注意事项:1. 仅在使用休眠模式时出现;
         2. 表示控制器处于休眠模式;
示例代码:local state = can.state(0)
         if state == can.STATE_SLEEP then
             log.info("CAN", "休眠模式状态")
         end

3.3 回调消息类型常量

3.3.1 can.CB_MSG(新消息回调)

常量含义:回调消息类型,有新数据写入接收缓存;
数据类型:number
取值范围:暂无;
注意事项:1. 表示收到新消息;
         2. 需要调用can.rx()读取;
示例代码:can.on(0, function(id, type, param)
             if type == can.CB_MSG then
             log.info("CAN", "收到新消息")
             -- 处理消息
              local succ, msg_id, msg_type, rtr, data = can.rx(0)    
             end
         end)

3.3.2 can.CB_TX(发送完成回调)

常量含义:回调消息类型,数据发送结束;
数据类型:number
取值范围:暂无;
注意事项:1. param=0表示发送成功;
         2. param0表示发送失败;
示例代码:
can.on(0, function(id, type, param)
    if type == can.CB_TX then
        if param == 0 then
            log.info("CAN", "发送成功")
        else
            log.error("CAN", "发送失败", param)
        end
    end
end)

3.3.3 can.CB_ERR(错误报告回调)

常量含义:回调消息类型,有错误报告;
数据类型:number
取值范围:暂无;
注意事项:1. param是错误码
         2. 参考错误码说明解析;
示例代码:
can.on(0, function(id, type, param)
    if type == can.CB_ERR then
        log.error("CAN", "总线错误", param)
        -- 解析错误码
        local direction = (param >> 16) & 0xFF
        local error_type = (param >> 8) & 0xFF
        local position = param & 0xFF
    end
end)

3.3.4 can.CB_STATE(状态变更回调)

常量含义:回调消息类型,总线状态变更;
数据类型:number
取值范围:暂无;
注意事项:1. param是新的状态值2. 也可用can.state()读出;
示例代码:
can.on(0, function(id, type, param)
    if type == can.CB_STATE then
        log.info("CAN", "状态变更", param)
        -- 根据新状态处理
        if param == can.STATE_BUSOFF then
            log.error("CAN", "进入离线状态")
        end
    end
end)

3.4 帧格式常量

3.4.1 can.EXT(扩展帧)

常量含义:扩展帧格式,29ID
数据类型:number
取值范围:暂无;
注意事项:1. ID范围为0~0x1FFFFFFF
         2. 优先级低于标准帧;
示例代码:-- 发送扩展帧
         can.tx(0, 0x12345678, can.EXT, false, true, "data")

3.4.2 can.STD(标准帧)

常量含义:标准帧格式,11ID
数据类型:number
取值范围:暂无;
注意事项:1. ID范围为0~0x7FF
         2. 优先级高于扩展帧;
示例代码:-- 发送标准帧
         can.tx(0, 0x123, can.STD, false, true, "data")

四、API函数详解

4.1 can.init(id, rx_message_cache_max)

功能

CAN总线初始化

注意事项

1、初始化前确保硬件连接正确

2、接收缓存大小影响性能,建议根据实际需求设置

3、初始化失败时需要检查硬件连接和参数配置

参数

id

参数含义:CAN总线ID
数据类型:number;
取值范围:0~n0表示can01表示can1,以此类推);
是否必选:是;
注意事项:一般情况只有1条总线,填0即可;
参数示例:0

rx_message_cache_max

参数含义:接收消息缓存的最大数量;
数据类型:number;
取值范围:≥0的整数,0表示使用平台默认值;
是否必选:否;
注意事项:缓存越大,可存储的消息越多,但占用更多内存;
参数示例:128

返回值

local result = can.init(id, rx_message_cache_max)

有一个返回值

result

含义说明:初始化是否成功;
数据类型:boolean
取值范围:true-成功,false-失败;
注意事项:失败后需要检查硬件连接和参数;
返回示例:true

示例

-- 基本初始化
local result = can.init(0)
if result then
    log.info("can.init", "初始化成功")
else
    log.error("can.init", "初始化失败")
end

-- 带缓存配置的初始化
local result = can.init(0, 256)
if result then
    log.info("can.init", "初始化成功,缓存256条消息")
end

4.2 can.on(id, func)

功能

注册CAN事件回调

注意事项

1、回调函数在事件发生时异步执行

2、需要处理所有类型的回调事件

3、回调函数执行时间不宜过长

参数

id

参数含义:CAN总线序号
数据类型:number
取值范围:0~n
是否必选:是;
注意事项:与can.init中的ID对应
参数示例:0

func

参数含义:事件处理函数(回调函数);
数据类型:function
取值范围:有效的Lua函数
是否必选:是;
注意事项:需要处理所有可能的回调类型;
参数示例:function(id, type, param) ... end

返回值

无返回值

示例

-- 注册事件回调
can.on(0, function(id, type, param)
    log.info("can.on", "收到事件", id, type, param)
    if type == can.CB_MSG then
        log.info("can.on", "新消息到达")
        -- 读取消息
        local succ, msg_id, msg_type, rtr, data = can.rx(id)
        if succ then
            log.info("can.on", "消息ID:", string.format("0x%X", msg_id))
            log.info("can.on", "数据:", data:toHex())
        end
    elseif type == can.CB_TX then
        if param == 0 then
            log.info("can.on", "发送成功")
        else
            log.error("can.on", "发送失败", param)
        end
    elseif type == can.CB_ERR then
        log.error("can.on", "总线错误", param)
    elseif type == can.CB_STATE then
        log.info("can.on", "状态变更", param)
    end
end)

4.3 can.timing(id, br, PTS, PBS1, PBS2, SJW)

功能

CAN总线配置时序;

注意事项

1、波特率计算公式:实际波特率 = 基础时钟 / 分频系数 / (1 + PTS + PBS1 + PBS2);

2、不同平台的基础时钟可能不同,参考can.capacity()获取;

3、时序参数影响通信的稳定性和抗干扰能力;

参数

id

参数含义:CAN总线序号
数据类型:number
取值范围:0~n
是否必选:是;
参数示例:0

br

参数含义:CAN总线通信波特率
数据类型:number
取值范围:通常为10000~100000010Kbps~1Mbps);
是否必选:是;
注意事项:默认1Mbps,需要根据网络要求设置;
参数示例:500000

PTS

参数含义:传播时间段长度;
数据类型:number
取值范围:1~8
是否必选:是;
注意事项:影响信号传播延迟补偿;
参数示例:6

PBS1

参数含义:相位缓冲段1长度;
数据类型:number
取值范围:1~8
是否必选:是;
注意事项:影响采样点位置;
参数示例:6

PBS2

参数含义:相位缓冲段2长度;
数据类型:number
取值范围:2~8
是否必选:是;
注意事项:需要大于PBS1
参数示例:3

SJW

参数含义:同步跳转宽度;
数据类型:number
取值范围:1~4
是否必选:否(默认为2);
注意事项:影响时钟同步能力;
参数示例:2

返回值

local result = can.timing(id, br, PTS, PBS1, PBS2, SJW)

有一个返回值

result

含义说明:时序配置是否成功;
数据类型:boolean
取值范围:true-成功,false-失败;
注意事项:暂无;
返回示例:true

示例

-- 配置500Kbps波特率
local result = can.timing(0, 500000, 6, 6, 3, 2)
if result then
    log.info("can.timing", "时序配置成功")
else
    log.error("can.timing", "时序配置失败")
end

-- 常用波特率配置参考
can.timing(0, 125000, 6, 6, 4, 2)  -- 125Kbps
can.timing(0, 250000, 6, 6, 4, 2)  -- 250Kbps
can.timing(0, 500000, 6, 6, 3, 2)  -- 500Kbps
can.timing(0, 1000000, 6, 6, 4, 2) -- 1Mbps

4.4 can.mode(id, mode)

功能

CAN总线设置工作模式

注意事项

1、工作模式影响CAN控制器的行为

2、监听模式可用于总线监控

3、自测模式用于硬件自检

参数

id

参数含义:CAN总线序号
数据类型:number
取值范围:0~n
是否必选:是;
参数示例:0

mode

参数含义:CAN控制器工作模式
数据类型:number
取值范围:can.MODE_NORMAL, can.MODE_LISTEN, can.MODE_TEST, can.MODE_SLEEP
是否必选:是;
注意事项:默认为MODE_NORMAL
参数示例:can.MODE_NORMAL

返回值

local result = can.mode(id, mode)

有一个返回值

result

含义说明:模式设置是否成功;
数据类型:boolean
取值范围:true-成功,false-失败;
注意事项:检查模式常量是否正确;
返回示例:true

示例

-- 设置为正常工作模式
local result = can.mode(0, can.MODE_NORMAL)
if result then
    log.info("can.mode", "模式设置成功")
else
    log.error("can.mode", "模式设置失败")
end

-- 设置为监听模式
can.mode(0, can.MODE_LISTEN)

-- 设置为自测模式
can.mode(0, can.MODE_TEST)

-- 设置为休眠模式
can.mode(0, can.MODE_SLEEP)

4.5 can.node(id, node_id, id_type)

功能

CAN总线设置节点ID,这是一种简易的过滤规则,只接收和ID完全匹配的消息

注意事项

1、与can.filter函数二选一使用

2、ID值越小,优先级越高

3、标准帧和扩展帧的ID范围不同

参数

id

参数含义:CAN总线序号
数据类型:number
取值范围:0~n
是否必选:是;
参数示例:0

node_id

参数含义:要接收的消息ID
数据类型:number
取值范围:标准帧0~0x7FF,扩展帧0~0x1FFFFFFF
是否必选:是;
注意事项:ID值越小,优先级越高,默认0x1FFFFFFF
参数示例:0x123

id_type

参数含义:帧类型(标准帧或扩展帧);
数据类型:number
取值范围:0/can.STD-标准帧,1/can.EXT-扩展帧;
是否必选:是;
参数示例:can.STD

返回值

local result = can.node(id, node_id, id_type)

有一个返回值

result

含义说明:节点设置是否成功;
数据类型:boolean
取值范围:true-成功,false-失败;
注意事项:检查ID范围和类型
返回示例:true

示例

-- 设置标准帧节点,接收ID为0x123的消息
local result = can.node(0, 0x123, can.STD)
if result then
    log.info("can.node", "节点设置成功")
else
    log.error("can.node", "节点设置失败")
end

-- 设置扩展帧节点
can.node(0, 0x12345678, can.EXT)

-- 设置高优先级节点(ID值小)
can.node(0, 0x100, can.STD)

4.6 can.filter(id, dual_mode, ACR, AMR)

功能

CAN总线设置接收过滤模式,当can.node不满足需求时才使用这个

注意事项

1、与can.node函数二选一使用

2、过滤模式比较复杂,参考SJA1000的Pelican模式下滤波

3、双过滤模式提供更灵活的过滤规则

参数

id

参数含义:CAN总线序号
数据类型:number
取值范围:0~n
是否必选:是;
参数示例:0

dual_mode

参数含义:是否使用双过滤模式;
数据类型:boolean
取值范围:true-双过滤,false-单过滤;
是否必选:是;
参数示例:false

ACR

参数含义:4字节接收代码寄存器;
数据类型:number
取值范围:0x00000000~0xFFFFFFFF
是否必选:是;
注意事项:大端格式,必须写0xnnnnnnnn格式
参数示例:0x12345678

AMR

参数含义:4字节接收屏蔽寄存器;
数据类型:number
取值范围:0x00000000~0xFFFFFFFF
是否必选:是;
注意事项:对应bit写0表示需要检测,写1表示不检测,0xFFFFFFFF表示接收全部;
参数示例:0x07

返回值

local result = can.filter(id, dual_mode, ACR, AMR)

有一个返回值

result

含义说明:过滤设置是否成功;
数据类型:boolean
取值范围:true-成功,false-失败;
注意事项:检查寄存器值格式;
返回示例:true

示例

-- 单过滤模式,等效于can.node(0, 0x12345678, can.EXT)
local result = can.filter(0, false, 0x12345678 << 3, 0x07)
if result then
    log.info("can.filter", "过滤设置成功")
else
    log.error("can.filter", "过滤设置失败")
end

-- 单过滤模式,等效于can.node(0, 0x123, can.STD)
can.filter(0, false, 0x123 << 21, 0x0001fffff)

-- 接收所有消息
can.filter(0, false, 0, 0xFFFFFFFF)

4.7 can.state(id)

功能

获取CAN总线当前状态

注意事项

1、状态反映总线的健康状况

2、需要根据状态进行相应的处理

3、离线状态需要手动恢复

参数

id

参数含义:CAN总线序号
数据类型:number
取值范围:0~n
是否必选:是
参数示例:0

返回值

local state = can.state(id)

state

含义说明:当前CAN总线状态
数据类型:number
取值范围:can.STATE_STOP, can.STATE_ACTIVE, can.STATE_PASSIVE, can.STATE_BUSOFF等
注意事项:需要根据状态进行相应处理;
返回示例:can.STATE_STOP

示例

-- 获取总线状态
local state = can.state(0)
log.info("can.state", "当前状态", state)

-- 根据状态处理
if state == can.STATE_ACTIVE then
    log.info("can.state", "总线正常")
elseif state == can.STATE_PASSIVE then
    log.warn("can.state", "被动错误状态")
elseif state == can.STATE_BUSOFF then
    log.error("can.state", "总线离线")
    -- 需要手动恢复
    can.reset(0)
end

4.8 can.tx(id, msg_id, id_type, RTR, need_ack, data)

功能

发送CAN消息

注意事项

1、数据长度最大8字节(CAN协议限制)

2、标准帧ID范围0-0x7FF,扩展帧ID范围0-0x1FFFFFFF

3、发送结果通过回调函数通知

参数

id

参数含义:CAN总线序号
数据类型:number
取值范围:0~n
是否必选:是;
参数示例:0

msg_id

参数含义:要发送的消息ID
数据类型:number
取值范围:标准帧0~0x7FF,扩展帧0~0x1FFFFFFF
是否必选:是;
注意事项:ID值越小,优先级越高,默认0x1FFFFFFF
参数示例:0x123

id_type

参数含义:帧类型(标准帧或扩展帧);
数据类型:number
取值范围:0/can.STD-标准帧,1/can.EXT-扩展帧;
是否必选:是;
参数示例:can.STD

RTR

参数含义:是否是遥控帧;
数据类型:boolean
取值范围:true-遥控帧,false-数据帧;
是否必选:否(默认为false);
参数示例:false

need_ack

参数含义:是否需要应答;
数据类型:boolean
取值范围:true-需要应答,false-不需要应答;
是否必选:否(默认为true);
参数示例:true

data

参数含义:要发送的数据;
数据类型:string/zbuff
取值范围:最大8字节;
是否必选:是;
注意事项:超过8字节会被截断;
参数示例:"\x00\x01\x02\x03\x04\x05\x06\x07"

返回值

local result = can.tx(id, msg_id, id_type, RTR, need_ack, data)

result

含义说明:发送操作结果;
数据类型:number
取值范围:0-成功,其他值-错误码详情请参考上方错误码;
注意事项:实际发送结果通过回调函数通知;
返回示例:0

示例

-- 发送标准帧数据
local result = can.tx(0, 0x123, can.STD, false, true, "Hello")
if result == 0 then
    log.info("can.tx", "发送请求成功")
else
    log.error("can.tx", "发送请求失败", result)
end

-- 发送扩展帧数据
can.tx(0, 0x12345678, can.EXT, false, true, "TestData")

-- 发送遥控帧
can.tx(0, 0x123, can.STD, true, true, "")

4.9 can.rx(id)

功能

接收CAN消息

注意事项

1、需要在回调函数中调用

2、缓存为空时返回失败

3、数据为string类型,可用zbuff处理

参数

id

参数含义:CAN总线序号
数据类型:number
取值范围:0~n
是否必选:是;
参数示例:0

返回值

local succ, msg_id, msg_type, rtr, data = can.rx(id)

有5个返回值

succ

含义说明:是否成功接收到消息;
数据类型:boolean
取值范围:true-成功,false-失败;
注意事项:失败表示缓存为空;
返回示例:true

msg_id

含义说明:接收到的消息ID
数据类型:number
取值范围:标准帧0~0x7FF,扩展帧0~0x1FFFFFFF
注意事项:仅在成功时有效;
返回示例:0x12345678

id_type

含义说明:帧类型(标准帧或扩展帧);
数据类型:number
取值范围:can.STD, can.EXT
注意事项:仅在成功时有效;
返回示例:can.STD

rtr

含义说明:是否是遥控帧;
数据类型:boolean
取值范围:true-遥控帧,false-数据帧;
注意事项:仅在成功时有效;
返回示例:true

data

含义说明:接收到的数据;
数据类型:string
取值范围:最大8字节;
注意事项:仅在成功时有效,可用zbuff处理
返回示例:\x01\x02\x03\x04\x05\x06\x07\x08

示例

-- 在回调函数中接收消息
can.on(0, function(id, type, param)
    if type == can.CB_MSG then
        log.info("can.rx", "收到消息")
        -- 读取消息
        local succ, msg_id, msg_type, rtr, data = can.rx(0)
        if succ then
            log.info("can.rx", "消息ID:", string.format("0x%X", msg_id))
            log.info("can.rx", "消息类型:", msg_type == can.EXT and "扩展帧" or "标准帧")
            log.info("can.rx", "遥控帧:", rtr and "是" or "否")
            log.info("can.rx", "数据长度:", #data)
            log.info("can.rx", "数据:", data:toHex())
        else
            log.info("can.rx", "缓存为空")
        end
    end
end)

-- 循环读取所有缓存消息
while true do
    local succ, id, type, rtr, data = can.rx(0)
    if not succ then
        break  -- 缓存已空
    end
    -- 处理消息
    log.info("can.rx", "消息ID:", string.format("0x%X", id), "数据:", data:toHex())
end

4.10 can.stop(id)

功能

立即停止当前的发送操作

注意事项

1、用于紧急停止发送

2、不会影响接收操作

3、发送队列会被清空

参数

id

参数含义:CAN总线序号
数据类型:number
取值范围:0~n
是否必选:是;
参数示例:0

返回值

local result = can.stop(id)

result

含义说明:停止操作是否成功;
数据类型:boolean
取值范围:true-成功,false-失败;
注意事项:检查总线ID是否正确
返回示例:true

示例

-- 停止当前发送
local result = can.stop(0)
if result then
    log.info("can.stop", "停止成功")
else
    log.error("can.stop", "停止失败")
end

-- 在错误处理中使用
if error_condition then
    can.stop(0)  -- 紧急停止发送
end

4.11 can.reset(id)

功能

CAN总线复位,一般用于从总线关闭状态恢复成主动错误

注意事项

1、用于从离线状态恢复

2、会重置错误计数器

3、保持当前配置不变

参数

id

参数含义:CAN总线序号
数据类型:number
取值范围:0~n
是否必选:是;
参数示例:0

返回值

local result = can.reset(id)

有一个返回值

result

含义说明:复位操作是否成功;
数据类型:boolean
取值范围:true-成功,false-失败;
注意事项:检查总线ID是否正确
返回示例:true

示例

-- 复位总线
local result = can.reset(0)
if result then
    log.info("can.reset", "复位成功")
else
    log.error("can.reset", "复位失败")
end

-- 在状态监控中使用
local state = can.state(0)
if state == can.STATE_BUSOFF then
    log.error("CAN", "总线离线,尝试复位")
    can.reset(0)
end

4.12 can.busOff(id)

功能

CAN总线关闭,此时可以重新进行timing、filter、node等配置

注意事项

1、主动进入离线状态

2、用于重新配置参数

3、需要重新初始化或复位才能恢复

参数

id

参数含义:CAN总线序号
数据类型:number
取值范围:0~n
是否必选:是;
参数示例:0

返回值

local result = can.busOff(id)

有一个返回值

result

含义说明:关闭操作是否成功;
数据类型:boolean
取值范围:true-成功,false-失败;
注意事项:检查总线ID是否正确
返回示例:true

示例

-- 主动关闭总线
local result = can.busOff(0)
if result then
    log.info("can.busOff", "关闭成功")
    -- 可以重新配置参数
    can.timing(0, 250000, 6, 6, 4, 2)
    can.node(0, 0x200, can.STD)
else
    log.error("can.busOff", "关闭失败")
end

4.13 can.debug(on_off)

功能 CAN调试开关,打开后有更详细的打印信息

注意事项

1、用于调试和问题排查

2、会增加日志输出量

3、建议在开发阶段使用

参数

on_off

参数含义:是否打开调试模式;
数据类型:boolean
取值范围:true-打开,false-关闭;
是否必选:是;
参数示例:true

返回值

示例

-- 打开调试模式
can.debug(true)
log.info("can.debug", "调试模式已打开")

-- 进行CAN操作
local result = can.init(0)
if result then
    can.timing(0, 500000, 6, 6, 3, 2)
end

-- 关闭调试模式
can.debug(false)
log.info("can.debug", "调试模式已关闭")

4.14 can.capacity(id)

功能

获取CAN时钟特性,包括基础时钟、分频系数范围。CAN的实际波特率=基础时钟/分频系数/(1+PTS+PBS1+PBS2),从时钟特性里能看出对应平台是否能配置出需要的波特率

注意事项

1、用于确定平台支持的波特率范围

2、不同平台的基础时钟可能不同

3、是配置时序的重要参考

参数

id

参数含义:CAN总线序号
数据类型:number
取值范围:0~n
是否必选:是;
参数示例:0

返回值

local result, clk, div_min, div_max, div_step = can.capacity(id)

有五个返回值 result, clk, div_min, div_max 和 div_step

result

含义说明:是否成功获取时钟特性;
数据类型:boolean
取值范围:true-成功,false-失败;
注意事项:失败时后续参数无效;
返回示例:true

clk

含义说明:CAN控制器的基础时钟频率
数据类型:number
取值范围:平台相关,通常为MHz级别
注意事项:仅在成功时有效;
返回示例:12809056

div_min

含义说明:支持的最小分频系数;
数据类型:number
取值范围:≥1
注意事项:仅在成功时有效;
返回示例:8504062

div_max

含义说明:支持的最大分频系数;
数据类型:number
取值范围:≥div_min
注意事项:仅在成功时有效;
返回示例:12823580

div_step

含义说明:分频系数的步进值;
数据类型:number
取值范围:通常为1
注意事项:仅在成功时有效;
返回示例:12823580

示例

-- 获取时钟特性
local result, clk, div_min, div_max, div_step = can.capacity(0)
if result then
    -- can.capacity 基础时钟: 12809056 Hz
    log.info("can.capacity", "基础时钟:", clk, "Hz")
    -- can.capacity 分频范围: 12823580 - 8504062 步进: 12823580
    log.info("can.capacity", "分频范围:", div_min, "-", div_max, "步进:", div_step)
    -- 计算支持的波特率范围
    local min_baud = clk / div_max / (1 + 1 + 8 + 8)  -- 最小波特率
    local max_baud = clk / div_min / (1 + 1 + 2 + 1)   -- 最大波特率
    -- can.capacity 支持波特率范围: 0.0836793 -  0.199773 bps
    log.info("can.capacity", "支持波特率范围:", min_baud, "-", max_baud, "bps")
else
    log.error("can.capacity", "获取时钟特性失败")
end

-- 验证波特率配置是否可行
local target_baud = 500000
local result, clk = can.capacity(0)
if result then
    -- 计算理论分频系数
    local div = clk / target_baud / (1 + 6 + 6 + 3)  -- 假设时序参数
    -- can.capacity 目标波特率 500000 需要分频系数   1.60113
    log.info("can.capacity", "目标波特率", target_baud, "需要分频系数", div)
end

4.15 can.deinit(id)

功能

完全关闭CAN总线

注意事项

1、关闭后需要重新初始化才能使用

2、会释放所有相关资源

3、建议在程序退出前调用

参数

id

参数含义:CAN总线序号
数据类型:number
取值范围:0~n
是否必选:是;
参数示例:0

返回值

local result = can.deinit(id)

result

含义说明:关闭操作是否成功;
数据类型:boolean
取值范围:true-成功,false-失败;
注意事项:检查总线ID是否正确
返回示例:true

示例

-- 关闭CAN总线
local result = can.deinit(0)
if result then
    log.info("can.deinit", "关闭成功")
else
    log.error("can.deinit", "关闭失败")
end

-- 多总线系统全部关闭
for i = 0, 3 do
    can.deinit(i)
end

五、产品支持说明

支持 LuatOS 开发的所有产品中只有Air780EPM,Air780EHM,Air780EGH,Air780EHV,Air8000系列,Air8101系列,Air6101系列产品支持can核心库。