# E32-433TBH-SC 应用层软件开发计划 ## 项目概述 | 项目属性 | 描述 | | --------- | -------------------------------- | | **目标MCU** | STM32F103C8T6 (Cortex-M3, 72MHz) | | **开发环境** | Keil MDK-ARM V5.32 | | **架构模式** | 裸机轮询 + 中断驱动 | | **当前状态** | HAL层就绪,基础通信验证通过 | *** ## 一、整体架构设计 ### 1.1 硬件资源映射 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 硬件连接关系 │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ UART1 (PA9/PA10) ◄────► RF433无线模块 (9600bps) │ │ │ │ UART2 (PA2/PA3) ◄────► 调试串口/上位机 (115200bps) │ │ │ │ UART3 (PB10/PB11) ◄────► 预留串口 (9600bps) │ │ │ │ MCU_DI1 (PB4) ◄────► 数字输入1 │ │ MCU_DI2 (PB5) ◄────► 数字输入2 │ │ MCU_DI3 (PB6) ◄────► 数字输入3 │ │ MCU_DI4 (PB7) ◄────► 数字输入4 │ │ │ │ RL_Control (PA15) ◄────► 继电器控制输出 │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` ### 1.2 软件分层架构 ``` ┌─────────────────────────────────────────────────────────────────────┐ │ 应用层 (Application) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │ │ │ UART2_Print │ │ 指令解析器 │ │ IO状态监控 │ │ 继电器控制 │ │ │ │ (调试输出) │ │ (ASCII指令) │ │ (DI1-4) │ │ (RL_Control)│ │ │ └──────────────┘ └──────────────┘ └──────────────┘ └────────────┘ │ ├─────────────────────────────────────────────────────────────────────┤ │ 服务层 (Service) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ RingBuffer │ │ CmdParser │ │ Debounce │ │ │ │ (环形缓冲区) │ │ (指令解析) │ │ (软件去抖) │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ ├─────────────────────────────────────────────────────────────────────┤ │ 驱动层 (Driver) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ UART Driver │ │ GPIO Driver │ │ RF433 Driver │ │ │ │ (usart.c) │ │ (gpio.c) │ │ (rf433.c) │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ ├─────────────────────────────────────────────────────────────────────┤ │ 硬件抽象层 (HAL - STM32) │ │ STM32F1xx_HAL_Driver / CMSIS │ └─────────────────────────────────────────────────────────────────────┘ ``` ### 1.3 核心模块交互关系 ``` ┌─────────────────┐ │ UART2_Print │ │ (调试输出) │ └────────▲────────┘ │ 日志/响应输出 │ ┌──────────────┐ ┌────────┴────────┐ ┌──────────────┐ │ UART1 │◄──►│ 指令解析器 │◄──►│ UART2 │ │ (RF433) │ │ (CmdParser) │ │ (调试/上位机) │ └──────────────┘ └────────┬────────┘ └──────────────┘ │ ┌──────────────┼──────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ IO状态监控 │ │ 继电器控制 │ │ RF433通信 │ │ (DI1-4) │ │ (RL_Control)│ │ (透传模式) │ └─────────────┘ └─────────────┘ └─────────────┘ ``` ### 1.4 任务调度模型 基于现有裸机架构,采用**时间片轮询**模型: ```c void main(void) { system_init(); while(1) { UART2_Print_Task(); // 发送缓冲区处理 IO_Monitor_Task(); // IO扫描与去抖 CmdParser_Task(); // 指令解析处理 __WFI(); // 等待中断,降低功耗 } } ``` **调度周期设计**: | 任务 | 执行周期 | 说明 | | ----------------- | ---- | ----------------- | | `UART2_Print_Task` | 1ms | 发送缓冲区数据处理 | | `IO_Monitor_Task` | 10ms | IO扫描与去抖,足够采样间隔 | | `CmdParser_Task` | 每轮 | 指令解析,响应无需微秒级 | *** ## 二、模块详细设计 ### 2.1 UART2_Print 模块 #### 2.1.1 模块职责 - 提供调试信息输出接口 - 支持 `printf` 风格的格式化输出 - 支持中断安全调用(通过环形缓冲区) #### 2.1.2 头文件设计 (`uart2_print.h`) ```c #ifndef __UART2_PRINT_H #define __UART2_PRINT_H #ifdef __cplusplus extern "C" { #endif #include #include #include #define UART2_TX_BUFFER_SIZE 256 void UART2_Print_Init(void); void UART2_Print_Send(const uint8_t *data, uint16_t len); void UART2_Print_String(const char *str); void UART2_Print_Printf(const char *fmt, ...); void UART2_Print_Task(void); bool UART2_Print_IsBusy(void); #ifdef __cplusplus } #endif #endif ``` #### 2.1.3 环形缓冲区实现 ```c #include "uart2_print.h" #include "usart.h" typedef struct { uint8_t buffer[UART2_TX_BUFFER_SIZE]; volatile uint16_t head; volatile uint16_t tail; volatile bool is_sending; } ring_buffer_t; static ring_buffer_t tx_ring = {0}; void UART2_Print_Init(void) { tx_ring.head = 0; tx_ring.tail = 0; tx_ring.is_sending = false; } void UART2_Print_Send(const uint8_t *data, uint16_t len) { if (len == 0) return; __disable_irq(); for (uint16_t i = 0; i < len; i++) { uint16_t next = (tx_ring.head + 1) % UART2_TX_BUFFER_SIZE; if (next == tx_ring.tail) { break; } tx_ring.buffer[tx_ring.head] = data[i]; tx_ring.head = next; } __enable_irq(); if (!tx_ring.is_sending) { UART2_Print_Task(); } } void UART2_Print_String(const char *str) { UART2_Print_Send((const uint8_t *)str, strlen(str)); } void UART2_Print_Task(void) { if (tx_ring.is_sending || tx_ring.tail == tx_ring.head) { return; } uint8_t byte = tx_ring.buffer[tx_ring.tail]; tx_ring.tail = (tx_ring.tail + 1) % UART2_TX_BUFFER_SIZE; tx_ring.is_sending = true; HAL_UART_Transmit_IT(&huart2, &byte, 1); } void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART2) { tx_ring.is_sending = false; if (tx_ring.tail != tx_ring.head) { UART2_Print_Task(); } } } ``` #### 2.1.4 printf 重定向 ```c #include #if defined(__CC_ARM) || defined(__ARMCC_VERSION) int fputc(int ch, FILE *f) { UART2_Print_Send((uint8_t *)&ch, 1); return ch; } #endif ``` *** ### 2.2 ASCII文本指令协议 #### 2.2.1 指令格式设计 **设计原则**: - 人类可读,方便调试 - 格式简单,易于解析 - 兼容常见串口调试工具 - **调试友好**:校验和FF为特权后门,跳过校验 **指令格式**: ``` $CMD,param1,param2*CS\r\n $ - 帧起始符 CMD - 指令名称(2-4字符) , - 参数分隔符 param - 参数(可选) * - 校验和起始符 CS - 校验和(2位十六进制,从$到*之间所有字符的异或值) 特权后门: CS=FF 时强制跳过校验,用于人工调试 \r\n - 帧结束符 ``` #### 2.2.2 指令定义 | 指令 | 参数1 | 参数2 | 功能说明 | 示例 | | ----- | ------------ | --------- | -------------- | ----------------------- | | `RL` | 继电器号(1) | 0/1 | 继电器控制 | `$RL,1,1*FF\r\n` | | `DI` | 通道号(0-4) | (无) | 查询IO状态 | `$DI,1*FF\r\n` | | `FWD` | 目标端口(1) | 数据 | 数据转发 | `$FWD,1,Hello*FF\r\n` | | `ECHO`| (无) | (无) | 心跳测试 | `$ECHO*FF\r\n` | **参数说明**: - `RL` 指令:参数2为 `0`=关闭,`1`=打开 - `DI` 指令:参数1为 `0`=查询所有,`1-4`=查询指定通道 #### 2.2.3 完整指令示例 **示例1:控制继电器1打开** ``` 指令: $RL,1,1*FF\r\n 解析: - RL: 继电器控制指令 - 参数1=1: 继电器编号1 - 参数2=1: 打开 - CS=FF: 跳过校验(调试模式) ``` **示例2:控制继电器1关闭** ``` 指令: $RL,1,0*FF\r\n 解析: - 参数2=0: 关闭 ``` **示例3:查询DI1状态** ``` 指令: $DI,1*FF\r\n 响应: $OK,DI,1,1*XX\r\n (DI1=HIGH) 或: $OK,DI,1,0*XX\r\n (DI1=LOW) ``` **示例4:查询所有DI状态** ``` 指令: $DI,0*FF\r\n 响应: $OK,DI,1010*XX\r\n (DI1=1, DI2=0, DI3=1, DI4=0) ``` **示例5:转发数据到RF433** ``` 指令: $FWD,1,Hello RF433*FF\r\n 说明: 将"Hello RF433"转发到UART1(RF433模块) ``` **示例6:带校验和的正式指令** ``` 指令: $RL,1,1*4C\r\n 校验和计算: '$' ^ 'R' ^ 'L' ^ ',' ^ '1' ^ ',' ^ '1' = 0x24 ^ 0x52 ^ 0x4C ^ 0x2C ^ 0x31 ^ 0x2C ^ 0x31 = 0x4C ``` #### 2.2.4 响应格式 **成功响应**: ``` $OK,RL,1,1*XX\r\n (继电器1已打开) $OK,DI,1,1*XX\r\n (DI1当前为HIGH) $OK,DI,1010*XX\r\n (所有DI状态) ``` **失败响应**: ``` $ERR,CMD*XX\r\n (未知指令) $ERR,CS*XX\r\n (校验和错误) $ERR,PARAM*XX\r\n (参数错误) ``` **IO状态上报(主动上报)**: ``` $DI_EVENT,1,1*XX\r\n (DI1变为HIGH) $DI_EVENT,3,0*XX\r\n (DI3变为LOW) ``` #### 2.2.5 安全防护设计 **防护目标**:防止畸形数据导致单片机死机(HardFault) **防护措施**: | 风险场景 | 防护措施 | 实现方式 | | -------- | -------- | -------- | | 缓冲区溢出 | 边界检查 | 写入前检查索引是否越界 | | 超长数据帧 | 超时重置 | 长时间未收到完整帧则重置状态机 | | 非法字符 | 字符过滤 | 只接受合法ASCII字符 | | 内存越界 | 固定长度 | 使用固定大小缓冲区,拒绝超长数据 | #### 2.2.6 指令解析器实现(带安全防护) ```c #include #include #define CMD_BUFFER_SIZE 64 #define CMD_MAX_LEN 8 #define PARAM_MAX_LEN 32 #define PARSE_TIMEOUT_MS 1000 typedef enum { PARSE_IDLE, PARSE_CMD, PARSE_PARAM1, PARSE_PARAM2, PARSE_CHECKSUM, PARSE_COMPLETE } parse_state_t; typedef struct { char cmd[CMD_MAX_LEN]; char param1[PARAM_MAX_LEN]; char param2[PARAM_MAX_LEN]; uint8_t received_cs; uint8_t calculated_cs; bool valid; bool skip_checksum; } cmd_frame_t; static parse_state_t parse_state = PARSE_IDLE; static cmd_frame_t current_frame; static uint8_t field_index = 0; static uint8_t checksum_acc = 0; static uint32_t last_rx_tick = 0; static void reset_parser(void) { parse_state = PARSE_IDLE; field_index = 0; checksum_acc = 0; memset(¤t_frame, 0, sizeof(current_frame)); } static bool is_valid_cmd_char(char c) { return isupper(c) || isdigit(c); } static bool is_valid_param_char(char c) { return isprint(c) && c != '*' && c != '\r' && c != '\n'; } static uint8_t hex_to_byte(char high, char low) { uint8_t val = 0; if (high >= '0' && high <= '9') val = (high - '0') << 4; else if (high >= 'A' && high <= 'F') val = (high - 'A' + 10) << 4; else if (high >= 'a' && high <= 'f') val = (high - 'a' + 10) << 4; if (low >= '0' && low <= '9') val |= (low - '0'); else if (low >= 'A' && low <= 'F') val |= (low - 'A' + 10); else if (low >= 'a' && low <= 'f') val |= (low - 'a' + 10); return val; } void CmdParser_FeedByte(uint8_t byte, uint32_t current_tick) { if (parse_state != PARSE_IDLE) { if (current_tick - last_rx_tick > PARSE_TIMEOUT_MS) { reset_parser(); return; } } last_rx_tick = current_tick; switch (parse_state) { case PARSE_IDLE: if (byte == '$') { reset_parser(); parse_state = PARSE_CMD; } break; case PARSE_CMD: if (byte == ',') { current_frame.cmd[field_index] = '\0'; parse_state = PARSE_PARAM1; field_index = 0; } else if (byte == '*') { current_frame.cmd[field_index] = '\0'; parse_state = PARSE_CHECKSUM; field_index = 0; } else if (is_valid_cmd_char(byte)) { if (field_index < CMD_MAX_LEN - 1) { current_frame.cmd[field_index++] = byte; checksum_acc ^= byte; } else { reset_parser(); } } else { reset_parser(); } break; case PARSE_PARAM1: if (byte == ',') { current_frame.param1[field_index] = '\0'; parse_state = PARSE_PARAM2; field_index = 0; } else if (byte == '*') { current_frame.param1[field_index] = '\0'; parse_state = PARSE_CHECKSUM; field_index = 0; } else if (is_valid_param_char(byte)) { if (field_index < PARAM_MAX_LEN - 1) { current_frame.param1[field_index++] = byte; checksum_acc ^= byte; } else { reset_parser(); } } else { reset_parser(); } break; case PARSE_PARAM2: if (byte == '*') { current_frame.param2[field_index] = '\0'; parse_state = PARSE_CHECKSUM; field_index = 0; } else if (is_valid_param_char(byte)) { if (field_index < PARAM_MAX_LEN - 1) { current_frame.param2[field_index++] = byte; checksum_acc ^= byte; } else { reset_parser(); } } else { reset_parser(); } break; case PARSE_CHECKSUM: if (byte == '\n') { current_frame.received_cs = hex_to_byte( current_frame.cmd[0], current_frame.cmd[1] ); current_frame.calculated_cs = checksum_acc; current_frame.skip_checksum = (current_frame.received_cs == 0xFF); if (current_frame.skip_checksum || current_frame.received_cs == current_frame.calculated_cs) { current_frame.valid = true; parse_state = PARSE_COMPLETE; } else { reset_parser(); } } else if (byte != '\r') { if (field_index < 2) { current_frame.cmd[field_index++] = byte; } } break; case PARSE_COMPLETE: reset_parser(); if (byte == '$') { parse_state = PARSE_CMD; } break; } } bool CmdParser_HasCompleteFrame(cmd_frame_t *frame) { if (parse_state == PARSE_COMPLETE && current_frame.valid) { if (frame) { memcpy(frame, ¤t_frame, sizeof(cmd_frame_t)); } return true; } return false; } void CmdParser_Acknowledge(void) { reset_parser(); } ``` **安全防护要点说明**: 1. **缓冲区边界检查**:每次写入前检查 `field_index < MAX_LEN` 2. **超时重置**:超过1秒未收到完整帧,自动重置状态机 3. **字符过滤**:只接受合法字符,非法字符触发重置 4. **固定长度缓冲区**:不使用动态内存分配,避免内存碎片 5. **校验和特权**:`CS=FF` 时跳过校验,方便人工调试 *** ### 2.3 IO状态监控模块 #### 2.3.1 检测方式 **推荐方案:定时扫描 + 软件去抖** | 方案 | 优点 | 缺点 | 适用场景 | | ---- | --------- | --------------- | ------ | | 外部中断 | 响应快 | 需要额外中断资源,硬件去抖复杂 | 低频事件 | | 定时扫描 | 简单可靠,统一管理 | 有扫描延迟 | 多路IO监控 | #### 2.3.2 软件去抖实现 ```c #define DEBOUNCE_COUNT 3 #define IO_CHANNEL_COUNT 4 typedef struct { GPIO_TypeDef *port; uint16_t pin; uint8_t current_state; uint8_t debounce_counter; uint8_t last_raw_state; } io_channel_t; static io_channel_t di_channels[IO_CHANNEL_COUNT] = { {GPIOB, GPIO_PIN_4, 0, 0, 0}, {GPIOB, GPIO_PIN_5, 0, 0, 0}, {GPIOB, GPIO_PIN_6, 0, 0, 0}, {GPIOB, GPIO_PIN_7, 0, 0, 0} }; static uint32_t last_scan_tick = 0; void IO_Monitor_Task(void) { if (HAL_GetTick() - last_scan_tick < 10) { return; } last_scan_tick = HAL_GetTick(); for (int i = 0; i < IO_CHANNEL_COUNT; i++) { io_channel_t *ch = &di_channels[i]; uint8_t raw_state = HAL_GPIO_ReadPin(ch->port, ch->pin) ? 1 : 0; if (raw_state != ch->last_raw_state) { ch->debounce_counter = 0; ch->last_raw_state = raw_state; } else { if (ch->debounce_counter < DEBOUNCE_COUNT) { ch->debounce_counter++; } else if (ch->current_state != raw_state) { ch->current_state = raw_state; IO_StateChangeHandler(i, raw_state); } } } } void IO_StateChangeHandler(uint8_t channel, uint8_t state) { UART2_Print_Printf("$DI_EVENT,%d,%d*XX\r\n", channel + 1, state); } uint8_t IO_Monitor_GetAllStates(void) { uint8_t states = 0; for (int i = 0; i < IO_CHANNEL_COUNT; i++) { if (di_channels[i].current_state) { states |= (1 << i); } } return states; } ``` #### 2.3.3 IO监控模块接口 ```c #ifndef __IO_MONITOR_H #define __IO_MONITOR_H #include void IO_Monitor_Init(void); void IO_Monitor_Task(void); uint8_t IO_Monitor_GetState(uint8_t channel); uint8_t IO_Monitor_GetAllStates(void); #endif ``` *** ### 2.4 继电器控制模块 #### 2.4.1 模块接口 ```c #ifndef __RELAY_CONTROL_H #define __RELAY_CONTROL_H #include #include void Relay_Init(void); void Relay_SetState(uint8_t relay_id, bool state); bool Relay_GetState(uint8_t relay_id); void Relay_Toggle(uint8_t relay_id); #endif ``` #### 2.4.2 实现代码 ```c #include "relay_control.h" #include "main.h" void Relay_Init(void) { HAL_GPIO_WritePin(RL_Control_GPIO_Port, RL_Control_Pin, GPIO_PIN_RESET); } void Relay_SetState(uint8_t relay_id, bool state) { if (relay_id == 1) { HAL_GPIO_WritePin(RL_Control_GPIO_Port, RL_Control_Pin, state ? GPIO_PIN_SET : GPIO_PIN_RESET); } } bool Relay_GetState(uint8_t relay_id) { if (relay_id == 1) { return HAL_GPIO_ReadPin(RL_Control_GPIO_Port, RL_Control_Pin) ? true : false; } return false; } void Relay_Toggle(uint8_t relay_id) { if (relay_id == 1) { HAL_GPIO_TogglePin(RL_Control_GPIO_Port, RL_Control_Pin); } } ``` *** ## 三、关键数据结构与接口 ### 3.1 简化的数据结构 ```c #ifndef __APP_TYPES_H #define __APP_TYPES_H #include #include #define IO_CHANNEL_COUNT 4 typedef enum { UART_PORT_1 = 1, UART_PORT_2 = 2, UART_PORT_3 = 3 } uart_port_t; typedef struct { char cmd[8]; char param1[16]; char param2[32]; uart_port_t source; bool valid; } cmd_frame_t; #endif ``` ### 3.2 设计说明 | 数据结构 | 用途 | 设计理由 | | -------- | ---- | -------- | | `uart_port_t` | 标识串口 | 简单枚举,UART1=RF433,UART2=调试口,UART3=预留 | | `cmd_frame_t` | 存储解析后的指令 | 固定长度字符数组,避免动态内存分配 | **为什么不需要复杂的数据结构?** 1. **ASCII指令格式**:人类可读,直接用字符串处理即可 2. **裸机轮询架构**:不需要消息队列,直接在主循环处理 3. **单一职责**:每个模块功能单一,不需要复杂的抽象 *** ## 四、实施路线图 ### 4.1 分阶段开发计划 ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ 开发阶段时间线 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ 第一阶段 (基础框架) 第二阶段 (核心功能) 第三阶段 (扩展) │ │ ├─ UART2_Print ├─ ASCII指令解析 ├─ RF433透传 │ │ ├─ IO监控模块 ├─ 继电器控制 ├─ UART3扩展 │ │ └─ 基础日志输出 └─ 指令响应 └─ 配置存储 │ │ │ │ 验证点: 验证点: 验证点: │ │ · 串口输出正常 · 指令响应正确 · 无线通信正常 │ │ · IO状态上报 · 继电器动作 · 长时间稳定性 │ │ │ └─────────────────────────────────────────────────────────────────────────┘ ``` ### 4.2 第一阶段:基础框架搭建 **目标**:实现调试输出能力和IO状态监控 **任务清单**: | 序号 | 任务 | 预计工作量 | 依赖 | | --- | --- | --- | --- | | 1.1 | 实现 `UART2_Print` 模块 | 1.5h | 无 | | 1.2 | 实现 `printf` 重定向 | 0.5h | 1.1 | | 1.3 | 实现 `IO_Monitor` 模块 | 1.5h | 无 | | 1.4 | 集成到 `main.c` 主循环 | 0.5h | 1.1, 1.3 | | 1.5 | 编写测试代码验证 | 0.5h | 1.4 | **验证方法**: ```c void phase1_test(void) { UART2_Print_Init(); IO_Monitor_Init(); printf("Phase 1 Test Start\r\n"); printf("System Clock: %d MHz\r\n", SystemCoreClock / 1000000); while (1) { IO_Monitor_Task(); UART2_Print_Task(); static uint32_t last_tick = 0; if (HAL_GetTick() - last_tick > 5000) { last_tick = HAL_GetTick(); uint8_t states = IO_Monitor_GetAllStates(); printf("DI States: 0x%02X\r\n", states); } } } ``` **验收标准**: - [ ] UART2能正常输出调试信息 - [ ] `printf` 函数可用 - [ ] DI1-DI4状态变化能实时上报 - [ ] 软件去抖功能正常 *** ### 4.3 第二阶段:指令解析与继电器控制 **目标**:实现ASCII指令解析和继电器控制 **任务清单**: | 序号 | 任务 | 预计工作量 | 依赖 | | --- | --- | --- | --- | | 2.1 | 实现ASCII指令解析器 | 2h | 第一阶段 | | 2.2 | 实现继电器控制模块 | 0.5h | 无 | | 2.3 | 实现指令响应机制 | 1h | 2.1, 2.2 | | 2.4 | UART2接收中断集成 | 0.5h | 2.1 | | 2.5 | 端到端测试 | 0.5h | 2.4 | **验证方法**: ```c void phase2_test(void) { UART2_Print_Init(); IO_Monitor_Init(); CmdParser_Init(); Relay_Init(); printf("Phase 2 Test Start\r\n"); while (1) { IO_Monitor_Task(); UART2_Print_Task(); CmdParser_Task(); } // 手动测试:通过串口发送指令 // 打开继电器1: $RL,1,1*FF\r\n // 关闭继电器1: $RL,1,0*FF\r\n // 查询DI1状态: $DI,1*FF\r\n // 查询所有DI: $DI,0*FF\r\n } ``` **验收标准**: - [ ] 能正确解析ASCII指令 - [ ] 校验和验证正常 - [ ] 继电器能按指令开关 - [ ] 指令执行后有响应输出 - [ ] 错误指令能被识别并返回错误信息 *** ### 4.4 第三阶段:RF433透传与扩展 **目标**:实现RF433无线透传功能 **任务清单**: | 序号 | 任务 | 预计工作量 | 依赖 | | --- | --- | --- | --- | | 3.1 | 实现FWD转发指令处理 | 1h | 第二阶段 | | 3.2 | 集成RF433模块驱动 | 1h | 3.1 | | 3.3 | 实现双向透传 | 1h | 3.2 | | 3.4 | 压力测试与优化 | 1h | 3.3 | **验证方法**: ```c // 测试场景: // 1. UART2发送: $FWD,1,Hello RF433*FF\r\n // 验证UART1(RF433)是否收到 "Hello RF433" // // 2. RF433接收数据后,自动转发到UART2 // 验证UART2是否收到RF433的数据 ``` **验收标准**: - [ ] UART2→RF433转发正常 - [ ] RF433→UART2转发正常 - [ ] 连续转发100次无丢包 - [ ] 长时间运行稳定(>30分钟) *** ### 4.5 文件组织 ``` Core/ ├── Inc/ │ ├── uart2_print.h # 调试打印模块 │ ├── io_monitor.h # IO监控模块 │ ├── cmd_parser.h # 指令解析模块 │ ├── relay_control.h # 继电器控制模块 │ └── app_types.h # 公共类型定义 ├── Src/ │ ├── main.c # 主程序(修改) │ ├── uart2_print.c # 调试打印实现 │ ├── io_monitor.c # IO监控实现 │ ├── cmd_parser.c # 指令解析实现 │ └── relay_control.c # 继电器控制实现 ``` *** ### 4.6 风险与缓解措施 | 风险 | 影响 | 缓解措施 | | --- | --- | --- | | 环形缓冲区溢出 | 数据丢失 | 增加缓冲区大小,添加溢出检测 | | 指令解析错误 | 功能异常 | 完善状态机,添加超时重置机制 | | 继电器控制时序 | 硬件损坏 | 添加最小间隔限制 | *** ## 五、总结 ### 架构设计要点 1. **简化分层**:应用层 → 服务层 → 驱动层,职责清晰 2. **ASCII指令**:人类可读,方便调试,易于扩展 3. **时间片轮询**:基于现有裸机架构,无需RTOS ### 协议设计亮点 1. **ASCII文本格式**:`$CMD,param1,param2*CS\r\n`,简单直观 2. **异或校验**:计算简单,适合嵌入式系统 3. **统一响应格式**:`$OK,...` 或 `$ERR,...` ### 关键技术决策 | 决策点 | 选择 | 理由 | | --- | --- | --- | | 指令格式 | ASCII文本 | 人类可读,方便调试,兼容串口工具 | | IO检测方式 | 定时扫描 | 4路统一管理,软件去抖灵活 | | 打印缓冲 | 环形缓冲区 | ISR安全,非阻塞发送 | ### 下一步行动 建议按照以下顺序开始编码: 1. 实现 `UART2_Print` 模块,验证调试输出 2. 实现 `IO_Monitor` 模块,验证IO监控 3. 实现 `CmdParser` 模块,验证指令解析 4. 实现 `Relay_Control` 模块,验证继电器控制