当前位置: 首页 > news >正文

CanFestival 协议栈解析笔记(一)

一、协议栈分层架构

┌──────────────────────────────────────────────┐
│ CiA 402 驱动层 (cia402_fsm / modes / servo) │ ← 应用层状态机
├──────────────────────────────────────────────┤
│ CANopen 协议核心 │
│ ┌────────┬───────┬──────┬───────┬─────────┐ │
│ │ NMT │ SDO │ PDO │ EMCY │ SYNC │ │ ← CiA 301 协议栈
│ │ co_nmt │co_sdo │co_pdo│co_emcy│co_sync │ │
│ └────────┴───────┴──────┴───────┴─────────┘ │
├──────────────────────────────────────────────┤
│ 对象字典 OD (co_od) — 二分查找 + 回调机制 │ ← 数据总线层
├──────────────────────────────────────────────┤
│ 硬件抽象层 (can_send 函数指针) │ ← 驱动接口
├──────────────────────────────────────────────┤
│ CAN 控制器中断 / 定时器中断 │ ← 中断层
└──────────────────────────────────────────────┘

关键设计特点:

模块核心函数被调用上下文
NMTco_nmt_process_command()CAN RX 中断
NMTco_nmt_tick()定时器中断 (心跳)
SDOco_sdo_server_process()CAN RX 中断
SDOco_sdo_server_tick()定时器中断 (超时)
PDOco_pdo_on_rpdo()CAN RX 中断
PDOco_pdo_on_sync()CAN RX 中断 (SYNC帧)
PDOco_pdo_mark_changed()应用层/主循环
PDOco_pdo_tick()/co_pdo_tick_ms()定时器中断
EMCYco_emcy_trigger()任意上下文
EMCYco_emcy_tick()定时器中断 (重发)

二、canDispatchTimeDispatch的中断关系

在经典 CanFestival 中,两者都存在,且都需要在中断上下文中被调用

canDispatch() ← 在 CAN RX 中断 ISR 中调用 TimeDispatch() ← 在硬件定时器 ISR(如 SysTick)中调用
场景分析(以 ARM Cortex-M 为例)
NVIC 优先级配置 (数字越小优先级越高): 场景 A — 同优先级(推荐): CAN_RX_IRQ 优先级 = 4 TIMER_IRQ 优先级 = 4 → 不会互相抢占 (Cortex-M 对同优先级用尾链 Tail-Chaining) → canDispatch 和 TimeDispatch 是原子的,不会嵌套 场景 B — CAN 优先级高于定时器: CAN_RX_IRQ 优先级 = 2 TIMER_IRQ 优先级 = 4 → canDispatch 可以抢占 TimeDispatch ⚠️ 危险 → TimeDispatch 修改 OD 数据时,canDispatch 也可能读写同一 OD 条目 场景 C — 定时器优先级高于 CAN: → TimeDispatch 可以抢占 canDispatch ⚠️ 同样危险

同优先级是安全

看关键代码路径。以RPDO 写入 OD为例,在co_pdo_on_rpdo()中:

// src/canopen/co_pdo.c — co_pdo_on_rpdo() od_write(pdo->od, me->index, me->subindex, buf, byte_len); 而 od_write() 内部是纯 memcpy(src/canopen/co_od.c:73): memcpy(entry->pdata, buf, size);

没有任何锁保护。如果TimeDispatch(定时器中断)正在通过co_pdo_tick_ms()遍历 TPDO 并调用od_read(),而canDispatch(CAN 中断)同时通过co_pdo_on_rpdo()写入同一个 OD 条目——这就是典型的数据竞争

但若两者同优先级,Cortex-M 内核保证:

  • 当 CPU 正在处理 TIMER_IRQ 时,同优先级的 CAN_RX_IRQ 不会被响应,而是等到 TIMER_IRQ 返回后才进入
  • 反之亦然

结论:canDispatchTimeDispatch在设计良好的系统中不会互相打断,前提是 CAN RX 中断和定时器中断配置为同一优先级。

三、高优先级 ADC 中断能否抢占它们?

能,而且这是一个需要特别注意的问题。

NVIC 优先级: ADC_IRQ 优先级 = 1 (最高) CAN_RX_IRQ 优先级 = 4 TIMER_IRQ 优先级 = 4 当 canDispatch 或 TimeDispatch 正在执行时,优先级更高的 ADC 电流采样中断会直接抢占: 时间轴: ──────────────────────────────────────────────────── TIMER_IRQ 开始 → TimeDispatch() 执行到一半 ↓ ADC 触发 ADC_IRQ 抢占 ───── ADC_ISR 执行 ───── 返回 ↓ TimeDispatch() 继续执行 ────────────────────────────────────────────────────
这是否危险?取决于 ADC ISR 做了什么。

危险场景 — ADC ISR 访问了 CANopen 数据:

如果 ADC ISR 中通过od_read()读取电流反馈值(如0x6078实际电流),而canDispatch正在通过od_write()修改同一 OD 条目:

// ADC ISR 中: od_read(od, 0x6078, 0, &buf, &size); // 读 current_actual_value // 同时 canDispatch 在 SDO 处理中: od_write(sdo->od, index, sub, &write_data, data_size);

由于od_read/od_write内部是裸memcpy,会导致读到撕裂值 (torn read)——数据部分来自写入前、部分来自写入后。

安全场景 — ADC ISR 只写自己的 buffer:

如果 ADC ISR 使用 DMA 双缓冲,将采集数据写入独立的adc_buffer[],在主循环中再同步到 OD,则抢占是安全的。

仓库的实际情况

查看 OD 回调机制(src/canopen/co_od.c:74-80):

if (od->post_write_cb) { uint32_t r = od->post_write_cb(index, subindex, entry->pdata, size, true); if (r != 0) return r; }
如果 ADC ISR 的高优先级抢占导致 post_write_cb 中的回调函数 (可能触发电机控制参数更新)执行到一半被更高优先级中断嵌套, 会产生不可预期的行为,因为OD 回调函数本身没有可重入保护。

四、总结与建议

问题结论
canDispatch 与 TimeDispatch 能否互抢?不应该。应配置 CAN RX 和 Timer 中断为同优先级,利用 Cortex-M 尾链机制保证原子性
高优先级 ADC ISR 能否抢占?。ARM NVIC 允许高优先级中断抢占低优先级
抢占是否有风险?有条件:若 ADC ISR 只写独立 buffer 则安全;若读写 OD 结构体则存在数据竞争

推荐的加固措施:

  1. 配置 CAN RX 和 Timer 中断为同一优先级(你已经提到)
  2. 保护 OD 关键区——在od_read/od_write周围使用__disable_irq()/__enable_irq()或 BASEPRI 屏蔽(参考经典 CanFestival 的EnterMutex/LeaveMutex宏)
  3. ADC ISR 使用独立的数据通道——通过 DMA 双缓冲写入,不直接访问od_entry_t.pdata
  4. 使用 BASEPRI 精确屏蔽——只屏蔽 CANopen 相关优先级,不阻断 ADC:
// 进入 CANopen 关键区时 UINT32 oldbasepri = __get_BASEPRI(); __set_BASEPRI(3 << (8 - __NVIC_PRIO_BITS)); // 屏蔽优先级 4-15 od_write(...); // 安全操作 __set_BASEPRI(oldbasepri); // 恢复, ADC (优先级1) 仍可抢占

这样 ADC 仍能实时采样,而 CANopen 协议栈内部的数据一致性得到保障。

以上就是完整的分析。

核心要点:canDispatch 和 TimeDispatch 若配置为同优先级则不会互相打断(利用 Cortex-M 尾链机制),但高优先级 ADC 中断确能抢占它们。是否安全取决于 ADC ISR 是否访问共享数据。如需加固,BASEPRI 是兼顾实时性和数据一致性的最优方案。

http://www.jsqmd.com/news/1173008/

相关文章:

  • 锂离子电池主动平衡技术解析与BQ25887应用实践
  • 数据结构(C语言描述)——顺序表的定义与实现
  • Navicat Premium Lite 17 免费版实战:5 分钟完成 MySQL 8.0 与 Redis 7.0 连接配置
  • Nginx SSL 配置深度排错:5种常见错误与解决方案(含证书链验证)
  • CSP-J 初赛动态规划 3 大高频考点解析:以 2023 年编辑距离真题为例
  • 高效DC-DC升压转换系统设计与单片机控制
  • code0 glm-5 企业实战:企业级 AI 网关怎么建,模型又该怎么分发
  • BiSheng-Autotuner常见问题解决:调试与故障排除完整手册
  • 【Springboot毕设全套源码+文档】基于springboot高校学习讲座预约系统(丰富项目+远程调试+讲解+定制)
  • 微信小程序摄影比赛投票使用教程|零基础搭建专业防刷摄影评选活动 - 微信投票小程序
  • VMWare 16 + Windows Server 2012 R2:3种网络模式(桥接/NAT/仅主机)配置与性能对比
  • BetterNCM-Installer终极指南:3分钟打造你的专属网易云音乐
  • Unity 2D游戏镜头防抖指南:Cinemachine参数调优与实战
  • STM32F205RB直流电机控制实战与Fusion开发技巧
  • PilotGo-plugin-syscare前端实现详解:基于Vue的热补丁管理界面
  • 微软Foundry平台:AI降本战略与11,000+模型服务解析
  • 2026江门卫生间免砸砖防水哪家专业?外墙、地下室、楼顶渗漏维修公司(7月) - 防水企业百科
  • 直流电机静音控制方案:TB9051FTG与PIC18F4553实战
  • AI编程进阶:系统化复用成熟方案提升Codex与Claude Code代码质量
  • Linux物理内存管理:从伙伴系统到性能调优实战指南
  • 2026年想入手大溪地珍珠项链?这些实用技巧帮你选到靠谱好商家 - GrowUME
  • AD7175-8与TM4C129XKCZAD构建高精度信号采集系统
  • 2026年7月最新泰州美度官方售后服务网点地址及客服电话一览 - 亨得利钟表维修中心
  • A3910与PIC18F25K42在电机控制中的高效应用
  • 告别Web IDE:用Claude Code在终端实现自动化代码审计与安全分析
  • 基于G6D-ASI与TM4C1299KCZAD的直流负载管理系统设计
  • SAP采购信息记录价格更新对比:PB00条件价格 vs 参考PO价格(ME1M视图)
  • IntelliJ IDEA 2026.1 与 VS Code 代码补全对比:5大维度实测与深度配置指南
  • Prometheus 监控 vSphere:VMware Exporter 与 Grafana Agent vSphere 组件 2 方案对比
  • 人形机器人关节模组:机电热控材五维耦合的精密系统