CANopenNode CO_new() 与 CO_config.h 宏机制详解
@[toc]
CO_new() 不是一个单纯的 malloc() 包装函数。它是 CANopenNode 把“协议栈功能选择”落到“对象实例、内存占用、CAN 接收过滤槽位、CAN 发送缓冲槽位”的集中入口。
换句话说,CO_config.h 里的宏回答的是:这个协议能力是否参与编译、是否保留对应代码路径、是否启用某些回调或动态配置特性。CO_new() 回答的是:在当前对象字典或 CO_config_t 配置下,实际要创建几个对象、每个对象占多少内存、每类 CAN 报文在 CANrx[] 和 CANtx[] 中从哪个下标开始。
CO_new() 的核心任务不是协议通信,而是对象装配
从代码结构看,CO_new() 可以分为四层:参数检查、对象分配、CAN 报文槽位统计、最终对象返回。
第一层是配置合法性检查。只有在 CO_MULTIPLE_OD 被定义时,函数才会把 config 视为有效配置来源,并检查 CNT_NMT、CNT_HB_CONS、CNT_EM、CNT_SDO_SRV、CNT_SDO_CLI、CNT_SYNC、CNT_RPDO、CNT_TPDO、CNT_TIME、CNT_LEDS、CNT_GFC、CNT_SRDO、CNT_LSS_SLV、CNT_LSS_MST、CNT_GTWA 等数量上限。这里的上限不是协议任意值,而是 CANopenNode 对通用对象工厂的实现约束,例如 NMT/SYNC/TIME/GFC/LSS/Gateway 这类对象通常只能有 0 或 1 个,而 RPDO/TPDO 可扩展到更大数量。
第二层是按模块分配对象。宏判断出某模块被编译启用后,CO_new() 再用 CO_GET_CNT(xxx) 判断当前实例数量是否大于 0。只有两个条件同时满足时,对象才会真正分配。例如 CO_CONFIG_SYNC_ENABLE 打开但 CO_GET_CNT(SYNC) == 0,co->SYNC 不会被创建。反过来,如果对象字典里有 SYNC 条目,但 CO_CONFIG_SYNC_ENABLE 被关闭,相关代码块也不会进入编译。
第三层是计算 CANrx[] 和 CANtx[]。每个协议对象需要若干接收槽位和发送槽位。NMT slave 需要接收 NMT 命令,Heartbeat producer 需要发送 heartbeat,SDO server 每个通道需要一个 RX 和一个 TX,SRDO 每个对象需要两路 RX 和两路 TX。CO_new() 使用 RX_CNT_xxx、TX_CNT_xxx 统计数量,再用线性累加生成 RX_IDX_xxx、TX_IDX_xxx。
第四层是失败处理。CO_alloc_break_on_fail() 体现的是“一处失败,整体失败”的分配策略。只要任意对象分配失败,流程跳出,后续通过 CO_delete(co) 释放已经分配的对象,避免部分初始化对象泄漏。
CO_config.h 的宏本质是 bitmask,不是单选枚举
CO_config.h 中的多数配置宏都按 bit 组合。例如:
1 |
这类宏的使用方式通常是:
1 |
所以 CO_CONFIG_EM 不是“Emergency 的模式编号”,而是一组功能位集合。#if ((CO_CONFIG_EM) & CO_CONFIG_EM_PRODUCER) != 0 这种判断表示:只要 producer 位存在,就编译 producer 相关代码;其他位是否存在不会影响这个判断。
这种设计的好处是模块可以细粒度裁剪。Emergency 可以只做 producer,不做 consumer;SDO client 可以只支持 segmented transfer,不支持 block transfer;PDO 可以打开同步触发,但不使用 bitwise mapping。代价是宏之间存在依赖关系,某些高级功能位不能孤立打开。例如 SDO block transfer 依赖 segmented transfer;PDO bitwise mapping 依赖 OD_IO access;Gateway ASCII 的 SDO/LSS/NMT 命令能力依赖底层对应主站模块。
CO_MULTIPLE_OD:把对象数量从静态宏切换到运行期配置结构
CO_MULTIPLE_OD 不是协议对象宏,而是 CANopenNode 对“多对象字典”场景的构建宏。
未定义 CO_MULTIPLE_OD 时,CO_new() 会忽略 config 参数,数量通常来自生成的 OD.h 中的静态计数宏。这适合一个固件只绑定一个对象字典的典型 MCU 从机场景。
定义 CO_MULTIPLE_OD 后,CO_new() 会使用 CO_config_t *config 中的 CNT_xxx 和 ENTRY_Hxxxx 字段。此时同一个协议栈代码可以服务不同对象字典实例,适合网关、多虚拟设备、测试工具或需要运行期选择 OD 的应用。代价是 CO_new() 必须计算每个模块的 RX/TX 数量和起始下标,并把这些结果保存在 co->RX_IDX_xxx、co->TX_IDX_xxx、co->CNT_ALL_RX_MSGS、co->CNT_ALL_TX_MSGS 中。
CO_GET_CNT(xxx) 的作用
CO_GET_CNT(xxx) 是 CO_new() 中最关键的数量抽象。它把“我要几个对象”这个问题隐藏起来:单 OD 模式下从编译期 OD 宏取值,多 OD 模式下从 config->CNT_xxx 取值。
例如:
1 | if (CO_GET_CNT(SYNC) == 1U) { |
这里表达的不是“SYNC 一定存在”,而是“如果当前配置中 SYNC 数量为 1,则创建 SYNC 对象”。CO_CONFIG_SYNC_ENABLE 决定这段代码是否存在,CO_GET_CNT(SYNC) 决定这段代码运行时是否分配。
ON_MULTI_OD(expr) 的作用
ON_MULTI_OD(expr) 是为了让某些变量和赋值只在多 OD 模式下存在。比如:
1 | ON_MULTI_OD(uint8_t RX_CNT_SYNC = 0); |
这些中间变量的主要用途是给后面的 RX_IDX_xxx、TX_IDX_xxx 计算服务。单 OD 模式下,CANopenNode 可以通过静态宏推导下标,不需要在 CO_new() 中保存这些运行期值。
CO_alloc_break_on_fail(ptr, count, size) 的作用
这个宏是 CO_new() 的统一分配入口。它承担三件事:按 count * size 分配内存、把分配量计入 mem、失败时跳出当前 do { ... } while(0) 流程。
它的意义不只是简化代码,而是保证所有对象分配具有一致的失败处理策略。没有这个宏,CO_new() 中每个对象都需要重复写“分配、判空、累计内存、失败清理”的逻辑,很容易出现某个分支遗漏清理或遗漏内存统计。
CO_USE_GLOBALS 的作用
CO_USE_GLOBALS 不是 CO_config.h 中的协议功能位,但它会改变对象存储方式。定义它时,CANopenNode 可以使用全局静态对象代替堆分配。对于裸机、强 MISRA 约束、禁止动态内存的工程,这个宏的意义很大:对象布局在编译期确定,启动期不依赖 malloc/calloc,失败路径也更容易受控。
它的代价是灵活性降低。需要的对象数量和模块组合通常要在编译期固定,不能像堆分配模式那样按不同配置动态装配。
CANrx[] 与 CANtx[]:宏最终会变成 CAN 驱动资源
CANopenNode 把接收过滤对象抽象为 CO_CANrx_t,把发送缓冲对象抽象为 CO_CANtx_t。CO_new() 最终分配:
1 | co->CANrx = ... CO_GET_CO(CNT_ALL_RX_MSGS) ... |
因此每打开一个协议能力,除了代码尺寸和对象内存,还可能增加 CAN 驱动层资源需求。STM32 bxCAN/FDCAN 驱动移植时,虽然 CANopenNode 的 CANrx[] 是软件对象,但最终仍要映射到底层过滤、回调分发或软件匹配逻辑。对象数量越多,初始化表越大,接收分发路径越长。
公共宏:所有模块共享的行为增强开关
CO_CONFIG_FLAG_CALLBACK_PRE
CO_CONFIG_FLAG_CALLBACK_PRE 的值是 0x1000,它表示“在高优先级接收或预处理路径完成后,允许调用一个预处理回调”。
这个宏不直接启用某个 CANopen 协议对象,而是给多个对象增加“收到事件后唤醒低优先级处理”的能力。典型场景是 RTOS:CAN RX 中断或高优先级线程只做轻量解析,然后通过 callback 通知主循环、任务或事件组继续处理。
它的原理是把实时接收路径和主处理路径解耦。CANopenNode 中很多对象都有 initCallbackPre() 类接口,例如 NMT、Heartbeat consumer、SDO、SYNC、TIME、SRDO、LSS master 等。打开这个位后,对应对象会编译进 callback 支撑字段和调用点。
在裸机轮询场景中,如果主循环足够快,可以不打开这个位;在 RTOS 中,如果希望 CAN 接收后立即唤醒处理任务,这个位通常有价值。
CO_CONFIG_FLAG_TIMERNEXT
CO_CONFIG_FLAG_TIMERNEXT 的值是 0x2000,它表示对象处理函数会计算 timerNext_us。
timerNext_us 不是协议字段,而是调度辅助量。它告诉上层“下一次最迟多久以后需要再次调用处理函数”。这对 RTOS 低功耗、tickless idle、定时器驱动主循环都有意义。
例如 Heartbeat consumer 需要监视超时,TPDO 需要 event timer 和 inhibit timer,SYNC producer 需要周期发送。打开 TIMERNEXT 后,这些对象不仅处理当前周期,还会给出下一次处理的时间建议。
如果工程主循环固定 1 ms 或更快轮询,这个位不是必须;如果工程希望根据协议对象最近的超时时间安排睡眠/唤醒,这个位可以减少无意义轮询。
CO_CONFIG_FLAG_OD_DYNAMIC
CO_CONFIG_FLAG_OD_DYNAMIC 的值是 0x4000,它表示对象字典变量被写入后,可以在正常运行期间动态重配置对应协议对象。
它解决的问题是:CANopen 的许多通信参数存在对象字典中,例如 Heartbeat consumer 的 0x1016、SYNC 的 0x1005/0x1006、TIME 的 0x1012、PDO 的通信参数和映射参数、附加 SDO 通道参数等。如果不打开动态行为,这些 OD 项通常只在通信复位或初始化阶段生效;打开后,SDO 写入 OD 项可能立即触发对象重配置。
这个宏让设备更符合“在线配置”的 CANopen 使用方式,但也增加运行期状态管理复杂度。对资源紧张且配置固定的从机,可以关闭它;对需要通过上位机实时调 PDO 映射、SYNC 参数、Heartbeat 监控表的设备,打开它更合适。
全局公共宏
CO_CONFIG_GLOBAL_FLAG_CALLBACK_PRE、CO_CONFIG_GLOBAL_RT_FLAG_CALLBACK_PRE、CO_CONFIG_GLOBAL_FLAG_TIMERNEXT、CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC 是公共开关的批量注入入口。
它们本身不代表新功能,而是为了避免在每个模块配置宏中重复写相同 flag。例如把 CO_CONFIG_GLOBAL_RT_FLAG_CALLBACK_PRE 定义为 CO_CONFIG_FLAG_CALLBACK_PRE 后,SYNC 和 PDO 等实时对象可以一起获得预处理回调能力。把 CO_CONFIG_GLOBAL_FLAG_OD_DYNAMIC 定义为 0,则默认关闭全局动态 OD 行为,只在需要的模块中单独打开。
NMT 与 Heartbeat:节点状态机和在线状态广播
CO_CONFIG_NMT
CO_CONFIG_NMT 配置的是 CO_NMT_Heartbeat 对象。这个对象在 CANopenNode 中同时承担 NMT consumer 和 Heartbeat producer 的基础职责。
在 CO_new() 中,只要 CO_GET_CNT(NMT) == 1U,就会分配 co->NMT。随后一定会预留 NMT slave 的接收槽位 RX_CNT_NMT_SLV = 1,因为节点需要接收 NMT master 的状态控制命令;同时会预留 Heartbeat producer 的发送槽位 TX_CNT_HB_PROD = 1,因为本节点需要向网络广播状态。
协议层面,NMT 用于控制节点状态,Heartbeat 用于报告节点状态。NMT command 的数据一般包含命令字节和目标 Node-ID;Heartbeat 的数据通常是一个 NMT 状态字节。PDO 的可用性与 NMT 状态强相关:Pre-operational 下 SDO 等配置服务可用,但 PDO 不参与过程数据;Operational 下 PDO 才进入正常过程数据通信。
CO_CONFIG_NMT_CALLBACK_CHANGE
这个位打开 NMT 状态变化回调。它关注的是“状态已经变了”,不是“收到了一帧 NMT 报文”。
例如设备从 Pre-operational 进入 Operational 时,应用层可能需要打开电机控制输出、允许采样数据进入 TPDO、启动某些周期任务。状态变化回调给应用一个统一入口,不需要应用自己反复查询 NMT 状态。
CO_CONFIG_NMT_MASTER
这个位启用简单 NMT master 能力。打开后,CO_new() 会在 NMT 对象存在时增加 TX_CNT_NMT_MST = 1,也就是本节点可以发送 NMT 命令。
普通从机通常不需要该功能。主站、网关、测试工具、产线配置工具可能需要它。Gateway ASCII 如果要支持 NMT 命令,底层也要有 NMT master 发送能力。
CO_CONFIG_HB_CONS
CO_CONFIG_HB_CONS 配置 Heartbeat consumer。它不是本节点发送心跳,而是本节点监视其他节点心跳。
在 CO_new() 中,只有 CO_CONFIG_HB_CONS_ENABLE 打开时才会进入 Heartbeat consumer 分支。若 CO_GET_CNT(HB_CONS) == 1U,函数会分配 co->HBcons,并根据 CO_GET_CNT(ARR_1016) 分配 co->HBconsMonitoredNodes。这里 ARR_1016 对应 OD 0x1016 的监控项数量,每个监控项通常对应一个远端节点。
Heartbeat consumer 的本质是超时监控。远端节点按自己的 0x1017 周期发送 heartbeat,本节点按 0x1016 配置的超时时间判断远端是否掉线、是否重新启动、NMT 状态是否变化。
CO_CONFIG_HB_CONS_ENABLE
这个位是 Heartbeat consumer 的总开关。没有它,后续 callback、query、dynamic OD 等细分位都没有意义。
CO_new() 中的对象分配和 RX 槽位统计都受它控制。打开后,实际 RX 槽位数量不是固定 1,而是等于被监控节点数量。因为每个远端 heartbeat 的 CAN-ID 通常是 0x700 + nodeId,每个监控对象需要匹配自己的 CAN-ID。
CO_CONFIG_HB_CONS_CALLBACK_CHANGE
这个位启用“公共 NMT 状态变化回调”。多个被监控节点共用一个状态变化回调入口。它适合只需要知道“有节点状态变化了”的应用。
CO_CONFIG_HB_CONS_CALLBACK_MULTI
这个位启用更细的多回调能力。它可以分别给 heartbeat started、timeout、remote reset、NMT changed 等事件注册回调。它适合网关、主站、诊断设备,因为这类设备要区分远端节点到底是启动、丢失、复位还是状态切换。
CO_CONFIG_HB_CONS_CALLBACK_CHANGE 和 CO_CONFIG_HB_CONS_CALLBACK_MULTI 不能同时使用。前者是简化公共回调,后者是按事件和节点细分的回调模型。
CO_CONFIG_HB_CONS_QUERY_FUNCT
这个位启用查询函数,例如按节点或下标查询 Heartbeat 状态、NMT 状态。它不改变协议报文,只增加应用读取状态的 API。
如果应用只靠回调驱动,不需要主动查询,可以关闭。若有诊断界面、状态机或日志任务需要随时读取远端节点状态,应打开。
Node Guarding:Heartbeat 之前的旧式节点监控机制
Node Guarding 是 CANopen 早期的节点监控机制,基于远程帧请求和应答。它与 Heartbeat 的差异在于:Heartbeat 是 producer 周期主动发送状态,Node Guarding 是 master 周期轮询 slave。
CO_CONFIG_NODE_GUARDING
这个宏配置 Node Guarding slave/master 对象。上传的 CO_config.h 中默认值为 0,说明默认不启用。对新工程而言,Heartbeat 更常用,Node Guarding 多用于兼容旧主站或旧设备。
CO_CONFIG_NODE_GUARDING_SLAVE_ENABLE
打开后,CO_new() 会分配 co->NGslave。这个对象让本节点响应 Node Guarding master 的远程请求。相关 OD 通常包括 0x100C Guard time 和 0x100D Life time factor。
CO_CONFIG_NODE_GUARDING_MASTER_ENABLE
打开后,CO_new() 会分配 co->NGmaster。这个对象让本节点主动轮询其他节点。它属于主站或诊断工具能力,普通从机通常不需要。
CO_CONFIG_NODE_GUARDING_MASTER_COUNT
这个宏限制 master 可监控节点数量。它不在 CO_new() 的分配片段中直接出现,但影响 Node Guarding master 内部表的容量和编译期资源。
Emergency:把内部错误转换成 CANopen EMCY 事件
CO_CONFIG_EM
CO_CONFIG_EM 配置 Emergency 对象。Emergency 是 CANopen 的错误控制与故障事件通知机制。它不负责“修复错误”,而是负责维护错误状态、生成错误寄存器、记录错误历史,并在错误首次发生或复位时发送 EMCY 报文。
在 CO_new() 中,只要 CO_GET_CNT(EM) == 1U,就会分配 co->em。如果 producer 或 history 被启用,函数还会根据 ARR_1003 + 1 分配 co->em_fifo。这里的 ARR_1003 对应预定义错误域 0x1003 的历史容量。
CO_CONFIG_EM_PRODUCER
这个位让本节点成为 Emergency producer。应用或协议栈调用错误上报接口时,首次出现的错误会触发 EMCY 报文。CO_new() 会增加 TX_CNT_EM_PROD = 1。
普通从机通常应启用 producer,因为主站需要知道从机错误。关闭 producer 后,错误可能仍在本地状态位中存在,但不会通过 EMCY 主动通知网络。
CO_CONFIG_EM_PROD_CONFIGURABLE
这个位允许 EMCY COB-ID 通过 OD 0x1014 配置。如果关闭,COB-ID 通常按预定义连接集固定为 CO_CAN_ID_EMERGENCY + nodeId,OD 0x1014 更接近只读常量。
它适合需要非默认 COB-ID、网关隔离或特殊网络规划的系统。普通从机使用默认 COB-ID 即可。
CO_CONFIG_EM_PROD_INHIBIT
这个位启用 OD 0x1015 EMCY inhibit time。inhibit time 用于限制 EMCY 报文最小发送间隔,避免故障抖动时连续刷屏导致总线拥塞。
它不改变错误是否存在,只改变报文发送节奏。对于电机驱动、IO 模块等可能短时间连续报错的设备,这个位可以保护总线。
CO_CONFIG_EM_HISTORY
这个位启用 OD 0x1003 预定义错误域。CO_new() 因此会分配 em_fifo,容量来自 ARR_1003 + 1。
错误历史让主站可以在故障发生后通过 SDO 查询过去出现过的错误。没有 history 时,EMCY 更偏向实时事件;有 history 时,设备具备基本故障追溯能力。
CO_CONFIG_EM_STATUS_BITS
这个位让内部 CO_EM_errorStatusBits_t 可从对象字典访问。它把协议栈内部错误状态位暴露给诊断工具或上位机。
它的风险是暴露实现细节。若设备只需要标准错误寄存器和 EMCY 报文,不一定需要打开;若需要深度诊断,可打开。
CO_CONFIG_EM_CONSUMER
这个位让本节点接收其他节点的 EMCY。CO_new() 会增加 RX_CNT_EM_CONS = 1。
普通从机通常不需要消费别人 EMCY;主站、网关、安全协调器或集中诊断设备才需要。它表示“我关心别人报错”,不是“我会报告自己的错误”。
CO_CONFIG_EM_ERR_STATUS_BITS_COUNT
这个宏定义内部错误状态位数量。默认 (10 * 8),也就是 80 bit。CANopenNode 栈自身使用前若干 bit,剩余部分可给制造商或设备 profile 使用。
如果应用要定义更多自有错误位,必须扩大这个值,否则错误位索引可能越界或无法表达。
CO_CONFIG_ERR_CONDITION_GENERIC
这个宏定义 0x1001 error register 中 generic error 位如何由内部 errorStatusBits[] 汇总得出。默认与 em->errorStatusBits[5] 相关。
它不是“上报 generic error 的 API”,而是“把一组内部错误状态归并到标准错误寄存器 bit0 的条件表达式”。
CO_CONFIG_ERR_CONDITION_CURRENT
这个宏定义 0x1001 中 current error 位的归并条件。默认未定义具体条件。电流类设备可以把过流、欠流、电流采样异常等内部错误位纳入该表达式。
CO_CONFIG_ERR_CONDITION_VOLTAGE
这个宏定义 voltage error 位的归并条件。电源、电池、驱动器类设备可以把过压、欠压、母线异常等错误位纳入。
CO_CONFIG_ERR_CONDITION_TEMPERATURE
这个宏定义 temperature error 位的归并条件。温度传感器、功率器件、驱动器可以把过温、温度传感器断线等错误位纳入。
CO_CONFIG_ERR_CONDITION_COMMUNICATION
这个宏定义 communication error 位的归并条件。默认会考虑协议栈内部通信错误状态位。通信错误通常包括 CAN 被动、bus off、PDO/SYNC/Heartbeat 相关异常等。
CO_CONFIG_ERR_CONDITION_DEV_PROFILE
这个宏定义 device profile specific error 位的归并条件。设备 profile 有特定错误分类时可以使用。
CO_CONFIG_ERR_CONDITION_MANUFACTURER
这个宏定义 manufacturer specific error 位的归并条件。厂家自定义错误如果不适合归入 generic/current/voltage/temperature/communication,可归入 manufacturer bit。
SDO server:远端访问本节点对象字典的服务器
CO_CONFIG_SDO_SRV
CO_CONFIG_SDO_SRV 配置 SDO server。SDO server 是 CANopen 设备可配置、可诊断的基础能力。远端 SDO client 通过 server 读取或写入本节点对象字典。
在 CO_new() 中,只要 CO_GET_CNT(SDO_SRV) > 0U,就会分配 co->SDOserver 数组。每个 SDO server 通道都需要一个接收槽位和一个发送槽位,因此 RX_CNT_SDO_SRV 和 TX_CNT_SDO_SRV 都等于 CNT_SDO_SRV。
普通从机至少需要一个默认 SDO server,否则主站难以通过标准方式配置对象字典。
CO_CONFIG_SDO_SRV_SEGMENTED
这个位启用 SDO server segmented transfer。Expedited transfer 适合 4 字节以内的小数据;segmented transfer 用于更长数据分段传输。
如果设备对象字典中存在较长字符串、域数据、较大数组,或需要兼容通用上位机,通常应启用 segmented transfer。
CO_CONFIG_SDO_SRV_BLOCK
这个位启用 SDO server block transfer。Block transfer 适合更大数据量,效率高于普通 segmented transfer。它依赖 segmented transfer,不能孤立使用。
在 MCU 上打开 block transfer 会增加缓冲需求和协议状态机复杂度。小型从机只做参数配置时,segmented 往往已经足够。
CO_CONFIG_SDO_SRV_BUFFER_SIZE
这个宏定义 SDO server 内部数据缓冲区大小。缓冲太小,大对象需要更多段;缓冲更大,RAM 占用更高。
如果启用 block transfer,缓冲区通常要显著增大;如果只做少量标量参数读写,可以保持较小值。
SDO client:本节点主动访问其他节点对象字典
CO_CONFIG_SDO_CLI
CO_CONFIG_SDO_CLI 配置 SDO client。SDO client 是主站、网关、配置工具常用能力:它可以主动读写远端节点对象字典。
在 CO_new() 中,只有 CO_CONFIG_SDO_CLI_ENABLE 打开才会编译相关分支。若 CO_GET_CNT(SDO_CLI) > 0U,函数会分配 co->SDOclient 数组,并为每个 client 通道增加一个 RX 和一个 TX。
普通纯从机通常不需要 SDO client。设备如果需要开机后自动配置其他节点、作为简易主站或实现 Gateway ASCII 的 SDO 命令,则需要它。
CO_CONFIG_SDO_CLI_ENABLE
这个位是 SDO client 总开关。没有它,后续 segmented、block、local 等位都不会进入 CO_new() 的对象分配路径。
CO_CONFIG_SDO_CLI_SEGMENTED
这个位启用 SDO client segmented transfer。它决定本节点作为 client 时是否能处理较长数据的分段上传/下载。
CO_CONFIG_SDO_CLI_BLOCK
这个位启用 SDO client block transfer。它依赖 segmented transfer,也依赖 FIFO 的某些能力,例如替代读取和 CRC16 支撑。它适合固件块、EDS/DOMAIN 数据或较大参数块传输。
CO_CONFIG_SDO_CLI_LOCAL
这个位启用 local transfer。如果 client 要访问的 server Node-ID 与本节点相同,数据可以直接在本地对象字典中搬运,不经过 CAN 总线。
这对 Gateway 或测试工具很有用:同一设备既有 SDO client,又有 SDO server 时,可以用相同 API 访问本地 OD 和远端 OD。
CO_CONFIG_SDO_CLI_BUFFER_SIZE
这个宏定义 SDO client 的循环缓冲大小。Segmented transfer 可以使用较小缓冲;block transfer 通常需要更大缓冲以提高 block size 和吞吐。
TIME:网络时间戳通信对象
CO_CONFIG_TIME
CO_CONFIG_TIME 配置 CANopen TIME 对象。TIME 的作用是把时间戳作为通信对象在网络上传递。它关注“现在是什么时间”,而不是“什么时候触发 PDO”。
在 CO_new() 中,只有 CO_CONFIG_TIME_ENABLE 打开时才会进入 TIME 分支。若 CO_GET_CNT(TIME) == 1U,函数分配 co->TIME,并为 consumer 预留一个 RX。如果 CO_CONFIG_TIME_PRODUCER 也打开,再预留一个 TX。
TIME 常用于日志、事件时间戳、分布式设备时间对齐。它不替代 RTOS tick,也不等价于 SYNC 周期。
CO_CONFIG_TIME_ENABLE
这个位启用 TIME object 和 TIME consumer。它表示本节点可以接收 TIME 报文并更新本地 CANopen 时间对象。
CO_CONFIG_TIME_PRODUCER
这个位让本节点可以产生 TIME 报文。网络中通常只应有一个明确的时间源,除非系统设计允许多时间源并有仲裁策略。
TIME 与 OD 动态配置
CO_CONFIG_FLAG_OD_DYNAMIC 对 TIME 的意义主要体现在 OD 0x1012。写 0x1012 可以启用/禁用 TIME producer 或 consumer,或者改变 COB-ID。打开动态配置后,这类写入不必等通信复位才生效。
SYNC:同步窗口和 PDO 同步触发的基础
CO_CONFIG_SYNC
CO_CONFIG_SYNC 配置 SYNC 对象。SYNC 的作用是给网络提供一个同步事件,使多个节点在同一节拍上采样输入、更新输出或发送同步 PDO。
在 CO_new() 中,只有 CO_CONFIG_SYNC_ENABLE 打开时才会进入 SYNC 分支。若 CO_GET_CNT(SYNC) == 1U,函数会分配 co->SYNC,并为 consumer 预留一个 RX。如果 CO_CONFIG_SYNC_PRODUCER 也打开,再预留一个 TX。
SYNC 与 PDO 的关系很紧密,但二者不是同一个对象。SYNC 是节拍;PDO 是数据。PDO 是否受 SYNC 控制,还取决于 CO_CONFIG_PDO_SYNC_ENABLE 以及对象字典中 PDO communication parameter 的 transmission type。
CO_CONFIG_SYNC_ENABLE
这个位启用 SYNC object 和 SYNC consumer。普通从机如果需要根据主站同步节拍采样或发送 PDO,就需要打开。
CO_CONFIG_SYNC_PRODUCER
这个位启用 SYNC producer。通常主站或同步源节点才需要。多个 SYNC producer 同时存在会造成同步源冲突,除非网络设计明确处理。
SYNC 的 OD 动态配置
SYNC 相关 OD 通常包括 0x1005 COB-ID SYNC message、0x1006 communication cycle period、0x1007 synchronous window length、0x1019 synchronous counter overflow value。打开 CO_CONFIG_FLAG_OD_DYNAMIC 后,写这些对象可以动态改变 SYNC 行为。
PDO:无协议开销的实时过程数据
CO_CONFIG_PDO
CO_CONFIG_PDO 配置 RPDO 和 TPDO。PDO 是 CANopen 中实时过程数据通道,报文数据区不携带 index/subindex,通信含义由对象字典映射表预先定义。
在 CO_new() 中,CO_CONFIG_RPDO_ENABLE 打开时才会分配 co->RPDO;CO_CONFIG_TPDO_ENABLE 打开时才会分配 co->TPDO。RPDO 消耗 RX 槽位,TPDO 消耗 TX 槽位。数量分别来自 CNT_RPDO 和 CNT_TPDO。
PDO 的优势是效率高:没有 SDO 那种命令字、index/subindex、响应确认。代价是配置必须先完成。主站和从机必须对同一个 COB-ID、长度和映射内容有一致理解。
CO_CONFIG_RPDO_ENABLE
这个位启用 Receive PDO。对本节点而言,RPDO 表示“接收来自网络的过程数据,并写入本地对象字典或应用接口”。
例如主站给从机下发控制字、目标速度、输出开关量时,从机通常通过 RPDO 接收。
CO_CONFIG_TPDO_ENABLE
这个位启用 Transmit PDO。对本节点而言,TPDO 表示“把本地过程数据发布到网络”。
例如从机上报状态字、实际速度、输入状态、传感器采样值时,通常通过 TPDO 发送。
CO_CONFIG_RPDO_TIMERS_ENABLE
这个位启用 RPDO 定时器,主要用于 RPDO 超时监控。若某个 RPDO 应该周期到达,但超过 event timer 未收到,设备可以报告通信异常。
它使 RPDO 不只是“收到就处理”,还具备“没有收到也能判错”的能力。
CO_CONFIG_TPDO_TIMERS_ENABLE
这个位启用 TPDO inhibit timer 和 event timer。Inhibit timer 限制最小发送间隔,event timer 提供周期触发。
没有这个位时,TPDO 仍可由同步或事件触发,但缺少标准定时器行为。对于状态量周期上报或抑制高频变化报文,这个位很重要。
CO_CONFIG_PDO_SYNC_ENABLE
这个位让 PDO 支持 SYNC 相关行为。它不自动创建 SYNC 对象,但让 PDO 处理函数理解同步触发语义。
如果关闭,即使 CO_CONFIG_SYNC_ENABLE 打开,PDO 模块也不会使用同步触发能力。若需要 synchronous PDO,两个宏都要匹配:SYNC 对象存在,PDO 模块支持 SYNC。
CO_CONFIG_PDO_OD_IO_ACCESS
这个位让 PDO 映射变量通过 OD_IO_t 读写函数访问,而不是直接从 OD 分配的内存取值。
它的价值是灵活:应用可以在读写回调中做边界检查、硬件寄存器同步、数据转换、互斥保护。代价是每次 PDO 访问更复杂,内存和 CPU 成本更高。
CO_CONFIG_PDO_BITWISE_MAPPING
这个位启用 bitwise mapping。默认 PDO 映射通常按字节处理;bitwise mapping 允许按 bit 粒度映射对象。
它依赖 CO_CONFIG_PDO_OD_IO_ACCESS,因为 bit 级偏移需要更灵活的访问模型。对于紧凑数字 IO、布尔量打包很有用;对于普通 8/16/32-bit 变量,byte-wise mapping 更简单。
Storage:0x1010/0x1011 背后的保存和恢复能力
CO_CONFIG_STORAGE
CO_CONFIG_STORAGE 配置 CANopenNode 的存储基础能力。它对应 CANopen 中通过 OD 0x1010 保存参数、通过 OD 0x1011 恢复默认参数的机制。
需要注意的是,CO_new() 上传片段没有直接分配 storage 对象。Storage 通常和目标平台 EEPROM、Flash、文件系统或 NVM 驱动绑定,属于协议栈与平台持久化之间的桥。
CO_CONFIG_STORAGE_ENABLE
这个位启用 storage。打开后,设备可以响应标准保存/恢复对象的写入请求,把指定 OD 组保存到非易失介质,或者恢复默认值。
在 STM32 上,真正的可靠性取决于底层 Flash 擦写策略、掉电保护、磨损均衡、参数校验。宏只打开协议栈侧框架,不自动保证 NVM 的电气可靠性。
LEDs:把 CANopen 状态转换为红绿灯语义
CO_CONFIG_LEDS
CO_CONFIG_LEDS 配置 CANopen LED indicators。它通常遵循 CiA 303-3 中红灯、绿灯的状态语义,用于把 NMT 状态、错误状态、LSS 状态转换为可显示的 LED 闪烁模式。
在 CO_new() 中,只有 CO_CONFIG_LEDS_ENABLE 打开且 CO_GET_CNT(LEDS) == 1U 时才会分配 co->LEDs。
CO_CONFIG_LEDS_ENABLE
这个位启用 LED 状态计算。它不会自动操作 GPIO,而是计算 LED 语义状态。应用或驱动需要把计算结果映射到实际引脚、定时器或 LED 控制任务。
因此它适合标准 CANopen 指示灯产品;如果设备没有 LED 或 LED 由应用完全自定义,可以关闭。
GFC:Global Fail-safe Command,全局安全命令
CO_CONFIG_GFC
CO_CONFIG_GFC 配置 GFC 对象。GFC 属于 CANopen safety 相关能力,用于在网络上传播全局 fail-safe 命令。
在 CO_new() 中,只有 CO_CONFIG_GFC_ENABLE 打开时才会进入 GFC 分支。若 CO_GET_CNT(GFC) == 1,函数分配 co->GFC,并设置 RX_CNT_GFC = 1、TX_CNT_GFC = 1。这表示 CANopenNode 的 GFC 对象同时预留 consumer 和 producer 所需槽位,具体行为再由 producer/consumer 位决定。
GFC 的协议语义比较直接:某个安全相关 producer 发送全局 fail-safe command,consumer 收到后进入应用定义的安全处理路径。它不是周期数据,也不是被定时监控的链路健康机制。
CO_CONFIG_GFC_ENABLE
这个位是 GFC 总开关。没有它,GFC producer/consumer 位没有实际意义。
CO_CONFIG_GFC_CONSUMER
这个位启用 GFC consumer。它表示本节点会接收全局安全命令,并把该事件传递给安全相关应用逻辑。
CO_CONFIG_GFC_PRODUCER
这个位启用 GFC producer。它表示本节点可以在检测到安全相关条件时发送全局 fail-safe command。
普通非安全从机通常不需要 GFC。若工程涉及安全链路,应结合系统安全需求、EN 50325-5/CiA 304 设计和硬件安全状态机一起评估,不能只靠打开宏完成安全设计。
SRDO:Safety Related Data Object,安全相关数据对象
CO_CONFIG_SRDO
CO_CONFIG_SRDO 配置 SRDO 对象。SRDO 与普通 PDO 相似,都是过程数据,但 SRDO 面向安全相关数据,通常采用双报文、互补校验和时间约束来降低错误数据被误采信的概率。
在 CO_new() 中,只有 CO_CONFIG_SRDO_ENABLE 打开时才会进入 SRDO 分支。若 CO_GET_CNT(SRDO) > 0U,函数会分配一个 co->SRDOGuard,再按 SRDO 数量分配 co->SRDO 数组。每个 SRDO 会消耗 2 * CNT_SRDO 个 RX 和 2 * CNT_SRDO 个 TX,因为安全相关通信通常需要成对报文。
CO_CONFIG_SRDO_ENABLE
这个位启用 SRDO object。打开后,SRDO 的初始化、处理函数和对象内存才会参与构建。
SRDO 不应被当成“更可靠的 PDO”简单替换。它需要对象字典中对应 SRDO 参数、映射参数、校验参数匹配,并需要应用在通信未建立或校验失败时进入安全态。
CO_CONFIG_SRDO_CHECK_TX
这个位启用发送前数据检查。它让 producer 在发出 SRDO 前检查映射数据关系,避免明显不一致的数据进入总线。
该检查提高发送端防错能力,但不能替代接收端校验和系统级安全状态机。安全通信必须按 producer 和 consumer 两端共同成立来理解。
CO_CONFIG_SRDO_MINIMUM_DELAY
这个宏定义 SRDO 两帧发送之间的最小延时,单位为微秒。它影响 SRDO producer 发送第一帧和第二帧之间的时间关系。
延时过小可能不满足某些安全时序假设;延时过大可能影响响应时间。该值应结合总线负载、安全周期、驱动发送路径和系统安全需求确定。
LSS:Layer Setting Services,节点 ID 和位速率配置服务
CO_CONFIG_LSS
CO_CONFIG_LSS 配置 LSS master/slave。LSS 用于在节点尚未拥有确定 Node-ID,或者需要通过设备身份参数重新配置 Node-ID/bit-rate 时进行网络层设置。
在 CO_new() 中,LSS slave 和 LSS master 分开处理。CO_CONFIG_LSS_SLAVE 打开时,如果 CO_GET_CNT(LSS_SLV) == 1U,函数分配 co->LSSslave,并增加一个 RX 和一个 TX。CO_CONFIG_LSS_MASTER 打开时,如果 CO_GET_CNT(LSS_MST) == 1U,函数分配 co->LSSmaster,也增加一个 RX 和一个 TX。
LSS 的核心不是普通过程数据,而是“识别设备并配置通信参数”。它通常依据 Vendor-ID、Product code、Revision number、Serial number 等身份信息定位设备。
CO_CONFIG_LSS_SLAVE
这个位启用 LSS slave。它适合需要出厂后由主站分配 Node-ID 的设备,尤其是同型号多节点同时接入总线、无法靠拨码开关区分 ID 的系统。
若设备 Node-ID 固定在固件或硬件拨码中,且不需要远程配置 bit-rate,可以关闭 LSS slave。
CO_CONFIG_LSS_SLAVE_FASTSCAN_DIRECT_RESPOND
这个位让 LSS slave 在 fastscan 接收路径中直接响应。它的目标是改善 fastscan 响应时延。
代价是接收路径承担更多工作。对于严格中断最小化的移植层,需要确认直接响应是否符合驱动设计;对于需要快速 LSS 扫描的设备,它可以缩短发现流程。
CO_CONFIG_LSS_MASTER
这个位启用 LSS master。它适合主站、网关、配置工具。普通从机通常不需要。
Gateway ASCII 若要支持 LSS 命令,底层必须有 LSS master 能力;否则文本命令即使被解析,也无法真正对总线上的 LSS slave 执行配置。
Gateway ASCII:把外部文本接口映射到 CANopen 操作
CO_CONFIG_GTW
CO_CONFIG_GTW 配置 CANopen gateway。Gateway 的重点是“从其他网络或文本接口访问 CANopen 网络”。在 CANopenNode 中,常见实现是 ASCII mapping,也就是通过串口、终端、stdio 或 socket 输入文本命令,再转为 SDO、NMT、LSS 等 CANopen 操作。
在 CO_new() 中,只有 CO_CONFIG_GTW_ASCII 打开时才会进入 gateway 分支。若 CO_GET_CNT(GTWA) == 1U,函数分配 co->gtwa。
Gateway 不等于 SDO client,但通常依赖 SDO client;不等于 NMT master,但可能调用 NMT master;不等于 LSS master,但可以暴露 LSS 命令。它更像协议栈外部的命令入口。
CO_CONFIG_GTW_MULTI_NET
这个位表示多网络接口 gateway 能力。上传的 CO_config.h 注释说明该功能当前未实现。因此在阅读代码时应把它视为接口预留,而不是可直接使用的多 CAN 网络网关能力。
CO_CONFIG_GTW_ASCII
这个位启用 ASCII gateway。打开后,CO_new() 才会分配 co->gtwa。
它通常还需要 FIFO 的 ASCII command 能力作为输入输出缓冲支撑。否则 gateway 对象即使存在,也缺少适合文本命令流的缓冲解析基础。
CO_CONFIG_GTW_ASCII_SDO
这个位让 ASCII gateway 支持 SDO read/write。它依赖 SDO client 能力,并通常需要 FIFO 的 ASCII datatype 解析能力。
CO_CONFIG_GTW_ASCII_NMT
这个位让 ASCII gateway 支持 NMT master 命令,例如启动、停止、复位远端节点。底层需要 NMT master 发送能力。
CO_CONFIG_GTW_ASCII_LSS
这个位让 ASCII gateway 支持 LSS master 命令,例如扫描未配置节点、设置 Node-ID、设置 bit-rate。底层需要 LSS master 能力。
CO_CONFIG_GTW_ASCII_LOG
这个位启用非标准 message log 读取。它更偏调试和诊断,不是 CiA 301 过程数据能力。
CO_CONFIG_GTW_ASCII_ERROR_DESC
这个位让 gateway 在响应 SDO 或 gateway 错误时输出额外错误描述。它提高人工调试体验,但会增加字符串和代码尺寸。
CO_CONFIG_GTW_ASCII_PRINT_HELP
这个位启用非标准 help 命令输出。它适合终端交互式调试,不适合极限裁剪固件。
CO_CONFIG_GTW_ASCII_PRINT_LEDS
这个位让 gateway 显示 CANopen 红绿 LED 状态。它依赖 LED 状态计算结果,适合命令行查看节点状态。
CO_CONFIG_GTW_BLOCK_DL_LOOP
这个宏定义 SDO block download 进行时,gateway 内部可连续调用 CO_SDOclientDownload() 的循环次数。它用于提升 block download 吞吐。
值越大,单次 gateway 处理可能占用更长 CPU 时间;值越小,响应更分散但吞吐较低。RTOS 中要考虑任务调度延迟,裸机中要考虑主循环阻塞时间。
CO_CONFIG_GTWA_COMM_BUF_SIZE
这个宏定义 ASCII gateway 命令缓冲区大小。普通短命令 200 字节可能足够;大量 block transfer 或长字符串写入时需要增大。
CO_CONFIG_GTWA_LOG_BUF_SIZE
这个宏定义 message log 缓冲区大小。它只影响 gateway 日志能力,不影响 CANopen 核心协议正确性。日志越大,RAM 占用越高。
CRC16:块传输和流式数据的校验支撑
CO_CONFIG_CRC16
CO_CONFIG_CRC16 配置 CRC 16 CCITT 计算。它不是一个 CANopen 通信对象,不会在 CO_new() 中分配 co->CRC 这类对象。它是 SDO block transfer、FIFO 校验或其他数据完整性场景的算法支撑。
CO_CONFIG_CRC16_ENABLE
这个位启用 CANopenNode 内部 CRC16 实现。需要 block transfer CRC 或 FIFO CRC 时应打开。
CO_CONFIG_CRC16_EXTERNAL
这个位表示使用外部 CRC16 实现。适合 MCU 有硬件 CRC、项目已有统一 CRC 库,或希望把算法实现放到平台层的场景。
FIFO:SDO 和 Gateway 的流式缓冲基础
CO_CONFIG_FIFO
CO_CONFIG_FIFO 配置 FIFO circular buffer。FIFO 不是独立协议,而是给 SDO client、Gateway ASCII、block transfer 等连续数据流提供缓冲能力。
CO_CONFIG_FIFO_ENABLE
这个位启用 FIFO。没有它,依赖 FIFO 的高级数据流能力无法工作。
CO_CONFIG_FIFO_ALT_READ
这个位启用替代读取方式。SDO client block transfer 可能依赖它来提高连续读写效率。
CO_CONFIG_FIFO_CRC16_CCITT
这个位让 FIFO 支持 CRC16 CCITT 计算。它通常与 block transfer 的数据校验有关,也可能被 Gateway 大块传输使用。
CO_CONFIG_FIFO_ASCII_COMMANDS
这个位让 FIFO 支持 ASCII command 流。Gateway ASCII 需要文本命令解析时会依赖它。
CO_CONFIG_FIFO_ASCII_DATATYPES
这个位让 FIFO 支持 ASCII 数据类型解析。Gateway 做 SDO read/write 时,需要把文本参数转换成 CANopen 数据类型,或把数据转换成文本输出。
Trace:记录变量随时间变化的非标准观测能力
CO_CONFIG_TRACE
CO_CONFIG_TRACE 配置 trace recorder。它不是 CiA 301 标准对象,而是 CANopenNode 提供的变量记录能力。
在 CO_new() 中,只要 CO_CONFIG_TRACE_ENABLE 打开且 CO_GET_CNT(TRACE) > 0,就会分配 co->trace 数组。
Trace 对调试现场问题很有价值,例如记录某个 OD 变量、状态字、错误位随时间变化。但它会占用 RAM 和 CPU,并可能暴露内部变量,不适合所有量产固件默认打开。
CO_CONFIG_TRACE_ENABLE
这个位启用 trace recorder。
CO_CONFIG_TRACE_OWN_INTTYPES
这个位表示不包含标准 inttypes.h,而由工程自己提供 PRIu32、PRId32 等格式宏。它解决某些嵌入式 C 库不完整或格式宏不兼容的问题。
Debug:按模块打开调试输出宏
CO_CONFIG_DEBUG
CO_CONFIG_DEBUG 配置 CANopenNode 不同部分的 debug message。它不改变协议行为,而是改变日志输出能力。
CO_CONFIG_DEBUG_COMMON
这个位定义默认 CO_DEBUG_COMMON(msg) 宏。目标平台需要提供实际输出方式,例如串口、RTT、日志缓冲或空实现。
CO_CONFIG_DEBUG_SDO_CLIENT
这个位启用 SDO client 调试输出。用于分析远端读写、超时、abort 等问题。
CO_CONFIG_DEBUG_SDO_SERVER
这个位启用 SDO server 调试输出。用于分析本节点 OD 访问、下载/上传请求、abort 原因等问题。
CO_new() 中每个协议分支的实际结果
下面不是配置建议,而是代码阅读视角:每个 #if 分支最终会让 CO_new() 多做哪些事。
NMT / Heartbeat 分支
触发条件是 CO_GET_CNT(NMT) == 1U。对象结果是 co->NMT。RX 结果是 NMT slave 接收槽位。TX 结果是 Heartbeat producer 发送槽位;若 CO_CONFIG_NMT_MASTER 打开,还会增加 NMT master 发送槽位。
协议含义是:节点可被 NMT master 控制状态,并周期发送自身状态。
Heartbeat consumer 分支
触发条件是 CO_CONFIG_HB_CONS_ENABLE 打开且 CO_GET_CNT(HB_CONS) == 1U。对象结果是 co->HBcons 和 co->HBconsMonitoredNodes。RX 数量来自 ARR_1016。
协议含义是:本节点监控多个远端 heartbeat producer。
Node Guarding 分支
触发条件是 slave/master 对应 enable 位打开。对象结果是 co->NGslave 或 co->NGmaster。
协议含义是:兼容旧式 master 轮询型节点监控。
Emergency 分支
触发条件是 CO_GET_CNT(EM) == 1U。对象结果是 co->em。若 producer 或 history 打开,会分配 co->em_fifo。RX/TX 由 consumer/producer 位决定。
协议含义是:本节点可以发送错误事件,也可以接收远端错误事件。
SDO server 分支
触发条件是 CO_GET_CNT(SDO_SRV) > 0U。对象结果是 co->SDOserver 数组。每个 server 一个 RX、一个 TX。
协议含义是:远端 client 可以访问本节点对象字典。
SDO client 分支
触发条件是 CO_CONFIG_SDO_CLI_ENABLE 打开且 CO_GET_CNT(SDO_CLI) > 0U。对象结果是 co->SDOclient 数组。每个 client 一个 RX、一个 TX。
协议含义是:本节点可以主动访问远端对象字典。
TIME 分支
触发条件是 CO_CONFIG_TIME_ENABLE 打开且 CO_GET_CNT(TIME) == 1U。对象结果是 co->TIME。consumer 增加 RX,producer 增加 TX。
协议含义是:接收或发布网络时间戳。
SYNC 分支
触发条件是 CO_CONFIG_SYNC_ENABLE 打开且 CO_GET_CNT(SYNC) == 1U。对象结果是 co->SYNC。consumer 增加 RX,producer 增加 TX。
协议含义是:接收或发布同步节拍。
RPDO 分支
触发条件是 CO_CONFIG_RPDO_ENABLE 打开且 CO_GET_CNT(RPDO) > 0U。对象结果是 co->RPDO。RX 数量来自 CNT_RPDO。
协议含义是:本节点消费过程数据。
TPDO 分支
触发条件是 CO_CONFIG_TPDO_ENABLE 打开且 CO_GET_CNT(TPDO) > 0U。对象结果是 co->TPDO。TX 数量来自 CNT_TPDO。
协议含义是:本节点生产过程数据。
LEDs 分支
触发条件是 CO_CONFIG_LEDS_ENABLE 打开且 CO_GET_CNT(LEDS) == 1U。对象结果是 co->LEDs。
协议含义是:计算 CANopen 标准红绿 LED 状态,不直接驱动 GPIO。
GFC 分支
触发条件是 CO_CONFIG_GFC_ENABLE 打开且 CO_GET_CNT(GFC) == 1。对象结果是 co->GFC。RX 和 TX 各增加一个。
协议含义是:全局安全命令的 producer/consumer 支撑。
SRDO 分支
触发条件是 CO_CONFIG_SRDO_ENABLE 打开且 CO_GET_CNT(SRDO) > 0U。对象结果是 co->SRDOGuard 和 co->SRDO。RX/TX 都是 CNT_SRDO * 2。
协议含义是:安全相关过程数据,每个对象消耗成对 CAN 报文槽位。
LSS slave 分支
触发条件是 CO_CONFIG_LSS_SLAVE 打开且 CO_GET_CNT(LSS_SLV) == 1U。对象结果是 co->LSSslave。RX/TX 各一个。
协议含义是:本节点可被 LSS master 识别和配置 Node-ID/bit-rate。
LSS master 分支
触发条件是 CO_CONFIG_LSS_MASTER 打开且 CO_GET_CNT(LSS_MST) == 1U。对象结果是 co->LSSmaster。RX/TX 各一个。
协议含义是:本节点可发现和配置其他 LSS slave。
Gateway ASCII 分支
触发条件是 CO_CONFIG_GTW_ASCII 打开且 CO_GET_CNT(GTWA) == 1U。对象结果是 co->gtwa。
协议含义是:通过文本接口访问 CANopen 网络,底层依赖 SDO client、NMT master、LSS master、FIFO 等能力组合。
Trace 分支
触发条件是 CO_CONFIG_TRACE_ENABLE 打开且 CO_GET_CNT(TRACE) > 0。对象结果是 co->trace。
协议含义是:非标准变量记录能力,用于观测和调试。
宏、协议对象和对象字典的三层关系
宏只决定“代码是否存在”。对象数量决定“对象是否创建”。对象字典决定“协议行为参数是什么”。这三层必须同时成立。
例如:
1 |
这只表示 SYNC 相关代码被编译进来,并允许 producer。是否真的分配 co->SYNC,还要看 CO_GET_CNT(SYNC)。SYNC COB-ID、周期、同步窗口和计数器溢出值,还要看 OD 0x1005、0x1006、0x1007、0x1019。
再例如:
1 |
这只表示 RPDO/TPDO 代码和同步 PDO 语义被编译进来。实际有几个 PDO,要看 CNT_RPDO、CNT_TPDO。每个 PDO 发什么、收什么、何时触发,要看 0x1400/0x1600、0x1800/0x1A00 系列对象。
这也是阅读 CO_new() 时最重要的结论:不要把 CO_CONFIG_xxx 当成对象实例;不要把 OD 条目当成功能已启用;不要把 CO_new() 当成协议初始化的全部。CO_new() 创建容器和对象,后续 CO_CANopenInit()、CO_CANopenInitPDO()、CO_CANopenInitSRDO() 等初始化函数才会把这些对象与 OD 条目、CAN-ID、nodeId、CAN module 下标真正绑定起来。






















