
CANopenNode协议栈之概述CANopenNode 是一个免费开源的 CANopen 协议栈采用 ANSI C 编写并运用了面向对象的设计模式。它实现了国际标准化的EN 50325-4基于 CAN 的嵌入式控制系统高层协议可运行于从 16 位微控制器到 PC 级硬件的各类设备上——无论是否带有实时操作系统。这是该协议栈的 版本 4采用了全新设计的对象字典实现提供了灵活的变量组织方式可同时供 C 应用代码和 CANopen 网络访问。CANopenNode 采用 Apache License 2.0 授权适用于商业和非商业用途。该协议栈已通过 CANopen 一致性测试工具确保符合 CiA 301 标准。什么是 CANopenCANopen 是一种基于 CANController Area Network总线构建的国际标准化高层协议。它定义了通信子协议、设备子协议和应用子协议从而实现了不同制造商设备间的互操作性。CANopenNode 实现了这些标准定义的核心通信服务并将其组织为多个模块直接映射至 CiA 规范编号CiA 标准 范围 仓库目录CiA 301 应用层与通信子协议 301/CiA 303 LED 指示灯规范 303/EN 50325-5 (CiA 304) 安全相关数据对象 (SRDO) 304/CiA 305 层设置服务 (LSS) 305/CiA 309 从其他网络访问 (ASCII 网关) 309/核心特性CANopenNode 围绕多项核心原则进行设计这使其在其他 CANopen 实现中脱颖而出。所有代码均严格采用非阻塞设计从而实现高效的多线程操作且不会发生优先级反转。通信对象被实现为带有相关联函数的独立 C 结构体遵循 ANSI C 约束下的面向对象模式。每个对象均可通过编译时配置宏进行单独启用或禁用使开发者能够仅包含其项目所需的功能——从而最小化代码体积和内存消耗。该协议栈支持多线程实时操作其中对时间要求苛刻的对象PDO、SYNC在高优先级实时线程中处理而对时间敏感度较低的对象SDO、Emergency、Gateway则在普通优先级的主线程中运行。或者所有内容也可以在单一线程中运行这在基于 Linux 的系统中很常见。协议栈还包含节能机制所有 CANopen 对象都会计算一个 timerNext_us 值用于告知操作系统在必须进行下一个处理周期之前的安全休眠最大时长。当将 CANopenNode 移植到新的微控制器时你只需编写特定于设备的文件 CO_driver_target.h 和 CO_driver.c。协议栈的其余部分均与平台无关。支持的通信协议CANopenNode 实现了一套全面的 CANopen 通信服务。每个协议都封装在各自的 .h/.c 文件对中并连接到一个或多个 CAN 报文对象协议 作用 关键文件对象字典 通信与应用之间的中心数据接口 CO_ODinterface.h/.cNMT 网络管理——启动、停止、重置设备 CO_NMT_Heartbeat.h/.cHeartbeat 用于设备监控的生产者/消费者错误控制 CO_NMT_Heartbeat.h/.c, CO_HBconsumer.h/.cNode Guarding 替代 Heartbeat 的传统错误控制方式 CO_Node_Guarding.h/.cPDO 过程数据对象——零协议开销广播实时数据 CO_PDO.h/.cSDO Server 服务数据对象——访问所有 OD 变量加速、分段、块传输 CO_SDOserver.h/.cSDO Client 访问网络中任意设备的 OD 变量 CO_SDOclient.h/.cEmergency 错误报文生产者/消费者 CO_Emergency.h/.cSYNC 用于网络同步 PDO 传输的同步生产者/消费者 CO_SYNC.h/.cTIME 用于毫秒级日期/时间同步的时间戳生产者/消费者 CO_TIME.h/.cLSS 层设置服务——节点 ID 和比特率配置主站与从站 CO_LSSmaster.h/.c, CO_LSSslave.h/.cSRDO 安全相关数据对象 (EN 50325-5) CO_SRDO.h/.cGFC 用于安全网络的全局故障安全命令 CO_GFC.h/.cGateway ASCII 用于 NMT 主站、LSS 主站、SDO 客户端的 CiA 309-3 接口 CO_gateway_ascii.h/.cLEDs CiA 303-3 绿/红 LED 状态指示灯 CO_LEDs.h/.cTrace 非标准的变量时序记录 CO_trace.h/.cStorage OD 变量的非易失性存储 CO_storage.h/.c, CO_storageEeprom.h/.c架构概述对象字典位于中心负责在通信对象和应用层之间进行中介。CO_t 结构体作为顶层容器保存了指向每个已初始化 CANopen 对象的引用而 CANopen.c 则提供了生命周期函数CO_new、CO_CANopenInit、CO_process用于统筹初始化和周期性处理。CO_t 结构体定义于 CANopen.h 中是应用程序与整个协议栈交互的唯一句柄。它包含了指向所有 CANopen 对象的指针——NMT、Emergency、SDO 服务器/客户端、PDO接收/发送、SYNC、TIME、LSS、SRDO、GFC、Gateway、LEDs 和 Trace——以及 CAN 模块及其收发报文缓冲区。在编译时通过配置宏禁用的对象将不会出现在该结构体中确保未使用的功能不会产生任何内存开销。线程模型与处理流CANopenNode 专为具有三个逻辑线程的多线程执行模型而设计尽管它也可以在单线程配置下运行。这种关注点分离确保了时间要求严苛的 CAN 处理绝不会被较慢的应用逻辑所阻塞CAN 接收线程 以最高优先级运行以极低延迟响应 CAN 中断。它通过 CAN 标识符识别传入的报文对其进行预处理并将数据复制到相应的 CANopen 对象中。定时器间隔线程 以固定周期通常为 1ms运行负责处理实时性要求苛刻的操作SYNC 生成/消费、RPDO 输入处理和 TPDO 输出传输。主线程 处理不需要严格时序的异步对象——SDO 服务器请求、Emergency 处理、Heartbeat 监控以及可选的网关功能。这三个线程都与对象字典进行交互后者充当共享数据接口。当变量在实时线程和主线程之间共享时锁定机制CO_LOCK_OD / CO_UNLOCK_OD会保护 OD 访问防止在并发访问场景下出现数据损坏。项目结构仓库按照 CiA 标准规范编号进行组织每个目录包含其相应协议层的实现文件CANopenNode/ ├── 301/ │ ├── CO_config.h # 协议栈配置宏启用/禁用功能 │ ├── CO_driver.h # 硬件抽象接口 │ ├── CO_ODinterface.* # 对象字典接口 │ ├── CO_NMT_Heartbeat.* # NMT 从站/主站 Heartbeat 生产者 │ ├── CO_HBconsumer.* # Heartbeat 消费者监控远程节点 │ ├── CO_Node_Guarding.* # 传统节点保护从站与主站 │ ├── CO_Emergency.* # Emergency 报文生产者/消费者 │ ├── CO_SDOserver.* # SDO 服务器加速/分段/块传输 │ ├── CO_SDOclient.* # SDO 客户端主站功能 │ ├── CO_PDO.* # 过程数据对象RPDO TPDO │ ├── CO_SYNC.* # 同步协议生产者与消费者 │ ├── CO_TIME.* # 时间戳协议 │ ├── CO_fifo.* # 用于 SDO/网关数据传输的 FIFO 缓冲区 │ └── crc16-ccitt.* # CRC16-CCITT 计算 ├── 303/ # CiA 303: LED 指示灯规范 │ └── CO_LEDs.* # 绿/红状态 LED 控制 ├── 304/ # EN 50325-5: 安全相关数据对象 │ ├── CO_SRDO.* # SRDO 协议安全相关通信 │ └── CO_GFC.* # 全局故障安全命令 ├── 305/ # CiA 305: 层设置服务 │ ├── CO_LSS.h # 通用 LSS 定义 │ ├── CO_LSSmaster.* # LSS 主站配置远程节点 │ └── CO_LSSslave.* # LSS 从站由主站配置 ├── 309/ # CiA 309: 从其他网络访问 │ └── CO_gateway_ascii.* # ASCII 命令接口NMT/LSS/SDO ├── storage/ # 非易失性存储系统 │ ├── CO_storage.* # 基础存储对象 │ ├── CO_storageEeprom.* # EEPROM 块设备存储 │ └── CO_eeprom.h # EEPROM 接口特定于目标 ├── extra/ # 非标准扩展 │ └── CO_trace.* # 随时间变化的变量追踪/记录 ├── example/ # 空白示例可在任何系统上编译 │ ├── main_blank.c # 带有线程结构的应用模板 │ ├── CO_driver_blank.c # 空白硬件驱动实现 │ ├── CO_driver_target.h # 空白目标特定定义 │ ├── OD.h / OD.c # 生成的对象字典 │ └── DS301_profile.* # 设备描述文件 (.eds/.xpd/.md) ├── doc/ # 文档 │ ├── objectDictionary.md # OD 接口说明 │ ├── deviceSupport.md # 支持的设备列表 │ ├── CHANGELOG.md # 更新日志 │ └── CANopenNode.png # 项目图标 ├── CANopen.h / CANopen.c # 顶层所有对象的初始化与处理 ├── Doxyfile # Doxygen 配置 ├── MISRA.md # MISRA C:2012 合规信息 ├── LICENSE # Apache 2.0 许可证 └── README.md # 项目自述文件核心 API 速览CANopen.h 和 CANopen.c 中的顶层 API 为完整的 CANopen 生命周期——从对象创建、初始化到周期性处理——提供了精简的接口。这些函数旨在涵盖最常见的 CANopen 配置同时保持足够的灵活性以适应自定义设置函数 阶段 用途CO_new() 启动 为所有 CANopen 对象分配内存堆或全局变量CO_delete() 关闭 释放所有已分配的内存CO_CANinit() 通信重置 使用指定比特率初始化 CAN 硬件驱动CO_LSSinit() 通信重置 初始化 LSS 从站以进行节点 ID/比特率配置CO_CANopenInit() 通信重置 初始化除 PDO 外的所有 CANopen 对象CO_CANopenInitPDO() 通信重置 初始化 PDO 对象必须最后调用CO_CANopenInitSRDO() 通信重置 初始化 SRDO 对象若启用安全功能CO_process() 运行时 周期性处理异步对象SDO、Emergency、NMT 等CO_process_SYNC() 运行时 周期性处理 SYNC实时线程约 1msCO_process_RPDO() 运行时 周期性处理 RPDO实时线程CO_process_TPDO() 运行时 周期性处理 TPDO实时线程CO_process_SRDO() 运行时 周期性处理 SRDO若启用则在实时线程中典型的生命周期遵循重置循环模式应用程序进入通信重置阶段在该阶段初始化所有对象随后进入运行时循环周期性调用 CO_process()。如果请求了通信或应用重置例如通过 NMT 命令循环将退出并返回初始化阶段。此模式在示例模板中有清晰的演示。配置与定制CANopenNode 可通过 301/CO_config.h 中定义的分层宏系统进行高度配置。每个 CANopen 对象都有各自的配置宏例如 CO_CONFIG_NMT、CO_CONFIG_PDO、CO_CONFIG_SYNC其中每个位都会启用或禁用特定功能。默认值可被目标特定的 CO_driver_target.h 文件覆盖并可通过可选的 CO_driver_custom.h 文件进一步完善。关键的全局配置标志包括标志 效果CO_CONFIG_FLAG_CALLBACK_PRE 在 CAN 接收预处理后启用可选的回调函数CO_CONFIG_FLAG_TIMERNEXT 启用 timerNext_us 计算以优化操作系统休眠CO_CONFIG_FLAG_OD_DYNAMIC 允许通过写入 OD 在运行时重新配置 CANopen 对象CO_MULTIPLE_OD 启用对多个对象字典的支持CO_USE_GLOBALS 使用全局静态变量代替堆内存分配对象字典本身由外部工具 CANopenEditor 生成该工具根据项目文件.xpd产出 OD.h 和 OD.c 文件。这些生成的文件定义了所有的 OD 变量、其类型、存储组以及驱动协议栈内存分配的计数宏OD_CNT_xxx。强烈不建议手动编辑这些文件。