Blog Post

Printf 重定向Retarget

2026/7/9
KeilBlog嵌入式

# 前言

HAL库新工程烧录后无法运行,keil在线调试STM32,点3次运行才能跑到main?

  1. 第1次点击运行:CPU从Reset_Handler启动,执行到main,再执行到printf。此时printf试图通过半主机通道向调试器(MDK/IAR)请求打印服务。但调试器还没完全准备好接管这个I/O请求,导致CPU进入HardFault卡死在BKPT指令上。程序“看起来”没跑起来。
  2. 第2或第3次点击运行:调试器被强制唤醒,处理了挂起的半主机请求,或者你手动复位后,调试器内部状态机被刷新,程序才勉强跳过那个陷阱。

# 解决方式

推荐方案2,使用微库较为局限,在实际工程中不建议这么做。

方案1:勾选微库(MicroLIB)

  • 在Keil中,点击魔术棒(Options for Target) -> Target 选项卡。
  • 勾选 “Use MicroLIB”

为什么必须勾? 标准C库的printf体积大,且依赖半主机。微库是一个轻量级库,它明确允许用户重写底层的fputc函数,而不走半主机通道。

方案2:手动屏蔽半主机

程序中加入以下代码:

#pragma import(__use_no_semihosting)

struct __FILE
{
  int handle;
};

FILE __stdout;

int _sys_exit(int x)
{
  return x = x;
}

< 不要忘记重定义fputc >

#include "stdio.h"

int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
  return ch;
}

#其他

  • 可以选择删除printf,调用或者重新封装串口发送函数,因为是重定向才导致这个问题。

  • 在实际工程中,若打印频次高,建议使用DMA方式操作串口发送。

  • 本文只讨论Keil下的HAL库操作,不保证其他平台其他库可以如此使用。