磁铁、舵机、网页控制、通信协议

This commit is contained in:
kirl committed 2026-07-29 21:11:16 +08:00
1 parent 230ad1254a
commit 40ee976236
17 files changed
+1879 -32

No files matched your search

+254
View File
@@ -0,0 +1,254 @@
# 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=GPIO8, RX=GPIO9
- 波特率: 115200, 8N1
### 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** |