【问题标题】:Using printf with two UARTs将 printf 与两个 UART 一起使用
【发布时间】:2013-01-23 09:30:03
【问题描述】:

我在 retarget.c 中实现了 fputcfgetc,以便在 Cortex-M3 上通过 UART0 成功使用 printf。

但是,我想要第二个 uart 通道来获取更多调试信息。如何使用 printf 像 UART0 一样完美地集成它?

例如,使用 fprintf 到自定义目标并检查 fputc 要将字符发送到哪个目标.. 例如。正常输出fprintf(UART0,"..");和调试输出fprintf(UART1,"..");

但是我看不到是否为 stdout 调用了 fopen,所以我正在努力了解如何手动实现它。 (如果我只是打电话给fprintf(RANDOM_VALUE,..),我不知道这会如何。

我想一旦我将它指向不同的“文件”,那么只需检查 fputc 中指向的内容,但这是我正在努力解决的 FILE 指针的初始设置。

也许可以通过某种方式来区分 stdout 和 stderr,尽管从两个单独的通道获取输入时我仍然遇到同样的问题。

fprintf 也在 microlib 中吗?如果没有,有没有更好的方法来实现这一点?

谢谢!

【问题讨论】:

  • 您使用的是哪个 libc 库?这将取决于您自己的流处理是否容易实现。
  • 我怎样才能找到这个?如果有帮助,我正在使用 Keil uVision。我看不到库类型的任何选项。
  • 如果你想实现一个自定义的特殊情况流,你可以将它的数字硬编码为你的程序可以使用的常量,或者你可以让你的 fopen() 识别一个特殊的字符串,例如"/dev/UART1" 这将使任何最终阅读您的代码的有经验的开发人员都非常清楚该功能。
  • 我认为使用 fopen 和 fclose 会很不方便,因为我希望它可以像标准输出一样可访问,并分布在代码中。仅选择一个随机 FILE* 值并在 fputc 中使用 switch 语句来检查该值是否有任何危险(如果匹配,则打印到 UART1;如果不匹配,则打印到 UART0)?还有其他东西使用这个指针吗?我担心的是,如果指针匹配内存映射中的任何其他内容,使用 fprintf 会干扰它吗?
  • 我想我可以声明一个变量并使用该指针来确保它没有指向任何有害的地方?

标签: c embedded serial-port printf


【解决方案1】:

fputc() 接受一个流指针参数,有两个标准输出流stdin、stdout 和stderr。在重定向的较低级别,它们分别与文件描述符 0、1 和 2 相关联,您可以使用此信息将 stderr 与设备驱动程序级别的备用 UART 相关联。

然后您可以使用 stderr 输出调试数据:

fprintf (stderr, "Error reading file" ) ;

例如。

最小的重定向(特定于 Keil ARM-MDK/RealView)可能如下所示:

struct __FILE 
{
    int handle;  
};

enum 
{
    STDIN_HANDLE,
    STDOUT_HANDLE,
    STDERR_HANDLE
} ;

FILE __stdin = {STDIN_HANDLE} ;
FILE __stdout = {STDOUT_HANDLE} ;
FILE __stderr = {STDERR_HANDLE} ;

int fputc(int ch, FILE *f) 
{
    int ret = EOF ;

    switch( f->handle )
    {
        case STDOUT_HANDLE :
            // Write character to UART0
            ...
            ret = ch ;
            break ;

        case STDERR_HANDLE :
            // Write character to UART1
            ...
            ret = ch ;
            break ;

        default :
            break ;

    return ret ;
}

显然,如果需要,这也是您可以挂接文件系统的地方,在这种情况下,您的 __FILE 结构无疑会有额外的成员。

如果您不想为此使用 stderr,则必须重新定位 fopen() 以将设备名称(例如“dbg:”)转换为所需端口的文件描述符,然后使用 stdio 来输出到关联的流。

fprintf 也在 microlib 中吗?如果没有,有没有更好的方法来实现这一点?

文档会告诉你,但是是的。 Microlib stdio 支持由#pragma import(__use_full_stdio) 指令控制,文档不清楚如果不使用它会排除什么。尝试不使用它,如果缺少任何东西,请使用它。也就是说,我可以想象 printf() 是作为标准输出流的 fprintf() 实现的,所以如果你有 printf(),你就有 fprintf()。

【讨论】:

  • 感谢您的示例.. 所以我假设 STDIN_HANDLE = 0、STDOUT_HANDLE = 1 等等..?如果我也需要两个通道的输入,你会如何拆分标准输入?
  • STDIN_HANDLE 等按枚举中列出的顺序从零开始自动枚举 - 这是 ISO C 行为所必需的;如果您希望绝对清楚,您可以显式地为枚举分配值,但这不是绝对必要的。 fgetc() 以类似的方式处理输入。我不相信有任何东西会阻止您接受stdoutstderr 的输入。如果控制台由单独的键盘和显示器组成,则可能需要进行区分,但不需要使用 UART。
  • 谢谢,我没有意识到您也可以使用 stdout 和 stderr 进行输入。我今天无法实现这一点,但现在应该有我需要的所有提示!你真的很有帮助!
  • @Mark:你一般不能;或者至少它可能是“未定义”的行为。然而,定义行为的是低级设备驱动程序,在这种情况下,您可以完全控制重定向。
  • 请注意,您始终可以通过定义额外的 FILE 对象来添加额外的“隐式打开”全局流,例如FILE dbgio_stream = {DBGIO_HANDLE};,然后创建一个全局指针FILE* dbgio = &dbgio_stream ; 并以与标准句柄类似的方式处理它。例如,您只需使用fprintf( dbgio, ...)。它确实假设您事先已经初始化了设备。如果您希望映射到不同或相同的设备,您甚至可以拥有单独的 dbgin 和 dbgout 流。
猜你喜欢
  • 2011-04-17
  • 2016-08-19
  • 2019-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多