本文档深入解析 roboto_usb2can 固件中的 CAN 总线健康监控与分级错误保护体系。该机制基于 Zephyr RTOS 的 CAN 子系统构建,通过状态回调拦截、滑动窗口计数与强制总线关闭三级策略,在单通道硬件上实现了对错误帧洪水、持续性 ERROR_PASSIVE 及总线关闭状态的实时防护,同时以三灯视觉系统将抽象的总线状态转化为可直观感知的物理信号。
Sources: main.c, roboto_usb2can.h
监控架构总览
整个监控体系由三个层级协同构成:底层硬件状态感知(Zephyr CAN 驱动)、中间层决策保护(错误监控器与状态机)、上层协议交互(GS-USB 事件与主机指令)。当 FDCAN 控制器检测到位错误、ACK 错误或填充错误时,硬件自动更新 TEC/REC 计数器并触发状态迁移;固件通过 can_set_state_change_callback() 注册的状态变更回调捕获这些迁移,在千毫秒级滑动窗口内评估风险,必要时执行强制 can_stop() 以隔离故障节点,最终通过 GS-USB 事件将状态同步至主机。
下图展示了从总线物理层到主机视觉反馈的完整数据流:
flowchart TD
subgraph HW["硬件层 (STM32G431 FDCAN)"]
FDCAN["FDCAN1 控制器"]
TEC["TEC 计数器"]
REC["REC 计数器"]
end
subgraph DRIVER["Zephyr CAN 驱动"]
STATE["状态机: ERROR_ACTIVE → WARNING → PASSIVE → BUS_OFF"]
CB["can_state_change_callback"]
end
subgraph APP["应用层保护逻辑"]
MON["can_error_monitor<br/>滑动窗口计数器"]
FLOOD["错误帧洪水检测<br/>>50帧/s"]
PASSIVE["持续性 ERROR_PASSIVE<br/>>10次"]
STOP["强制 Bus-Off<br/>can_stop()"]
end
subgraph GS_USB["GS-USB / cannectivity"]
EVT["gs_usb_event<br/>STARTED/STOPPED"]
HOST["主机 SocketCAN/candleLight"]
end
subgraph UI["视觉反馈"]
YELLOW["黄灯 CAN 状态"]
BLUE["蓝灯 USB 状态"]
end
FDCAN --> TEC & REC
TEC & REC --> STATE
STATE --> CB
CB --> MON
MON --> FLOOD & PASSIVE
FLOOD --> STOP
PASSIVE --> STOP
STOP --> EVT
CB --> YELLOW
EVT --> BLUE & YELLOW
HOST -->|"can start"| EVT
Sources: main.c, roboto_usb2can.h
错误监控子系统
错误监控的核心数据结构是 struct can_error_monitor,其设计兼顾了单通道当前需求与多通道未来扩展。每个通道拥有独立的计数器与标志位,通过 err_monitors[] 数组索引,与 can_devices[] 数组一一对应。
该结构体包含四个关键字段:err_frame_count 记录当前滑动窗口内的错误帧数量;err_passive_count 累积 ERROR_PASSIVE 状态的进入次数;window_start_ms 标记统计窗口的起始时间戳;throttled 与 forced_busoff 两个布尔标志分别表示是否已触发限流报警和是否已执行强制总线关闭。这种分离设计允许系统在单次窗口内先进入"限流"视觉报警状态,再在阈值突破后执行物理层保护动作,避免误杀。
滑动窗口机制以 CAN_ERR_WINDOW_MS(1000 毫秒)为周期,在每次状态回调进入时通过 k_uptime_get() 获取当前时间。若时间差超过窗口宽度,所有计数器与标志位清零,重新开始统计。这一机制确保了对错误帧洪水的响应具有时效性,不会因为历史累积而误触发保护。
Sources: roboto_usb2can.h, main.c
分级错误保护策略
固件实现了三层递进式错误保护,分别对应不同的总线故障严重程度:
第一层:错误帧洪水检测。 当单窗口内非 ERROR_ACTIVE 状态变更次数超过 CAN_ERR_FRAME_THRESHOLD(50 次/秒)时,系统判定总线存在错误帧洪水。此时固件不会立即断开总线,而是先将 throttled 置位,更新蓝灯与黄灯至快速闪烁模式(100 ms 周期),向用户发出强烈视觉告警。这种"先告警、后保护"的设计为短暂电磁干扰提供了容错空间。
第二层:持续性 ERROR_PASSIVE 强制关闭。 若总线反复进入 ERROR_PASSIVE 状态(TEC > 127 或 REC > 127),每次进入都会累加 err_passive_count。当累积次数超过 CAN_ERR_PASSIVE_LIMIT(10 次)时,系统判定存在持续性物理层故障(如终端电阻缺失、波特率不匹配或线缆损坏),立即调用 can_stop() 强制总线关闭,并将 forced_busoff 置位。此时黄灯切换至极慢闪烁模式(4 秒周期),蓝灯保持错误快速闪烁,明确指示故障已导致服务中断。
第三层:BUS_OFF 状态捕获。 当硬件自动进入 BUS_OFF(TEC > 255)时,回调直接标记 forced_busoff = true,同步更新双灯状态。需要特别指出的是,本固件不实现自动恢复——总线关闭后不会主动调用 can_start(),而是依赖主机通过 GS-USB 协议发送 START 指令重新初始化通道。这一设计遵循了故障沉默原则:在物理层故障未排除前,避免故障节点持续干扰总线。
Sources: main.c
CAN 状态机回调处理
can_state_change_callback() 是连接 Zephyr CAN 驱动与应用保护策略的唯一入口。该回调接收三个参数:设备指针、当前状态枚举值 can_state、以及包含 TEC/REC 的 can_bus_err_cnt 结构体。回调内部首先执行滑动窗口的时间检查与重置逻辑,随后根据状态分支处理。
对于 CAN_STATE_ERROR_ACTIVE,系统将 err_passive_count 清零、forced_busoff 复位,黄灯恢复至正常中速闪烁(1 Hz)。这一分支是"健康恢复"路径,意味着总线已从错误状态中自愈。CAN_STATE_ERROR_WARNING(TEC/REC > 96)仅触发日志警告与黄灯慢闪(每 2 秒),不执行保护动作,属于预警阶段。CAN_STATE_ERROR_PASSIVE 会同时触发日志、黄灯错误快闪以及前文所述的持续性计数保护。CAN_STATE_BUS_OFF 与 CAN_STATE_STOPPED 则分别对应硬件总线关闭与主机主动停止,两者在 LED 表现上均映射为黄灯关闭状态,但日志级别不同(BUS_OFF 为 ERR,STOPPED 为 INF)。
所有状态变更均通过 LOG_DBG/LOG_WRN/LOG_ERR 输出,配合 prj.conf 中可配置的日志级别,可在调试阶段完整追踪 TEC/REC 的数值变化。
Sources: main.c
视觉反馈与 LED 联动
错误保护机制与 LED 控制系统通过函数调用实现紧耦合联动,而非事件总线解耦。这种设计在资源受限的 STM32G431 上降低了延迟与内存开销。
黄灯(CAN_LED)直接由 can_state_change_callback() 控制,其四种闪烁模式与 CAN 状态一一对应:中速闪烁(500 ms 亮/500 ms 灭)代表正常;慢速闪烁(200 ms 亮/1800 ms 灭)代表 WARNING;快速闪烁(100 ms 亮/100 ms 灭)代表 ERROR_PASSIVE 或洪水检测;极慢闪烁(50 ms 亮/3950 ms 灭)代表 STOPPED 或 BUS_OFF。蓝灯(USB_LED)则在错误洪水检测或强制 Bus-Off 时同步进入快速错误模式,提示用户故障已影响整个适配器可用性。
绿灯(Activity LED)由独立的 50 ms 定时器驱动,与错误保护路径解耦。GS-USB 事件回调 status_led_event() 对 RX/TX 活动实现了低通滤波:通过 sys_timepoint 检查,确保两次绿灯闪烁间隔不小于 100 ms,避免高频通信导致 LED 常亮而失去闪烁指示意义。
与 GS-USB 协议栈的协同
错误保护机制的最终动作——强制 can_stop()——将 CAN 控制器置于 STOPPED 等效状态,但并不会断开 USB 连接或停止 GS-USB 类服务。主机仍可通过 USB 与固件通信,查询设备状态或发送控制指令。
当主机通过 SocketCAN 或 candleLight 工具发送通道启动指令时,GS-USB 协议栈内部调用 can_start(),随后通过 gs_usb_ops.event 回调触发 GS_USB_EVENT_CHANNEL_STARTED 事件。status_led_event() 捕获该事件后,重置 STOPPED 时间滤波器,并将黄灯恢复至活跃状态。这一过程自然完成了"故障隔离 → 人工排查 → 主机恢复"的闭环。固件在 main() 初始化阶段通过 gs_usb_register() 将 status_led_event 注册为全局事件处理器,确保通道生命周期事件均被捕获。
值得注意的是,status_led_event() 还包含了对 Linux 内核虚假 STOPPED 事件的防御:若两次 STOPPED 事件间隔小于 MIN_STOP_INTERVAL_MS(1000 ms),则忽略该事件并输出警告日志。这是针对特定 Linux 5.x/6.1 内核 gs_usb 驱动的已知兼容性处理。
配置参数与可扩展性
所有监控阈值均以宏常量形式集中定义于头文件,便于针对不同总线环境进行编译时调整。
| 参数名 | 默认值 | 说明 |
|---|---|---|
CAN_ERR_FRAME_THRESHOLD |
50 | 每秒允许的最大错误帧数量,超出即触发洪水检测 |
CAN_ERR_WINDOW_MS |
1000 | 滑动窗口时长,单位为毫秒 |
CAN_ERR_PASSIVE_LIMIT |
10 | 累计 ERROR_PASSIVE 次数上限,超出即强制 Bus-Off |
err_monitors[] 与 can_devices[] 均采用数组形式定义,当前长度固定为 1,但通过 ARRAY_SIZE() 遍历初始化,且注释明确标注了"extensible"设计意图。若未来硬件升级为双通道 FDCAN,仅需扩展数组元素并相应调整设备树中的通道节点,无需修改回调逻辑。prj.conf 中已将 CONFIG_USBD_GS_USB_MAX_CHANNELS 预留为 1,若扩展需同步修改该配置以分配足够的 USB 端点与缓冲资源。
Sources: roboto_usb2can.h, main.c, prj.conf
故障排查参考
下表汇总了现场可观察到的 LED 现象与对应的总线健康状态,供无调试接口环境下的快速诊断:
| 蓝灯 (USB) | 黄灯 (CAN) | 含义 | 建议操作 |
|---|---|---|---|
| 中速闪烁 | 中速闪烁 | 正常工作中 | 无需操作 |
| 中速闪烁 | 慢速闪烁 | 总线存在间歇性错误,处于 WARNING 状态 | 检查总线负载、终端电阻 |
| 快速闪烁 | 快速闪烁 | 错误帧洪水检测触发 | 检查是否有节点波特率不匹配或总线短路 |
| 快速闪烁 | 极慢闪烁 | 强制 Bus-Off 或硬件 BUS_OFF | 排查物理层故障后,在主机侧重启 CAN 通道 |
| 中速闪烁 | 极慢闪烁 | 通道被主机正常停止 | 在主机侧执行通道启动指令 |
如需进一步了解 Zephyr RTOS 的 CAN 驱动实现细节与 USB 协议栈配置,可分别参考 Zephyr RTOS 与 USB 协议栈 与 硬件架构与接口规范。LED 控制系统的完整实现细节请参阅 状态指示与 LED 控制系统。