4. 串口接收
任务目标
实现串口单字节接收的两种同步方式:裸机方式在中断服务函数中置位标志位,主循环轮询标志并处理数据;RTOS方式在中断中释放二值信号量,接收任务获取信号量后处理数据。通过两种实现的对比,理解中断与任务间同步机制的差异。
环境依赖
本案例基于STM32F407 + HAL库 + FreeRTOS(CMSIS-RTOS v2封装)。裸机部分不依赖RTOS,RTOS部分需要FreeRTOS运行环境。串口使用USART1,中断已使能。
裸机实现:中断标志同步
// main.c 裸机部分关键代码
volatile uint8_t rx_flag = 0;
volatile uint8_t rx_data = 0;
// USART1中断服务函数(在stm32f4xx_it.c中)
void USART1_IRQHandler(void)
{
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE)) {
rx_data = (uint8_t)(huart1.Instance->DR & 0xFF);
rx_flag = 1; // 置位标志
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);
}
}
// 主循环
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
while (1) {
if (rx_flag) {
rx_flag = 0; // 清除标志
// 处理接收到的数据,例如回显
HAL_UART_Transmit(&huart1, &rx_data, 1, 100);
}
}
}
RTOS实现:二值信号量同步
// RTOS部分关键代码(使用CMSIS-RTOS v2)
osSemaphoreId_t bin_sem_handle;
osThreadId_t rx_task_handle;
// 接收任务
void rx_task(void *argument)
{
uint8_t data;
for (;;) {
// 等待信号量,超时时间设为无限
if (osSemaphoreAcquire(bin_sem_handle, osWaitForever) == osOK) {
data = rx_data; // rx_data在中断中赋值
// 处理数据,例如回显
HAL_UART_Transmit(&huart1, &data, 1, 100);
}
}
}
// USART1中断服务函数(RTOS版本)
void USART1_IRQHandler(void)
{
if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE)) {
rx_data = (uint8_t)(huart1.Instance->DR & 0xFF);
// 释放二值信号量(中断安全版本)
osSemaphoreRelease(bin_sem_handle);
__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);
}
}
// 初始化
void rtos_init(void)
{
bin_sem_handle = osSemaphoreNew(1, 0, NULL); // 初始计数值为0
osThreadNew(rx_task, NULL, NULL);
}
关键对比
| 对比项 | 裸机中断标志 | RTOS信号量 |
|---|---|---|
| 同步机制 | 主循环轮询标志位 | 任务阻塞等待信号量 |
| CPU占用 | 主循环持续轮询,空转消耗CPU | 任务阻塞时让出CPU,可运行其他任务 |
| 实时性 | 依赖主循环轮询频率 | 信号量释放后任务立即就绪,调度延迟低 |
| 多事件处理 | 需为每个事件设置独立标志 | 可为不同事件创建不同信号量或队列 |
| 代码复杂度 | 简单直接 | 需要RTOS初始化与任务创建 |
运行与验证
- 裸机验证:编译烧录后,用串口助手向开发板发送单字节(如0x55),观察是否回显相同字节。若回显正确,说明中断标志同步工作正常。
- RTOS验证:在RTOS工程中执行相同操作,发送单字节应收到相同回显。可通过调试器观察:接收任务在无数据时处于Blocked状态(阻塞在信号量等待),发送数据后任务转为Ready/Running状态。
- 对比判定:两种实现均能正确回显数据即通过。进一步可测量:在RTOS中同时运行一个LED闪烁任务,观察闪烁不受串口接收影响;裸机中若主循环被其他耗时操作占用,串口响应会延迟。
注意事项
- 中断中调用
osSemaphoreRelease是安全的(CMSIS-RTOS v2提供中断安全版本),但不可在中断中调用osSemaphoreAcquire。 - 二值信号量初始计数值必须设为0,否则第一次获取会立即成功而无需中断触发。
- 裸机标志位需使用
volatile修饰,防止编译器优化导致主循环无法感知中断修改。