CAN总线协议功能库开发详解

在实时性要求较高的嵌入式系统中,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 映射:

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

Air6101 系列:

CAN_TXD(发送):映射到 GPIO44

CAN_RXD(接收):映射到 GPIO45

CAN_STB(待机模式控制):映射到 GPIO46

1.3 错误码说明

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

错误位置编码表:

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() 函数可以自动解析错误位置

典型错误组合
image

1.3.2 实际错误码示例

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

image

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

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

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

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

1.3.3 错误码详细解析表

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

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

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

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

示例:基本错误处理
image

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 < 128

STATE_PASSIVE:TEC ≥ 128 或 REC ≥ 128

STATE_BUSOFF:TEC ≥ 256

手动恢复:使用 can.reset() 从总线关闭状态恢复

错误监控:通过 CB_ERR 和 CB_STATE 回调事件实时监控

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

二、核心示例

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

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

image

三、常量详解

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

3.1 CAN 工作模式常量

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

image

3.1.2 can.MODE_LISTEN(监听模式)

image

3.1.3 can.MODE_TEST(自测模式)

image

3.1.4 can.MODE_SLEEP(休眠模式)

image

3.2 CAN 状态常量

3.2.1 can.STATE_STOP(停止状态)

image

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

image

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

image

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

image

3.2.5 can.STATE_LISTEN(监听状态)

image

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

image

3.2.7 can.STATE_SLEEP(休眠状态)

image

3.3 回调消息类型常量

3.3.1 can.CB_MSG(新消息回调)

image

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

image

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

image

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

image

3.4 帧格式常量

3.4.1 can.EXT(扩展帧)

image

3.4.2 can.STD(标准帧)

image

四、API 函数详解

4.1 can.init(id, rx_message_cache_max)

功能

CAN 总线初始化

注意事项

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

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

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

参数

id
image

rx_message_cache_max
image

返回值

local result = can.init(id, rx_message_cache_max)

有一个返回值

result
image

示例
image

4.2 can.on(id, func)

功能

注册 CAN 事件回调

注意事项

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

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

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

参数

id
image

func
image

返回值

无返回值

示例
image

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
image

br
image

PTS
image

PBS1
image

PBS2

image

SJW
image

返回值

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

有一个返回值

result

image

示例
image

常用波特率配置参考

image

配置选择建议
image

4.4 can.mode(id, mode)

功能

CAN 总线设置工作模式

注意事项

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

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

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

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

参数

id
image

mode
image

返回值

local result = can.mode(id, mode)

有一个返回值

result

image

示例
image

4.5 can.node(id, node_id, id_type)

功能

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

注意事项

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

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

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

参数

id
image

node_id
image

id_type
image

返回值

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

有一个返回值

result
image

示例
image

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。

can.filter(0, false, 0x12345678 << 3, 0x07)-- 扩展帧

can.filter(0, false, 0x123 << 21, 0x007fffff)-- 标准帧

参数

id

image

dual_mode

image

ACR

image

AMR
image

返回值

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

有一个返回值

result

image

示例
image

4.7 can.state(id)

功能

获取 CAN 总线当前状态

注意事项

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

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

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

参数

id
image

返回值

local state = can.state(id)

state
image

示例
image

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
image

msg_id

image

id_type

image

RTR

image

need_ack

image

data
image

返回值

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

result

image

示例

image

4.9 can.rx(id)

功能

接收 CAN 消息

注意事项

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

2、缓存为空时返回失败;

3、数据为 string 类型;

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

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

参数

id
image

返回值

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

有 5 个返回值

succ
image

msg_id
image

msg_type

image

rtr

image

data
image

示例
image

4.10 can.reset(id)

功能

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

注意事项

1、用于从离线状态恢复

2、会重置错误计数器

3、保持当前配置不变

参数

id
image

返回值

local result = can.reset(id)

有一个返回值

result
image

示例
image

4.11 can.busOff(id)

功能

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

注意事项

1、主动进入离线状态

2、用于重新配置参数

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

can.reset(0) --复位

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

参数

id
image

返回值

local result = can.busOff(id)

有一个返回值

result

image

示例
image

4.12 can.debug(on_off)

功能

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

注意事项

1、用于调试和问题排查

2、会增加日志输出量

3、建议在开发阶段使用

参数

on_off
image

返回值

示例

image

4.13 can.capacity(id)

功能

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

注意事项

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

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

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

参数

id
image

返回值

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

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

result

image

clk

image

div_min
image

div_max
image

div_step
image

示例
image

4.14 can.deinit(id)

功能

完全关闭 CAN 总线

注意事项

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

2、会释放所有相关资源

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

参数

id

image

返回值

local result = can.deinit(id)

result
image

示例
image

4.15 can.stop(id)

功能

立即停止当前的发送操作

注意事项

1、用于紧急停止发送

2、不会影响接收操作

参数

id

image

返回值

local result = can.stop(id)

result
image

示例
image

五、模组支持说明

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

今天的内容就分享到这里了~

posted @ 2026-02-10 14:50  合宙LuatOS  阅读(70)  评论(0)    收藏  举报