dreameng

使用Hal库实现printf串口输出

2026/05/03
13
0

1. 开启USART

2. 生成代码设置(非必须)

  • 勾选“Generate peripheral initialization as a pair of '.c/.h' files per peripheral”,否则STM32CubeMX 默认会将所有外设的初始化代码都堆在 main.c​ 中。如果希望生成独立的usart.c​文件,勾选此项。

3. 重写printf的输出函数

#ifdef __GNUC__
  /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
     set to 'Yes') calls __io_putchar() */
  #define PUTCHAR int __io_putchar(int ch)
#else
  #define PUTCHAR int fputc(int ch, FILE *f)
#endif /* __GNUC__ */

PUTCHAR
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
  return ch;
}
  • 为了能够适配不同的编译器,代码使用了条件编辑

    • ​fputc​:如果你使用 Keil MDK (ARMCC) 或 IAR,标准库函数 printf​ 底层调用的是 fputc​

    • __GNUC__​:这是一个预定义宏。如果你使用基于 GCC 的工具链(如 STM32CubeIDE、),它会被定义。GCC 下 printf​ 最终调用的是 __io_putchar​

  • 如果使用的是MDK环境开发,则有可能导致__GNC__宏定义识别有误,可以使用以下代码实现重定向输出

    /* 
     * 显式定义,确保编译器和链接器能 100% 识别
     * Keil 环境下真正起作用的是 fputc
     */
    int fputc(int ch, FILE *f)
    {
        USART_SendData(DEBUG_USARTx, (uint8_t)ch);
        while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);
        return (ch);
    }
    
    /* 
     * 只有当你以后可能把代码移到 STM32CubeIDE (GCC) 时,
     * 才需要额外增加这个兼容层
     */
    #ifdef __GNUC__
    int __io_putchar(int ch)
    {
        return fputc(ch, NULL);
    }
    #endif

4. 测试代码

// 省略之前代码...
while (1)
  {
    /* USER CODE END WHILE */
    printf("红灯亮:%d秒\r\n", 1);
    HAL_GPIO_WritePin(GPIOA, RED_LED_Pin, GPIO_PIN_RESET); // 打开红灯
    HAL_Delay(1000); // Delay 1 second

    printf("绿灯亮:%d秒\r\n", 1);
    HAL_GPIO_WritePin(GPIOA, GREEN_LED_Pin, GPIO_PIN_RESET); // 打开绿灯
    HAL_Delay(1000); // Delay 1 second

    printf("蓝灯亮:%d秒\r\n", 1);
    HAL_GPIO_WritePin(GPIOA, BLUE_LED_Pin, GPIO_PIN_RESET); // 打开蓝灯
    HAL_Delay(1000); // Delay 1 second

    printf("所有灯灭:%d秒\r\n", 1);
    HAL_GPIO_WritePin(GPIOA, RED_LED_Pin | GREEN_LED_Pin | BLUE_LED_Pin, GPIO_PIN_SET); // Set PA1, PA2, PA3(关闭LED)
    HAL_Delay(1000); 
    /* USER CODE BEGIN 3 */
  }
// 省略之后代码...
  • 串口监听输出

输出浮点数时,不输出数值

如果使用cmake,大概率在打印输出浮点数时,不输出内容。是由于嵌入式芯片资源有限,编译器默认用的 newlib-nano 库为了节省空间,把 printf 里处理浮点数的代码给“阉割”了 。‌‌‌

解决方法

target_link_options(${CMAKE_PROJECT_NAME} PRIVATE
    # Add user defined linker options
    -u _printf_float
)