跳转至

8 can-CAN总线控制

作者:魏健强 | 最后修改:2026-04-02

一、概述

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 映射

  • Air780 系列(EHV/EGH/EPM 等):

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

    • CAN_TXD(发送):映射到 GPIO36

    • CAN_RXD(接收):映射到 GPIO37
    • CAN_STB(待机模式控制):映射到 GPIO35
    • Air8101 系列

    • CAN_TXD(发送):映射到 GPIO44

    • CAN_RXD(接收):映射到 GPIO45
    • CAN_STB(待机模式控制):映射到 GPIO46

1.3 错误码说明

CAN 总线错误码是 4 字节小端格式的 uint32 数值,通过 can.CB_ERR 回调报告详细的总线错误信息。错误码的每个字节都有特定含义,帮助开发者精确定位 CAN 通信中的问题。:

--[[
错误码格式:
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
]]

错误位置编码表

CAN 错误位置编码对应 CAN 帧中的具体字段,帮助精确定位错误发生的位置。

CAN 帧结构概述

CAN 帧主要包含以下部分:

帧起始(SOF):1 位,标志帧开始

仲裁场:包含标识符和 RTR 位

控制场:包含 IDE 位、保留位和数据长度

数据场:0-8 字节数据

CRC 场:CRC 序列和 CRC 定界符

应答场:ACK 槽和 ACK 定界符

帧结束:7 个隐性位

1.3.1 错误位置编码详解

使用说明

1、 标准帧 vs 扩展帧:标准帧使用 11 位标识符,扩展帧使用 29 位标识符

2、位编号:从帧起始开始,按传输顺序编号(第 1 位=SOF)

3、 错误定位:结合错误类型和位置编码,可以精确判断错误原因

4.、实际应用:通过 parseCanError() 函数可以自动解析错误位置

典型错误组合

错误码
方向+类型+位置
典型场景
排查建议
0x00000000
TX+位错误+未指定
总线短路、终端电阻缺失
检查线路连接和终端电阻
0x00000101
RX+格式错误+未指定
波特率不匹配
检查所有设备波特率设置
0x00000202
TX+填充错误+标识符高位
电磁干扰、位定时错误
检查屏蔽接地、降低波特率
0x00010019
RX+位错误+ACK槽
单节点网络
正常现象,或增加网络节点
0x00000808
TX+位错误+CRC序列
数据完整性问题
检查数据内容、线路质量

1.3.2 实际错误码示例

重要说明:错误码是 4 字节小端格式的 uint32,计算方法是:

错误码 = (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0
-- 由于byte3固定为0x00,简化为:
错误码 = (byte2 << 16) | (byte1 << 8) | byte0

示例 1:发送位错误,位置未指定(0x00000000)

错误码:0x00000000
解析:发送方向(byte2=0x00) + 位错误(byte1=0x00) + 位置未指定(byte0=0x00)
计算:(0x00 << 16) | (0x00 << 8) | 0x00 = 0x00000000
典型场景:总线短路、终端电阻缺失、线路断开
实际应用:CANH和CANL短路,或终端电阻未连接

示例 2:接收格式错误,位置未指定(0x00010100)

错误码:0x00010100
解析:接收方向(byte2=0x01) + 格式错误(byte1=0x01) + 位置未指定(byte0=0x00)
计算:(0x01 << 16) | (0x01 << 8) | 0x00 = 0x00010100
典型场景:接收到不符合CAN协议的帧、波特率不匹配
实际应用:网络上其他设备波特率设置不一致

示例 3:发送填充错误,标识符高位(0x00000202)

错误码:0x00000202
解析:发送方向(byte2=0x00) + 填充错误(byte1=0x02) + 标识符高位(byte0=0x02)
计算:(0x00 << 16) | (0x02 << 8) | 0x02 = 0x00000202
典型场景:波特率不匹配导致位定时错误、电磁干扰
实际应用:标识符传输过程中违反位填充规则

示例 4:接收位错误,应答槽(0x00010019)

错误码:0x00010019
解析:接收方向(byte2=0x01) + 位错误(byte1=0x00) + 应答槽(byte0=0x19)
计算:(0x01 << 16) | (0x00 << 8) | 0x19 = 0x00010019
典型场景:总线上没有其他活跃节点、网络拓扑问题
实际应用:单节点网络中发送数据,没有收到应答信号

1.3.3 错误码详细解析表

下表列出了常见的 CAN 错误码及其含义,帮助开发者快速诊断总线问题:

错误码数值
方向
错误类型
错误位置
典型场景
排查方法
0x00000000
TX
位错误
未指定
总线短路到地或电源
检查CANH/CANL线路连接,测量终端电阻
0x00010000
RX
位错误
未指定
接收到错误的数据位
检查波特率设置是否匹配,线路干扰
0x00000100
TX
格式错误
未指定
帧格式不符合协议
检查控制器配置,重启设备
0x00000101
RX
格式错误
未指定
接收到格式错误的帧
确认发送方设备工作正常
0x00000200
TX
填充错误
未指定
位填充规则违反
检查数据内容,减少连续相同位
0x00010200
RX
填充错误
未指定
接收到违反填充规则的帧
检查线路质量,降低波特率
0x00000303
TX
位错误
帧起始
总线空闲时检测到错误
检查总线是否被其他设备占用
0x00010303
RX
位错误
帧起始
帧起始位检测失败
检查线路连接,测量信号质量
0x00000808
TX
位错误
CRC序列
CRC计算错误
检查数据完整性,降低波特率
0x00010808
RX
位错误
CRC序列
接收到CRC错误的帧
确认发送方CRC计算正确
0x00000019
TX
位错误
应答槽
无设备响应
检查网络上是否有其他活跃节点
0x00010019
RX
位错误
应答槽
不应答时检测到应答位
检查设备地址过滤设置

注意:表格中的错误码数值是按照标准格式计算的,实际应用中可能会因硬件实现差异而略有不同。

1.3.4 结合 can.CB_ERR 回调的实际应用

can.CB_ERR 是 CAN 总线错误报告回调,当总线检测到错误时触发。下面通过实际例子说明如何使用错误码:

示例:基本错误处理

-- 注册CAN错误回调
can.on(0, function(id, type, param)
    if type == can.CB_ERR then
        -- param参数就是4字节错误码
        log.error("CAN错误", "错误码:", string.format("0x%08X", param))

        -- 解析错误码
        local direction = (param >> 16) & 0xFF      -- byte2: 方向
        local error_type = (param >> 8) & 0xFF    -- byte1: 错误类型  
        local position = param & 0xFF             -- byte0: 错误位置

        -- 判断错误方向
        if direction == 0 then
            log.info("错误方向", "发送错误")
        else
            log.info("错误方向", "接收错误") 
        end

        -- 判断错误类型
        if error_type == 0 then
            log.info("错误类型", "位错误")
        elseif error_type == 1 then
            log.info("错误类型", "格式错误")
        elseif error_type == 2 then
            log.info("错误类型", "填充错误")
        end

        -- 输出错误位置
        log.info("错误位置", string.format("0x%02X", position))
    end
end)

1.4 CAN 错误计数器机制

1.4.1 错误计数器基本原理

CAN 协议通过发送错误计数器(TEC)接收错误计数器(REC)来监控总线错误状态,实现故障隔离和自动恢复机制:

  • TEC (Transmit Error Counter):发送错误计数器,记录发送过程中的错误
  • REC (Receive Error Counter):接收错误计数器,记录接收过程中的错误
  • 错误计数规则
  • 检测到错误时:TEC 或 REC 增加(通常 +8)
  • 成功发送/接收时:TEC 或 REC 减少(通常-1)
  • 不同的错误类型有不同的计数增减值

1.4.2 错误状态转换

基于错误计数器值,CAN 控制器会在三种状态间转换:

1.4.3 错误帧行为差异

不同状态下的错误帧行为:

  • 主动错误帧:6 个连续的显性位( dominant bits),强烈干扰总线
  • 被动错误帧:6 个连续的隐性位(recessive bits),轻微干扰总线

1.4.4 总线关闭恢复机制

当进入总线关闭状态时:

  1. 自动恢复过程:需要检测到 128 次连续的 11 个隐性位
  2. 恢复时间:约 2.6ms(标准帧间隔)到 5.2ms(扩展帧间隔)
  3. 手动恢复:可通过 can.reset() 函数强制恢复

1.4.5 实际应用意义

错误计数器机制的作用:

  • 故障隔离:防止单个故障节点持续干扰总线
  • 自动恢复:给故障节点恢复通信的机会
  • 状态监控:通过 can.state() 获取当前错误状态
  • 预防性维护:监控错误趋势,提前发现潜在问题

1.4.6 LuatOS 中的错误计数器实现

在 LuatOS 中,错误计数器的具体数值(TEC/REC)由底层硬件自动管理,用户层面主要通过状态常量来感知:

  • 状态获取:使用 can.state() 函数获取当前状态
  • 状态解释
  • STATE_ACTIVE:TEC < 128 且 REC < 128
  • STATE_PASSIVE:TEC ≥ 128 或 REC ≥ 128
  • STATE_BUSOFF:TEC ≥ 256
  • 手动恢复:使用 can.reset() 从总线关闭状态恢复
  • 错误监控:通过 CB_ERRCB_STATE 回调事件实时监控

注意:LuatOS 目前未提供直接读取 TEC/REC 具体数值的 API,主要通过状态转换来反映错误计数器的变化。

二、核心示例

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

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

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

--[[
@module  can_normal
@summary CAN总线正常模式使用示例
@version 1.0
@date    2025.11.25
@author  魏健强
@usage
本文件为CAN总线正常模式使用示例,核心业务逻辑为:
1. 初始化CAN总线
2. 发送和接收CAN总线数据

本文件没有对外接口,直接在main.lua模块中require "can_normal"就可以加载运行;
]] 
local can_id = 0
local stb_pin = 28 -- stb引脚根据实际情况写,不用的话,也可以不写
local rx_id = 0x12345677
local tx_id = 0x12345678
local test_cnt = 0
local tx_buf = zbuff.create(8) -- 创建zbuff
local send_queue = {} -- 发送队列
local MAX_SEND_QUEUE_LEN = 50 -- 发送队列最大长度
local send_res = false

-- 数据插入发送队列
local function can_send_data(id, msg_id, id_type, RTR, need_ack, data)
    if #send_queue >= MAX_SEND_QUEUE_LEN then
        log.error("can_send_data", "send queue full")
    end
    table.insert(send_queue, {
        id = id,
        msg_id = msg_id,
        id_type = id_type,
        RTR = RTR,
        need_ack = need_ack,
        data = data
    })
    sys.publish("CAN_SEND_DATA_EVENT")
end

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("发送成功")
            send_res = true
        else
            log.info("发送失败")
            send_res = false
        end
        sys.publish("CAN_SEND_DATA_RES")
    end
    if cb_type == can.CB_ERR then
        -- param参数就是4字节错误码
        log.error("CAN错误", "错误码:", string.format("0x%08X", param))

        -- 解析错误码
        local direction = (param >> 16) & 0xFF -- byte2: 方向
        local error_type = (param >> 8) & 0xFF -- byte1: 错误类型  
        local position = param & 0xFF -- byte0: 错误位置

        -- 判断错误方向
        if direction == 0 then
            log.info("错误方向", "发送错误")
        else
            log.info("错误方向", "接收错误")
        end

        -- 判断错误类型
        if error_type == 0 then
            log.info("错误类型", "位错误")
        elseif error_type == 1 then
            log.info("错误类型", "格式错误")
        elseif error_type == 2 then
            log.info("错误类型", "填充错误")
        end

        -- 输出错误位置
        log.info("错误位置", string.format("0x%02X", position))
    end
    if cb_type == can.CB_STATE then
        log.info("CAN新状态", param)
    end
end

local function can_tx_test(data)
    while true do
        sys.wait(10000)
        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_send_data(can_id, 0x123, can.STD, false, true, "Hello") -- 发送标准帧数据
        can_send_data(can_id, 0x123, can.STD, true, true, "") -- 发送遥控帧数据
        can_send_data(can_id, tx_id, can.EXT, false, true, tx_buf)--发送扩展帧数据
    end
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, 6, 6, 4, 2) -- CAN总线配置时序

local num = 0x00f -- 接收屏蔽寄存器值,对应bit写0表示需要检测,写1表示不检测,默认是0xff ff ff ff,不过滤全接收
-- 接收消息过滤(以下四行代码四选一使用)
can.node(can_id, rx_id, can.EXT) -- 只接收消息id为rx_id的扩展帧数据
-- can.filter(can_id, false, 0x123 << 21, (num << 21) | 0x1FFFFF) -- 接收消息id为0x12开头的标准帧数据,0x120~0x12f,左移后接收屏蔽寄存器的值需要用1填充,所以 | 0x1FFFFF
-- can.filter(can_id, false, 0x12345678 << 3, (num << 3) | 0x7) -- 接收消息id为0x1234开头的扩展帧数据,0x12340000~0x1234ffff,左移后接收屏蔽寄存器的值需要用1填充,所以 | 0x7
-- can.filter(can_id, false, 0, 0xFFFFFFFF) -- 接收所有消息

-- 模式
can.mode(can_id, can.MODE_NORMAL) -- 一旦设置mode就开始正常工作了,此时不能再设置node,timing,filter等

local function send_task()
    local send_item
    local result, buff_full
    -- 遍历数据发送队列send_queue
    while true do
        sys.waitUntil("CAN_SEND_DATA_EVENT",1000)
        while #send_queue > 0 do
            send_res = false
            -- 取数据发送
            send_item = table.remove(send_queue, 1)
            while not send_res do
                can.tx(send_item.id, send_item.msg_id, send_item.id_type, send_item.RTR, send_item.need_ack,
                    send_item.data)
                sys.waitUntil("CAN_SEND_DATA_RES",500)
                -- 循环发送直到发送成功
            end
        end
    end
end
sys.taskInit(send_task)
sys.taskInit(can_tx_test)

三、常量详解

核心库常量,顾名思义是由合宙 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(主动错误状态)

常量含义:1.CAN控制器主动错误状态"主动"指的是主动报错,当节点处于这个状态时,如果检测到错误,它会主动地向总线发送错误标志(错误帧)来通知其他节点。
         2.主动错误状态是CAN总线通信中的正常状态,表示CAN总线通信 完全正常 ,没有检测到任何严重问题;
数据类型:number
注意事项:1. 正常通信时的状态;
         2. 发送错误计数器(TEC) < 128且接收错误计数器(REC) < 128
         3. 节点可以主动发送错误帧来报告总线错误;
         4. 可参考1.4章节的描述;
示例代码:
local state = can.state(0)
if state == can.STATE_ACTIVE then
    log.info("CAN", "总线正常")
end

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

常量含义:CAN控制器被动错误状态,错误计数器值较高;
数据类型:number
注意事项:1. 发送错误计数器(TEC)  128或接收错误计数器(REC)  128
         2. 节点只能被动等待其他节点报错,不能主动发送错误帧;
         3. 仍可正常收发消息,但建议监控+记录;
         4. 消息不会丢失,CAN协议保证数据完整性
         5. 通信延迟可能增加,需要等待额外8个隐性位(被动错误标志);
         6. 可参考1.4章节的描述;
示例代码:local state = can.state(0)
         if state == can.STATE_PASSIVE then
            log.warn("CAN", "被动错误状态")
          end

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

常量含义:CAN控制器总线关闭状态,错误过多导致离线;
数据类型:number
注意事项:1. 发送错误计数器(TEC) > 255时进入;
         2. 节点完全退出总线通信,不能收发任何消息;
         3. 需要手动恢复或等待自动恢复;
示例代码: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()读取数据;
         3. 重要:收到此消息后,需要循环读取并处理数据,直到没有数据可读(类似UART读取逻辑);
         4. 如果只读取一次,可能会遗漏后续到达的消息;
示例代码:-- 定义CAN消息处理函数
         function canMessageHandler(id, type, param)
             if type == can.CB_MSG then
                 log.info("CAN", "收到新消息")
                 -- 循环读取所有可用消息
                 while true do
                     local succ, msg_id, msg_type, rtr, data = can.rx(id)
                     if not succ then
                         break  -- 没有更多数据了
                     end
                     -- 处理单条消息
                     log.info("CAN", "消息ID:", string.format("0x%03X", msg_id))
                     log.info("CAN", "数据长度:", #data)
                     log.info("CAN", "数据内容:", data:toHex())
                 end
             end
         end

         -- 注册回调函数
         can.on(0, canMessageHandler)

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

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

        -- 注册发送完成回调
        can.on(0, canTxHandler)

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

常量含义:回调消息类型,有错误报告;
数据类型:number
注意事项:1. param是错误码
         2. 参考1.3章节的错误码说明解析;
示例代码:-- 基础错误处理函数
            function canErrorHandler(id, type, param)
                if type == can.CB_ERR then
                    log.error("CAN", "总线错误码:", string.format("0x%08X", param))
                    -- 解析错误码
                    local direction = (param >> 16) & 0xFF      -- byte2: 方向(0=TX, 1=RX)
                    local error_type = (param >> 8) & 0xFF     -- byte1: 错误类型
                    local position = param & 0xFF              -- byte0: 错误位置

                    log.info("CAN错误", "方向:", direction==0 and "发送" or "接收",
                                       "类型:", error_type,
                                       "位置:", string.format("0x%02X", position))
                end
            end

            -- 注册错误处理回调
            can.on(0, canErrorHandler)

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

常量含义:回调消息类型,总线状态变更;
数据类型:number
注意事项:1. param是新的状态值
         2. 也可用can.state()读出;
示例代码:-- 定义CAN状态变更处理函数
            function canStateHandler(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

            -- 注册状态变更回调
            can.on(0, canStateHandler)

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;
取值范围:最大128条数据,0表示使用平台默认值128;
是否必选:否;
注意事项:缓存越大,可存储的消息越多,但占用更多内存;
参数示例: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

参数含义:CAN事件处理回调函数,当CAN总线发生事件时会被调用
         function func(id, type, param)
            -- id: CAN总线序号,number类型
            -- type: 事件类型,number类型,可能值包括can.CB_MSG(新消息到达)、can.CB_TX(发送完成)、can.CB_ERR(总线错误)、can.CB_STATE(状态变更)
            -- param: 事件参数,根据事件类型不同而变化,具体含义如下:
            -- type为can.CB_TX时:param为true表示发送成功,false表示发送失败
            -- type为can.CB_ERR时:param为错误码
            -- type为can.CB_STATE时:param为状态值
            -- type为can.CB_MSG时:param无意义(通常为nil)
            log.info("can_event_callback", "id", id, "type", type, "param", param)
        end
数据类型:function
取值范围:有效的Lua函数
是否必选:是;
注意事项:1. 需要处理所有可能的回调类型;
         2. 回调函数会在每次CAN事件发生时被执行
         3. 回调函数中不可用sys.wait等阻塞task的函数
参数示例:如下所示,定义了一个函数can_callbackcan_callback就可以做为此参数传入
         function can_callback(id, type, param)
            log.info("can.on", "收到事件", id, type, param)
         end

返回值

无返回值

示例

-- 定义CAN事件处理函数
function can_callback(id, type, param)
    log.info("can.on", "收到事件", id, type, param)
    if type == can.CB_MSG then
        log.info("can.on", "新消息到达")
        -- **重要**:循环读取所有可用消息,直到缓存为空
        while true do
            local succ, msg_id, msg_type, rtr, data = can.rx(id)
            if not succ then
                break  -- 缓存已空,退出循环
            end
            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

-- 注册事件回调
can.on(0, can_callback)

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

功能

CAN 总线配置时序;

注意事项

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

2、不同平台的基础时钟可能不同,参考 can.capacity()获取; 例如:基础时钟为51.2M,(1 + PTS + PBS1 + PBS2)取值最高为 1+8+8+8 = 25,此时如果要达到10K的波特率所需的分频系数为 51.2M的基础时钟/25(1 + PTS + PBS1 + PBS2) = 204 因为分频系数最大为64所以不支持10K的波特率,最低支持的波特率为 51.2M基础时钟/64最大分频系数/25(1 + PTS + PBS1 + PBS2) = 32K

3、时序参数影响通信的稳定性和抗干扰能力,可使用下方"常用波特率配置参考"中的参数组合;

参数

id

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

br

参数含义:目标波特率,底层自动计算分频系数;
数据类型:number
取值范围:32000~100000032Kbps~1Mbps);
是否必选:是;
注意事项:1. 只需设置目标值,底层自动匹配合适的分频系数;
         2. 实际波特率由硬件分频系数与PTS/PBS1/PBS2共同决定
         3. 配置失败时可用can.capacity()验证目标波特率可行性;
         4. 默认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, 4, 2)  -- 500Kbps
can.timing(0, 1000000, 6, 6, 4, 2) -- 1Mbps

常用波特率配置参考

-- 125Kbps系列(125K及倍数)- 适用于汽车电子、工业控制
can.timing(0, 125000, 6, 6, 4, 2)  -- 125Kbps:低速控制和长距离工业控制。
can.timing(0, 250000, 6, 6, 4, 2)  -- 250Kbps:中低速车辆(如车身舒适系统)、农业设备
can.timing(0, 1000000, 6, 6, 4, 2) -- 1Mbps:高速车载网络(如发动机ECU、变速箱控制)

-- 50Kbps系列(50K及倍数)- 适用于长距离传输
can.timing(0, 50000, 6, 6, 3, 2)   -- 50Kbps:长距离传输
can.timing(0, 100000, 6, 6, 3, 2)  -- 100Kbps:中等距离
can.timing(0, 400000, 6, 6, 3, 2)  -- 400Kbps:中高速应用

-- 500Kbps - 工业标准,平衡速度和距离
can.timing(0, 500000, 6, 6, 4, 2)  -- 500Kbps:主流汽车网络、工业控制

配置选择建议

应用场景
推荐波特率
线缆长度
特点
低速控制和长距离工业控制。
125Kbps
<400m
汽车标准,兼容性好
中低速车辆(如车身舒适系统)、农业设备
250Kbps
<200m
工业常用,稳定性高
主流汽车网络、工业控制
500Kbps
<100m
最常用,平衡性能
高速车载网络(如发动机ECU、变速箱控制)
1Mbps
<40m

高速传输,距离较短
长距离监控
50Kbps
<500m
低速长距离,抗干扰强

4.4 can.mode(id, mode)

功能

CAN 总线设置工作模式

注意事项

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

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

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

4、休眠模式(降低 can 控制器功耗):主动发送消息,或 can 总线其他设备发送数据时会唤醒,进入正常模式,想要重新进入休眠模式需要 can.mode(0, can.MODE_SLEEP)重新设置。

参数

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、ACR 和 AMR 是逐位配合工作的,其核心规则通常如下:

  • 当 AMR 的某一位设置为 0​:表示这一位是“需要关心”的。此时,接收到的报文 ID 中对应的位必须与 ACR 中对应的位完全一致,才算匹配成功
  • 当 AMR 的某一位设置为 1​:表示这一位是“不关心”(屏蔽)的。接收到的报文 ID 中对应的位无论是 0 还是 1,都会被接受,而不再与 ACR 的对应位进行比较

我们可以用一个简单的例子来说明:

假设您希望只接收标识符为 00001010(二进制,即 0x0A)的 CAN 帧。

  • 您可以设置 ACR = 0x0A。
  • 为了确保所有位都精确匹配,您需要设置 AMR = 0x00​(每一位的屏蔽位都是 0,表示每一位都必须匹配)。
  • 这样,只有 ID 完全等于 0x0A 的报文才能通过滤波。

如果您希望接收一组 ID,例如所有 ID 高 4 位是 1010 的报文,可以这样设置:

  • 设置 ACR = 0xA0​(假设高 4 位为 1010)。
  • 设置 AMR = 0x0F​(低 4 位屏蔽,不关心;高 4 位必须匹配)。
  • 这样,ID 在 0xA00xAF 范围内的报文都会被接收。

因为常见的 CAN 接收过滤器 在 ACR/AMR 寄存器里并不是把 ID 从位0开始放的:低 3 位(bit0..2)通常被保留给控制位(如 RTR/IDE 等),29 位的扩展 ID 要从 bit3 开始对齐到寄存器。因此要把扩展帧的 29 位 ID 左移 3 位,才能把 ID 的最低位对齐到寄存器中的 bit3。而标准帧需要左移21位。

简单记法:扩展帧 ID 对齐 = ID << 3;标准帧 ID 对齐 = ID << 21。

local num = 0x00f -- 接收屏蔽寄存器值,对应bit写0表示需要检测,写1表示不检测,默认是0xff ff ff ff,不过滤全接收 -- can.filter(can_id, false, 0x123 << 21, (num << 21) | 0x1FFFFF) -- 接收消息id为0x12开头的标准帧数据,0x120~0x12f,左移后接收屏蔽寄存器的值需要用1填充,所以 | 0x1FFFFF -- can.filter(can_id, false, 0x12345678 << 3, (num << 3) | 0x7) -- 接收消息id为0x1234开头的扩展帧数据,0x12340000~0x1234ffff,左移后接收屏蔽寄存器的值需要用1填充,所以 | 0x7

参数

id

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

dual_mode

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

ACR

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

AMR

参数含义:4字节接收屏蔽寄存器,参考上方注意事项,用来确定和ACR完全一致时接收数据。或和ACR某几位数据相同时就接收数据
数据类型: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, 0x007fffff)

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

4.7 can.state(id)

功能

获取 CAN 总线当前状态

注意事项

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

2、需要根据状态进行相应的处理:STATE_BUSOFF 总线离线状态时需要 can.reset(0)手动复位,其他状态为正常状态或自行恢复。

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-成功,其他值-错误码详情请参考上方1.3章节的错误码;
注意事项:实际发送结果通过回调函数通知;
返回示例: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 类型;

4、需要循环读取并处理数据,直到没有数据可读(类似 UART 读取逻辑);

5、 如果只读取一次,可能会遗漏后续到达的消息;

参数

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

msg_type

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

rtr

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

data

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

示例

-- 在回调函数中接收消息
can.on(0, function(id, type, param)
    if type == can.CB_MSG then
        log.info("CAN", "收到新消息")
        -- 循环读取所有可用消息
        while true do
            local succ, msg_id, msg_type, rtr, data = can.rx(id)
            if not succ then
                break -- 没有更多数据了
            end
            -- 处理单条消息
            log.info("CAN", "消息ID:", string.format("0x%03X", msg_id))
            log.info("CAN", "数据长度:", #data)
            log.info("CAN", "数据内容:", data:toHex())
        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.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.11 can.busOff(id)

功能

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

注意事项

1、主动进入离线状态

2、用于重新配置参数

3、需要重新设置工作模式或复位才能恢复

can.reset(0) --复位

can.mode(0, can.MODE_NORMAL) --重新设置正常模式,设置后恢复正常状态

参数

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.12 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.13 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控制器的基础时钟频率,单位HZ
数据类型:number
取值范围:平台相关,通常为MHz级别
注意事项:仅在成功时有效;
返回示例:51200000

div_min

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

div_max

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

div_step

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

示例

-- 获取时钟特性
local result, clk, div_min, div_max, div_step = can.capacity(0)
if result then
    -- can.capacity 基础时钟: 51.2M Hz
    log.info("can.capacity", "基础时钟:", clk, "Hz")
    -- can.capacity 分频范围: 1 - 64 步进: 1
    log.info("can.capacity", "分频范围:", div_min, "-", div_max, "步进:", div_step)
    -- 计算支持的波特率范围
    clk = clk / 1000 -- 转换为kHz
    local min_baud = clk / div_max / (1 + 8 + 8 + 8)  -- 最小波特率
    -- can.capacity 支持波特率范围: 32K -  1M bps
    log.info("can.capacity", "支持波特率范围:", min_baud, "-", 1000, "kbps")
else
    log.error("can.capacity", "获取时钟特性失败")
end

4.14 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

4.15 can.stop(id)

功能

立即停止当前的发送操作

注意事项

1、用于紧急停止发送

2、不会影响接收操作

参数

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

五、产品支持说明

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