CAN / FDCAN(控制器局域网)
LibXR::CAN 与 LibXR::FDCAN 提供跨平台的控制器局域网通信抽象,支持:
- 经典 CAN 帧 (
ClassicPack) 与 CAN FD 帧 (FDPack) 的统一订阅接口; - 仲裁相位与数据相位的位时序配置;
- 错误状态查询与虚拟错误帧上报;
- 无锁订阅者列表,支持在 ISR 中安全分发。
适用于车辆通信、传感器网络等实时总线场景。
CAN 抽象接口(LibXR::CAN)
帧类型 Type
enum class Type : uint8_t {
STANDARD = 0, ///< 标准数据帧(11-bit ID)。Standard data frame (11-bit ID).
EXTENDED = 1, ///< 扩展数据帧(29-bit ID)。Extended data frame (29-bit ID).
REMOTE_STANDARD = 2, ///< 标准远程帧。Standard remote frame.
REMOTE_EXTENDED = 3, ///< 扩展远程帧。Extended remote frame.
ERROR = 4, ///< 错误帧(虚拟事件)。Error frame (virtual event).
TYPE_NUM = 5 ///< 类型数量上界。Number of frame types.
};
STANDARD/EXTENDED:经典 CAN 数据帧,对应 11/29 bit ID。REMOTE_*:经典 CAN 远程帧(RTR),不携带有效数据载荷,但 DLC 仍可用于表示“请求的数据长度”(0–8)。ERROR:虚拟错误帧(不对应实际总线帧),用于通过ClassicPack上报控制器错误事件。TYPE_NUM:内部使用,用于为每种帧类型分配订阅者链表数组长度。
对于 CAN FD(FD 帧),协议本身不支持 Remote Frame 概念;FD 侧订阅回调当前仅支持
STANDARD/EXTENDED(见下文FDCAN::Register约束)。
位时序与工作模式
BitTiming – 仲裁相位位时序
struct BitTiming {
uint32_t brp = 0; ///< 预分频。Baud rate prescaler.
uint32_t prop_seg = 0; ///< 传播段。Propagation segment.
uint32_t phase_seg1 = 0; ///< 相位段 1。Phase segment 1.
uint32_t phase_seg2 = 0; ///< 相位段 2。Phase segment 2.
uint32_t sjw = 0; ///< 同步跳宽。Synchronization jump width.
};
用于描述仲裁相位(Nominal Phase)的位时序参数,与具体硬件控制器(如 bxCAN、FDCAN 等)的寄存器配置一一对应。
Mode – 工作模式
struct Mode {
bool loopback = false; ///< 回环模式。Loopback mode.
bool listen_only = false; ///< 只听(静默)模式。Listen-only (silent) mode.
bool triple_sampling = false; ///< 三采样。Triple sampling.
bool one_shot = false; ///< 单次发送模式。One-shot transmission.
};
loopback:启用控制器内部回环,不经物理总线发送。listen_only:静默监视模式,不主动发送,只监听总线。triple_sampling:开启三采样,提高抗噪能力(取决于硬件支持)。one_shot:单次发送模式,发送失败不自动重发。
Configuration – CAN 配置
struct Configuration {
uint32_t bitrate = 0; ///< 仲裁相位目标波特率。Target nominal bitrate.
float sample_point = 0.0f; ///< 仲裁相位采样点(0~1)。Nominal sample point (0–1).
BitTiming bit_timing; ///< 位时序配置。Bit timing configuration.
Mode mode; ///< 工作模式。Operating mode.
};
bitrate/sample_point:宏观配置,一般供上层位时序计算器使用。bit_timing:具体位时序参数,通常由平台相关工具或算法生成。mode:控制器工作模式开关。
配置接口
virtual ErrorCode SetConfig(const CAN::Configuration &cfg) = 0;
virtual uint32_t GetClockFreq() const = 0;
SetConfig:按照Configuration配置 CAN 控制器。实现需保证在禁用控制器或安全状态下修改位时序。GetClockFreq:返回 CAN 外设输入时钟频率(Hz),可用于上层位时序计算。
错误状态查询 ErrorState
struct ErrorState {
uint8_t tx_error_counter = 0; ///< 发送错误计数 TEC。Transmit error counter (TEC).
uint8_t rx_error_counter = 0; ///< 接收错误计数 REC。Receive error counter (REC).
bool bus_off = false; ///< 是否处于 BUS-OFF。True if controller is bus-off.
bool error_passive = false; ///< 是否处于 Error Passive。True if error-passive.
bool error_warning = false; ///< 是否处于 Error Warning。True if error-warning.
};
错误状态查询接口:
virtual ErrorCode GetErrorState(ErrorState &state) const;
- 默认实现返回
ErrorCode::NOT_SUPPORT,不修改state。 - 具体实现(如 bxCAN / FDCAN)可重载,从硬件寄存器读取 TEC/REC 及状态位。
- 调用方可根据
bus_off/error_passive/error_warning实现诊断与容错逻辑。
经典 CAN 帧结构 ClassicPack
#pragma pack(push, 1)
struct ClassicPack {
uint32_t id; ///< CAN ID(11/29 bit 或 ErrorID)。CAN ID (11/29 bits or ErrorID).
Type type; ///< 帧类型。Frame type.
uint8_t dlc; ///< 数据长度(0~8)。Data length code (0–8).
uint8_t data[8]; ///< 数据载荷。Data payload (up to 8 bytes).
};
#pragma pack(pop)
id:- 对于
STANDARD:只使用低 11 bit; - 对于
EXTENDED:只使用低 29 bit; - 对于
ERROR:使用错误虚拟 ID(见下文ErrorID)。
- 对于
type:帧类型。dlc:数据长度码,范围0–8。- 数据帧(
STANDARD/EXTENDED):表示data[]有效字节数; - 远程帧(
REMOTE_*):可用于表示“请求的数据长度”(0–8),data[]无效/忽略。
- 数据帧(
data:数据载荷,仅对数据帧有意义,远程帧可忽略。
错误虚拟帧 ErrorID
错误事件通过 ClassicPack::type == Type::ERROR + 虚拟 ID 上报。虚拟 ID 空间定义如下:
static constexpr uint32_t CAN_ERROR_ID_PREFIX = 0xFFFF0000u;
enum class ErrorID : uint32_t {
CAN_ERROR_ID_GENERIC = CAN_ERROR_ID_PREFIX,
CAN_ERROR_ID_BUS_OFF = CAN_ERROR_ID_PREFIX + 1,
CAN_ERROR_ID_ERROR_PASSIVE = CAN_ERROR_ID_PREFIX + 2,
CAN_ERROR_ID_ERROR_WARNING = CAN_ERROR_ID_PREFIX + 3,
CAN_ERROR_ID_PROTOCOL = CAN_ERROR_ID_PREFIX + 4,
CAN_ERROR_ID_ACK = CAN_ERROR_ID_PREFIX + 5,
CAN_ERROR_ID_STUFF = CAN_ERROR_ID_PREFIX + 6,
CAN_ERROR_ID_FORM = CAN_ERROR_ID_PREFIX + 7,
CAN_ERROR_ID_BIT0 = CAN_ERROR_ID_PREFIX + 8,
CAN_ERROR_ID_BIT1 = CAN_ERROR_ID_PREFIX + 9,
CAN_ERROR_ID_CRC = CAN_ERROR_ID_PREFIX + 10,
CAN_ERROR_ID_OTHER = CAN_ERROR_ID_PREFIX + 11,
};
辅助静态方法:
static constexpr uint32_t FromErrorID(ErrorID e) noexcept;
static constexpr bool IsErrorId(uint32_t id) noexcept;
static constexpr ErrorID ToErrorID(uint32_t id) noexcept;
FromErrorID:将ErrorID转为可写入ClassicPack::id的虚拟 ID。IsErrorId:判断给定id是否处于错误 ID 空间(即(id & 0xFFFF0000u) == CAN_ERROR_ID_PREFIX)。ToErrorID:将虚拟 ID 解释为ErrorID枚举。
由于标准 CAN ID 最大为 11/29 bit,
0xFFFF0000虚拟 ID 空间与真实总线 ID 不重叠,不会产生冲 突。
典型用法(驱动在检测到错误中断时):
LibXR::CAN::ClassicPack pack{};
pack.type = LibXR::CAN::Type::ERROR;
pack.id = LibXR::CAN::FromErrorID(LibXR::CAN::ErrorID::CAN_ERROR_ID_BUS_OFF);
pack.dlc = 0;
pack.data[0] = 0; // 可选:用于携带附加信息(如实现需要)
can.OnMessage(pack, /*in_isr=*/true); // 由驱动在接收/错误路径中调用分发
回调与过滤
回调类型
using Callback = LibXR::Callback<const ClassicPack &>;
回调由 OnMessage(pack, in_isr) 触发,内部调用形式为:
cb.Run(in_isr, pack);
因此用户侧回调可按如下签名实现:
in_isr:指示当前是否在中断上下文中调用。True if called in ISR context.pack:接收到的帧。Received frame.
pack为只读引用,回调实现不得保存该引用用于异步访问。
回调可能在中断上下文中被调用,实现应确保:
- 不执行阻塞操作(如锁等待、长时间 I/O);
- 不调用不允许在中断中使用的 RTOS API;
- 如需与任务通信,建 议仅在线程安全/无锁队列中快速写入数据。
过滤模式 FilterMode
enum class FilterMode : uint8_t {
ID_MASK = 0, ///< 掩码匹配:(id & start_id_mask) == end_id_mask。
///< Mask match: (id & start_id_mask) == end_id_mask.
ID_RANGE = 1 ///< 区间匹配:start_id_mask <= id && id <= end_id_mask。
///< Range match: start_id_mask <= id && id <= end_id_mask.
};
具体语义:
ID_MASK模式:匹配条件为
(id & start_id_mask) == end_id_maskID_RANGE模式:匹配条件为
start_id_mask <= id && id <= end_id_mask
其中
id为接收帧ClassicPack::id。使用STANDARD/EXTENDED类型时,上层应确保start_id_mask/end_id_mask在对应位宽内。
过滤器结构 Filter
struct Filter {
FilterMode mode; ///< 过滤模式。Filter mode.
uint32_t start_id_mask; ///< 起始 ID 或掩码。Start ID or mask.
uint32_t end_id_mask; ///< 结束 ID 或匹配值。End ID or match value.
Type type; ///< 帧类型。Frame type.
Callback cb; ///< 回调函数。Callback function.
};
- 当
mode == ID_MASK时:start_id_mask视为掩码(mask);end_id_mask为匹配值(match value)。
- 当
mode == ID_RANGE时:start_id_mask为起始 ID(start ID);end_id_mask为结束 ID(end ID)。
注册回调 Register
void Register(Callback cb,
Type type,
FilterMode mode = FilterMode::ID_RANGE,
uint32_t start_id_mask = 0,
uint32_t end_id_mask = UINT32_MAX);
- 支持按帧类型(
type)分别注册回调,内部为每种Type维护一个订阅者无锁链表。 - 同一帧类型下可注册多个过滤器,所有匹配的回调都会被调用(多订阅者)。
- 默认过滤器为区间
[0, UINT32_MAX],即对指定type的所有 ID 放行。
示例:订阅所有标准数据帧(11-bit ID)区间 [0x100, 0x1FF]:
LibXR::CAN &can = ...;
can.Register(
LibXR::CAN::Callback(
[](bool in_isr, const LibXR::CAN::ClassicPack &pack) {
(void)in_isr;
// 快速处理,不要阻塞
}),
LibXR::CAN::Type::STANDARD,
LibXR::CAN::FilterMode::ID_RANGE,
0x100, 0x1FF);
示例:订阅所有错误虚拟帧:
can.Register(
LibXR::CAN::Callback(
[](bool in_isr, const LibXR::CAN::ClassicPack &pack) {
(void)in_isr;
if (!LibXR::CAN::IsErrorId(pack.id)) {
return;
}
auto err = LibXR::CAN::ToErrorID(pack.id);
// 根据 err 做诊断
}),
LibXR::CAN::Type::ERROR,
LibXR::CAN::FilterMode::ID_RANGE,
LibXR::CAN::FromErrorID(LibXR::CAN::ErrorID::CAN_ERROR_ID_GENERIC),
LibXR::CAN::FromErrorID(LibXR::CAN::ErrorID::CAN_ERROR_ID_OTHER));
发送与接收分发
发送消息 AddMessage(TX)
virtual ErrorCode AddMessage(const ClassicPack &pack) = 0;
- 发送方向 API(Transmit path):由上层调用,用于将
ClassicPack发送到 CAN 总 线(具体实现由派生驱动决定:直接发送 / 入硬件 mailbox / 入软件队列)。 - 返回值用于标识发送是否成功(例如:busy、队列满、参数非法等)。
接收分发 OnMessage(RX dispatch helper)
protected:
void OnMessage(const ClassicPack &pack, bool in_isr);