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. param≠0表示发送失败;
示例代码:
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(扩展帧)
常量含义:扩展帧格式,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;
取值范围:≥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~1000000(10Kbps~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核心库。