233 lines
11 KiB
C
233 lines
11 KiB
C
/**
|
||
* @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 <stdint.h>
|
||
|
||
#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 */
|