/** * @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 #include #include #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 */