stm32 printf实现

1. 标准库 printf 原理

2.1.1 printf函数的C标准库实现原理

printf 函数是 C 语言标准库中实现输入输出功能的一个核心函数。它通过可变参数列表(variadic function)的方式,允许开发者传递不同类型和数量的参数,并将这些参数格式化输出到标准输出设备。在内部, printf 利用格式化字符串来定义输出的格式,这个字符串包含了文本内容以及转换说明符(如 %d , %f , %s 等)。

在 C 标准库中, printf 的实现通常涉及以下几个关键步骤:

  1. 通过格式化字符串解析出转换说明符。
  2. 根据转换说明符确定对应参数的类型。
  3. 根据参数类型进行相应的数据解析和格式化处理。
  4. 将格式化后的数据输出到标准输出。

printf 在实际输出之前,通常会使用缓冲机制暂存输出数据,以减少实际的 I/O 操作次数,提高效率。
2.1.2 标准输出的缓冲机制

标准输出的缓冲机制是一种常见的优化手段,它使得数据在写入到输出设备之前可以先存储在缓冲区中。缓冲区的大小和策略会影响程序的性能,例如, printf 默认使用的是行缓冲(line buffering),这意味着当输出数据包含换行符( \n )或者缓冲区满时,缓冲区中的数据会被刷新(flush)到真正的输出设备。

缓冲机制的设计对程序性能有着极大的影响。对于嵌入式系统而言,正确的管理缓冲区能有效减少 CPU 的使用率和内存的开销。

2. stm32中的printf实现

由标准库的printf的实现原理,唯一的不同就在于,最后一步用fputc来将数据输出到标准输出,由于我们stm32里面没有标准输出。所以我们只有在main中初始化好usart,之后覆写fputc函数来将输出重定向到我们的usart串口中。

3. 为什么是否使用微库都能成功?

在STM32中,无论是使用微库(Micro LIB) 还是标准C库(Standard C Library),都能通过重写fputc函数实现printf到串口的输出,核心原因是两者都遵循C语言标准库的接口约定,且printf的底层实现逻辑对“输出设备”的依赖性较弱。具体原因如下:

1. 微库(Micro LIB)与标准库的共性:依赖fputc作为输出入口

printf函数的本质是“格式化字符串输出”,其核心逻辑是将格式化后的字符序列通过一个底层函数逐个输出。在C标准中,这个底层输出函数就是fputc(用于向指定的FILE流写入单个字符)。

无论是微库还是标准库,printf的实现都会最终调用fputc 来完成字符的实际输出。因此,只要重写fputc函数,将其指向串口发送,printf的输出就会被“劫持”到串口,与使用哪种库无关。

2. 微库与标准库的差异不影响fputc的重写逻辑

两者的主要区别体现在功能完整性、代码体积、底层实现细节上,但这些差异不影响对fputc的重写:

  • 微库(Micro LIB):
    是为嵌入式系统优化的轻量级库,功能简化(如不支持完整的文件I/O、多线程安全等),代码体积小,直接运行在裸机环境中。其fputc的默认实现可能是“空操作”或指向一个无效设备,但允许用户通过重写函数来替换默认行为。

  • 标准C库(如GNU C Library, Newlib):
    功能完整,支持复杂的I/O操作(如文件、管道等),但代码体积较大。在嵌入式环境中,标准库通常需要“移植层”(如syscalls.c)来适配硬件,其中fputc的默认实现可能依赖底层的write系统调用,但用户仍可通过重写fputc(或write)来覆盖默认行为,将输出定向到串口。

3. 重写fputc的本质:覆盖默认输出目标

无论是哪种库,fputc都是一个可重定义的弱函数(或通过链接优先级被用户实现覆盖)。用户重写的fputc会取代库中的默认实现,成为printf调用的实际输出函数。

例如:

  • 微库中,fputc的默认实现可能直接返回(无输出),用户重写后,printf会使用用户定义的串口发送逻辑。
  • 标准库中,fputc可能默认尝试向“标准输出设备(stdout)”写入,但在嵌入式系统中“stdout”无实际硬件对应,用户重写后,输出会被强制转向串口。

总结

微库和标准库都能支持printf重定向到串口,核心原因是:
两者都遵循printf依赖fputc输出字符的标准逻辑,而fputc的实现可以被用户重写,从而将输出目标从默认设备(无实际意义)改为串口。

差异仅在于库的底层实现细节(如是否需要额外的移植层文件),但不影响重定向的核心原理。因此,无论选择哪种库,只要正确重写fputc,就能实现串口输出。