从STM32将printf()重定向到Qt Creator控制台

kdpv.svg


通常在调试微控制器软件时,有必要将调试消息,日志,捕获的数据和其他内容输出到PC屏幕。 同时,我希望输出速度更快,并且这些行不应在任何地方显示,而应直接在IDE中显示-可以这样说,而不会偏离代码。 实际上,这是关于文章的-我如何尝试使用printf()在我最喜欢的Qt Creator环境(但不是非常微控制器)中输出和显示。


通常,您可以想出很多方法来从微控制器输出文本信息。 但是,最常用的技术并不多:



半主机相当慢,RTT与Segger *硬件和软件解决方案相关,USB并非在每个微控制器中都存在。 因此,通常情况下,我更喜欢最后两个-使用UART和ITM。 关于它们,将在下面讨论。


* 更新。 -实际上,正如评论中所建议的,事实并非如此。 在软件和硬件方面都有选项。 因此,在上述方法中,RTT可能是最通用的。


并立即对接下来将要使用的软件进行一些说明。 作为一个操作系统,我现在拥有Fedora 28,并且当前与微控制器一起使用的软件捆绑是:



在GCC中重定向printf()


因此,为了在GCC中重定向printf()的输出,您需要添加链接器键


-specs=nosys.specs -specs=nano.specs 

如果需要显示浮点数,则需要记住键


 -u_printf_float 

并实现_write()函数。 例如这样的东西


 int _write(int fd, char* ptr, int len) { (void)fd; int i = 0; while (ptr[i] && (i < len)) { retarget_put_char((int)ptr[i]); if (ptr[i] == '\n') { retarget_put_char((int)'\r'); } i++; } return len; } 

其中retarget_put_char()是将字符直接加载到所需接口的函数。


printf()-> ITM-> Qt Creator


仪器跟踪宏单元(ITM)是Cortex-M3 / M4 / M7内核内部的一个块,用于无创地输出(跟踪)各种类型的诊断信息。 要实现有关ITM的printf(),您需要了解以下内容:


  • 使用TRACECLKIN时钟,其频率通常等于核心频率
  • 具有32个所谓的激励端口用于数据输出
  • CMSIS包含ITM_SendChar()函数,该函数将符号加载到激励端口0中
  • 数据通过同步总线(TRACEDATA,TRACECLK)或异步单线SWO线(TRACESWO)输出
  • SWO行通常与JTDO多路复用,这意味着它只能在SWD的调试模式下工作
  • SWO的提款使用曼彻斯特代码或NRZ(UART 8N1)进行
  • 数据以某种格式的帧进行传输-您在接收端需要一个解析器
  • ITM通常是通过IDE或相应的实用程序配置的(但是,没有人禁止在程序代码中进行设置-这样,在不增加调试会话的情况下,SWO的输出将起作用)

使用ITM的最方便方法是使用NRZ编码通过SWO输出-因此,您只需要一行,并且不仅可以使用具有特殊输入的调试器而且可以使用常规USB-UART适配器来接收数据,尽管速度较低。


我使用调试器跟踪该路径,并被迫修改我的中文STLink-V2以支持SWO。 然后一切都变得很简单-我们将JTDO / TRACESWO微控制器连接到相应的调试器引脚,然后进行软件配置。


Openocd具有命令“ tpiu config”-使用它可以配置显示跟踪信息的方法(有关详细信息,请参见《 OpenOCD用户指南》 )。 例如使用参数


 tpiu config internal /home/esynr3z/itm.fifo uart off 168000000 

将输出配置到文件/home/esynr3z/itm.fifo,使用NRZ编码,并根据TRACECLKIN频率168 MHz计算最大传输速度-对于STLink,它是2 MHz。 还有另一个团队


 itm port 0 1 

将启用零端口进行数据传输。


OpenOCD源代码包括itmdump实用程序(contrib / itmdump.c)-使用该实用程序,您可以从接收到的数据中解析字符串。


要编译,我们输入


 gcc itmdump.c -o itmdump 

在启动时,指定必要的文件/管道/ ttyUSB *和-d1开关,以便将接收到的数据字节显示为字符串


 ./itmdump -f /home/esynr3z/itm.fifo -d1 

还有最后一个。 要通过SWO发送字符,我们对_write()进行了补充,并添加了一个函数


 int retarget_put_char(int ch) { ITM_SendChar((uint32_t)ch); return 0; } 

因此,总体计划是这样的:在Qt Creator中,我们配置openocd将SWO上收到的所有信息保存到先前创建的命名管道中,然后我们可以读取管道,解析字符串并使用作为外部工具运行的itmdump显示它。 当然,还有一种解决问题的更优雅的方法-为Qt Creator编写适当的插件。 但是,我希望下述方法对某人有用。


转到Bare Metal插件的设置(工具->选项->设备-> Bare Metal)。


config_baremetal.png


选择使用的GDB服务器,并将行初始化命令添加到列表的末尾


 monitor tpiu config internal /home/esynr3z/itm.fifo uart off 168000000 monitor itm port 0 1 

现在,在调试器将光标置于main()的最开始之前,将配置ITM。


将itmdump添加为外部工具(工具->外部->配置...)。


external_itmdump.png


不要忘记设置变量


 QT_LOGGING_TO_CONSOLE=1 

将实用程序输出显示到Qt Creator控制台(面板7常规消息)。


现在打开itmdump,激活调试模式,开始执行代码,...什么都没有发生。 但是,如果中断调试,则itmdump执行将结束,并且通过printf()打印的所有行都将出现在“常规消息”选项卡上。


经过简短的研究,发现应将itmdump中的行缓冲并显示在stderr中-然后在调试程序时以交互方式出现在控制台中。 我将itmdump的修改版本上传到GitHub


还有一个警告。 如果先前未运行itmdump,则启动时的调试将挂起“ monitor tpiu config ...”命令的执行。 发生这种情况的原因是,在openocd内部打开管道(/home/esynr3z/itm.fifo)进行写操作被阻塞,并且调试器将挂起,直到打开管道以从另一端进行读取。


这有点令人不愉快,尤其是在某些时候不需要ITM的情况下,但是您必须空转它,或者不停地切换GDB服务器或在其设置中删除/添加行。 因此,我不得不挖掘一些openocd资源,并找到需要替代小型拐杖的地方。


在src / target / armv7m_trace.c文件中,有一行带有所需的打开过程


 armv7m->trace_config.trace_file = fopen(CMD_ARGV[cmd_idx], "ab"); 

它需要替换为


 int fd = open(CMD_ARGV[cmd_idx], O_CREAT | O_RDWR, 0664); armv7m->trace_config.trace_file = fdopen(fd, "ab"); 

现在我们的管道将立即打开而不发亮。 因此,您可以保留Bare Metal设置,而itmdump仅在必要时运行。


结果,调试期间的消息输出看起来像这样


debug.png


printf()-> UART-> Qt Creator


在这种情况下,一切都差不多:


  • 在代码中添加具有UART初始化功能
  • 我们实现了retarget_put_char(),该字符将被发送到收发器缓冲区
  • 我们连接USB-UART适配器
  • 向外部工具添加一个实用程序,该实用程序将从虚拟COM端口读取行并将其显示在屏幕上。

我在C- uartdump中绘制了一个这样的实用程序。 使用非常简单-您只需要指定端口名称和波特率即可。


external_uartdump.png


但是,值得注意的是一项功能。 该实用程序不依赖于调试,并且Qt Creator不提供任何选项来关闭正在运行的外部工具。 因此,为了停止读取COM端口,我添加了另一个外部工具。


external_uartdump_close.png


好吧,以防万一,我将为显示在屏幕快照GitHub上的项目的CMake模板附加一个链接。

Source: https://habr.com/ru/post/zh-CN440024/


All Articles