refactor(protocol): 新增 comm_protocol 协议收发解析层,ble_transport 退化为纯传输

- 删除 protocol.h,帧定义和标定命令码统一迁入 comm_protocol.h
- 新增 comm_protocol.c:上行组包(CoP/标定响应)、下行解析+msgq 分发
- ble_transport 剥离所有协议解析和参数 getter,仅保留字节收发+rx 回调注册
- main.c 初始化顺序:ble_transport → comm_protocol → adv_start → cal_init
- 更新 README 架构图和项目结构表,补充标定流程章节
- 更新 BALANCE_BOARD_PROTOCOL.md 增加标定命令/响应帧格式文档
This commit is contained in:
2026-05-15 18:06:33 +08:00
parent 1a65bf0f22
commit 0979866a7c
8 changed files with 627 additions and 329 deletions
+31 -7
View File
@@ -2,15 +2,39 @@
#include <stdbool.h>
#include <stdint.h>
/** @brief BLE 原始数据接收回调类型。 */
typedef void (*ble_rx_cb_t)(const uint8_t *data, uint16_t len);
/**
* @brief 初始化 BLE 协议栈和 NUS 服务。
*
* @retval 0 成功。
* @retval 负值 初始化错误码。
*/
int ble_transport_init(void);
/** @brief 启动 BLE 可连接广播。 */
void ble_transport_adv_start(void);
/**
* @brief 通过 NUS 发送二进制数据,内部自动重试最多 3 次。
*
* @param data 待发送字节缓冲区。
* @param len 字节数。
*/
void ble_transport_send(const uint8_t *data, uint16_t len);
/**
* @brief 检查 BLE 连接是否就绪(已连接且通知已使能)。
*
* @retval true 可发送数据。
* @retval false 未就绪。
*/
bool ble_transport_is_ready(void);
/* --- 下行参数 getter --- */
float ble_transport_get_resistance_K(void);
float ble_transport_get_resistance_B(void);
float ble_transport_get_resistance_Tau(void);
float ble_transport_get_spotter_threshold(void);
bool ble_transport_get_spotter_enabled(void);
int64_t ble_transport_get_last_heartbeat_ms(void);
/**
* @brief 注册上层原始数据接收回调,NUS 收到数据时透传调用。
*
* @param cb 回调函数指针。
*/
void ble_transport_register_rx_cb(ble_rx_cb_t cb);
+242
View File
@@ -0,0 +1,242 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/crc.h>
/* ═══════════════════════════════════════════════════════════════════
* 帧常量
* ═══════════════════════════════════════════════════════════════════ */
/* 双帧头比单字节魔数更容易在连续字节流里完成包起始判定 */
#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);
-136
View File
@@ -1,136 +0,0 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <zephyr/sys/crc.h>
/* ─── 帧常量 ─── */
/* 双帧头比单字节魔数更容易在连续字节流里完成包起始判定。 */
#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 /* 下行: 心跳包 */
/* CoP flags 位定义 */
#define COP_FLAG_FORCE_VALID 0x01U /* 总力 > 阈值,CoP 坐标有意义 */
/* ─── 板面几何 & 传感器标定 ─── */
/* 传感器到板面中心的半宽、半长 (cm)。 */
#define BOARD_HALF_WIDTH_CM 42.5f
#define BOARD_HALF_LENGTH_CM 16.0f
/*
* ADC → kg 换算推导:
* GML670 50kg: 灵敏度 1.75 mV/V, 激励 5V → 满量程输出 8.75 mV
* ADS1256: PGA=64, VREF=2.5V → 满量程 ±78.125 mV
* 50kg 对应 ADC counts = 8.75 / 78.125 × 8388607 ≈ 939524
* 1 LSB = 50.0 / 939524 ≈ 5.322e-5 kg
* 如实际激励电压或 VREF 不同,按比例修正此系数。
*/
#define SENSOR_EXCITATION_V 5.0f
#define SENSOR_SENSITIVITY_MVV 1.75f
#define SENSOR_RATED_LOAD_KG 50.0f
#define ADS1256_VREF_V 2.5f
#define ADS1256_PGA 64
#define ADC_COUNTS_AT_RATED \
((SENSOR_SENSITIVITY_MVV * SENSOR_EXCITATION_V) / (2.0f * ADS1256_VREF_V * 1000.0f / ADS1256_PGA) * 8388607.0f)
#define ADC_TO_FORCE_SCALE (SENSOR_RATED_LOAD_KG / ADC_COUNTS_AT_RATED)
/* 总重低于此值时 CoP 无意义(除零保护),单位 kg */
#define COP_MIN_FORCE_THRESHOLD 0.5f
/* ─── 上行: 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; /* = PROTO_SYNC0 */
uint8_t sync1; /* = PROTO_SYNC1 */
uint8_t type; /* = PROTO_TYPE_COP */
uint8_t flags; /* bit0: force_valid */
int16_t cop_x; /* 压力中心 X (cm),板面坐标系 */
int16_t cop_y; /* 压力中心 Y (cm),板面坐标系 */
int16_t force; /* 总重量 (kg) */
uint8_t crc; /* CRC-8/MAXIM, 校验 type~force */
} __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; /* = PROTO_SYNC0 */
uint8_t sync1; /* = PROTO_SYNC1 */
uint8_t type; /* = PROTO_TYPE_ENCODER */
uint8_t flags; /* 预留,填 0 */
float cable_length; /* 拉索长度 (mm) */
float velocity; /* 拉索速度 (mm/s) */
uint8_t crc; /* CRC-8/MAXIM */
} __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; /* = PROTO_SYNC0 */
uint8_t sync1; /* = PROTO_SYNC1 */
uint8_t type; /* = PROTO_TYPE_RESISTANCE */
float K; /* 刚度 (N/m) */
float B; /* 阻尼 (Ns/m) */
float Tau; /* 时间常数 (s) */
uint8_t crc; /* CRC-8/MAXIM */
} __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; /* = PROTO_SYNC0 */
uint8_t sync1; /* = PROTO_SYNC1 */
uint8_t type; /* = PROTO_TYPE_SPOTTER */
float threshold; /* 保护力阈值 (N) */
uint8_t enable; /* 0 = 关闭, 1 = 启用 */
uint8_t crc; /* CRC-8/MAXIM */
} __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; /* = PROTO_SYNC0 */
uint8_t sync1; /* = PROTO_SYNC1 */
uint8_t type; /* = PROTO_TYPE_HEARTBEAT */
uint8_t counter; /* 滚动计数,用于活性检测 */
uint8_t crc; /* CRC-8/MAXIM */
} __attribute__((packed));
union heartbeat_pkt_t {
struct heartbeat_frame_t frame;
uint8_t bytes[sizeof(struct heartbeat_frame_t)];
};