王增 发表于 2025-4-17 09:33:27

printf能直接通过串口1打印出数据

printf("APP_Version: %s\r\n", APP_Version);
为什么这个能直接通过串口1打印出数据呢??
这是怎么实现的?? 编译器将标准输出挂在到了串口1上面了么??




看不到stdio.c, 后台是怎么实现的?? 有谁知道么??

国芯人工智能 发表于 2025-4-17 09:38:00

在嵌入式系统中,printf函数通常用于格式化输出数据。默认情况下,printf函数会将数据输出到标准输出设备(通常是控制台)。然而,在嵌入式系统中,标准输出设备可能并不存在,或者需要通过特定的硬件接口(如串口)进行输出。因此,开发者需要将printf函数的输出重定向到特定的硬件接口,例如串口1。

1. printf函数的工作原理
printf函数是C标准库中的一个函数,用于格式化输出数据。它的底层实现依赖于putchar或write等函数,这些函数负责将字符逐个输出到标准输出设备。在嵌入式系统中,标准输出设备通常被重定向到串口或其他硬件接口。

2. 重定向printf到串口1
在嵌入式系统中,将printf函数的输出重定向到串口1通常需要以下步骤:

2.1 实现putchar或write函数
为了将printf的输出重定向到串口1,开发者需要实现一个自定义的putchar或write函数,该函数将字符发送到串口1。例如:

c
int ioputchar(int ch) {
    // 将字符发送到串口1
    UARTSendData(UART1, (uint8t)ch);
    while (UARTGetFlagStatus(UART1, UARTFLAGTXE) == RESET);
    return ch;
}

2.2 重定向标准输出
在实现putchar或write函数后,开发者需要将标准输出重定向到该函数。这通常通过修改ioputchar函数的定义或使用特定的库函数来实现。例如,在某些开发环境中,可以通过以下方式重定向标准输出:

c
int write(int file, char ptr, int len) {
    int DataIdx;
    for (DataIdx = 0; DataIdx < len; DataIdx++) {
      ioputchar(ptr++);
    }
    return len;
}

2.3 初始化串口1
在重定向标准输出之前,开发者需要确保串口1已经正确初始化。这通常包括配置串口的波特率、数据位、停止位和校验位等参数。例如:

c
void UART1Init(void) {
    // 配置串口1的波特率、数据位、停止位和校验位
    UARTInitTypeDef UARTInitStruct;
    UARTInitStruct.BaudRate = 115200;
    UARTInitStruct.WordLength = UARTWORDLENGTH8B;
    UARTInitStruct.StopBits = UARTSTOPBITS1;
    UARTInitStruct.Parity = UARTPARITYNONE;
    UARTInitStruct.Mode = UARTMODETXRX;
    UARTInitStruct.HwFlowCtl = UARTHWCONTROLNONE;
    UARTInitStruct.OverSampling = UARTOVERSAMPLING16;
    UARTInit(UART1, &UARTInitStruct);
    UARTCmd(UART1, ENABLE);
}

3. 编译器的作用
编译器在编译过程中并不会自动将标准输出重定向到串口1。重定向标准输出是开发者在代码中显式实现的。编译器的作用是将printf函数的调用转换为对底层输出函数(如putchar或write)的调用。因此,开发者需要确保这些底层输出函数能够正确地将数据发送到串口1。

4. 后台实现
在嵌入式系统中,printf函数的后台实现通常涉及以下几个步骤:

1. 格式化字符串:printf函数首先根据格式化字符串和参数生成输出字符串。
2. 调用底层输出函数:生成的字符串通过putchar或write函数逐个字符地输出。
3. 硬件接口发送:putchar或write函数将字符发送到串口1,串口1再将字符发送到外部设备。

5. 总结
在嵌入式系统中,printf函数能够直接通过串口1打印出数据,是因为开发者将标准输出重定向到了串口1。这通常通过实现自定义的putchar或write函数,并确保串口1正确初始化来实现。编译器在编译过程中并不会自动完成这一重定向,开发者需要在代码中显式地实现这一功能。

通过这种方式,开发者可以方便地使用printf函数进行调试和信息输出,而无需关心底层的硬件接口细节。

[本文内容由国芯人工智能辅助生成,仅供参考]

王增 发表于 2025-4-17 09:44:27

用户没有主动增加这部分程序,这个是怎么实现的?? 编译器??还是库函数中自带的??

zhx 发表于 2025-4-17 09:50:59

王增 发表于 2025-4-17 09:44
用户没有主动增加这部分程序,这个是怎么实现的?? 编译器??还是库函数中自带的??
...

你这个明显是某个 RTOS系统,RTOS替你重定向了,仔细找,应该能找到

DebugLab 发表于 2025-4-17 10:00:25

用了库函数?

王增 发表于 2025-4-17 10:01:41

纯裸机程序,没有用操作系统

王增 发表于 2025-4-17 10:02:24

DebugLab 发表于 2025-4-17 10:00
用了库函数?

用了,标准库的printf

zhx 发表于 2025-4-17 10:04:02

你的日志信息,谁给你实现的?

DebugLab 发表于 2025-4-17 10:05:31

我写的串口printf程序供参考:
https://www.stcaimcu.com/thread-4598-1-1.html
https://www.stcaimcu.com/thread-16321-1-1.html
可以改为snprintf,避免缓冲区溢出



zhx 发表于 2025-4-17 10:06:01

王增 发表于 2025-4-17 10:02
用了,标准库的printf
标准 printf 的fputc 是可以自己定义的


/* 重定义fputc函数, printf函数最终会通过调用fputc输出字符串到串口 */
int fputc(int ch, FILE *f)
{
    while ((USART_UX->ISR & 0X40) == 0);    /* 等待上一个字符发送完成 */

    USART_UX->TDR = (uint8_t)ch;            /* 将要发送的字符 ch 写入到DR寄存器 */
    return ch;
}
写这样一个名字的函数,编译时printf 就会替换原来的输出

你搜索这个函数
页: [1] 2
查看完整版本: printf能直接通过串口1打印出数据