/** * @file can_id.h * @brief CAN 29-bit ID 编解码 — 电力二次设备协议栈 (v2.0) * * ==================== 修订版 ID 编码方案 ==================== * * CAN 总线仲裁规则:ID 值越小,优先级越高。因此 bit28(最高位)决定优先级。 * 本方案将"优先级类别"放在最高 3 位,确保紧急报文(保护动作、遥控执行) * 在总线竞争中优先胜出。 * * 28 27 26 | 25 24 23 22 21 20 19 | 18 17 16 15 14 13 12 | 11 10 9 8 | 7 6 5 4 3 2 1 0 * ┌─────────────┬─────────────────────────────┬─────────────────────────────┬─────────────────┬─────────────────────────────────┐ * │ 优先级类别 │ 目标设备地址 (7-bit) │ 源设备地址 (7-bit) │ 报文类型 │ 批次号 / 命令序列号 / 帧序号 │ * │ PRIO (3bit) │ DST_ADDR (0x00~0x7F) │ SRC_ADDR (0x00~0x7F) │ MSG_TYPE (4bit) │ SEQ / BATCH (8-bit) │ * │ bit28~26 │ bit25~19 │ bit18~12 │ bit11~8 │ bit7~0 │ * └─────────────┴─────────────────────────────┴─────────────────────────────┴─────────────────┴─────────────────────────────────┘ * * 优先级类别 (PRIO, bit28-26): * 000 = 紧急 保护动作信号、遥控直跳 (最高优先级) * 001 = 突发 遥信变位(COS)、遥测越限 (高优先级) * 010 = 命令 遥控选择/执行、定值读写、遥调 (中优先级) * 011 = 对时 广播对时、PTP 辅助 (中优先级) * 100 = 周期 定时遥信/遥测上送(主动总召模式) (低优先级) * 101 = 文件 文件传输/固件升级 (最低优先级) * 110 = 管理 心跳、链路测试、版本查询 (最低优先级) * 111 = 保留 用户自定义扩展 * * 报文类型 (MSG_TYPE, bit11-8) 随 PRIO 不同而有不同含义: * 过程数据类 (PRIO=周期 或 PRIO=突发): * 0000 = 遥信定时 0001 = 遥测定时 0010 = 遥信突发 0011 = 遥测突发 * 0100-0111 = 扩展保留 * 命令类 (PRIO=命令): * 0000 = 遥控-选择 0001 = 遥控-执行 0010 = 遥控-撤销 * 0011 = 定值-写入 0100 = 定值-召唤 0101 = 遥调 * 0110 = 命令ACK 0111 = 命令NAK * 紧急类 (PRIO=紧急): * 0000 = 保护动作 0001 = 遥控直跳 * 对时类 (PRIO=对时): * 0000 = 广播对时 0001 = 对时请求 * 文件类 (PRIO=文件): * 0000 = 首帧 0001 = 续帧 0010 = 尾帧 * 0011 = 文件ACK 0100 = 文件NAK * 管理类 (PRIO=管理): * 0000 = 心跳 0001 = 链路测试 0010 = 版本查询 * * 批次号/序列号 (SEQ, bit7-0): * - 过程数据: 批次号,周期性递增(0-255 循环),用于接收方去重和检测丢帧 * - 命令帧: 命令序列号,匹配请求与应答 * - 文件帧: 帧序号(0-255) * - 管理帧: 保留(0x00) */ #ifndef CAN_ID_H #define CAN_ID_H #include #ifdef __cplusplus extern "C" { #endif /* ================================================================ * 优先级类别 PRIO (bit28-26) * ================================================================ */ typedef enum { PRIO_URGENT = 0, /* 紧急: 保护动作、遥控直跳 */ PRIO_BURST = 1, /* 突发: 遥信变位(COS)、遥测越限 */ PRIO_COMMAND = 2, /* 命令: 遥控选择/执行、定值读写、遥调 */ PRIO_TIMESYNC= 3, /* 对时: 广播对时、PTP 辅助 */ PRIO_PERIODIC= 4, /* 周期: 定时遥信/遥测上送(主动总召) */ PRIO_FILE = 5, /* 文件: 文件传输/固件升级 */ PRIO_MGMT = 6, /* 管理: 心跳、链路测试、版本查询 */ PRIO_RESERVED= 7, /* 保留: 用户自定义扩展 */ } can_prio_e; const char* can_prio_name(can_prio_e prio); /* ================================================================ * 报文类型 MSG_TYPE (bit11-8) — 通用定义 * 具体含义取决于对应的 PRIO 类别 * ================================================================ */ /* ---- 过程数据类 (PRIO=周期 / PRIO=突发) ---- */ typedef enum { MSG_PD_YX_TIMED = 0x0, /* 遥信-定时上送 */ MSG_PD_YC_TIMED = 0x1, /* 遥测-定时上送 */ MSG_PD_YX_BURST = 0x2, /* 遥信-突发变位(COS/SOE) */ MSG_PD_YC_BURST = 0x3, /* 遥测-突发越限 */ MSG_PD_EXT_4 = 0x4, /* 扩展保留 */ MSG_PD_EXT_5 = 0x5, MSG_PD_EXT_6 = 0x6, MSG_PD_EXT_7 = 0x7, } can_msg_pd_e; /* ---- 命令类 (PRIO=命令) ---- */ typedef enum { MSG_CMD_YK_SELECT = 0x0, /* 遥控-选择 */ MSG_CMD_YK_EXEC = 0x1, /* 遥控-执行 */ MSG_CMD_YK_CANCEL = 0x2, /* 遥控-撤销 */ MSG_CMD_DZ_WRITE = 0x3, /* 定值-写入 */ MSG_CMD_DZ_READ = 0x4, /* 定值-召唤(读取) */ MSG_CMD_YT = 0x5, /* 遥调 */ MSG_CMD_ACK = 0x6, /* 命令成功应答 */ MSG_CMD_NAK = 0x7, /* 命令失败应答 */ } can_msg_cmd_e; /* ---- 紧急类 (PRIO=紧急) ---- */ typedef enum { MSG_URG_PROTECT = 0x0, /* 保护动作信号 */ MSG_URG_TRIP = 0x1, /* 遥控直跳 */ } can_msg_urgent_e; /* ---- 对时类 (PRIO=对时) ---- */ typedef enum { MSG_TS_BROADCAST = 0x0, /* 广播对时 */ MSG_TS_REQUEST = 0x1, /* 对时请求 */ } can_msg_timesync_e; /* ---- 文件类 (PRIO=文件) ---- */ typedef enum { MSG_FT_FIRST = 0x0, /* 文件首帧 (含文件名、总段数、总长度) */ MSG_FT_CONT = 0x1, /* 文件续帧 (含段序号 + 段数据) */ MSG_FT_LAST = 0x2, /* 文件尾帧 (含末尾数据 + CRC32) */ MSG_FT_ACK = 0x3, /* 文件应答(成功) */ MSG_FT_NAK = 0x4, /* 文件应答(失败+错误码) */ MSG_FT_READ_REQ = 0x5, /* 文件召唤请求 (主站→从站, 请求上传文件) */ MSG_FT_READ_ACK = 0x6, /* 文件召唤就绪 (从站→主站, 文件存在, 即将发送) */ MSG_FT_READ_NAK = 0x7, /* 文件召唤拒绝 (从站→主站, 文件不存在等) */ } can_msg_file_e; /* ---- 管理类 (PRIO=管理) ---- */ typedef enum { MSG_MG_HEARTBEAT = 0x0, /* 心跳 */ MSG_MG_LINK_TEST = 0x1, /* 链路测试请求 */ MSG_MG_LINK_ACK = 0x2, /* 链路测试应答 */ MSG_MG_VER_QUERY = 0x3, /* 版本查询 */ MSG_MG_VER_ACK = 0x4, /* 版本应答 */ } can_msg_mgmt_e; /* ---- 统一的报文类型(无符号,供通用接口使用) ---- */ typedef uint8_t can_msg_type_t; /* ================================================================ * CAN ID 字段位偏移与掩码 * ================================================================ */ #define CAN_ID_PRIO_SHIFT 26 #define CAN_ID_PRIO_MASK 0x1C000000 /* bit28-26 */ #define CAN_ID_DST_SHIFT 19 #define CAN_ID_DST_MASK 0x03F80000 /* bit25-19 */ #define CAN_ID_SRC_SHIFT 12 #define CAN_ID_SRC_MASK 0x0007F000 /* bit18-12 */ #define CAN_ID_MSG_SHIFT 8 #define CAN_ID_MSG_MASK 0x00000F00 /* bit11-8 */ #define CAN_ID_SEQ_MASK 0x000000FF /* bit7-0 */ #define CAN_ID_BROADCAST 0x7F /* 广播地址 */ /* ================================================================ * ID 编解码 API(全部 inline,无函数调用开销) * ================================================================ */ /** 组装 29-bit CAN ID */ static inline uint32_t can_id_build(can_prio_e prio, uint8_t dst_addr, uint8_t src_addr, can_msg_type_t msg_type, uint8_t seq) { return ((uint32_t)(prio & 0x07) << CAN_ID_PRIO_SHIFT) | ((uint32_t)(dst_addr & 0x7F) << CAN_ID_DST_SHIFT) | ((uint32_t)(src_addr & 0x7F) << CAN_ID_SRC_SHIFT) | ((uint32_t)(msg_type & 0x0F) << CAN_ID_MSG_SHIFT) | ((uint32_t)(seq & 0xFF)); } /* ---- 字段提取 ---- */ static inline can_prio_e can_id_get_prio(uint32_t can_id) { return (can_prio_e)((can_id & CAN_ID_PRIO_MASK) >> CAN_ID_PRIO_SHIFT); } static inline uint8_t can_id_get_dst(uint32_t can_id) { return (can_id & CAN_ID_DST_MASK) >> CAN_ID_DST_SHIFT; } static inline uint8_t can_id_get_src(uint32_t can_id) { return (can_id & CAN_ID_SRC_MASK) >> CAN_ID_SRC_SHIFT; } static inline can_msg_type_t can_id_get_msg_type(uint32_t can_id) { return (can_msg_type_t)((can_id & CAN_ID_MSG_MASK) >> CAN_ID_MSG_SHIFT); } static inline uint8_t can_id_get_seq(uint32_t can_id) { return (uint8_t)(can_id & CAN_ID_SEQ_MASK); } /* ---- 判断函数 ---- */ static inline int can_id_is_broadcast(uint32_t can_id) { return can_id_get_dst(can_id) == CAN_ID_BROADCAST; } static inline int can_id_is_for_me(uint32_t can_id, uint8_t my_addr) { uint8_t dst = can_id_get_dst(can_id); return (dst == my_addr) || (dst == CAN_ID_BROADCAST); } /** 判断是否为过程数据帧(遥信/遥测) */ static inline int can_id_is_process_data(uint32_t can_id) { can_prio_e p = can_id_get_prio(can_id); return (p == PRIO_PERIODIC) || (p == PRIO_BURST); } /** 判断是否为命令帧 */ static inline int can_id_is_command(uint32_t can_id) { return can_id_get_prio(can_id) == PRIO_COMMAND; } /** 判断是否为命令应答(ACK或NAK) */ static inline int can_id_is_cmd_response(uint32_t can_id) { if (can_id_get_prio(can_id) != PRIO_COMMAND) return 0; can_msg_type_t mt = can_id_get_msg_type(can_id); return (mt == MSG_CMD_ACK) || (mt == MSG_CMD_NAK); } /* ---- 调试辅助 ---- */ int can_id_to_string(uint32_t can_id, char *buf, int buf_size); #ifdef __cplusplus } #endif #endif /* CAN_ID_H */