Files
balance-board/README.md
T
pNexus 0d2fd30b3a fix(README): 修正文档中协议字段链接路径
fix(main): 修正主循环中延时以保持程序运行
2026-05-07 09:03:45 +08:00

303 lines
12 KiB
Markdown
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.
# GML670 Balance Board
> 当前目录是 `GML670x4_V2`。代码内的 CMake 项目名是 `GML670_V2`BLE 设备名是 `GML670_System`。
>
> 这份 README 只描述当前 `V2` 工程已经落地的实现。更细的协议字段和示例帧见 [BALANCE_BOARD_PROTOCOL.md](./doc/BALANCE_BOARD_PROTOCOL.md)。
---
## 目录
1. [产品概述](#1-产品概述)
2. [硬件组成](#2-硬件组成)
3. [系统架构](#3-系统架构)
4. [功能需求与当前状态](#4-功能需求与当前状态)
5. [BLE 协议规格](#5-ble-协议规格)
6. [ADS1256 / CoP 规格](#6-ads1256--cop-规格)
7. [开发环境与板级配置](#7-开发环境与板级配置)
8. [项目结构](#8-项目结构)
9. [构建与烧录](#9-构建与烧录)
10. [日志与当前约束](#10-日志与当前约束)
---
## 1. 产品概述
本工程是平衡板的 nRF5340 App Core 固件,当前版本的主链路很简单:
- 上电后等待 2 秒,初始化 BLE NUS 传输层并启动广播
- 初始化 ADS1256,完成一次 tare 去皮
- 创建独立的传感器线程,循环读取 4 路差分输入
- 在板端完成重量换算和 CoPCenter of Pressure,压力中心)解算
- 通过 BLE NUS 持续上报 17 字节 CoP 二进制帧
- 接收基站下发的阻力参数、Spotter 参数和心跳帧,并保存在内存里
当前实现已经把采样、解算和上行链路拆成了独立模块:`main.c` 只负责启动,BLE 在 `src/ble_transport.c`ADS1256 在 `src/ads1256.c`,采样与 CoP 在 `src/sensor.c`
## 2. 硬件组成
当前仓库里能直接确认的硬件如下:
| 器件 | 型号 / 对象 | 接口 | 作用 | 状态 |
|------|-------------|------|------|------|
| 主控 | nRF5340 DK App Core | - | 运行 BLE、SPI 采样和 CoP 解算 | ✅ |
| ADC | ADS1256 | SPI1 | 4 路多通道采样 | ✅ |
| 传感器输入 | 4 路差分输入 | ADS1256 MUX | 用于四角重量采样 | ✅ |
| 基站链路 | Nordic UART Service | BLE | 上报 CoP、接收控制参数 | ✅ |
| 编码器链路 | 预留 | BLE 协议 | 编码器上行帧已定义,当前固件不发送 | 🔲 |
### ADS1256 相关引脚
`app.overlay` 当前把 ADS1256 接到 `spi1`,连线如下:
| 信号 | nRF5340 引脚 | 说明 |
|------|--------------|------|
| SCK | `P1.15` | `spi1` 时钟 |
| MOSI | `P1.13` | `spi1` 主发从收 |
| MISO | `P1.14` | `spi1` 主收从发 |
| CS | `P1.12` | 片选 |
| DRDY | `P1.11` | 数据就绪输入 |
| RESET | `P1.10` | ADS1256 复位控制 |
虽然 `app.overlay` 里有 `ads1256@0` 节点,但当前实现没有走单独的 Zephyr ADS1256 驱动,实际访问路径还是 `src/ads1256.c` 里直接操作 `spi1` 和 GPIO alias。
## 3. 系统架构
```text
4 路差分传感器
|
v
ADS1256 + SPI1
|
v
src/ads1256.c
|
v
src/sensor.c
|
+--> 去皮 / 17 次均值 / 死区抑制 / 重量换算 / CoP 解算
|
+--> CoP 二进制帧 --> src/ble_transport.c --> BLE NUS Notify
|
+<-- Resistance / Spotter / Heartbeat <-- BLE NUS Write
```
`src/main.c` 本身不承载采样逻辑,它只按顺序调用 `ble_transport_init()``ble_transport_adv_start()``sensor_init()`
## 4. 功能需求与当前状态
### 4.1 采样与解算
| 项目 | 说明 | 状态 |
|------|------|------|
| 4 路轮询采样 | 通过 ADS1256 MUX 依次读取 `0x01 / 0x23 / 0x45 / 0x67` 四组差分通道 | ✅ |
| tare 去皮 | 每路读取 17 个样本,排序后取中位数作为零点偏移 | ✅ |
| 运行期滤波 | 每帧每通道采 17 次并取均值 | ✅ |
| 死区抑制 | 去皮后的均值落在 `-250 ~ 250` counts 时压到 `0` | ✅ |
| 重量换算 | `ADC_TO_FORCE_SCALE` 已按 GML670 50 kg 传感器参数推导 | ✅ |
| CoP 解算 | 根据四角重量计算 `cop_x / cop_y / force` | ✅ |
| 有效力阈值 | 总重量低于 `0.5 kg` 时,CoP 坐标置零并清除 `force_valid` | ✅ |
### 4.2 BLE 通信
| 项目 | 说明 | 状态 |
|------|------|------|
| BLE 角色 | `Peripheral`,使用 NUS 私有服务 | ✅ |
| 广播内容 | 广播包带 NUS UUID,扫描响应带设备名 | ✅ |
| 连接参数请求 | 连接后请求 `7.5-15 ms` 区间 | ✅ |
| CoP 上行 | 固定 17 字节二进制帧,目标频率约 `50 Hz` | ✅ |
| NUS 发送保护 | 仅在连接存在且通知已使能时发送,失败时短间隔重试 3 次 | ✅ |
| 断线重广播 | 断开 1 秒后重新广播 | ✅ |
| NUS 下行解析 | Resistance / Spotter / Heartbeat 三类帧已接收解析 | ✅ |
| 编码器上行 | 协议已预留,主循环未发送 | 🔲 |
### 4.3 控制参数与保护逻辑
| 项目 | 说明 | 状态 |
|------|------|------|
| Resistance 参数接收 | 接收 `K / B / Tau` 并保存到传输层静态变量 | ✅ |
| Spotter 参数接收 | 接收 `threshold / enable` 并保存到传输层静态变量 | ✅ |
| Heartbeat 接收 | 接收后刷新最近心跳时间戳 | ✅ |
| 阻力闭环控制 | 参数已接入协议层,控制逻辑未落地 | 🔲 |
| Spotter 保护动作 | 参数已接入协议层,保护动作未落地 | 🔲 |
## 5. BLE 协议规格
平衡板工作在 BLE `Peripheral` 角色,通过 Nordic UART Service 与基站交换二进制帧。
- 上行方向:平衡板 -> 基站,使用 NUS `Notify`
- 下行方向:基站 -> 平衡板,使用 NUS `Write`
- 广播内容:广播包带 NUS UUID,扫描响应带设备名 `GML670_System`
### 5.1 NUS UUID
| 项目 | UUID | 方向 | 说明 |
|------|------|------|------|
| Service | `6E400001-B5A3-F393-E0A9-E50E24DCCA9E` | - | NUS 主服务 |
| TX Characteristic | `6E400003-B5A3-F393-E0A9-E50E24DCCA9E` | 平衡板 -> 基站 | Notify,上报 CoP 数据 |
| RX Characteristic | `6E400002-B5A3-F393-E0A9-E50E24DCCA9E` | 基站 -> 平衡板 | Write,下发控制参数 |
### 5.2 连接参数
| 参数 | 当前值 | 说明 |
|------|--------|------|
| 广播模式 | `BT_LE_ADV_CONN_FAST_2` | 上电和断线后走同一入口启动 |
| 连接间隔请求 | `7.5-15 ms` | `interval_min = 6``interval_max = 12` |
| 从站延迟 | `0` | 不跳过连接事件 |
| 监督超时 | `4000 ms` | `timeout = 400` |
### 5.3 帧类型总览
| 类型名 | 值 | 方向 | 长度 | 频率 | 说明 |
|--------|----|------|------|------|------|
| `PROTO_TYPE_COP` | `0x01` | 平衡板 -> 基站 | `17` | 约 `50 Hz` | CoP 上行帧 |
| `PROTO_TYPE_ENCODER` | `0x02` | 平衡板 -> 基站 | `13` | TBD | 编码器上行帧,预留 |
| `PROTO_TYPE_RESISTANCE` | `0x10` | 基站 -> 平衡板 | `15` | 按需 | 阻力参数帧 |
| `PROTO_TYPE_SPOTTER` | `0x11` | 基站 -> 平衡板 | `8` | 按需 | Spotter 参数帧 |
| `PROTO_TYPE_HEARTBEAT` | `0x20` | 基站 -> 平衡板 | `4` | `~1 Hz` | 心跳帧 |
### 5.4 当前上行主帧
CoP 帧固定 17 字节,结构如下:
| 字节偏移 | 字段 | 类型 | 说明 |
|----------|------|------|------|
| `0` | `sync0` | `uint8` | 固定 `0xAA` |
| `1` | `sync1` | `uint8` | 固定 `0x55` |
| `2` | `type` | `uint8` | 固定 `0x01` |
| `3` | `seq` | `uint8` | 包序号,循环递增 |
| `4` | `flags` | `uint8` | `bit0` 表示 `force_valid` |
| `5-8` | `cop_x` | `float32 LE` | 压力中心 X 坐标,单位 mm |
| `9-12` | `cop_y` | `float32 LE` | 压力中心 Y 坐标,单位 mm |
| `13-16` | `force` | `float32 LE` | 总重量,单位 kg |
更完整的字段说明和示例帧见 [BALANCE_BOARD_PROTOCOL.md](./BALANCE_BOARD_PROTOCOL.md)。
## 6. ADS1256 / CoP 规格
### 6.1 初始化与采样流程
当前固件的 ADS1256 侧流程如下:
```text
上电
|
+-- 配置 CS / DRDY / RESET GPIO
+-- 绑定 SPI1,频率 500 kHz
+-- 硬件复位 ADS1256
+-- 发送 CMD_SDATAC,关闭连续输出模式
+-- 写寄存器: STATUS=0x06, MUX=0x01, ADCON=0x07, DRATE=0xC0
+-- 发送 CMD_SELFCAL
+-- 回读关键寄存器做校验
+-- tare 去皮
|
+-- 传感器线程循环
|
+-- 依次切换 4 路 MUX
+-- 每路读 17 次并求均值
+-- 扣除零点偏移
+-- 死区抑制
+-- 重量换算
+-- CoP 解算
+-- 打包 CoP 帧并发送
```
### 6.2 关键参数
| 项目 | 当前值 | 说明 |
|------|--------|------|
| SPI 频率 | `500 kHz` | `spi_cfg.frequency = 500000` |
| STATUS | `0x06` | `ACAL=1``BUFEN=1` |
| PGA | `64` | `ADCON = 0x07` |
| 采样率 | `3750 SPS` | `DRATE = 0xC0` |
| 每帧均值次数 | `17` | `AVG_COUNT = 17` |
| 去皮窗口 | `17` | 每路取 17 点,中位数为零点 |
| 死区 | `±250 counts` | `DEADZONE_THRESHOLD = 250` |
| 板面半宽 | `100 mm` | `BOARD_HALF_WIDTH_MM = 100.0f` |
| 板面半长 | `100 mm` | `BOARD_HALF_LENGTH_MM = 100.0f` |
| CoP 最小有效总重 | `0.5 kg` | `COP_MIN_FORCE_THRESHOLD = 0.5f` |
| 调试日志频率 | 每 10 帧一次 | 目标运行状态下约 `5 Hz` |
### 6.3 CoP 判定规则
- 四路去皮后的 ADC 值先乘 `ADC_TO_FORCE_SCALE`,换算成重量
- 总重量低于 `0.5 kg` 时,`cop_x``cop_y``0.0f`
- 此时 `flags.bit0 = 0`,表示 CoP 坐标没有物理意义
- 总重量达到阈值后,再根据四角坐标做加权平均
当前四路传感器与板面坐标的映射如下:
| 通道 | 位置 | X | Y |
|------|------|---|---|
| `S0` | 右前 `FR` | `+100` | `+100` |
| `S1` | 右后 `BR` | `+100` | `-100` |
| `S2` | 左后 `BL` | `-100` | `-100` |
| `S3` | 左前 `FL` | `-100` | `+100` |
## 7. 开发环境与板级配置
当前工程采用标准 Zephyr / NCS 应用结构,目标板为 `nrf5340dk/nrf5340/cpuapp`
`prj.conf` 当前可以直接确认的配置点包括:
- `CONFIG_BT=y`,启用 BLE
- `CONFIG_BT_PERIPHERAL=y`,工作在外设角色
- `CONFIG_BT_NUS=y`,启用 Nordic UART Service
- `CONFIG_BT_DEVICE_NAME="GML670_System"`,设置广播设备名
- `CONFIG_BT_NUS_SECURITY_ENABLED=n`,关闭 NUS 安全限制
- 日志走 RTT`CONFIG_USE_SEGGER_RTT=y`
- 关闭 UART Console`CONFIG_CONSOLE=n``CONFIG_UART_CONSOLE=n`
- 关闭 UART 日志后端:`CONFIG_LOG_BACKEND_UART=n`
- 关闭串口驱动:`CONFIG_SERIAL=n`
`app.overlay` 当前只保留了 SPI1、ADS1256 设备节点和三根控制 GPIO 的定义,没有把 UART Console 复用回来。
## 8. 项目结构
当前阅读和维护时最关键的文件如下:
| 路径 | 说明 |
|------|------|
| `src/main.c` | 启动入口,负责初始化 BLE 和传感器子系统 |
| `src/ads1256.c` | ADS1256 SPI 读写、复位、寄存器配置和校验 |
| `src/sensor.c` | 采样线程、tare、均值滤波、重量换算和 CoP 打包 |
| `src/ble_transport.c` | BLE 广播、NUS 收发、断线重广播和下行参数缓存 |
| `inc/protocol.h` | 二进制协议结构、重量换算系数、几何参数和阈值 |
| `app.overlay` | SPI1、CS、DRDY、RESET 的板级连线 |
| `prj.conf` | BLE、日志和控制台相关配置 |
| `BALANCE_BOARD_PROTOCOL.md` | 协议专项说明、字段表和示例帧 |
## 9. 构建与烧录
在已经配置好 Zephyr / NCS 环境的前提下,可以直接在工程根目录执行:
```bash
west build -b nrf5340dk/nrf5340/cpuapp -p always .
west flash
```
如果只是重新编译当前应用,默认目标仍然是 `nrf5340dk/nrf5340/cpuapp`
## 10. 日志与当前约束
### 10.1 日志
当前日志走 RTT,不占用 UART Console。启动后可以看到如下类型的信息:
- 启动 banner
- BLE transport 初始化结果
- ADS1256 关键寄存器回读结果
- tare 开始和结束
- Resistance / Spotter 下行参数日志
- 周期性的四角重量、总重、CoP 和帧耗时日志
Heartbeat 接收路径已经接入,但默认 `DBG` 级别下才单独打印。
### 10.2 当前约束
- 阻力参数、Spotter 参数和 Heartbeat 目前只做到协议接收、内存保存和日志留痕
- 编码器上行帧只在协议头里预留,当前没有实际数据源
- 传感器线程没有额外 `k_msleep()` 节流,实际帧率由 ADS1256 采样和 BLE 发送耗时共同决定
- README 中的频率描述按当前 `3750 SPS + 4 通道 * 17 次均值` 这套实现估算,目标运行频率约 `50 Hz`
- 当前实现默认四角传感器是固定几何布局,半宽和半长都是 `100 mm`