Files
edc-mcu/components/protocol/protocol.h
T
2026-08-03 04:09:26 +08:00

247 lines
7.7 KiB
C
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.
/**
* @file protocol.h
* @brief EDC 通讯协议 — I2C / UART 双通道, Cycle 收发, CRC16 校验
*
* 顶层概念:
* Point = { x, y, angle_x10 }
* Fragment = { source: Point, target: Point }
* Cycle = { cycle_id, frag_count (2~4), fragments[] }
*
* 帧格式 (大端序):
* ┌──────┬──────┬──────┬──────┬───────────────────┬──────────┐
* │ SOF1 │ SOF2 │ CMD │ LEN │ DATA (N bytes) │ CRC16 │
* │ 0xAA │ 0x55 │ 1B │ 1B │ variable │ 2B (LE) │
* └──────┴──────┴──────┴──────┴───────────────────┴──────────┘
* ◄──────── CRC 计算范围 (CMD + LEN + DATA) ─────────►
*
* Cycle 数据帧 DATA 区:
* cycle_id(2B, BE) + frag_count(1B, 2~4) + fragments(N × 12B)
*
* Fragment (12B, 大端):
* src.x(2B) src.y(2B) src.angle(2B) dst.x(2B) dst.y(2B) dst.angle(2B)
*
* 交互流程:
* MPU ──[CYCLE_DATA]──► MCU (I2C + UART 冗余下发)
* MCU ──[CYCLE_DONE]──► MPU (UART 上报完成, 含 cycle_id)
* MCU ──[CYCLE_REQ]───► MPU (UART 请求下一 Cycle)
*/
#ifndef EDC_PROTOCOL_H
#define EDC_PROTOCOL_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "driver/gpio.h"
#include "driver/uart.h"
#include "driver/i2c_slave.h"
#ifdef __cplusplus
extern "C" {
#endif
/* ================================================================
* 协议常量
* ================================================================ */
#define EDC_PROTO_SOF1 0xAA
#define EDC_PROTO_SOF2 0x55
#define EDC_PROTO_FRAME_MAX 80 /* 最大帧长: 2+1+1+3+4*12+2 = 57 < 80 */
#define EDC_PROTO_FRAME_MIN 6 /* SOF(2) + CMD(1) + LEN(1) + CRC(2) */
#define EDC_FRAG_MIN 2 /* 每 Cycle 最少 fragment 数 */
#define EDC_FRAG_MAX 4 /* 每 Cycle 最多 fragment 数 */
#define EDC_FRAG_BYTES 12 /* 单个 fragment 字节数 (3×uint16×2) */
/* ---- I2C 默认 ---- */
#define EDC_I2C_PORT_DEFAULT 0
#define EDC_I2C_SLAVE_ADDR 0x2A
#define EDC_I2C_SDA_DEFAULT GPIO_NUM_2
#define EDC_I2C_SCL_DEFAULT GPIO_NUM_3
#define EDC_I2C_BUF_SIZE 256
/* ---- UART 默认 ---- */
#define EDC_UART_PORT_DEFAULT UART_NUM_0
#define EDC_UART_BAUDRATE 115200
#define EDC_UART_RX_DEFAULT GPIO_NUM_12
#define EDC_UART_TX_DEFAULT GPIO_NUM_11
#define EDC_UART_BUF_SIZE 512
/* ================================================================
* 命令字
* ================================================================ */
typedef enum {
EDC_CMD_CYCLE_DATA = 0x01, /* MPU → MCU: 下发 Cycle 数据 */
EDC_CMD_CYCLE_DONE = 0x02, /* MCU → MPU: Cycle 执行完成 */
EDC_CMD_CYCLE_REQUEST = 0x03, /* MCU → MPU: 请求下一个 Cycle */
EDC_CMD_RECOGNIZE = 0x04, /* MCU → MPU: 触发视觉识别 */
EDC_CMD_TASK_START = 0x05, /* MCU → MPU: 任务开始 (按钮按下即发, 与 RECOGNIZE 区分) */
EDC_CMD_ACK = 0x80, /* 通用应答 */
EDC_CMD_NACK = 0xFF, /* 错误 */
} edc_cmd_t;
/* ================================================================
* 数据结构
* ================================================================ */
/** 单个坐标点 (source 或 target) */
typedef struct {
uint16_t x;
uint16_t y;
uint16_t angle_x10; /* 角度 × 10, 如 450 = 45.0° */
} edc_point_t;
/** 一个运动片段: 从 source 移动到 target */
typedef struct {
edc_point_t source;
edc_point_t target;
} edc_fragment_t;
/** 一个 Cycle: 包含 2~4 个 fragment */
typedef struct {
uint16_t cycle_id;
uint8_t frag_count; /* 实际 fragment 数 (2~4) */
edc_fragment_t fragments[EDC_FRAG_MAX];
uint32_t timestamp_ms; /* 本地时间戳 (不随帧传输) */
} edc_cycle_t;
/** 帧来源通道 */
typedef enum {
EDC_CH_NONE = 0,
EDC_CH_I2C = 1,
EDC_CH_UART = 2,
} edc_channel_t;
/** 每通道统计 */
typedef struct {
uint32_t cycles_ok;
uint32_t cycles_err;
uint32_t bytes_total;
} edc_channel_stat_t;
/** 统一收发统计 */
typedef struct {
edc_channel_stat_t i2c;
edc_channel_stat_t uart;
edc_channel_t last_channel;
uint32_t last_tick;
uint32_t done_sent; /* 已发送 DONE 次数 */
uint32_t request_sent; /* 已发送 REQUEST 次数 */
} edc_proto_stat_t;
/** 帧解析状态 */
typedef enum {
EDC_PARSE_OK = 0,
EDC_PARSE_WAITING = 1,
EDC_PARSE_CRC_ERR = -1,
EDC_PARSE_LEN_ERR = -2,
EDC_PARSE_SOF_ERR = -3,
EDC_PARSE_CMD_ERR = -4,
} edc_parse_result_t;
/** 帧解析上下文 (逐字节喂入) */
typedef struct {
uint8_t buf[EDC_PROTO_FRAME_MAX];
uint8_t len;
uint8_t idx;
edc_parse_result_t state;
} edc_frame_ctx_t;
/* ================================================================
* 配置
* ================================================================ */
typedef struct {
bool i2c_enable;
int i2c_port;
uint16_t i2c_addr;
gpio_num_t i2c_sda;
gpio_num_t i2c_scl;
bool uart_enable;
uart_port_t uart_port;
int uart_baudrate;
gpio_num_t uart_rx;
gpio_num_t uart_tx;
} edc_proto_config_t;
/* ================================================================
* CRC16 / 组帧 / 逐字节解析 (底层)
* ================================================================ */
uint16_t edc_crc16(const uint8_t *data, size_t len);
/** 将 Cycle 序列化为协议字节流 (含帧头与 CRC) */
int edc_cycle_pack(const edc_cycle_t *cycle, uint8_t *out);
/** 逐字节解析器 (与旧版兼容, 内部使用 edc_cycle_parse_byte) */
edc_parse_result_t edc_cycle_parse_byte(edc_frame_ctx_t *ctx, uint8_t byte,
edc_cycle_t *cycle);
/** 从完整缓冲区一次性解析 Cycle */
edc_parse_result_t edc_cycle_parse_buf(const uint8_t *data, size_t len,
edc_cycle_t *cycle);
/* ================================================================
* 统一收发器 API (I2C 从机 + UART 双向)
* ================================================================ */
/**
* @brief 初始化协议栈 (I2C 从机 + UART)
* @param cfg 传 NULL 使用默认值
*/
void edc_proto_init(const edc_proto_config_t *cfg);
/**
* @brief 轮询双通道接收 + 返回最新 Cycle (非阻塞)
* @return >0: 有新 Cycle; 0: 无; <0: 错误
*/
int edc_proto_poll(edc_cycle_t *cycle);
/**
* @brief 发送 CYCLE_DONE (通过 UART)
* @param cycle_id 已完成的 Cycle ID
*/
void edc_proto_send_done(uint16_t cycle_id);
/**
* @brief 发送 CYCLE_REQUEST (通过 UART)
*/
void edc_proto_send_request(void);
/**
* @brief 发送 RECOGNIZE 触发视觉识别 (通过 UART)
*/
void edc_proto_send_recognize(void);
/**
* @brief 发送 TASK_START 通知 MPU 任务开始计时 (通过 UART, 按钮按下即发,
* 与 RECOGNIZE 区分, 不触发识别)
*/
void edc_proto_send_task_start(void);
/**
* @brief 获取统计信息
*/
void edc_proto_get_stats(edc_proto_stat_t *stat);
/* ---- FreeRTOS 任务 + 回调 ---- */
void edc_proto_task(void *pvParam);
/**
* @brief Cycle 回调 (弱符号, 用户实现)
* @param cycle 已校验的有效 Cycle
* @param ch 来源通道
*/
void edc_proto_on_cycle(const edc_cycle_t *cycle, edc_channel_t ch);
#ifdef __cplusplus
}
#endif
#endif /* EDC_PROTOCOL_H */