STM32 HAL 库串口通信

lin, kelriclink 发布于 22 天前 136 次阅读


一、HAL 库三种数据发送模式详解

HAL 库提供了阻塞、中断和 DMA 三种机制来实现数据的串口发送。假设您的 USART1 句柄为 huart1

1. 阻塞式发送 (Polling Mode)

这是最简单、最直接的方式。程序会进入一个等待循环,直到所有数据发送完毕或达到设定的超时时间才会返回。

  • 函数原型:C
  • HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
  • 优点: 使用简单,代码逻辑清晰,适合发送少量数据。
  • 缺点: 在发送过程中,CPU 一直处于等待状态(阻塞),不能处理其他任务。
  • 代码示例:C
  • char *message = "Using Blocking Mode\r\n"; // HAL_MAX_DELAY 表示无限等待,直到发送完成 HAL_UART_Transmit(&huart1, (uint8_t *)message, strlen(message), HAL_MAX_DELAY);

2. 中断式发送 (Interrupt Mode)

发送任务会被交给 UART 外设和中断系统处理,函数调用会立即返回。发送过程由 TxE(发送寄存器空)中断驱动。

  • 函数原型:C
  • HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
  • 优点: 函数调用后立即返回,CPU 可以继续执行主循环任务(非阻塞)。
  • 缺点: 频繁进出中断会增加 CPU 切换开销。
  • 关键回调函数:发送完成后,HAL 库会调用回调函数:C
  • void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART1) { // 数据已全部发送完毕,可以在这里进行后续处理 } }

3. DMA 传输发送 (DMA Mode)

DMA(直接内存访问)允许外设直接与内存之间传输数据,完全不需要 CPU 干预。这是最高效的传输方式,函数调用是非阻塞的。

  • 配置要求: 必须在 CubeMX 或代码中为 USART1 配置 DMA 传输通道(TX 通道)。
  • 函数原型:C
  • HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);
  • 优点: 传输效率极高,CPU 占用极低,最适合大数据量和高波特率传输。
  • 缺点: 配置相对复杂,需要配置 DMA 控制器。
  • 关键回调函数:DMA 传输完成后,也会调用与中断方式相同的回调函数:C
  • void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART1) { // DMA 传输已完成,可以准备下一次 DMA 传输 } }

二、printf 重定向:调试的利器

为了方便地像在 PC 上一样使用 printf 格式化输出,我们需要重定向标准 C 库的 fputc 函数,让其底层调用我们的串口发送函数。

步骤 1: 实现 fputc 函数

在您的项目文件中(如 usart.c),添加以下 fputc 函数的实现。推荐使用阻塞式发送来实现 fputc,因为它简单可靠。

C

#include <stdio.h>

// 声明外部的 UART 句柄(请根据您的项目名称修改)
extern UART_HandleTypeDef huart1;

/**
 * @brief 将 fputc 重定向到 USART1 
 * * printf 最终会调用 fputc 来发送每一个字符。
 */
int fputc(int ch, FILE *f)
{
    uint8_t temp[1] = {(uint8_t)ch};

    // 使用阻塞式发送单个字节,确保字符被发送
    HAL_UART_Transmit(&huart1, temp, 1, 0xFFFF); 
    
    // [可选] 兼容 Windows 终端:将换行符 '\n' 自动转换为回车换行 "\r\n"
    if (ch == '\n') {
        uint8_t carriage_return = '\r';
        HAL_UART_Transmit(&huart1, &carriage_return, 1, 0xFFFF);
    }
    
    return ch;
}

步骤 2: 在程序中调用 printf

完成重定向后,只需确保您的文件包含了 <stdio.h>,即可在任何地方使用 printf 函数:

C

#include "stdio.h"
#include "main.h" // 确保包含了 HAL_GetTick() 等必要的头文件

// ...
uint32_t ms_tick = HAL_GetTick();

// 现在,这条格式化输出会通过串口发送出去!
printf("System running! Current Tick: %lu ms.\r\n", ms_tick);

总结

掌握这三种 HAL 库发送模式,您就可以灵活应对不同的应用需求:

  • printf 重定向 + 阻塞式发送最佳调试方案,简单可靠。
  • 中断式/DMA 方式:用于正式通信协议,追求高效率和低 CPU 占用。