linux_learn/learn/CAN/include/can_frame.h

397 lines
14 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @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 <stdint.h>
#include <linux/can.h>
#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 */