#pragma once #include #include #include #include /* ═══════════════════════════════════════════════════════════════════ * 帧常量 * ═══════════════════════════════════════════════════════════════════ */ /* 双帧头比单字节魔数更容易在连续字节流里完成包起始判定 */ #define PROTO_SYNC0 0xAAU #define PROTO_SYNC1 0x55U #define PROTO_TYPE_COP 0x01U /* 上行: 压力中心 (CoP) 帧 */ #define PROTO_TYPE_ENCODER 0x02U /* 上行: 电机编码器帧(预留) */ #define PROTO_TYPE_RESISTANCE 0x10U /* 下行: 阻力参数 (K, B, Tau) */ #define PROTO_TYPE_SPOTTER 0x11U /* 下行: Spotter Mode 保护阈值 */ #define PROTO_TYPE_HEARTBEAT 0x20U /* 下行: 心跳包 */ #define PROTO_TYPE_CAL_CMD 0x30U /* 下行: 标定命令 */ #define PROTO_TYPE_CAL_RESP 0x31U /* 上行: 标定响应 */ /* CoP flags 位定义 */ #define COP_FLAG_FORCE_VALID 0x01U /* 总力 > 阈值,CoP 坐标有意义 */ /* ═══════════════════════════════════════════════════════════════════ * 帧结构定义 * ═══════════════════════════════════════════════════════════════════ */ /* ─── 上行: CoP 帧 (11 bytes, 50 Hz) ─── * [AA 55 01 flags cop_x(2) cop_y(2) force(2) crc] */ struct cop_frame_t { uint8_t sync0; uint8_t sync1; uint8_t type; uint8_t flags; int16_t cop_x; int16_t cop_y; int16_t force; uint8_t crc; } __attribute__((packed)); union cop_pkt_t { struct cop_frame_t frame; uint8_t bytes[sizeof(struct cop_frame_t)]; }; /* ─── 上行: 电机编码器帧 (13 bytes, 预留) ─── * [AA 55 02 flags cable_length(4) velocity(4) crc] */ struct encoder_frame_t { uint8_t sync0; uint8_t sync1; uint8_t type; uint8_t flags; float cable_length; float velocity; uint8_t crc; } __attribute__((packed)); union encoder_pkt_t { struct encoder_frame_t frame; uint8_t bytes[sizeof(struct encoder_frame_t)]; }; /* ─── 下行: 阻力参数帧 (16 bytes) ─── * [AA 55 10 K(4) B(4) Tau(4) crc] */ struct resistance_frame_t { uint8_t sync0; uint8_t sync1; uint8_t type; float K; float B; float Tau; uint8_t crc; } __attribute__((packed)); union resistance_pkt_t { struct resistance_frame_t frame; uint8_t bytes[sizeof(struct resistance_frame_t)]; }; /* ─── 下行: Spotter Mode 帧 (9 bytes) ─── * [AA 55 11 threshold(4) enable crc] */ struct spotter_frame_t { uint8_t sync0; uint8_t sync1; uint8_t type; float threshold; uint8_t enable; uint8_t crc; } __attribute__((packed)); union spotter_pkt_t { struct spotter_frame_t frame; uint8_t bytes[sizeof(struct spotter_frame_t)]; }; /* ─── 下行: 心跳帧 (5 bytes, ~1 Hz) ─── * [AA 55 20 counter crc] */ struct heartbeat_frame_t { uint8_t sync0; uint8_t sync1; uint8_t type; uint8_t counter; uint8_t crc; } __attribute__((packed)); union heartbeat_pkt_t { struct heartbeat_frame_t frame; uint8_t bytes[sizeof(struct heartbeat_frame_t)]; }; /* ═══════════════════════════════════════════════════════════════════ * 标定子命令码 & 状态码 * ═══════════════════════════════════════════════════════════════════ */ #define CAL_SUBCMD_START_L1 0x01U #define CAL_SUBCMD_TARE_CH 0x02U #define CAL_SUBCMD_GAIN_CH 0x03U #define CAL_SUBCMD_COMMIT_L1 0x04U #define CAL_SUBCMD_ABORT 0x05U #define CAL_SUBCMD_START_L2 0x10U #define CAL_SUBCMD_RECORD_GRID 0x11U #define CAL_SUBCMD_COMMIT_L2 0x12U #define CAL_SUBCMD_ERASE 0x20U #define CAL_SUBCMD_QUERY 0x21U #define CAL_STATUS_OK 0x00U #define CAL_STATUS_ERR_STATE 0x01U #define CAL_STATUS_ERR_NVS 0x02U #define CAL_STATUS_ERR_PARAM 0x03U /* ─── 下行: 标定命令帧 (10 bytes) ─── * [AA 55 30 subcmd target param(4) crc] */ struct cal_cmd_frame_t { uint8_t sync0; uint8_t sync1; uint8_t type; uint8_t subcmd; uint8_t target; float param; uint8_t crc; } __attribute__((packed)); union cal_cmd_pkt_t { struct cal_cmd_frame_t frame; uint8_t bytes[sizeof(struct cal_cmd_frame_t)]; }; /* ─── 上行: 标定响应帧 (14 bytes) ─── * [AA 55 31 status subcmd data(8) crc] */ struct cal_resp_frame_t { uint8_t sync0; uint8_t sync1; uint8_t type; uint8_t status; uint8_t subcmd; uint8_t data[8]; uint8_t crc; } __attribute__((packed)); union cal_resp_pkt_t { struct cal_resp_frame_t frame; uint8_t bytes[sizeof(struct cal_resp_frame_t)]; }; /* ═══════════════════════════════════════════════════════════════════ * 下行消息队列 payload * ═══════════════════════════════════════════════════════════════════ */ struct comm_msg_resistance { float K, B, Tau; }; struct comm_msg_spotter { float threshold; bool enable; }; struct comm_msg_heartbeat { uint8_t counter; }; enum comm_msg_type { COMM_MSG_RESISTANCE, COMM_MSG_SPOTTER, COMM_MSG_HEARTBEAT, }; struct comm_msg { enum comm_msg_type type; union { struct comm_msg_resistance resistance; struct comm_msg_spotter spotter; struct comm_msg_heartbeat heartbeat; }; }; /* ═══════════════════════════════════════════════════════════════════ * 协议层 API * ═══════════════════════════════════════════════════════════════════ */ /** * @brief 初始化协议层,注册 BLE 传输层收数据回调。 * * 必须在 ble_transport_init() 之后调用。 * * @retval 0 成功。 */ int comm_protocol_init(void); /** * @brief 组包并发送 CoP 上行帧。 * * @param flags COP_FLAG_* 位组合。 * @param cop_x 压力中心 X (cm)。 * @param cop_y 压力中心 Y (cm)。 * @param force 总力 (kg)。 */ void comm_protocol_send_cop(uint8_t flags, int16_t cop_x, int16_t cop_y, int16_t force); /** * @brief 组包并发送标定响应上行帧。 * * @param status 结果状态码 (CAL_STATUS_*)。 * @param subcmd 对应的子命令回显。 * @param data 8 字节响应数据。 */ void comm_protocol_send_cal_resp(uint8_t status, uint8_t subcmd, const uint8_t data[8]); /** * @brief 获取下行消息队列指针,业务层通过 k_msgq_get() 消费。 * * @return 指向内部 msgq 的指针。 */ struct k_msgq *comm_protocol_get_msgq(void);