test_led_strip/doc/升级操作指南.md
zhangyisong 27ae21bbea Add UID management, LED strip debug, and MCUboot signing updates
- Add UID encoding/decoding with flash storage and CRC16 validation
- Add write UID (0x07) and LED strip color (0x08) protocol commands
- Add debug LED strip configuration option
- Switch MCUboot signing from RSA-2048 to ECDSA-P256
- Add SMP serial client for firmware upload over UART
- Add firmware version output on boot
- Update upgrade documentation with ECDSA key generation steps
2026-08-20 21:16:30 +08:00

308 lines
11 KiB
Markdown

# 光磁头固件升级操作指南
## 1. 环境准备
### 1.1 安装依赖
```bash
# GUI 工具必需:串口库
pip install pyserial
# 固件签名工具(imgtool 随 Zephyr/MCUboot 自带,也可单独安装)
pip install imgtool
# 可选:命令行升级(smpmgr)——GUI 已内置 SMP 客户端,不装也能升级
pip install smpmgr
```
### 1.2 生成签名密钥(量产阶段)
```bash
# 在应用目录下生成密钥对
cd /home/issac-zys/code/zephyr_prj_template/app/app_photomagnetic
mkdir -p keys
imgtool keygen -k keys/mykey.pem -t ecdsa-p256
# 更新 sysbuild.conf 中的密钥路径与签名类型
# SB_CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y
# SB_CONFIG_BOOT_SIGNATURE_KEY_FILE="${APP_DIR}/keys/mykey.pem"
```
> 开发阶段可跳过此步,使用 MCUboot 自带的开发密钥 `root-ec-p256.pem`。
> ⚠️ **密钥算法必须与签名类型一致**:
> - `-t ecdsa-p256` 对应 `SB_CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y`
> - `-t rsa-2048` 对应 `SB_CONFIG_BOOT_SIGNATURE_TYPE_RSA=y`
> 不匹配会报链接错误(如 `undefined reference to 'ecdsa_pub_key'`)。
> 本项目 boot 分区只有 48KB,建议用 ECDSA-P256(RSA 会把 MCUboot 撑到接近极限)。
> 更换密钥后必须**完整重建**(删 build 目录),否则签名/验签用的还是旧公钥。
---
## 2. 首次构建与烧录(需要调试器)
### 2.1 构建 MCUboot + 应用
```bash
cd /home/issac-zys/code/zephyr_prj_template
# 构建(sysbuild 模式,自动构建 MCUboot + 应用)
west build -p auto -b dr2501a_g0b0ce/stm32g0b0xx \
--sysbuild app/app_photomagnetic \
-d build
```
构建产物:
- `build/mcuboot/zephyr/zephyr.hex` / `.bin` — MCUboot bootloader(0x08000000)
- `build/app_photomagnetic/zephyr/zephyr.signed.hex` / `.bin` — 签名后的应用固件(0x0800C000)
> ⚠️ 注意:顶层 `west flash -d build` 只会烧应用镜像,不会烧 MCUboot!MCUboot 必须单独烧录。
### 2.2 烧录(一次性,需要调试器)
```bash
# ① 先烧 MCUboot 到 0x08000000(覆盖出厂 godtek bootloader,只需一次)
west flash -d build/mcuboot
# ② 再烧签名应用到 slot0 (0x0800C000)
west flash -d build
```
> 两条命令顺序不能反:没有 MCUboot 时,0x0800C000 的镜像头无法被引导,设备会完全无反应。
### 2.3 验证首次启动
串口调试口(usart3)应输出:
```
*** Booting MCUboot build ...
*** Booting Zephyr OS build ...
[dfu] init: upgrade command enabled (0xFF)
[com] init: rx table=4, id sent
[main] init on dr2501a_g0b0ce
```
协议口(usart1)应输出 ID 帧: `[7E E7][01][03][XX XX XX][CRC]`
---
## 3. 后续升级(通过 UART,不需要调试器)
### 3.1 升级流程图
```
┌─────────────────────────────────────────────────────────────┐
│ 主板 / 测试工具 │
│ 1. 发送升级命令 [7E E7][FF][01][01][CRC] │
│ 2. 等待 300ms(小板重启) │
│ 3. 通过 smpmgr 上传固件(3 秒内) │
│ 4. 标记待测试 + 复位 │
│ 5. 等待新固件启动 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 光磁头(小板) │
│ 1. 收到升级命令 → 直接重启 │
│ 2. MCUboot 启动 → 等待 SMP 命令(3 秒) │
│ 3. 收到 SMP 命令 → 进入 serial recovery │
│ 4. 接收固件 → 直接写入 slot0(主槽,串口恢复固定写主槽) │
│ 5. 复位 → MCUboot 校验签名 → 启动新固件 │
└─────────────────────────────────────────────────────────────┘
```
> ⚠️ **重要:串口恢复(serial recovery)是把镜像直接写入 slot0(主槽)**,
> 不是写 slot1 再 swap(那是应用内 mcumgr DFU 的做法)。因此:
> 1. **升级过程中绝不能抢占/打开串口**(比如用其他串口工具、或让本工具重新 Connect),
> 否则上传中断会把 slot0 擦到一半,设备无法启动;
> 2. 上传中断后设备不会变砖——MCUboot 仍在运行并停留在 serial recovery 等待区,
> 直接用 smpmgr 重新上传即可恢复(或 `west flash -d build` 重烧应用);
> 3. 设备异常停在 bootloader 时,再点一次升级按钮也能恢复(0xFF 命令对 MCUboot 无害,
> smpmgr 会自动连上当前等待中的 serial recovery)。
### 3.2 使用 smpmgr CLI 升级
#### 步骤 1: 发送升级命令
使用串口工具或 Python 发送升级命令:
```python
import serial, struct, time
def send_upgrade(port='/dev/ttyUSB0'):
cmd = bytes([0x7E, 0xE7, 0xFF, 0x01, 0x01])
crc = 0xFFFF
for b in cmd[2:]:
crc ^= b
for _ in range(8):
crc = (crc >> 1) ^ 0xA001 if crc & 1 else crc >> 1
cmd += struct.pack('<H', crc)
with serial.Serial(port, 115200, timeout=1) as ser:
ser.write(cmd)
print(f"已发送升级命令: {cmd.hex()}")
time.sleep(0.3) # 等待重启
send_upgrade('/dev/ttyUSB0')
```
#### 步骤 2: 使用 smpmgr 上传固件
```bash
# 一键升级(串口恢复模式直接写 slot0 主槽,上传完自动复位,无需 test/confirm)
# 必须在小板重启后 3 秒内执行;--timeout 10 给擦除/慢速上传留足超时
smpmgr --port /dev/ttyUSB0 --timeout 10 upgrade \
build/app_photomagnetic/zephyr/zephyr.signed.bin
```
也可以手动分步:
```bash
# 上传固件到 slot0(主槽)
smpmgr --port /dev/ttyUSB0 image upload build/app_photomagnetic/zephyr/zephyr.signed.bin
# 复位,让 MCUboot 校验并启动新固件
smpmgr --port /dev/ttyUSB0 os reset
```
#### 步骤 3: 验证新固件
```bash
# 查看串口输出,确认新固件版本(协议口应输出新固件的 ID 帧)
# 串口恢复直接写主槽、无 swap,固件已生效,无需 confirm。
```
### 3.3 使用 GUI 工具升级(推荐)
已集成到 `scripts/test_gui.py` 中,升级功能**进程内封装了 SMP 客户端**(`smp_client.py`),
不再依赖外部 smpmgr/mcumgr 命令:
1. 打开 GUI 工具: `python scripts/test_gui.py`
2. 连接串口
3. 点击 **"Upgrade Firmware"** 按钮
4. 选择 `.signed.bin` 固件文件
5. 等待升级完成(GUI 会显示上传进度,自动复位并重连)
升级期间 GUI 会锁定 Connect 按钮/端口选择,防止串口被抢占导致上传中断。
> 可选:仍可用命令行一键升级(需已安装 smpmgr):
> `smpmgr --port /dev/ttyUSB0 --timeout 10 upgrade build/app_photomagnetic/zephyr/zephyr.signed.bin`
### 3.4 自动化升级脚本
```bash
#!/bin/bash
# upgrade.sh - 光磁头固件升级脚本
# 用法: ./upgrade.sh /dev/ttyUSB0 zephyr.signed.bin
PORT=${1:-/dev/ttyUSB0}
FIRMWARE=${2:-build/app_photomagnetic/zephyr/zephyr.signed.bin}
echo "=== 光磁头固件升级 ==="
echo "串口: $PORT"
echo "固件: $FIRMWARE"
# 检查固件文件
if [ ! -f "$FIRMWARE" ]; then
echo "错误: 固件文件不存在"
exit 1
fi
# 发送升级命令
echo "1. 发送升级命令..."
python3 -c "
import serial, struct, time
with serial.Serial('$PORT', 115200, timeout=1) as ser:
cmd = bytes([0x7E, 0xE7, 0xFF, 0x01, 0x01])
crc = 0xFFFF
for b in cmd[2:]:
crc ^= b
for _ in range(8):
crc = (crc >> 1) ^ 0xA001 if crc & 1 else crc >> 1
cmd += struct.pack('<H', crc)
ser.write(cmd)
print(f' 已发送: {cmd.hex()}')
"
# 等待重启
echo "2. 等待小板重启..."
sleep 0.3
# 上传固件(串口恢复直接写 slot0 主槽,上传完自动复位)
echo "3. 上传固件..."
smpmgr --port "$PORT" --timeout 10 upgrade "$FIRMWARE"
if [ $? -ne 0 ]; then
echo "错误: 固件上传失败"
echo "提示: 上传中断会擦掉 slot0 一半,但设备停在 bootloader 中不会变砖,"
echo " 重新执行本脚本即可恢复(或 west flash -d build 重烧应用)。"
exit 1
fi
echo "=== 升级完成,等待新固件启动 ==="
```
---
## 4. 恢复(串口恢复模式无 swap,没有回滚机制)
> 串口恢复(serial recovery)是直接把镜像写入 slot0 主槽,固件即刻生效、
> 不存在“待测试/回滚”的概念。旧版文档里的 swap/回滚描述来自应用内 mcumgr DFU,
> 本方案不适用。
### 4.1 上传中断/镜像损坏后的恢复
上传中断只会擦掉 slot0 一部分,MCUboot 仍然在运行并停留在 serial recovery 等待区,
**不需要调试器**即可恢复:
```bash
# 直接重传即可(设备已在 bootloader 等待,无需再发升级命令)
smpmgr --port /dev/ttyUSB0 --timeout 10 upgrade \
build/app_photomagnetic/zephyr/zephyr.signed.bin
```
或使用 GUI:再次点击 **Upgrade Firmware**(0xFF 命令对 MCUboot 无害,smpmgr 会自动连上)。
### 4.2 强制恢复(需要调试器)
如果连 MCUboot 都进不去(比如误烧了 boot 分区):
```bash
# 重新烧录 MCUboot + 应用
west flash -d build/mcuboot
west flash -d build
```
---
## 5. 故障排查
| 现象 | 原因 | 解决方案 |
|---|---|---|
| smpmgr 超时 | 上传未在 3 秒窗口内开始 | 先确认设备已进 bootloader(串口有 MCUboot 日志)再上传 |
| 上传中断 | 串口被其他工具/GUI 占用 | 关闭其他串口工具,重新上传(需从头传,不支持断点续传) |
| 上传后无法启动 | 上传中断擦坏了 slot0 | 设备停在 bootloader,直接重传或 `west flash -d build` 重烧应用 |
| 签名验证失败 | 密钥不匹配 | 用构建时相同的密钥(开发期 `root-ec-p256.pem`)签名 |
| smpmgr 连接失败 | 端口错误/被占用 | 确认端口号,关闭占用程序后再试 |
---
## 6. 注意事项
1. **3 秒窗口**:小板重启后,MCUboot 等待 SMP 命令 3 秒;超时则正常启动应用
2. **波特率**:应用层协议和 smpmgr 都使用 115200
3. **签名**:开发阶段使用默认密钥 `root-ec-p256.pem`;量产必须替换(且必须用 ECDSA-P256,RSA 装不进 48KB 分区)
4. **升级期间勿占用串口**:上传过程中不要打开其他串口工具、不要让 GUI 重新 Connect,否则上传中断会擦坏 slot0
5. **调试器**:首次烧录 MCUboot 需要;后续升级不需要
6. **断电保护**:升级中断电 → MCUboot 仍在运行,停在 serial recovery 等待区,重新上传即可恢复,设备不会变砖
---
## 7. 工具对比
| 工具 | 安装方式 | 优点 |
|---|---|---|
| **GUI 工具(推荐)** | 集成在 `scripts/test_gui.py`,内置 SMP 客户端 | 图形界面,一键升级,无需额外依赖(smpmgr/mcumgr 均可不要) |
| **smpmgr** | `pip install smpmgr` | 命令行,支持 Serial/BLE/UDP,适合脚本化 |
| **mcumgr CLI** | Go 编译/下载二进制 | Zephyr 官方工具,功能完整 |