RT-Thread中使用Jlink的RTT调试
RT-Thread中的finsh命令非常实用,但是一般都会占用掉了一个串口。或者有时候手边没有串口线时,就不方便调试了。
由于我用的时jlink烧录器,之前使用调试工具-RTT来代替串口进行信息的交互和调试。所以我就想,能否用RTT来替代串口,运行finsh命令。
RTT( Real Time Terminal)是SEGGER公司新出的可以在嵌入式应用中与用户进行交互的实时终端。J-Link驱动4.90之后的版本都支持RTT。具体详情自选查找。
下面说一下步骤。
首先,新建一个项目时,就要选控制台串口,以及对应的管脚,我们按照默认的选就可以了。
输入项目名,完成各个配置后,打开rtconfig.h文件,会看到:
这里关键的是:RT_USING_DEVICE,RT_USING_CONSOLE和RT_CONSOLE_DEVICE_NAME
很明显,使用了uart1这个串口去给控制台。具体如何控制的,我们看一下关键代码:
kservice.c中的rt_kprintf()打印函数,实际输出函数。
/**
* This function will print a formatted string on system console
*
* @param fmt the format
*/
void rt_kprintf(const char *fmt, ...)
{
va_list args;
rt_size_t length;
static char rt_log_buf[RT_CONSOLEBUF_SIZE];
va_start(args, fmt);
/* the return value of vsnprintf is the number of bytes that would be
* written to buffer had if the size of the buffer been sufficiently
* large excluding the terminating null byte. If the output string
* would be larger than the rt_log_buf, we have to adjust the output
* length. */
length = rt_vsnprintf(rt_log_buf, sizeof(rt_log_buf) - 1, fmt, args);
if (length > RT_CONSOLEBUF_SIZE - 1)
length = RT_CONSOLEBUF_SIZE - 1;
#ifdef RT_USING_DEVICE
if (_console_device == RT_NULL)
{
rt_hw_console_output(rt_log_buf);
}
else
{
rt_uint16_t old_flag = _console_device->open_flag;
_console_device->open_flag |= RT_DEVICE_FLAG_STREAM;
rt_device_write(_console_device, 0, rt_log_buf, length);
_console_device->open_flag = old_flag;
}
#else
rt_hw_console_output(rt_log_buf);
#endif
va_end(args);
}
简单理解就是:
如果用了有效的device,就用device输出。如果不用device就用rt_hw_console_output()这个函数输出。
device还是要用的,那么我们就把命令台的device改成无效的就行。
所以我们可以简单的修改:
#define RT_CONSOLE_DEVICE_NAME ""
把uart1去掉。这样就可以让程序用device但是命令台device却无效了。
然后去board.c中,实现rt_hw_console_output()这个函数。记得要添加RTT的头文件。(RTT相关文件自己找,然后添加进项目。)
#include "SEGGER_RTT.h"
void rt_hw_console_output(const char *str)
{
rt_size_t i = 0, size = 0;
size = rt_strlen(str);
for (i = 0; i < size; i++)
{
if (*(str + i) == '\n')
{
break;
}
}
SEGGER_RTT_printf(0,"%s",str);
}
然后,编译,运行,打开J-Link RTT Viewer,就能看到
(J-Link RTT Viewer使用说明自己查找,强调一下,input–sending中选send on enter)
输出就这么简单就完成了,但是我们会发现,输入什么命令都没有效果,接着我们来解决输入问题。
输入关键点在shell.c文件,中的finsh_getchar()函数。
static int finsh_getchar(void)
{
#ifdef RT_USING_DEVICE
#ifdef RT_USING_POSIX
return getchar();
#else
char ch = 0;
RT_ASSERT(shell != RT_NULL);
while (rt_device_read(shell->device, -1, &ch, 1) != 1)
rt_sem_take(&shell->rx_sem, RT_WAITING_FOREVER);
return (int)ch;
#endif
#else
extern char rt_hw_console_getchar(void);
return rt_hw_console_getchar();
#endif
}
一样的,用了用了device就从device中读取,否则用rt_hw_console_getchar()这个函数。
现在问题来了,这边没有无效的命令台device的判断条件了,所以我们只好自己修改这部分代码
static int finsh_getchar(void)
{
#ifdef RT_USING_DEVICE
#ifdef RT_USING_POSIX
return getchar();
#else
char ch = 0;
RT_ASSERT(shell != RT_NULL);
if(shell->device!=RT_NULL)
{
while (rt_device_read(shell->device, -1, &ch, 1) != 1)
rt_sem_take(&shell->rx_sem, RT_WAITING_FOREVER);
return (int)ch;
}
else
{
extern char rt_hw_console_getchar(void);
return rt_hw_console_getchar();
}
#endif
#else
extern char rt_hw_console_getchar(void);
return rt_hw_console_getchar();
#endif
}
然后,去board.c中实现rt_hw_console_getchar()这个函数。
char rt_hw_console_getchar(void)
{
int ch = -1;
char tempbuffer[2];
uint8_t NumBytes=0;
NumBytes = SEGGER_RTT_Read(0, &tempbuffer[0], 1);
if(NumBytes==1)
{
ch=tempbuffer[0];
}
return ch;
}
一个字符一个字符的读取,那么也就只能这样了。
接着试试效果:
输入help命令,成功了!
到此为止,RTT实际已经添加完毕了。
然后,我们追求更加实用的话,我们会希望拥有我们自己的命令,比如上图里的log就是我自己添加的。
方法很简单,添加代码
int msh_log(int argc, char **argv)
{
rt_kprintf("my log run!:");
if (argc==2)
{
rt_kprintf("arg is %s\n",argv[1]);
}
rt_kprintf("\n");
return 0;
}
FINSH_FUNCTION_EXPORT_ALIAS(msh_log, __cmd_log, RT-Thread shell log.);
执行效果就是这样的了。
还可以参考这样的用法:
void myhello(void)
{
rt_kprintf("hello RT-Thread!\n");
}
MSH_CMD_EXPORT(myhello , my hello to RT-Thread);
(使用的工具:RT-Thread studio 2.0 内核rt-thread 4.0.2)