STM32H743IIT6 printf 无输出问题排查与解决 STM32H743IIT6 printf 无输出问题排查与解决STM32H743IIT6 printf 无输出问题排查与解决问题现象开发环境问题根因分析1. printf 底层调用链2. 问题工程中的代码分析代码位置 1syscalls.c 中的 _write() 实际被链接代码位置 2main.c 中的 fputc() 未被调用3. 调用链验证4. 问题总结解决方案方案 A实现 __io_putchar()推荐 ✅方案 B直接修改 _write() 调用 HAL方案 C删除 syscalls.c在 main.c 中重写 _write()完整解决方案实施步骤方案 A第 1 步修改 main.c第 2 步确认 USART1 初始化正确第 3 步测试验证第 4 步编译下载附加排查FIFO 配置问题printf 重定向原理速查表ARM GCC 中两种重定向方式对比常见其他 printf 问题排查清单总结STM32H743IIT6 printf 无输出问题排查与解决问题现象在 STM32CubeIDE 中使用printf函数向串口打印数据时串口助手没有任何输出程序编译无报错、无警告但数据无法通过 USART1 发送出去。开发环境项目信息MCUSTM32H743IIT6IDESTM32CubeIDE编译器ARM GCC串口USART1异步模式115200-8-N-1HAL 库STM32H7 HAL问题根因分析1. printf 底层调用链不同编译器下printf的底层调用链不同编译器printf 调用链ARM GCC(STM32CubeIDE)printf()→_write()→__io_putchar()ARMCC(Keil MDK)printf()→fputc()IARprintf()→fputc()/__write()关键结论在 STM32CubeIDEARM GCC中fputc()不会被printf调用2. 问题工程中的代码分析工程中同时存在两套重定向代码但组合后恰好无效代码位置 1syscalls.c中的_write()实际被链接// syscalls.cexternint__io_putchar(intch)__attribute__((weak));// weak 声明默认空实现externint__io_getchar(void)__attribute__((weak));__attribute__((weak))int_write(intfile,char*ptr,intlen){intDataIdx;for(DataIdx0;DataIdxlen;DataIdx){__io_putchar(*ptr);// 调用的是 weak 空函数}returnlen;}_write()是printf最终调用的函数但它内部依赖__io_putchar()来实际发送每一个字符。而__io_putchar被声明为weak工程中没有实现它所以链接器使用了默认空实现 —— 字符被丢弃什么也不会发送。代码位置 2main.c中的fputc()未被调用// main.cintfputc(intch,FILE*f){HAL_UART_Transmit(huart1,(uint8_t*)ch,1,HAL_MAX_DELAY);returnch;}这段代码是 Keil/IAR 风格的重定向写法在 GCC 环境下printf根本不会调用fputc()。这段代码虽然编译通过但完全无效。3. 调用链验证通过编译生成的.map和.list文件可以验证printf() ← 用户调用 └── _write() ← syscalls.c 中定义被链接 └── __io_putchar() ← weak 空实现什么也不做而fputc()在整个调用链中完全不存在验证了它未被链接到 printf 路径中。4. 问题总结┌──────────────────────────────────────────────────────────┐ │ 编译环境ARM GCC (STM32CubeIDE) │ │ │ │ printf() ──→ _write() ──→ __io_putchar() ← 实际链路 │ │ ↑ │ │ WEAK 空实现 │ │ 字符被丢弃 │ │ │ │ fputc() ──→ HAL_UART_Transmit() ← 定义了但未调用 │ │ │ │ ❌ 结果printf 调用 _write_write 调用空的 __io_putchar │ │ 字符全部丢失串口无任何输出 │ └──────────────────────────────────────────────────────────┘解决方案有三种方案可选推荐方案 A。方案 A实现__io_putchar()推荐 ✅在main.c或usart.c中定义__io_putchar()/* USER CODE BEGIN 0 */#includestdio.h#ifdef__GNUC__#definePUTCHAR_PROTOTYPEint__io_putchar(intch)#else#definePUTCHAR_PROTOTYPEintfputc(intch,FILE*f)#endifPUTCHAR_PROTOTYPE{HAL_UART_Transmit(huart1,(uint8_t*)ch,1,HAL_MAX_DELAY);returnch;}/* USER CODE END 0 */优点兼容 GCC/Keil/IAR 三种编译器可移植性好无需修改syscalls.c。方案 B直接修改_write()调用 HAL修改syscalls.c中的_write()函数#includeusart.h__attribute__((weak))int_write(intfile,char*ptr,intlen){externUART_HandleTypeDef huart1;HAL_UART_Transmit(huart1,(uint8_t*)ptr,len,HAL_MAX_DELAY);returnlen;}优点一次发送整块数据比逐字节发送效率高。缺点阻塞式发送发送大量数据时会占用 CPU。方案 C删除syscalls.c在main.c中重写_write()从工程中移除syscalls.c文件在main.c中添加/* USER CODE BEGIN 0 */#includestdio.hint_write(intfile,char*ptr,intlen){HAL_UART_Transmit(huart1,(uint8_t*)ptr,len,HAL_MAX_DELAY);returnlen;}/* USER CODE END 0 */注意如果syscalls.c中还有其他半主机semihosting相关的函数如_read,_lseek等删除时需要一并处理。完整解决方案实施步骤方案 A第 1 步修改main.c在/* USER CODE BEGIN 0 */和/* USER CODE END 0 */之间添加以下代码/* USER CODE BEGIN 0 */#includestdio.h#includestring.h#ifdef__GNUC__#definePUTCHAR_PROTOTYPEint__io_putchar(intch)#else#definePUTCHAR_PROTOTYPEintfputc(intch,FILE*f)#endifPUTCHAR_PROTOTYPE{HAL_UART_Transmit(huart1,(uint8_t*)ch,1,HAL_MAX_DELAY);returnch;}/* USER CODE END 0 */第 2 步确认 USART1 初始化正确在main()函数中确认MX_USART1_UART_Init()已被调用。第 3 步测试验证在main()函数的while(1)循环中添加测试代码/* USER CODE BEGIN 3 */printf(STM32H743IIT6 USART1 Test\r\n);printf(System Clock: %d MHz\r\n,HAL_RCC_GetHCLKFreq()/1000000);HAL_Delay(1000);/* USER CODE END 3 */第 4 步编译下载点击Build编译工程确认无错误无警告点击Debug下载并运行打开串口助手波特率 1152008N1观察输出预期输出STM32H743IIT6 USART1 Test System Clock: 480 MHz附加排查FIFO 配置问题在usart.c中发现 USART1 初始化时有冗余的 FIFO 操作// 当前代码冗余HAL_UARTEx_SetTxFifoThreshold(huart1,UART_TXFIFO_THRESHOLD_8_8);HAL_UARTEx_SetRxFifoThreshold(huart1,UART_RXFIFO_THRESHOLD_8_8);HAL_UARTEx_DisableFifoMode(huart1);// 禁用 FIFO 使前面的阈值设置无效建议简化为// 推荐代码HAL_UARTEx_DisableFifoMode(huart1);先设置阈值再禁用 FIFO 不会导致崩溃但属于无效操作建议清理。printf 重定向原理速查表编译器重定向函数实现位置ARM GCC (CubeIDE)_write()或__io_putchar()syscalls.c或main.cARMCC (Keil MDK)fputc()main.cIAR EWARMfputc()或__write()main.cARM GCC 中两种重定向方式对比方式函数粒度效率方式 1_write()整块数据len 字节高一次 DMA/阻塞发送方式 2__io_putchar()逐字节1 字节低len 次 HAL 调用如果syscalls.c中已有_write()实现优先选择实现__io_putchar()方案 A如果追求效率可以选择方案 B 直接修改_write()。常见其他 printf 问题排查清单症状可能原因排查方法编译报错undefined reference to _write缺少 syscalls.c添加 syscalls.c 或手动实现_write()printf 打印乱码波特率不匹配检查串口助手波特率是否与代码一致printf 只打印第一个字符时钟配置错误检查SystemClock_Config()时钟树printf 打印一半停止缓冲区溢出使用fflush(stdout)或增大堆栈浮点数 printf 无效未链接浮点库链接器添加-u _printf_float中断中 printf 卡死中断优先级问题printf 应放在主循环中断中仅设置标志位总结项目说明问题根因ARM GCC 工具链中printf→_write()→__io_putchar()但__io_putchar()仅有 weak 空声明无实际实现直接原因错误地实现了fputc()Keil 风格在 GCC 下不会被 printf 调用解决核心实现__io_putchar()或修改_write()直接调用HAL_UART_Transmit()推荐方案方案 A —— 使用条件编译实现__io_putchar()兼容多种编译器