/** * @file can_frame.h * @brief CAN 帧数据域组帧/解帧 — 电力二次设备协议栈 (v2.0) * * v2.0 变更: * - 遥信从 1-bit 改为 2-bit 双位置 (IEC 61850 DPS) * - 遥测从 uint16 改为 float (IEEE 754 大端) * - 增加突发打包格式 (一帧可携带多个突变点) * - 定时数据与突发数据使用不同帧格式 * * 遥信双位置编码: * 00 = 中间态/不确定 (intermediate) * 01 = 分 (OFF) * 10 = 合 (ON) * 11 = 无效/故障 (invalid/faulty) * * 遥测浮点:IEEE 754 single-precision, 大端字节序 (与网络字节序一致) */ #ifndef CAN_FRAME_H #define CAN_FRAME_H #include #include #ifdef __cplusplus extern "C" { #endif /* ================================================================ * 常量 * ================================================================ */ #define CAN2_MAX_DLEN 8 /* CAN 2.0 最大数据长度 */ #define CANFD_MAX_DLEN 64 /* CAN FD 最大数据长度 */ /* 遥信双位置编码 */ #define YX_DPS_INTERMEDIATE 0 /* 中间态 */ #define YX_DPS_OFF 1 /* 分 */ #define YX_DPS_ON 2 /* 合 */ #define YX_DPS_INVALID 3 /* 无效/故障 */ /* 遥测浮点数特殊值 */ #define YC_FLOAT_NAN 0x7FC00000 /* NaN — 无效/未初始化 */ #define YC_FLOAT_OVERFLOW 0x7F800000 /* +Inf — 溢出/超量程 */ /* 突发队列容量 */ #define BURST_QUEUE_MAX 256 /* 最大排队点数 */ #define BURST_MAX_PER_FRAME 9 /* CAN FD 单帧最多打包点数 (每个点7字节) */ /* ================================================================ * 错误码 * ================================================================ */ typedef enum { ERR_OK = 0x0000, ERR_INVALID_POINT = 0x0001, ERR_NOT_SELECTED = 0x0002, ERR_TIMEOUT = 0x0003, ERR_ADDR_OUT_RANGE = 0x0004, ERR_VALUE_OUT_RANGE = 0x0005, ERR_WRITE_FLASH_FAIL = 0x0006, ERR_CRC_FAIL = 0x0007, ERR_FILE_NOT_FOUND = 0x0008, ERR_SEG_OUT_ORDER = 0x0009, ERR_BUSY = 0x000A, ERR_UNKNOWN_CMD = 0x000B, } can_err_code_e; /* ================================================================ * 数据域结构体 — 过程数据 (遥信/遥测) * ================================================================ */ /** * 定时遥信帧 — 多点上送 * 数据域格式: 起始地址(2B) + 遥信位对数据(N字节) * 每字节 = 4 个遥信点 (每点 2-bit) * bit[1:0] = 点0, bit[3:2] = 点1, bit[5:4] = 点2, bit[7:6] = 点3 */ typedef struct { uint16_t start_addr; /* 遥信起始地址 */ uint8_t data[CANFD_MAX_DLEN - 2]; /* 位对数据 */ uint8_t data_len; /* 有效字节数 (每字节=4个点) */ } can_yx_timed_t; /** * 定时遥测帧 — 多点上送 * 数据域格式: 起始地址(2B) + 浮点值数组 (每个4字节, IEEE 754) */ typedef struct { uint16_t start_addr; /* 遥测起始地址 */ float values[(CANFD_MAX_DLEN - 2) / 4]; /* 浮点值数组 */ uint8_t value_count; /* 遥测值个数 */ } can_yc_timed_t; /** * 突发遥信点 — 单点 (用于队列元素) * 变位时推入队列,协议栈短时间内取空并打包发送 */ typedef struct { uint16_t addr; /* 遥信地址 */ uint8_t state; /* 新状态 (2-bit DPS 编码) */ uint32_t timestamp_ms; /* 时标 (毫秒, CLOCK_MONOTONIC) */ } can_yx_burst_point_t; /** * 突发遥信打包帧 — 可携带多个突变点 (CAN FD 模式) * 数据域格式: 点数(1B) + [地址(2B)+状态(1B)+时标(4B)] * N * 每个点 7 字节, FD 64字节最多 9 个点 */ typedef struct { uint8_t point_count; /* 本帧包含的突发点数 */ can_yx_burst_point_t points[BURST_MAX_PER_FRAME]; } can_yx_burst_packed_t; /** * 突发遥测点 — 单点 (用于队列元素) */ typedef struct { uint16_t addr; /* 遥测地址 */ float value; /* 遥测值 (IEEE 754) */ uint32_t timestamp_ms; /* 时标 (毫秒) */ } can_yc_burst_point_t; /** * 突发遥测打包帧 — 可携带多个突变点 * 数据域格式: 点数(1B) + [地址(2B)+浮点值(4B)+时标(4B)] * N * 每个点 10 字节, FD 64字节最多 6 个点 */ typedef struct { uint8_t point_count; can_yc_burst_point_t points[6]; /* 64/10 = 6 */ } can_yc_burst_packed_t; /* ================================================================ * 数据域结构体 — 命令类 * ================================================================ */ /* 遥控选择/执行 */ typedef struct { uint8_t point; /* 遥控点号 */ uint16_t timeout_ms; /* 超时(仅选择步骤有效) */ } can_yk_req_t; /* 遥控应答 */ typedef struct { uint8_t point; uint8_t result; uint16_t err_code; } can_yk_ack_t; /* 定值写入请求 */ typedef struct { uint16_t addr; float value; /* 定值(浮点) */ } can_dz_req_t; /* 定值召唤请求 — 读取当前定值 (addr=0xFFFF 表示召唤全部) */ typedef struct { uint16_t addr; /* 定值地址, 0xFFFF=召唤全部 */ } can_dz_read_req_t; /* 定值召唤应答 — 返回当前定值 */ typedef struct { uint16_t addr; uint8_t result; /* 0=成功 */ uint16_t err_code; float value; /* 当前定值 (仅 result=0 时有效) */ } can_dz_read_ack_t; /* 定值应答 */ typedef struct { uint16_t addr; uint8_t result; uint16_t err_code; } can_dz_ack_t; /* 遥调请求 */ typedef struct { uint8_t point; uint8_t op_type; /* 0=升, 1=降, 2=急停, 3=设值 */ float value; /* 目标值(浮点) */ } can_yt_req_t; /* ================================================================ * 数据域结构体 — 紧急类 * ================================================================ */ /* 保护动作信号 */ typedef struct { uint8_t event_type; /* 事件类型码 */ uint8_t phase; /* 故障相别 (0=A,1=B,2=C,3=AB,...) */ uint16_t fault_value; /* 故障量 (规格化值) */ uint32_t timestamp_ms; /* 时标 */ } can_urgent_protect_t; /* ================================================================ * 数据域结构体 — 文件传输类 * ================================================================ */ typedef struct { uint16_t file_id; uint16_t total_seg; uint32_t file_size; char filename[56]; /* FD: 64-8=56 字节留给文件名 */ } can_ft_first_t; typedef struct { uint16_t file_id; uint16_t seg_num; uint8_t data[CANFD_MAX_DLEN - 4]; uint8_t data_len; } can_ft_seg_t; typedef struct { uint16_t file_id; uint16_t seg_num; uint8_t data[CANFD_MAX_DLEN - 8]; uint8_t data_len; uint32_t crc32; /* 末帧末尾 4 字节 */ } can_ft_last_t; typedef struct { uint16_t file_id; uint8_t result; uint16_t err_code; } can_ft_ack_t; /* 文件召唤请求 (主站→从站: 请求上传文件) */ typedef struct { uint16_t file_id; /* 文件 ID (主站分配) */ char filepath[56]; /* 请求的文件路径/通配符 */ } can_ft_read_req_t; /* 文件召唤就绪应答 (从站→主站: 文件存在, 即将开始发送) */ typedef struct { uint16_t file_id; uint32_t file_size; /* 文件大小 */ uint16_t total_seg; /* 总段数 */ char filename[48]; /* 实际文件名 */ } can_ft_read_ack_t; /* ================================================================ * 数据域结构体 — 对时 / 管理类 * ================================================================ */ typedef struct { uint32_t sec; /* 秒 (UNIX 时间戳) */ uint32_t nsec; /* 纳秒 */ } can_ts_frame_t; typedef enum { MG_CMD_HEARTBEAT = 0x01, MG_CMD_LINK_TEST = 0x02, MG_CMD_VER_QUERY = 0x03, MG_CMD_VER_ACK = 0x04, } mg_cmd_e; typedef struct { uint8_t cmd; uint8_t param; uint16_t reserved; } can_mg_frame_t; /* ================================================================ * 突发数据队列 (应用层 → 协议栈) * ================================================================ */ /** * 遥信突发队列 * 应用层检测到遥信变位后 push,协议栈定时 drain 并打包发送 */ typedef struct { can_yx_burst_point_t items[BURST_QUEUE_MAX]; volatile int head; /* 生产者(应用层/中断)写入位置 */ volatile int tail; /* 消费者(协议栈)读取位置 */ volatile int count; /* 当前队列长度 */ } can_burst_yx_queue_t; typedef struct { can_yc_burst_point_t items[BURST_QUEUE_MAX]; volatile int head; volatile int tail; volatile int count; } can_burst_yc_queue_t; /* ---- 队列操作 (lock-free, 单生产者单消费者) ---- */ /** 初始化突发队列 */ void can_burst_yx_queue_init(can_burst_yx_queue_t *q); void can_burst_yc_queue_init(can_burst_yc_queue_t *q); /** 推入队列 (应用层调用,可在中断上下文) */ int can_burst_yx_queue_push(can_burst_yx_queue_t *q, const can_yx_burst_point_t *pt); int can_burst_yc_queue_push(can_burst_yc_queue_t *q, const can_yc_burst_point_t *pt); /** 弹出队列 (协议栈调用) 返回实际弹出的点数 */ int can_burst_yx_queue_pop(can_burst_yx_queue_t *q, can_yx_burst_point_t *out, int max); int can_burst_yc_queue_pop(can_burst_yc_queue_t *q, can_yc_burst_point_t *out, int max); /** 队列是否为空 */ int can_burst_yx_queue_empty(const can_burst_yx_queue_t *q); int can_burst_yc_queue_empty(const can_burst_yc_queue_t *q); /* ================================================================ * Pack / Unpack API * ================================================================ */ /* 过程数据 — 定时 */ int can_pack_yx_timed(const can_yx_timed_t *yx, uint8_t *data, int max_len); int can_pack_yc_timed(const can_yc_timed_t *yc, uint8_t *data, int max_len); int can_unpack_yx_timed(can_yx_timed_t *yx, const uint8_t *data, int len); int can_unpack_yc_timed(can_yc_timed_t *yc, const uint8_t *data, int len); /* 过程数据 — 突发打包 */ int can_pack_yx_burst(const can_yx_burst_packed_t *pkt, uint8_t *data, int max_len); int can_pack_yc_burst(const can_yc_burst_packed_t *pkt, uint8_t *data, int max_len); int can_unpack_yx_burst(can_yx_burst_packed_t *pkt, const uint8_t *data, int len); int can_unpack_yc_burst(can_yc_burst_packed_t *pkt, const uint8_t *data, int len); /* 命令类 */ int can_pack_yk_req(const can_yk_req_t *yk, uint8_t *data, int max_len); int can_pack_yk_ack(const can_yk_ack_t *ack, uint8_t *data, int max_len); int can_pack_dz_req(const can_dz_req_t *dz, uint8_t *data, int max_len); int can_pack_dz_ack(const can_dz_ack_t *ack, uint8_t *data, int max_len); int can_pack_dz_read_req(const can_dz_read_req_t *dz, uint8_t *data, int max_len); int can_pack_dz_read_ack(const can_dz_read_ack_t *ack, uint8_t *data, int max_len); int can_pack_yt_req(const can_yt_req_t *yt, uint8_t *data, int max_len); int can_unpack_yk_req(can_yk_req_t *yk, const uint8_t *data, int len); int can_unpack_yk_ack(can_yk_ack_t *ack, const uint8_t *data, int len); int can_unpack_dz_req(can_dz_req_t *dz, const uint8_t *data, int len); int can_unpack_dz_ack(can_dz_ack_t *ack, const uint8_t *data, int len); int can_unpack_dz_read_req(can_dz_read_req_t *dz, const uint8_t *data, int len); int can_unpack_dz_read_ack(can_dz_read_ack_t *ack, const uint8_t *data, int len); int can_unpack_yt_req(can_yt_req_t *yt, const uint8_t *data, int len); /* 文件类 */ int can_pack_ft_first(const can_ft_first_t *ft, uint8_t *data, int max_len); int can_pack_ft_seg(const can_ft_seg_t *ft, uint8_t *data, int max_len); int can_pack_ft_last(const can_ft_last_t *ft, uint8_t *data, int max_len); int can_pack_ft_ack(const can_ft_ack_t *ack, uint8_t *data, int max_len); int can_pack_ft_read_req(const can_ft_read_req_t *req, uint8_t *data, int max_len); int can_pack_ft_read_ack(const can_ft_read_ack_t *ack, uint8_t *data, int max_len); int can_unpack_ft_first(can_ft_first_t *ft, const uint8_t *data, int len); int can_unpack_ft_seg(can_ft_seg_t *ft, const uint8_t *data, int len); int can_unpack_ft_last(can_ft_last_t *ft, const uint8_t *data, int len); int can_unpack_ft_ack(can_ft_ack_t *ack, const uint8_t *data, int len); int can_unpack_ft_read_req(can_ft_read_req_t *req, const uint8_t *data, int len); int can_unpack_ft_read_ack(can_ft_read_ack_t *ack, const uint8_t *data, int len); /* 对时/管理 */ int can_pack_ts(const can_ts_frame_t *ts, uint8_t *data, int max_len); int can_pack_mg(const can_mg_frame_t *mg, uint8_t *data, int max_len); int can_unpack_ts(can_ts_frame_t *ts, const uint8_t *data, int len); int can_unpack_mg(can_mg_frame_t *mg, const uint8_t *data, int len); /* 工具函数 */ uint32_t can_crc32(const uint8_t *data, int len); const char* can_err_str(uint16_t err_code); /** * 从 2-bit 编码中提取单个遥信点的状态 (0-3) * @param bitmap 遥信位对数据 * @param offset 点偏移 (0 = 第一个点) */ static inline uint8_t can_yx_get_dps(const uint8_t *bitmap, int offset) { int byte_idx = offset / 4; int bit_pos = (offset % 4) * 2; return (bitmap[byte_idx] >> bit_pos) & 0x03; } /** * 设置单个遥信点的双位置状态 */ static inline void can_yx_set_dps(uint8_t *bitmap, int offset, uint8_t state) { int byte_idx = offset / 4; int bit_pos = (offset % 4) * 2; bitmap[byte_idx] &= ~(0x03 << bit_pos); bitmap[byte_idx] |= ((state & 0x03) << bit_pos); } #ifdef __cplusplus } #endif #endif /* CAN_FRAME_H */