跳转至

can - can操作库

以下为关键点梳理

一. 常量定义

(1)CAN 工作模式常量

  • can.MODE_NORMAL:正常工作模式。
  • can.MODE_LISTEN:监听模式(仅接收,不发送)。
  • can.MODE_TEST:自测模式(自收自发)。
  • can.MODE_SLEEP:休眠模式。

(2)CAN 状态常量

  • can.STATE_STOP:停止工作状态。
  • can.STATE_ACTIVE:主动错误状态(一般情况下为该状态)。
  • can.STATE_PASSIVE:被动错误状态(总线上错误较多时进入,但仍可正常收发)。
  • can.STATE_BUSOFF:离线状态(总线错误非常多,不能收发,需手动退出)。
  • can.STATE_LISTEN:监听状态(选择监听模式时进入)。
  • can.STATE_TEST:自收自发状态(选择自测模式时进入)。
  • can.STATE_SLEEP:休眠状态(选择休眠模式时进入)。

(3)回调消息类型常量

  • can.CB_MSG:有新数据写入缓存。
  • can.CB_TX:数据发送完成(需通过 param 判断成功/失败).
  • can.CB_ERR:错误报告(param 为错误码)
  • can.CB_STATE:总线状态变更,后续param参数是新的状态,也可以用 can.state 读出。

(4)帧格式常量

  • can.EXT:扩展帧。
  • can.STD:标准帧。

二. API 函数详解

(1) 初始化与配置

can.init(id, rx_message_cache_max)
  • 功能:初始化 CAN 总线。
  • 参数:

    • id:总线 ID(0 表示 can01 表示 can1,通常默认为 0)。
    • rx_message_cache_max:接收缓存消息数最大值128-2048 单位:条。
    • 返回值:boolean(成功返回 true,失败返回 false)。
    • 示例:
can.init(0,128)  -- 默认初始化 can0,使用默认缓存大小
can.timing(id, br, PTS, PBS1, PBS2, SJW)
  • 功能:配置 CAN 总线的波特率及时序参数。
  • 参数:

    • id:总线 ID。
    • br:波特率(默认 1Mbps)。
    • PTS:传播时间段(范围 1~8,默认 5)。
    • PBS1:相位缓冲段 1(范围 1~8,默认 4)。
    • PBS2:相位缓冲段 2(范围 2~8,默认 3)。
    • SJW:同步补偿宽度(范围 1~4,默认 2)。
    • 返回值:boolean
    • 示例:
can.timing(0, 500000, 5, 4, 3, 2)  -- 配置 500kbps 波特率
can.mode(id, mode)
  • 功能:设置 CAN 总线工作模式。
  • 参数:

    • id:总线 ID。
    • mode:模式常量(如 can.MODE_NORMAL)。
    • 返回值:boolean
    • 示例:
can.mode(0, can.MODE_NORMAL)  -- 设置为正常模式
can.filter(id, dual_mode, ACR, AMR)
  • 功能:设置消息过滤规则。
  • 参数:

    • id:总线 ID。
    • dual_mode:是否启用双滤波模式。
    • ACR:接受代码寄存器(过滤 ID 值)。
    • AMR:接受掩码寄存器(掩码值)。
    • 返回值:boolean
    • 注意:具体实现依赖硬件特性。
can.node(id, node_id, id_type)
  • 功能:设置 CAN 节点 ID 及类型。
  • 参数:

    • id:总线 ID。
    • node_id:节点 ID。
    • id_type:ID 类型(can.EXTcan.STD)。
    • 返回值:boolean

(2) 数据收发

can.tx(id, msg_id, id_type, RTR, need_ack, data)
  • 功能:发送 CAN 消息。
  • 参数:

    • id:总线 ID。
    • msg_id:消息 ID。
    • id_type:帧类型(can.EXTcan.STD)。
    • RTR:是否为远程传输请求(true/false)。
    • need_ack:是否需要应答(true/false)。
    • data:字符串或者zbuff对象。
    • 返回值:无。
    • 示例:
can.tx(0, 0x123, can.STD, false, true, string.char(0x01, 0x02))
can.rx(id)
  • 功能:接收 CAN 消息。
  • 参数:id(总线 ID)。
  • 返回值:消息数据(如 {id=0x123, data={0x01, 0x02}})。
  • 示例:
local msg = can.rx(0)
log.info("Received Message", msg)

(3) 状态与事件

can.on(id, func)
  • 功能:注册事件回调函数。
  • 参数:

    • id:总线 ID。
    • func:回调函数(参数:id, type, param)。
    • 示例:
can.on(0, function(id, type, param)
  if type == can.CB_MSG then
    log.info("New Message", param)
  elseif type == can.CB_ERR then
    log.error("Error", param)
  end
end)
can.state(id)
  • 功能:获取当前总线状态。
  • 参数:id
  • 返回值:状态常量(如 can.STATE_ACTIVE)。
can.stop(id)
  • 功能:停止指定总线。
  • 参数:id
can.reset(id)
  • 功能:重置总线(恢复默认配置)。
  • 参数:id
can.deinit(id)
  • 功能:反初始化总线。
  • 参数:id
can.debug(on_off)
  • 功能:启用/禁用调试信息输出。
  • 参数:on_off(布尔值)。

三. 使用建议与注意事项

1. 初始化顺序:

can.init(0,128)          -- 初始化
can.timing(...)     -- 配置波特率
can.mode(...)       -- 设置模式
can.filter(...)     -- 配置过滤器
can.on(...)         -- 注册回调

2. 消息发送与接收:

  • 使用 can.tx 发送消息,确保参数正确。

  • 通过 can.rx 拉取消息,或在回调中处理 can.CB_MSG

3. 状态监控:

  • 定期检查 can.state,处理离线状态(can.STATE_BUSOFF)。

  • 错误回调 can.CB_ERR 需记录 param 错误码。 4. 调试与性能:

  • 启用 can.debug(true) 输出底层日志。

  • 接收缓存 rx_message_cache_max 需根据需求调整,避免消息丢失。


四. 示例代码

PROJECT = "candemo"
VERSION = "1.0.0"
sys = require("sys")
log.style(1)
local SELF_TEST_FLAG = false --自测模式标识,写true就进行自收自发模式,写false就进行正常收发模式
local node_a = true   -- A节点写true, B节点写false
local can_id = 0
local rx_id
local tx_id
local stb_pin = 28      -- stb引脚根据实际情况写,不用的话,也可以不写
if node_a then          -- A/B节点区分,互相传输测试
    rx_id = 0x12345678
    tx_id = 0x12345677
else
    rx_id = 0x12345677
    tx_id = 0x12345678
end
local test_cnt = 0
local tx_buf = zbuff.create(8)  --创建zbuff
local function can_cb(id, cb_type, param)
    if cb_type == can.CB_MSG then
        log.info("有新的消息")
        local succ, id, id_type, rtr, data = can.rx(id)
        while succ do
            log.info(mcu.x32(id), #data, data:toHex())
            succ, id, id_type, rtr, data = can.rx(id)
        end
    end
    if cb_type == can.CB_TX then
        if param then
            log.info("发送成功")
        else
            log.info("发送失败")
        end
    end
    if cb_type == can.CB_ERR then
        log.info("CAN错误码", mcu.x32(param))
    end
    if cb_type == can.CB_STATE then
        log.info("CAN新状态", param)
    end
end

local function can_tx_test(data)
    if node_a then
        log.info("node a tx")
    else
        log.info("node b tx")
    end
    test_cnt = test_cnt + 1
    if test_cnt > 8 then
        test_cnt = 1
    end
    tx_buf:set(0,test_cnt)  --zbuff的类似于memset操作,类似于memset(&buff[start], num, len)
    tx_buf:seek(test_cnt)   --zbuff设置光标位置(可能与当前指针位置有关;执行后指针会被设置到指定位置)
    can.tx(can_id, tx_id, can.EXT, false, true, tx_buf)
end
-- can.debug(true)
--gpio.setup(stb_pin,0)   -- 配置STB引脚为输出低电平
gpio.setup(stb_pin,1)   -- 如果开发板上STB信号有逻辑取反,则要配置成输出高电平
can.init(can_id, 128)            -- 初始化CAN,参数为CAN ID,接收缓存消息数的最大值
can.on(can_id, can_cb)            -- 注册CAN的回调函数
can.timing(can_id, 1000000, 5, 4, 3, 2)     --CAN总线配置时序
if SELF_TEST_FLAG then
    can.node(can_id, tx_id, can.EXT)    -- 测试模式下,允许接收的ID和发送ID一致才会有新数据提醒
    can.mode(can_id, can.MODE_TEST)     -- 如果只是自身测试硬件好坏,可以用测试模式来验证,如果发送成功就OK
else
    can.node(can_id, rx_id, can.EXT)
    can.mode(can_id, can.MODE_NORMAL)   -- 一旦设置mode就开始正常工作了,此时不能再设置node,timing,filter等
end

sys.timerLoopStart(can_tx_test, 2000)
sys.run()

五. 常见问题

  • Q:如何解决 can.STATE_BUSOFF 离线状态?

    • 需先调用 can.reset(id) 恢复总线,再重新初始化配置。
    • Q:如何确保消息发送成功?

    • 通过 can.CB_TX 回调判断发送结果,或检查返回值(若 API 支持)。

    • Q:如何处理高频率消息接收?

    • 增大 rx_message_cache_max,或在回调中及时处理消息。


以下为详细说明

常量

常量 类型 解释
can.MODE_NORMAL number 正常工作模式
can.MODE_LISTEN number 监听模式
can.MODE_TEST number 自测模式
can.MODE_SLEEP number 休眠模式
can.STATE_STOP number 停止工作状态
can.STATE_ACTIVE number 主动错误状态,一般都是这个状态
can.STATE_PASSIVE number 被动错误状态,总线上错误多会进入这个状态,但是还能正常收发
can.STATE_BUSOFF number 离线状态,总线上错误非常多会进入这个状态,不能收发,需要手动退出
can.STATE_LISTEN number 监听状态,选择监听模式时进入这个状态
can.STATE_TEST number 自收自发状态,选择自测模式时进入这个状态
can.STATE_SLEEP number 休眠状态,选择休眠模式时进入这个状态
can.CB_MSG number 回调消息类型,有新数据写入缓存
can.CB_TX number 回调消息类型,数据发送结束,需要根据后续param确定发送成功还是失败
can.CB_ERR number 回调消息类型,有错误报告,后续param是错误码
can.CB_STATE number 回调消息类型,总线状态变更,后续param是新的状态,也可以用can.state读出
can.STATE_TEST number 自收自发状态,选择自测模式时进入这个状态
can.STATE_SLEEP number 休眠状态,选择休眠模式时进入这个状态

can.init(id, rx_message_cache_max)

CAN总线初始化

参数

传入值类型 解释
int id, 如果只有一条总线写0或者留空, 有多条的,can0写0,can1写1, 如此类推, 一般情况只有1条
int rx_message_cache_max,接收缓存消息数最大值128-2048 单位:条

返回值

返回值类型 解释
boolean 成功返回true,失败返回false

例子

can.init(0,128)

can.on(id, func)

注册CAN事件回调

参数

传入值类型 解释
int id, 如果只有一条总线写0或者留空, 有多条的,can0写0,can1写1, 如此类推, 一般情况只有1条
function 回调方法

返回值

返回值类型 解释
nil 无返回值

例子

can.on(1, function(id, type, param)
    log.info("can", id, type, param)
end)

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

CAN总线配置时序

参数

传入值类型 解释
int id, 如果只有一条总线写0或者留空, 有多条的,can0写0,can1写1, 如此类推, 一般情况只有1条
int br, 波特率, 默认1Mbps
int PTS, 传播时间段, 范围1~8,默认5
int PBS1, 相位缓冲段1,范围1~8,默认4
int PBS2, 相位缓冲段2,范围2~8,默认3
int SJW, 同步补偿宽度值,范围1~4,默认2

返回值

返回值类型 解释
boolean 成功返回true,失败返回false

例子

can.timing(0, 1000000, 5, 4, 3, 2)
can.timing(0, 650000, 9, 6, 4, 2)
can.timing(0, 500000, 5, 4, 3, 2)
can.timing(0, 250000, 5, 4, 3, 2)
can.timing(0, 125000, 5, 4, 3, 2)
can.timing(0, 100000, 5, 4, 3, 2)
can.timing(0, 50000, 9, 6, 4, 2)
can.timing(0, 25000, 9, 6, 4, 2)

can.mode(id, mode)

CAN总线设置工作模式

参数

传入值类型 解释
int id, 如果只有一条总线写0或者留空, 有多条的,can0写0,can1写1, 如此类推, 一般情况只有1条
int mode, 见MODE_XXX,默认是MODE_NORMAL

返回值

返回值类型 解释
boolean 成功返回true,失败返回false

例子

can.mode(0, can.MODE_NORMAL)

can.node(id, node_id, id_type)

CAN总线设置节点ID,这是一种简易的过滤规则,只接收和ID完全匹配的消息,和can.filter选择一个使用

参数

传入值类型 解释
int id, 如果只有一条总线写0或者留空, 有多条的,can0写0,can1写1, 如此类推, 一般情况只有1条
int node_id, 节点ID, 标准格式11位或者扩展格式29位,根据is_extend_id决定,默认值是0x1fffffff,id值越小,优先级越高
int id_type,ID类型,填1或者can.EXT为扩展格式,填0或者can.STD为标准格式

返回值

返回值类型 解释
boolean 成功返回true,失败返回false

例子

can.node(0, 0x12345678, can.EXT)
can.node(0, 0x123, can.STD)

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

CAN总线设置接收过滤模式,当can.node不满足需求时才使用这个,和can.node选择一个使用,过滤模式比较复杂,请参考SJA1000的Pelican模式下滤波

参数

传入值类型 解释
int id, 如果只有一条总线写0或者留空, 有多条的,can0写0,can1写1, 如此类推, 一般情况只有1条
boolean dual_mode, 是否是双过滤模式,true是,false不是,默认是false
int ACR, 接收代码寄存器值,必须写0xnnnnnnnn这样的格式,大端格式赋值到4个ACR寄存器上,默认值是0
int AMR, 接收屏蔽寄存器值,必须写0xnnnnnnnn这样的格式,大端格式赋值到4个AMR寄存器上,对应bit写0表示需要检测,写1表示不检测,默认是0xffffffff,不过滤全接收

返回值

返回值类型 解释
boolean 成功返回true,失败返回false

例子

can.filter(0, false, 0x12345678, 0x07) --效果等同于can.node(0, 0x12345678, can.EXT)
can.filter(0, false, 0x123, 0x0001fffff) --效果等同于can.node(0, 0x123, can.STD)

can.state(id)

CAN工作状态

参数

传入值类型 解释
int id, 如果只有一条总线写0或者留空, 有多条的,can0写0,can1写1, 如此类推, 一般情况只有1条

返回值

返回值类型 解释
int 返回值见STATE_XXX

例子

can.state(0)

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

CAN发送一条消息

参数

传入值类型 解释
int id, 如果只有一条总线写0或者留空, 有多条的,can0写0,can1写1, 如此类推, 一般情况只有1条
int msg_id, 节点ID, 标准格式11位或者扩展格式29位,根据is_extend_id决定,默认值是0x1fffffff,id值越小,优先级越高
int id_type, ID类型,填1或者can.EXT为扩展格式,填0或者can.STD为标准格式
boolean RTR, 是否是双过滤模式,true是,false不是,默认是false
boolean need_ack,是否需要应答,true是,false不需要,默认是true
string/zbuff data, 需要发送的数据, 如果是zbuff会从指针起始位置开始发送,最多发送8字节

返回值

返回值类型 解释
boolean 成功返回true,失败返回false

例子

local data="\x00\x01\x02\x03\x04\x05\x06\x07"
can.tx(id, 0x12345678, can.EXT, false, true,data) --发送01 02 03 04 05 06 07 08
--为什么会这么写呢,为什么string类型的值可以发16进制呢?这个是lua字符串的转义字符控制的 \xhh 1到2位十六进制所代表的任意字符  二位十六进制
--第二种发送string.fromHex()的方式可以这样写发送的内容也是为01 02 03 04 05 06 07 08
local data=string.fromHex("0102030405060708")
can.tx(can_id, tx_id, can.EXT, false, true, data)
--用zbuff这样操作
local tx_buf = zbuff.create(8)  --创建zbuff
local len = tx_buf:write("\x00\x01\x02\x03\x04\x05\x06\x07")
can.tx(can_id, tx_id, can.EXT, false, true, tx_buf)
--也可以这样写
local tx_buf = zbuff.create(8)  --创建zbuff
local len = tx_buf:write(0x1a,0x30,0x31,0x32,0x00,0x01,0x02,0x03)
can.tx(can_id, tx_id, can.EXT, false, true, tx_buf)
--关于DLC的解释:
-- CAN协议中,数据帧的长度(Data Length Code,DLC)是指数据字段的字节数。CAN帧的总长度为8位,其中前6位为数据字段的字节数,后2位为控制字段。
-- DLC的取值范围为0~8,其中0表示空帧,1~8表示数据字段的字节数。
-- 空帧的DLC为0,表示不含数据字段。
-- 标准帧的DLC为8,表示8字节数据字段。
--DLC为底层自动处理,所以不需要用户自己处理。

--关于need_ack的解释:
-- CAN协议中的应答机制是其核心可靠性保障措施之一,主要通过以下方式实现:
-- 一、应答机制的核心组成
-- ACK应答位 
--   数据帧中的应答场包含2位ACK应答位。接收节点若正确接收到数据帧,会在应答间隙(ACK SLOT)期间发送一个显性位(通常为1)进行响应;若未收到有效应答,则发送错误帧触发重传。
-- 应答间隙(ACK SLOT)
--   位于应答场中间,接收节点在此期间发送ACK信号。若发送节点未收到预期应答,则认为传输失败并触发重传机制。
-- 应答界定符(ACK DELIMITER)
--   应答场的第一个位为隐性位,第二个位为显性位,用于标识应答开始。若在应答间隙检测到显性位,则不视为有效应答。
-- 二、应答机制的工作流程
-- 数据传输
-- 发送节点发送数据帧后进入等待应答状态,总线进入仲裁阶段。
-- 仲裁与应答
--   多个节点同时发送时,通过CAN-ID进行仲裁,优先级高的节点获胜。
--   接收节点验证CRC校验,若正确则发送ACK信号,否则丢弃数据帧。
-- 错误处理 
--   若发送节点未收到ACK,会启动自动重传机制,最多重传3次。
--   若连续3次重传失败,发送节点进入Busoff状态(停止发送),等待1秒后重新初始化。
--   通俗点来讲,就是发送完数据之后,对方会有应答内容,如果没有有效应答,设置成重发就会重新发送数据,不重发就不会重发数据

can.rx(id)

从缓存里读出一条消息

参数

传入值类型 解释
int id, 如果只有一条总线写0或者留空, 有多条的,can0写0,can1写1, 如此类推, 一般情况只有1条

返回值

返回值类型 解释
boolean 是否读出数据,true读出,false没有读出,缓存已经空了,或者id不对
int 消息ID
int ID类型,返回1为can.EXT扩展格式,返回0为can.STD标准格式
boolean 是否是遥控帧,true是,false不是
string 数据

例子

local succ, id, type, rtr, data = can.rx(0)
--数据长度可以用#data获取

can.stop(id)

立刻停止当前的发送

参数

传入值类型 解释
int id, 如果只有一条总线写0或者留空, 有多条的,can0写0,can1写1, 如此类推, 一般情况只有1条

返回值

返回值类型 解释
boolean 成功返回true,失败返回false

例子

can.stop(0)

can.reset(id)

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

参数

传入值类型 解释
int id, 如果只有一条总线写0或者留空, 有多条的,can0写0,can1写1, 如此类推, 一般情况只有1条

返回值

返回值类型 解释
boolean 成功返回true,失败返回false

例子

can.reset(0)

can.deinit(id)

CAN完全关闭

参数

传入值类型 解释
int id, 如果只有一条总线写0或者留空, 有多条的,can0写0,can1写1, 如此类推, 一般情况只有1条

返回值

返回值类型 解释
boolean 成功返回true,失败返回false

例子

can.state(0)

can.debug(on_off)

CAN debug开关,打开后有更详细的打印

参数

传入值类型 解释
boolean true打开,false关闭
return nil

返回值

例子

can.debug(true)