- protocol: 修复 edc_cycle_parse_byte 状态机死代码 (switch 恒 return, 收帧逻辑不可达), 并加 LEN 未就绪保护 - protocol: UART 默认引脚改为 GPIO11/12 (UART0) - sdkconfig.defaults: 控制台切至 USB Serial/JTAG, 避免与协议 UART0 冲突 - web_ctrl: 新增协议状态 API 与 Cycle 快照; on_cycle 回 DONE 并按 cycle_id 去重 - main: 接入协议初始化与接收任务, 更新接线注释 - 实测: MPU 每秒下发 Cycle, I2C/UART 双通道 12/12 收, DONE 12/12 回
256 lines
7.4 KiB
Markdown
256 lines
7.4 KiB
Markdown
# EDC 通讯协议
|
||
|
||
## 概述
|
||
|
||
本协议用于 MPU 与 MCU (ESP32-C5) 之间的数据交互,支持 **I2C** 与 **UART** 双通道冗余传输。
|
||
|
||
| 属性 | 值 |
|
||
|------|-----|
|
||
| 帧头 | `0xAA 0x55` |
|
||
| 字节序 | 大端 (Big-Endian) |
|
||
| CRC | CRC-16/MODBUS (多项式 `0x8005`) |
|
||
| I2C 模式 | ESP32 从机, 地址 `0x2A` |
|
||
| UART 波特率 | `115200`, 8N1 |
|
||
|
||
---
|
||
|
||
## 1. 数据模型
|
||
|
||
```
|
||
Point = { x: uint16, y: uint16, angle_x10: uint16 }
|
||
Fragment = { source: Point, target: Point }
|
||
Cycle = { cycle_id: uint16, frag_count: uint8 (2~4), fragments: Fragment[] }
|
||
```
|
||
|
||
- **Point**: 单个坐标点, `angle_x10 = 角度 × 10` (如 `450` = 45.0°)
|
||
- **Fragment**: 一次运动片段, 从 `source` 移动到 `target`
|
||
- **Cycle**: 一个执行周期, 包含 2~4 个 Fragment
|
||
|
||
---
|
||
|
||
## 2. 帧格式
|
||
|
||
```
|
||
┌────────┬────────┬────────┬────────┬───────────────────┬──────────────┐
|
||
│ SOF1 │ SOF2 │ CMD │ LEN │ DATA (N bytes) │ CRC16 (LE) │
|
||
│ 0xAA │ 0x55 │ 1B │ 1B │ variable │ 2B │
|
||
└────────┴────────┴────────┴────────┴───────────────────┴──────────────┘
|
||
◄──────────── CRC 计算范围 (CMD + LEN + DATA) ────────────►
|
||
```
|
||
|
||
- **SOF**: 帧头, 固定 `0xAA 0x55`
|
||
- **CMD**: 命令字 (见 §3)
|
||
- **LEN**: DATA 区字节数
|
||
- **DATA**: 有效载荷
|
||
- **CRC**: CRC-16/MODBUS, 低位在前
|
||
|
||
---
|
||
|
||
## 3. 命令字
|
||
|
||
| 命令 | 值 | 方向 | 说明 |
|
||
|------|-----|------|------|
|
||
| `CYCLE_DATA` | `0x01` | MPU → MCU | 下发 Cycle 数据 |
|
||
| `CYCLE_DONE` | `0x02` | MCU → MPU | Cycle 执行完成 |
|
||
| `CYCLE_REQUEST` | `0x03` | MCU → MPU | 请求下一个 Cycle |
|
||
| `ACK` | `0x80` | 双向 | 通用应答 (预留) |
|
||
| `NACK` | `0xFF` | 双向 | 错误 (预留) |
|
||
|
||
---
|
||
|
||
## 4. 帧类型详解
|
||
|
||
### 4.1 CYCLE_DATA (MPU → MCU)
|
||
|
||
```
|
||
SOF(2) + CMD=0x01 + LEN + DATA + CRC(2)
|
||
|
||
DATA:
|
||
┌──────────────┬────────────┬─────────────────────────┐
|
||
│ cycle_id │ frag_count │ fragments │
|
||
│ uint16 (BE) │ uint8 │ N × 12B │
|
||
│ 2B │ 1B │ 12~48B │
|
||
└──────────────┴────────────┴─────────────────────────┘
|
||
|
||
LEN = 3 + frag_count × 12
|
||
```
|
||
|
||
**Fragment (12B)**:
|
||
```
|
||
┌──────────┬──────────┬───────────────┬──────────┬──────────┬───────────────┐
|
||
│ src.x │ src.y │ src.angle_x10 │ dst.x │ dst.y │ dst.angle_x10 │
|
||
│ uint16 │ uint16 │ uint16 │ uint16 │ uint16 │ uint16 │
|
||
│ 2B │ 2B │ 2B │ 2B │ 2B │ 2B │
|
||
└──────────┴──────────┴───────────────┴──────────┴──────────┴───────────────┘
|
||
```
|
||
|
||
**示例** (2 个 Fragment, `cycle_id=1`):
|
||
|
||
```
|
||
AA 55 01 1B 00 01 02
|
||
00 64 00 C8 01 C2 01 2C 01 90 02 58 ← Fragment 0
|
||
00 96 01 2C 03 84 01 F4 02 BC 04 B0 ← Fragment 1
|
||
XX XX ← CRC16
|
||
```
|
||
|
||
解析:
|
||
| 字段 | 值 | 含义 |
|
||
|------|-----|------|
|
||
| cycle_id | `0x0001` = 1 | 第 1 个 Cycle |
|
||
| frag_count | `0x02` = 2 | 2 个 Fragment |
|
||
| F0 src | (100, 200), 45.0° | |
|
||
| F0 dst | (300, 400), 60.0° | |
|
||
| F1 src | (150, 300), 90.0° | |
|
||
| F1 dst | (500, 700), 120.0° | |
|
||
|
||
### 4.2 CYCLE_DONE (MCU → MPU)
|
||
|
||
```
|
||
SOF(2) + CMD=0x02 + LEN=2 + cycle_id(2B, BE) + CRC(2)
|
||
|
||
总长度: 8 字节
|
||
```
|
||
|
||
**示例** (`cycle_id=1` 完成):
|
||
```
|
||
AA 55 02 02 00 01 XX XX
|
||
```
|
||
|
||
### 4.3 CYCLE_REQUEST (MCU → MPU)
|
||
|
||
```
|
||
SOF(2) + CMD=0x03 + LEN=0 + CRC(2)
|
||
|
||
总长度: 6 字节
|
||
```
|
||
|
||
**示例**:
|
||
```
|
||
AA 55 03 00 XX XX
|
||
```
|
||
|
||
---
|
||
|
||
## 5. CRC-16/MODBUS
|
||
|
||
| 参数 | 值 |
|
||
|------|-----|
|
||
| 多项式 | `0x8005` (逆序 `0xA001`) |
|
||
| 初始值 | `0xFFFF` |
|
||
| 输入反射 | 是 |
|
||
| 输出反射 | 是 |
|
||
| 异或输出 | `0x0000` |
|
||
| 校验范围 | CMD + LEN + DATA |
|
||
|
||
### 参考实现 (C)
|
||
|
||
```c
|
||
uint16_t edc_crc16(const uint8_t *data, size_t len)
|
||
{
|
||
uint16_t crc = 0xFFFF;
|
||
for (size_t i = 0; i < len; i++) {
|
||
crc ^= data[i];
|
||
for (int j = 0; j < 8; j++) {
|
||
if (crc & 0x0001)
|
||
crc = (crc >> 1) ^ 0xA001;
|
||
else
|
||
crc >>= 1;
|
||
}
|
||
}
|
||
return crc;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 传输通道
|
||
|
||
### 6.1 I2C (冗余接收)
|
||
|
||
- ESP32 作为 **I2C 从机**, 地址 `0x2A` (7-bit)
|
||
- MPU 作为 I2C 主机
|
||
- MPU 将完整帧直接 `Write` 到从机地址
|
||
- ESP32 通过 ISR 回调接收并 CRC 校验
|
||
- 默认引脚: SDA=GPIO2, SCL=GPIO3
|
||
|
||
### 6.2 UART (双向)
|
||
|
||
- **RX**: 接收 CYCLE_DATA (冗余备份)
|
||
- **TX**: 发送 CYCLE_DONE / CYCLE_REQUEST
|
||
- 默认引脚: TX=GPIO11, RX=GPIO12 (UART0)
|
||
- 波特率: 115200, 8N1
|
||
- 注: ESP32-C5 日志控制台使用 USB Serial/JTAG, UART0 专用于本协议
|
||
|
||
### 6.3 冗余策略
|
||
|
||
同一帧 CYCLE_DATA 同时通过 I2C 和 UART 发送, **任一通道先收到有效帧即采纳**, 通过 `edc_proto_stat_t.last_channel` 可判断来源。
|
||
|
||
---
|
||
|
||
## 7. 交互流程
|
||
|
||
```
|
||
MPU MCU (ESP32-C5)
|
||
│ │
|
||
│──── CYCLE_DATA (I2C+UART) ────────►│ 下发 Cycle
|
||
│ │
|
||
│ │ 执行 fragments[0..N]
|
||
│ │
|
||
│◄──── CYCLE_DONE (UART) ───────────│ 上报完成
|
||
│ │
|
||
│──── CYCLE_DATA (I2C+UART) ────────►│ 下发下一个...
|
||
│ │
|
||
— 或 —
|
||
│ │
|
||
│ (超时无数据) │
|
||
│ │
|
||
│◄──── CYCLE_REQUEST (UART) ────────│ 主动请求
|
||
```
|
||
|
||
---
|
||
|
||
## 8. MCU 端 API
|
||
|
||
```c
|
||
#include "protocol.h"
|
||
|
||
// 初始化 (I2C 从机 + UART)
|
||
edc_proto_init(NULL);
|
||
|
||
// 方式一: 手动轮询
|
||
edc_cycle_t cycle;
|
||
if (edc_proto_poll(&cycle) > 0) {
|
||
// 处理 cycle.fragments[0..frag_count-1]
|
||
edc_proto_send_done(cycle.cycle_id);
|
||
}
|
||
|
||
// 方式二: FreeRTOS 任务 + 回调
|
||
xTaskCreate(edc_proto_task, "proto", 4096, NULL, 5, NULL);
|
||
|
||
void edc_proto_on_cycle(const edc_cycle_t *cycle, edc_channel_t ch) {
|
||
// ch: EDC_CH_I2C 或 EDC_CH_UART
|
||
// 执行完调用:
|
||
edc_proto_send_done(cycle->cycle_id);
|
||
}
|
||
|
||
// 超时请求
|
||
edc_proto_send_request();
|
||
|
||
// 查看统计
|
||
edc_proto_stat_t stat;
|
||
edc_proto_get_stats(&stat);
|
||
// stat.i2c.cycles_ok, stat.uart.cycles_err, stat.done_sent ...
|
||
```
|
||
|
||
---
|
||
|
||
## 9. 帧总长速查
|
||
|
||
| frag_count | DATA 长度 | 帧总长 (SOF+CMD+LEN+DATA+CRC) |
|
||
|------------|-----------|------|
|
||
| 2 | 3 + 24 = 27 | 2 + 1 + 1 + 27 + 2 = **33** |
|
||
| 3 | 3 + 36 = 39 | 2 + 1 + 1 + 39 + 2 = **45** |
|
||
| 4 | 3 + 48 = 51 | 2 + 1 + 1 + 51 + 2 = **57** |
|
||
| DONE | 2 | **8** |
|
||
| REQUEST | 0 | **6** |
|