如何从 Newlib 在 GCC 中实现 printf?

cDr*_*mer 4 c embedded printf gcc newlib

我正在努力使用 GCC 将 printf 从 newlib 正确实现到我的 esp32 中。

我已经阅读了 newlib 文档,它为我提供了有关如何调用 printf 的一般信息,但没有向我解释后端实现。

根据我当前的研究,我确定 printf 将格式化字符串输出到 STDOUT。在 PC 上,这对我来说更容易理解,因为有一个控制台窗口可以显示 printf 的格式化输出,但是在嵌入式系统上,我知道您必须告诉库将 printf 的格式化输出重定向到哪里,这就是我正在想办法。

再次,根据我的研究,我了解到需要一些功能来完成此任务,特别是功能_write

我发现如何弥合 printf 和使用该_write函数之间的差距非常困难。我希望这里有人能帮助我理解如何正确实现 printf。

如果我错过了一些清楚解释这一点的文档,那么请将我重定向到该文档。我尝试阅读 newlib 文档以及 GCC 相关文档,但没有真正提到如何使用 printf,但是有大量关于如何调用 printf 和格式化字符串的文档,但这部分很简单。我需要知道如何从 MCU 的 STDOUT 获取格式化字符串。

谢谢大家!

Cli*_*ord 8

在 Newlib 中,您不实现printf()库中包含的内容。您只需实现一组最小的系统调用即可支持该库。流设备 sycalls API 由openclosereadwrite或带有后缀的可重入版本_r)组成 - 如果您使用多线程并且需要每个线程errno(在任何特定于实现的可重入要求中),则此中的可重入性非常有用。

如果您正在实现的只是( 、等stdout使用的流)并且仅支持单个设备(通常是 UART)并且不关心重定向或重入的能力,则、和可以为空,并且可以简单地将提供的缓冲区直接输出到低级串行 I/O API:printf()putchar()puts()openclosereadwrite

int _write(int handle, char *data, int size ) 
{
    int count ;

    handle = handle ; // unused

    for( count = 0; count < size; count++) 
    {
        outputByte( data[count] ) ;  // Your low-level output function here.
    }

    return count;
}
Run Code Online (Sandbox Code Playgroud)

注意handle这里没有使用。因为stdout它将是 1(stdin= 0 和stderr= 2)。如果您想要单独的输出设备,或者如果您支持其他设备或文件系统和/或重定向,handle则将使用该参数。它用于识别由 开启的蒸汽。通过忽略所有流输出(例如将以相同的方式处理并输出到相同的设备);在许多情况下(其中只是获取调试输出的一种方法,或者您的应用程序没有您不关心的文件系统。stdoutstderrfopenstdoutopenfprintf()printf()

给定该write函数,printf()将“正常工作”(以最简单的方式),因为在幕后所有 stdio 输出函数都调用write)。建议使用缓冲且非阻塞的低电平输出函数(例如中断驱动的UART 驱动程序)。

显然,如果您也想接受输入stdin,您可以实现类似的read功能。

如果你想要一个堆(malloc()等),你还需要实现sbrk/ sbrk_r。如果 Newlib 系统调用中没有其他内容,我建议您至少实现这一点。

Bill Gaitliff在嵌入式系统中的移植和使用 Newlib中讨论了对系统调用实现的更复杂的处理,而此处讨论了基本实现,而Newlib 文档本身提供了与上述类似的示例最小实现存根。