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 总线关闭恢复机制
当进入总线关闭状态时:
- 自动恢复过程:需要检测到 128 次连续的 11 个隐性位
- 恢复时间:约 2.6ms(标准帧间隔)到 5.2ms(扩展帧间隔)
- 手动恢复:可通过
can.reset()函数强制恢复
1.4.5 实际应用意义
错误计数器机制的作用:
- 故障隔离:防止单个故障节点持续干扰总线
- 自动恢复:给故障节点恢复通信的机会
- 状态监控:通过
can.state()获取当前错误状态 - 预防性维护:监控错误趋势,提前发现潜在问题
1.4.6 LuatOS 中的错误计数器实现
在 LuatOS 中,错误计数器的具体数值(TEC/REC)由底层硬件自动管理,用户层面主要通过状态常量来感知:
- 状态获取:使用
can.state()函数获取当前状态 - 状态解释:
STATE_ACTIVE:TEC < 128 且 REC < 128STATE_PASSIVE:TEC ≥ 128 或 REC ≥ 128STATE_BUSOFF:TEC ≥ 256- 手动恢复:使用
can.reset()从总线关闭状态恢复 - 错误监控:通过
CB_ERR和CB_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. param≠0表示发送失败;
示例代码:-- 定义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(扩展帧)
常量含义:扩展帧格式,29位ID;
数据类型:number;
注意事项:1. ID范围为0~0x1FFFFFFF;
2. 优先级低于标准帧;
示例代码:-- 发送扩展帧
can.tx(0, 0x12345678, can.EXT, false, true, "data")
3.4.2 can.STD(标准帧)
常量含义:标准帧格式,11位ID;
数据类型: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~n(0表示can0,1表示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_callback,can_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~1000000(32Kbps~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 在
0xA0到0xAF范围内的报文都会被接收。
因为常见的 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 核心库。