

勾选“Generate peripheral initialization as a pair of '.c/.h' files per peripheral”,否则STM32CubeMX 默认会将所有外设的初始化代码都堆在 main.c 中。如果希望生成独立的usart.c文件,勾选此项。
#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// 省略之前代码...
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
)