linux_learn/learn/CAN/include/can_id.h

233 lines
11 KiB
C
Raw 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_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 */