搞定了底层的“收发”和协议层的“规则”,现在我们要把它们组装成一个能实际跑起来的系统。
在没有 RTOS(实时操作系统)的 裸机(Bare Metal) 环境下,最大的挑战是:如何在等待从机回复(通常需要几十毫秒)的同时,不阻塞主程序的其他业务(如扫描按键、刷新屏幕)?
绝对不能用 HAL_Delay()!本章将带你构建一个基于 有限状态机(FSM) 和 非阻塞定时器 的高效处理框架。
1. 告别 HAL_Delay:前后台系统设计
初学者写单线通讯经常这样写:
// 错误示范:阻塞式代码
HAL_UART_Transmit(&huart1, tx_data, len, 100); // 1. 阻塞发送
HAL_GPIO_WritePin(DIR_Port, DIR_Pin, RX_MODE); // 2. 切方向
HAL_Delay(50); // 3. 死等从机回复 <--- 致命代码!在这里 CPU 啥也干不了
if(HAL_UART_Receive(&huart1, rx_data, len, 100) == HAL_OK) { ... }
这种写法会导致整个系统卡顿。高效的裸机架构应分为两层:
-
前台(中断层):负责“快进快出”的工作。如 DMA 搬运完成、IDLE 中断置位。它只负责置标志位。
-
后台(主循环):负责复杂的协议解析和业务逻辑。它通过轮询标志位和检查时间戳来推动状态机流转。
2. 核心设计:有限状态机 (FSM)
我们将整个通讯过程抽象为几个状态。主循环每次运行到串口任务时,根据当前状态决定做什么。
状态定义
-
IDLE (空闲):总线无事,等待上层应用发起请求。
-
TX_BUSY (发送中):DMA 正在干活,等待发送完成(TC)。
-
RX_WAIT (等待回复):请求已发出,正在计时等待从机应答。这是最关键的“非阻塞”阶段。
-
PROCESSING (处理中):收到了完整数据,正在校验和解析(耗时操作)。
3. 代码实战:非阻塞驱动框架
3.1 定义状态结构体
为了代码整洁,把所有相关的变量打包。
typedef enum {
STATE_IDLE,
STATE_TX_BUSY,
STATE_RX_WAIT,
STATE_PROCESSING
} UART_State_t;
typedef struct {
UART_State_t State;
uint32_t TxStartTime; // 发送开始时间戳
uint32_t RxStartTime; // 等待接收开始时间戳
uint32_t TimeoutLimit; // 超时阈值
uint8_t RetryCount; // 重发计数
uint8_t TxBuffer[64];
uint8_t RxBuffer[64]; // 这

最低0.47元/天 解锁文章
604

被折叠的 条评论
为什么被折叠?



