12

我正在手臂微处理器上编程,并试图通过 UART 使用打印语句进行调试。我不想添加stdlibs只是为了调试。stdio.h有没有办法在没有/的情况下打印到控制台iostream.h?我可以自己写printf()吗?

或者,我可以使用 DMA 控制器并直接写入 UART 来执行此操作。但是我想避免这种情况是可能的。使用内置的测试功能“echo”或“remote loop-back”我知道我已经正确配置了UART。

4

4 回答 4

10

简短的回答:是的,完全有可能同时使用这两种解决方案。

如果要支持所有数据类型和格式, printf 函数会非常复杂。但是编写可以以几种不同的基数输出字符串或整数的东西并不难(大多数人只需要十进制和十六进制,但八进制可能只需要在具有十进制和十六进制后再添加 3-4 行代码)。

通常,printf 是这样写的:

 int printf(const char *fmt, ...)
 {
     int ret;
     va_list args; 

     va_start(args, fmt)
     ret = do_xprintf(outputfunc, NULL, fmt, args); 
     va_end(args);
     return ret;
}

然后do_xprintf()为所有变体(printf、sprintf、fprintf 等)做所有艰苦的工作

int do_xprintf(void (*outputfunc)(void *extra, char c), void *extra, const char *fmt, va_list args)
{
    char *ptr = fmt;
    while(1)
    {
       char c = *ptr++;

       if (c == '%')
       {
            c = *ptr++; // Get next character from format string. 
            switch(c)
            {
               case 's': 
                  char *str = va_arg(args, const char *);
                  while(*str)
                  {
                      count++;
                      outputfunc(extra, *str);
                      str++;
                  }
                  break; 
               case 'x': 
                  base = 16;
                  goto output_number;

               case 'd':
                  base = 10;
         output_number:
                  int i = va_arg(args, int);
                  // magical code to output 'i' in 'base'. 
                  break;

               default:
                  count++;
                  outputfunc(extra, c);
                  break;
         }
         else
             count++;
             outputfunc(extra, c);
     }
     return count;
 }                

现在,您需要做的就是填写上述代码的一些位并编写一个 outputfunc() 输出到您的串行端口。

请注意,这是粗略的草图,我敢肯定代码中存在一些错误 - 如果您想支持浮点或“宽度”,您将不得不多做一些工作......

(注意额外参数 - 对于输出到FILE *文件指针的 a ,对于sprintf,您可以传递缓冲区的结构和缓冲区中的位置,或类似的东西)

于 2013-05-08T21:52:39.037 回答
3

在您使用的特定系统的上下文之外,“控制台”的概念没有太多意义。通常在嵌入式程序中没有控制台的真正概念。

您正在寻找的是一种从系统中获取数据的方法。如果你想使用 UART,并且你没有使用像 GNU/linux 这样的高级操作系统,你将需要编写自己的 I/O 驱动程序。通常,这意味着首先通过寄存器写入将 UART 配置为所需的比特率/奇偶校验/流量控制。对于任何类型的稳健 IO,您都希望它是中断驱动的,因此您需要为使用循环缓冲区的 tx 和 rx 编写 ISR。

完成后,您可以编写自己的 printf,如 Mats 所示。

于 2013-05-08T22:45:27.630 回答
2

由于通过嵌入式系统中的串行端口打印出信息会修改主程序的时序,因此我发现的最佳解决方案是发送以 2 个字节编码的小消息(有时 1 个字节可以正常工作),然后使用PC 来解码这些消息并提供必要的信息,其中可能包括统计数据和您可能需要的一切。这样,我只在主程序中增加了一点点开销,让 PC 完成处理消息的繁重工作。也许是这样的:

  • 1 字节消息:位 7:4 = 模块 ID,位 3:0 = 调试信息。

  • 2 字节消息:位 15:12 = 模块 ID,位 11:8 = 调试信息,位 7:0 = 数据。

然后,在 PC 软件中,您必须声明一个表格,其中包含映射到给定模块 ID/调试信息对的消息,并使用它们打印在屏幕上。

也许它不如伪 printf 函数灵活,因为你需要在 PC 中解码一组固定的消息,但它不会增加太多开销,正如我之前提到的。

希望能帮助到你。

费尔南多

于 2013-05-20T11:49:00.193 回答
1

我发现对于后台调试,将字符排入循环缓冲区,然后由 uart 发送寄存器上的轮询例程排出,这是我选择的方法。

入队例程基于字符、字符串和可变大小(到十六进制或固定宽度的十进制)。一个豪华的缓冲区例程可以指示一个保留字符的溢出。

该方法对目标操作的开销/影响最低,可以(小心地)在中断例程中使用,并且这个想法很容易转移,所以我忽略了我使用过的所有系统上的调试器。

于 2013-05-09T07:30:43.517 回答