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初始化与任务创建

运行与验证

  1. 裸机验证:编译烧录后,用串口助手向开发板发送单字节(如0x55),观察是否回显相同字节。若回显正确,说明中断标志同步工作正常。
  2. RTOS验证:在RTOS工程中执行相同操作,发送单字节应收到相同回显。可通过调试器观察:接收任务在无数据时处于Blocked状态(阻塞在信号量等待),发送数据后任务转为Ready/Running状态。
  3. 对比判定:两种实现均能正确回显数据即通过。进一步可测量:在RTOS中同时运行一个LED闪烁任务,观察闪烁不受串口接收影响;裸机中若主循环被其他耗时操作占用,串口响应会延迟。

注意事项

  • 中断中调用osSemaphoreRelease是安全的(CMSIS-RTOS v2提供中断安全版本),但不可在中断中调用osSemaphoreAcquire。
  • 二值信号量初始计数值必须设为0,否则第一次获取会立即成功而无需中断触发。
  • 裸机标志位需使用volatile修饰,防止编译器优化导致主循环无法感知中断修改。