一、什么是重定向

重定向的实现原理,本质上是 拦截并自定义 C 标准库函数的底层输出行为,例如把 printf() 的输出,从默认的“终端/标准输出”改为你想要的输出设备(如串口)

C 标准库默认把 printf() 输出重定向到 “标准输出设备” stdout。在嵌入式中没有屏幕或终端,所以我们自己告诉它“标准输出”该往哪里发,这就是重定向。

也就是说,printf重定向告诉系统:“以后所有 printf() 输出的字符都不要去默认终端,而是通过 UART 发送出去。”

重定向的原理图

printf("A"); 
   ↓
标准库调用内部的 fputc()
   ↓
你自己重写的 fputc() → 发送到 USART

也可以重定向:

fgetc() ← scanf():重写 fgetc(),实现串口接收。

puts() ← fputs():同理,也通过 fputc() 输出字符

二、什么是fputc()

重写fputc() printf()重定向的核心原理,适用于 STM32 等无终端系统。

那什么是fputc()?

int fputc(int ch, FILE *f);

fputc() 是 C 标准库中的一个字符输出函数,它将单个字符 ch 写入文件流 f,例如:

fputc('A', stdout);  // 向标准输出写入 'A'

三、printf() 和 fputc() 的关系

printf() 的调用路径:

printf("ABC");

//内部调用路径

-> printf("ABC");
    -> vfprintf(stdout, "ABC", ...);
        -> fputc('A', stdout);
        -> fputc('B', stdout);
        -> fputc('C', stdout);

printf() 是一个标准库函数,它本身不负责输出字符!

  • printf() 不关心输出设备,只负责“产生字符”

  • 具体往哪里输出?交给了 fputc()(字符级输出函数)

printf() 的作用是:

  1. 解析格式字符串(如 %d, %s )

  2. 把参数格式化成字符串

  3. 将这些字符逐个输出

  4. 每一个字符,最终都会通过 fputc() 或 _write() 输出(由编译器决定)

在 PC 上:

fputc() 输出默认是终端窗口或控制台。

在嵌入式系统(如 STM32)上:

默认没有终端窗口,我们通常需要:把 fputc() 输出的内容转为串口(UART)输出

所以我们要 重写 fputc() 函数,将其输出改为发送到 UART。

当我们重写 fputc() 之后,链接器在链接时查找 fputc() 的定义:

  • 如果你自己实现了 fputc() ,则用你的版本

  • 如果你没有提供,才会用 C 标准库中默认的 fputc()(通常是空实现或弱定义)

大概就是,你接管了 printf() 底层的输出机制,替换了它原本默认调用的输出接口。

四、fputc() 重定向到串口的步骤(基于STM32 )

第一步:写串口发送函数

void uart_send(uint8_t data)
{
    USART_SendData(USART1, data);
    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
}

第二步:重写 fputc() 函数

#include <stdio.h>  // 必须包含

int fputc(int ch, FILE *f)
{
    uart_send((uint8_t)ch);  // 将字符通过串口发送
    return ch;               // 返回字符值表示成功
}

//有些库需要 __attribute__((weak)) 或声明为 extern
//具体取决于你使用的编译器(Keil、GCC、IAR)是否允许重载标准 I/O 函数。

 五、完整示例(基于Keil MDK 标准库)

#include "stm32f4xx.h"
#include <stdio.h>

void uart_send(uint8_t data)
{
    USART_SendData(USART1, data);
    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
}

int fputc(int ch, FILE *f)
{
    uart_send((uint8_t)ch);
    return ch;
}

int main(void)
{
    uart_init();                // 此处不详述,一般包含GPIO初始化,USART初始化,NVIC初始化

    printf("Hello UART!\r\n");  // 会通过 uart_send() 打印
    while (1);
}

六、补充 FILE *f 参数

  • 在 fputc 中这个参数通常是 stdout 。

  • 在重定向中我们一般忽略 f,除非你想同时支持多个输出通道(比如串口 + LCD)

七、其他重定向方式(高级)

编译器 方式 示例
GCC 重写 _write() 用于 newlib
Keil 重写 fputc() 最常见
IAR 重写 __write() 类似 GCC

重定向就到这里啦! 

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐