开发环境: JLink版本:V9。4 JLink驱动版本:V760hx8664 Keil:V5。30 在嵌入式开发过程中,经常需要进行打印调试,通常使用串口进行打印输出,但通常串口资源有限,这时就可以通过JLink工具里面自带的RTT实现打印,从而节约一个串口资源。1RTT简介 RTT全称是RealTimeTransmit(实时传输),是Segger公司推出的调试手段之一。它是一种用于嵌入式中与用户进行交互的技术。 使用RTT可以从MCU快速输出调试信息和数据,且不影响MCU的实时性。只要支持JLink的MCU就可使用RTT功能,兼容性非常强。 RTT支持两个方向的多个通道,上到主机,下到目标,它可以用于不同的目的,为用户提供尽可能多的自由。默认实现每个方向使用一个通道,用户可在在调试终端输入和输出。 使用JLinkRTTViewer,可用于虚拟终端,允许打印到多个窗口(例如,一个用于标准输出,一个对于错误输出,一个用于调试输出)。 RTT的通信可以通过不同的应用程序完成,可以使用SDK集成到自定义的应用程序中,可本地连接,可远程连接。 Segger也给出了相应的实例,使用起来非常简单。 关于RTT的更多介绍请参看Segger官网: https:www。segger。comproductsdebugprobesjlinktechnologyaboutrealtimetransfer2RTT的工作原理 RTT在MCU的存储器中使用SeggerRTT控制块结构管理数据的读写操作。控制块对于每个可用的信道都在内存中包含了一个ID,用于描述通道缓冲区及其状态,通过JLink或者环形缓冲结构区(链表)都可以通过ID找到对应的控制块。 可用信道的最大数目可以在编译时配置,并且每个缓冲区都可以在MCU运行时配置和使用。上下缓冲区可以分开处理。每个通道都可以配置为阻塞或非阻塞。 在阻塞模式下,应用程序将等待缓冲区写满,直到可以写入所有内存为止,这将导致应用程序处于阻塞状态,但可以防止数据丢失。 在非阻塞模式下,只会写入适合缓冲区的数据,或完全不写入缓冲区,其余的数据将被丢弃。这样即使没有连接调试器,也可以实时运行。开发人员不必创建特殊的调试版本,并且代码可以保留在发布应用程序中。 当RTT处于活动状态时,无论是通过RTTViewer等应用程序直接使用RTT,还是通过Telnet连接到使用JLink的应用程序(如调试器),JLink都会在目标的已知RAM区域中自动搜索SeggerRTT控制块。RAM区域或控制块的特定地址也可以通过主机应用程序设置以加快检测速度,否则无法自动找到控制块。 下图显示了SeggerRTT控制块的内部结构: RTT不需要通过额外SWO引脚,即可实现printf输出,它也不需要对目标进行任何配置或在调试环境中进行任何配置,甚至可以在不同的目标速度下使用。3RTT的性能 RTT的性能(耗时)远高于SWO。平均一行文本可以在一微秒或更短的时间内输出,基本上只需要做一个memcopy()的时间。 RTT的最大速度取决于目标缓冲区大小和目标接口速度。即使使用512字节的小型目标缓冲区,高版本的JLink的速度高达1MiBs,而使用低版本的JLink只有0。5MiBs。 RTT实现代码使用大约500字节的ROM和(n(通道数)(24字节ID24字节))的RAM。推荐的大小是1kByte(上行信道)和16到32字节(下行信道),这取决于输入输出的负载。 Memory Usage ROMUsage 500Bytes RAMUsage 24Bytesfixed(24SizeofBuffer)Byteschannel4JLink驱动安装及RTT工具简介4。1驱动安装 在使用RTT之前,先要JLink驱动。 JLink驱动下载链接:https:www。segger。comdownloadsjlink 根据自己的电脑选择相应的软件,笔者的使用的是Windows64bit的。下载好JLink驱动程序后,双击安装即可,这里就不在赘述了。4。2RTT工具简介 安装完成后,会有三个与RTT相关的软件。 1。JLinkRTTViewer JLinkRTTViewer是在调试主机上使用RTT功能的WindowsGUI应用程序。 RTTViewer可以独立使用,打开自己与JLink的连接,并与正在运行的调试会话目标或并行,连接到它并使用现有的JLink连接。 RTTViewer支持RTT的主要功能:通道0上的终端输出将文本输入发送到通道0最多16个虚拟终端,只有一个目标通道控制文本输出:彩色文本,擦除控制台在通道1上记录数据 本文主要讲解JLinkRTTViewer的使用。 2。JLinkRTTClient JLinkRTTClient可以充当Telnet客户端,但在调试会话关闭时会尝试自动重新连接到JLink。 【PS】要想使用JLinkRTTClient,需要开启telnet。如果你的电脑没有开启telnet功能,需要打开启用或关闭Windows功能,打开方法如下: 然后在里面找到telnet客户端,启动即可。 3。JLinkRTTLogger 使用JLinkRTTLogger可以读取来自上行通道1的数据并将其记录到文件中。 例如,可用于向主机发送性能分析数据。JLinkRTTLogger与JLink建立专用连接,可独立使用,无需运行调试器。 JLinkRTTLogger的源代码可用作将RTT集成到其他PC应用程序(如调试器)的起点,并且是JLinkSDK的一部分。 5RTT移植及RTTViewer使用5。1RTTViewer快速使用 【Note】笔者后文将使用STM32F103演示RTT的使用。 1。添加RTT文件 安装完JLink驱动之后,在安装目录下有相应的RTT源码包。 笔者的安装目录是:C:ProgramFilesSEGGERJLinkSamplesRTT 解压SEGGERRTTV760h。zip文件,加解压后文件内容如下: 将RTT复制到自己的基础工程中,笔者使用的是带串口的基础工程。 另外还需要将Config目录下的SEGGERRTTConf。h复制到工程目录下的RTT文件夹中,值得注意的是,不同的JLlink驱动版本,Config文件存放的地方是不同的。 最后工程目录如下: 然后将RTT下的所有文件添加到Keil工程中。 值得注意的是,需要将RTT的头文件路径也添加到工程中。 值得注意的是,如果直接将Config目录下的SEGGERRTTConf。h复制到工程目录下,还需要修改SEGGERRTT。h文件中SEGGERRTTConf。h的路径。修改后如下: 当然也可直接将Config目录复制到工程目录下,这样只需要添加头Config文件路径即可。 这里就更具自己喜好添加吧。 2。添加测试代码 修改main。c中代码,修改后如下:briefmianparamNoneretvalintintmain(void){STBSPUSARTDevBSPUSARTDev0USARTDEV0CONFIG;ConfiguretheNVICPreemptionPriorityBitsNVICPriorityGroupConfig(NVICPriorityGroup2);SystickinitSysTickInit();USART1配置模式为1152008N1,中断接收BSPUSARTInit(BSPUSARTDev0,115200,0,1);无限循环while(1){printf(sr,HelloWorldfromSEGGER!);SEGGERRTTWriteString(0,HelloWorldfromSEGGER!r);Delayms(1000);}} 笔者这里还使用了串口打印输出,用于对比。 【PS】关于STM32F103的串口工程请参看笔者博客: 串口通信:https:bruceou。blog。csdn。netarticledetails79341769 3。测试 编译下载,启动RTTViewer软件。 选择相应的目标设备,这里就根据自己的MCU选择相应的型号。 其他默认即可。 最后点击‘OK’,就会看到打印信息。 当然。我们使用串口也能看到串口打印。 可以看到不管是使用RTT,还是使用串口其效果都是一样的。是不是很nice!5。2RTTViewer多终端使用 另外,上面的实例中使用的终端0,还可以同时使用多个终端。 核心代码如下:briefmianparamNoneretvalintintmain(void){STBSPUSARTDevBSPUSARTDev0USARTDEV0CONFIG;ConfiguretheNVICPreemptionPriorityBitsNVICPriorityGroupConfig(NVICPriorityGroup2);SystickinitSysTickInit();USART1配置模式为1152008N1,中断接收BSPUSARTInit(BSPUSARTDev0,115200,0,1);无限循环while(1){STM32RTTViewerSEGGERRTTConfigUpBuffer(0,RTTUP,NULL,0,SEGGERRTTMODENOBLOCKSKIP);SEGGERRTTConfigUpBuffer(1,RTTUP,NULL,0,SEGGERRTTMODENOBLOCKSKIP);RTTViewerSTM32SEGGERRTTConfigDownBuffer(0,RTTDOWN,NULL,0,SEGGERRTTMODENOBLOCKSKIP);SEGGERRTTConfigDownBuffer(1,RTTDOWN,NULL,0,SEGGERRTTMODENOBLOCKSKIP);printf(sr,HelloWorldfromSEGGER,Terminal0!);SEGGERRTTSetTerminal(0);SEGGERRTTWriteString(0,HelloWorldfromSEGGER,Terminal0!r);printf(sr,HelloWorldfromSEGGER,Terminal1!);SEGGERRTTSetTerminal(1);SEGGERRTTWriteString(0,HelloWorldfromSEGGER,Terminal1!r);Delayms(1000);}} 编译下载,添加Terminal1,打印如下: 同样使用串口打印: 5。3RTTViewer自定义颜色 RTT还可以自定义打印颜色,在SEGGERRTT。h文件可以查看不同颜色的宏定义。 核心代码如下:briefmianparamNoneretvalintintmain(void){STBSPUSARTDevBSPUSARTDev0USARTDEV0CONFIG;ConfiguretheNVICPreemptionPriorityBitsNVICPriorityGroupConfig(NVICPriorityGroup2);SystickinitSysTickInit();USART1配置模式为1152008N1,中断接收BSPUSARTInit(BSPUSARTDev0,115200,0,1);无限循环while(1){STM32RTTViewerSEGGERRTTConfigUpBuffer(0,RTTUP,NULL,0,SEGGERRTTMODENOBLOCKSKIP);SEGGERRTTConfigUpBuffer(1,RTTUP,NULL,0,SEGGERRTTMODENOBLOCKSKIP);RTTViewerSTM32SEGGERRTTConfigDownBuffer(0,RTTDOWN,NULL,0,SEGGERRTTMODENOBLOCKSKIP);SEGGERRTTConfigDownBuffer(1,RTTDOWN,NULL,0,SEGGERRTTMODENOBLOCKSKIP);printf(sr,HelloWorldfromSEGGER,Terminal0!);SEGGERRTTSetTerminal(0);SEGGERRTTWriteString(0,RTTCTRLTEXTREDHelloWorldfromSEGGER,Terminal0!r);printf(sr,HelloWorldfromSEGGER,Terminal1!);SEGGERRTTSetTerminal(1);SEGGERRTTprintf(0,RTTCTRLTEXTGREENHelloWorldfromSEGGER,Terminal1!r);Delayms(1000);}} 效果如下: 5。4RTTViewerprintf重定向 RTT还可以使用printf重定向,只需要简单修改fputc()函数即可。brief重定向c库函数printf到USART1paramNoneretvalNoneintfputc(intch,FILEf){ifdefined(RTT)SEGGERRTTPutChar(0,ch);else清楚标志位USARTClearFlag(USART1,USARTFLAGTC);发送一个字节数据到USART1USARTSendData(USART1,(uint8t)ch);等待发送完毕while(USARTGetFlagStatus(USART1,USARTFLAGTC)RESET);endifreturn(ch);} 核心代码如下briefmianparamNoneretvalintintmain(void){STBSPUSARTDevBSPUSARTDev0USARTDEV0CONFIG;ConfiguretheNVICPreemptionPriorityBitsNVICPriorityGroupConfig(NVICPriorityGroup2);SystickinitSysTickInit();USART1配置模式为1152008N1,中断接收BSPUSARTInit(BSPUSARTDev0,115200,0,1);无限循环while(1){SEGGERRTTSetTerminal(0);SEGGERRTTWriteString(0,RTTCTRLTEXTREDHelloWorldfromSEGGER,Terminal0!r);printf(printf:sr,HelloWorldfromSEGGER,Terminal0!);SEGGERRTTSetTerminal(1);SEGGERRTTWriteString(0,RTTCTRLTEXTGREENHelloWorldfromSEGGER,Terminal1!r);printf(printf:sr,HelloWorldfromSEGGER,Terminal1!);Delayms(1000);}} 最后效果如下: 5。5RTTViewer打印float RTTViewer不能打印出float类型的数据,要想打印浮点数,最简单的办法就是将浮点型数据转为字符串。 在C的标准库中有两个转换的函数:intsprintf(charstr,constcharformat,。。。);intsnprintf(charstr,sizetsize,constcharformat,。。。); 下面是转换实例:briefmianparamNoneretvalintintmain(void){STBSPUSARTDevBSPUSARTDev0USARTDEV0CONFIG;floatfData3。1415926;charchData〔32〕;ConfiguretheNVICPreemptionPriorityBitsNVICPriorityGroupConfig(NVICPriorityGroup2);SystickinitSysTickInit();USART1配置模式为1152008N1,中断接收BSPUSARTInit(BSPUSARTDev0,115200,0,1);sprintf(chData,。4f,fData);无限循环while(1){SEGGERRTTSetTerminal(0);SEGGERRTTprintf(0,floatvalues,chData);Delayms(1000);}} 最后编译下载。 打印信息如下: 当然啦,也可以自己实现浮点数转字符串的函数,有兴趣的可以试试。 另外还可通过telnet(127。0。0。1:19021)连接JLinkRTTViewer查看打印信息。笔者这里使用xShell,当然也可使用其他的telnet工具。 连接成功后其打印信息如下: 在SEGGERRTTV760h。zip文件中的Examples目录下还有很多实例,大家都可以参看。 RTT不仅可以打印输出,也是可以输入的,可以参看MainRTTInputEchoApp。c实例。 附上RTTAPI。 函数 描述 SEGGERRTTRead() Readdatafromaninputbuffer。 SEGGERRTTWrite() Writedatatoanoutputbuffer。 SEGGERRTTWriteString() Writeazeroterminatedstringtoanoutputbuffer。 SEGGERRTTprintf() Writeaformattedstringtoanoutputbuffer。 SEGGERRTTGetKey() Getonecharacterfrominputbuffer0。 SEGGERRTTHasKey() Checkifacharacterisavailableininputbuffer0。 SEGGERRTTWaitKey() Waitforacharactertobeavailableininputbuffer0andgetit。 SEGGERRTTConfigUpBuffer() Configureanup(output)buffer。 SEGGERRTTConfigDownBuffer() Configureadown(input)buffer。 SEGGERRTTInit() InitializeRTTControlBlockstructurewhenusingRAMonlytargets。 SEGGERRTTSetTerminal() SetthevirtualTerminaltouseforoutputonchannel0viaWriteandWriteString。 SEGGERRTTTerminalOut() Sendazeroterminatedstringviaavirtualterminal。6RTTClient使用 这里使用前面的任何一个工程。 1。点击MDK的Debug按钮进去Debug模式。 2。打开RTTClient,显示如下: 出现以上信息表示连接成功。 3。点击MDK的Run全速运行。控制台开始输出调试信息。 是不是很方便。7RTTLogger使用 使用前面任意一个工程。 编译下载程序,打开RTTLogger后如下所示: 在RTTLogger中,‘’后面可以自由输入,按回车键确认。无输入的情况下,按回车键跳过。一直回车键,笔者这里使用的通道0,RTTLogger默认使用的通道1。 打开上述路径中的。log文件,里面的内容如下所示。 以上说明成功的生成了log文件。 当然啦,关于RTT更多使用请访问Segger官网学习吧。