You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在STM32中正确重定向fprintf到多个UART目标?

解决多UART目标的fprintf输出问题

问题根源

你直接将USART1(外设基地址)强转为FILE*是错误的——newlib的FILE是包含缓冲区、操作函数指针等的结构体,并非硬件地址。这种非法转换会导致newlib无法识别该“文件对象”,自然不会触发_write_r。

核心解决思路

利用newlib的重入函数机制处理标准流(stdout/stderr),同时通过fopencookie创建绑定特定UART的自定义FILE对象,实现多目标输出切换。


方案一:重定向标准流到不同UART

通过重写_write_r,根据文件描述符(fd)区分标准流,映射到对应UART:

#include <stdio.h>
#include <reent.h>
#include <unistd.h>
#include <errno.h>

// 实现各UART的批量发送函数
static void uart1_send(const char* data, size_t len) {
    for (size_t i = 0; i < len; i++) {
        while (!(USART1->SR & USART_SR_TXE)); // 等待发送寄存器空
        USART1->DR = data[i];
    }
}

static void uart2_send(const char* data, size_t len) {
    for (size_t i = 0; i < len; i++) {
        while (!(USART2->SR & USART_SR_TXE));
        USART2->DR = data[i];
    }
}

// 重写newlib重入版_write函数
int _write_r(struct _reent *reent, int fd, char *ptr, size_t len) {
    switch (fd) {
        case STDOUT_FILENO:  // stdout对应fd=1
            uart1_send(ptr, len);
            break;
        case STDERR_FILENO:  // stderr对应fd=2
            uart2_send(ptr, len);
            break;
        default:
            reent->_errno = EBADF; // 非法文件描述符
            return -1;
    }
    return len; // 返回已发送字节数
}

使用方式:

fprintf(stdout, "输出到UART1的内容\n");
fprintf(stderr, "输出到UART2的内容\n");

方案二:创建自定义UART对应的FILE对象

如果需要更多UART输出目标,用fopencookie绑定自定义操作函数,生成合法的FILE对象:

#include <stdio.h>
#include <sys/cookie_io_functions.h>

// UART3的发送函数
static ssize_t uart3_write(void *cookie, const char *buf, size_t size) {
    for (size_t i = 0; i < size; i++) {
        while (!(USART3->SR & USART_SR_TXE));
        USART3->DR = buf[i];
    }
    return size;
}

// 自定义FILE的关闭函数(可空实现)
static int uart3_close(void *cookie) {
    return 0;
}

// 全局自定义FILE对象
FILE* uart3_file;

// 初始化自定义FILE
void uart3_file_init(void) {
    uart3_file = fopencookie((void*)USART3, "w", (cookie_io_functions_t){
        .read = NULL,  // 写模式不需要读函数
        .write = uart3_write,
        .seek = NULL,  // UART无需定位操作
        .close = uart3_close
    });
}

使用方式:

// 先初始化
uart3_file_init();
// 直接用fprintf输出到UART3
fprintf(uart3_file, "输出到UART3的内容\n");

额外优化建议

  • 缓冲输出:用setbuf(uart3_file, NULL)关闭缓冲(实时输出),或自定义缓冲区减少UART调用次数,提升效率。
  • 编译选项:确保使用newlib/newlib-nano时开启重入模式(默认已开启,无需额外配置)。
  • 非阻塞发送:如果需要异步输出,可结合DMA实现UART发送,避免阻塞主程序。

内容的提问来源于stack exchange,提问作者yo3hcv

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 08:08:49