前言
在嵌入式开发过程中,经常需要进行打印调试,通常使用串口进行打印输出,
但通常串口资源有限,这时就可以通过J-Link工具里面自带的RTT实现打印,从而节约一个串口资源。
一、安装J-Link驱动
J-Link官网下载驱动
驱动自行选择一个版本进行安装,不建议安装太高的版本,毕竟大家使用的J-Link可能不是正品的。
二、查找RTT源文件
在J-Link驱动的安装路径下,找到RTT
最后直接将RTT添加到工程中即可(复制SEGGER_RTT_Conf.h
到RTT文件夹)
三、使用
自行在工程中添加头文件,添加头文件的路径
使用非常简单,无需初始化,就添加下面3行代码就可以输出
#include "SEGGER_RTT.h"
SEGGER_RTT_SetTerminal(0); // 选择终端(输入0-15)
SEGGER_RTT_printf(0,"Terminal(0) text \r\n"); // 输出日志
四、打开J-Link RTT Viewer
不出意外就可以正常输出了
可以手动打开相对应的终端
快捷键
F2:快速连接
F3:断开连接
五、输出不同颜色的文字
大家可以输入以下代码,体验一下
//方法1
SEGGER_RTT_SetTerminal(0);
SEGGER_RTT_printf(0,RTT_CTRL_TEXT_RED"Terminal(0)\r\n");
//方法2
SEGGER_RTT_SetTerminal(1);
SEGGER_RTT_printf(0,RTT_CTRL_TEXT_BRIGHT_RED);
SEGGER_RTT_printf(0,"Terminal(1)\r\n");
六、自定义封装一下
如果在程序中直接调用下面这两行代码,能用,但是感觉不优雅。
SEGGER_RTT_SetTerminal(0);
SEGGER_RTT_printf(0,RTT_CTRL_TEXT_RED"Terminal(0)\r\n");
我们可以使用#define
进行定义一下
#define LOG_DATA(fmt, ...) SEGGER_RTT_SetTerminal(0); SEGGER_RTT_printf(0, fmt, ##__VA_ARGS__)
#define LOG_STATUS(fmt, ...) SEGGER_RTT_SetTerminal(1); SEGGER_RTT_printf(0, fmt, ##__VA_ARGS__)
#define LOG_ERROR(fmt, ...) SEGGER_RTT_SetTerminal(2); SEGGER_RTT_printf(0, fmt, ##__VA_ARGS__)
在程序中调用下面封装好得宏,简单又优雅 不支持输出中文的
LOG_DATA("LOG_DATA\r\n");
LOG_STATUS("LOG_STATUS\r\n");
LOG_ERROR("LOG_ERROR\r\n");
七、float类型输出
float
类型数据是无法直接打印的
float float_data = 3.14;
LOG_DATA("float_data = %f \r\n",float_data );
不过我们可以借用sprintf
函数
#include<stdio.h>
char str[40];
float float_data = 3.14;
sprintf(str,"float_data = %f \r\n",float_data );
LOG_DATA("%s",str);
八、自定义输出
Jlink_RTT.c
#include "Jlink_RTT.h"
#include <string.h>
static char log_buf[256]; // 日志缓存buff
/**
* @brief 普通日志输出,不附带任何参数(白色)
*
* @param format 输出日志
* @param ...
*/
void PRINTF_RTT_LOG(const char *format, ...)
{
va_list args;
va_start(args, format);
vsnprintf(log_buf, sizeof(log_buf), format, args);
printf_rtt("%s", log_buf);
va_end(args);
}
/**
* @brief 类型(LOGD)白色字体
*
* @param func 当前函数名
* @param line 当前函数位置
* @param format 输出日志
* @param ...
*/
void PRINTF_RTT_LOGD(const char *func, const long line, const char *format, ...)
{
va_list args;
va_start(args, format);
printf_rtt("(%s:%ld) ", func, line);
vsnprintf(log_buf, sizeof(log_buf), format, args);
printf_rtt("%s\r\n", log_buf);
va_end(args);
}
/**
* @brief 类型(LOGI)绿色字体
*
* @param func 当前函数名
* @param line 当前函数位置
* @param format 输出日志
* @param ...
*/
void PRINTF_RTT_LOGI(const char *func, const long line, const char *format, ...)
{
va_list args;
va_start(args, format);
printf_rtt(RTT_CTRL_TEXT_BRIGHT_GREEN); // 字体颜色:绿色
printf_rtt("(%s:%ld) ", func, line);
vsnprintf(log_buf, sizeof(log_buf), format, args);
printf_rtt("%s\r\n", log_buf);
printf_rtt(RTT_CTRL_RESET); // 恢复默认字体颜色
va_end(args);
}
/**
* @brief 类型(LOGE)红色字体
*
* @param func 当前函数名
* @param line 当前函数位置
* @param format 输出日志
* @param ...
*/
void PRINTF_RTT_LOGE(const char *func, const long line, const char *format, ...)
{
va_list args;
va_start(args, format);
printf_rtt(RTT_CTRL_TEXT_BRIGHT_RED); // 字体颜色:红色
printf_rtt("(%s:%ld) ", func, line);
vsnprintf(log_buf, sizeof(log_buf), format, args);
printf_rtt("%s\r\n", log_buf);
printf_rtt(RTT_CTRL_RESET); // 恢复默认字体颜色
va_end(args);
}
/**
* @brief 类型(LOGW)紫色字体
*
* @param func 当前函数名
* @param line 当前函数位置
* @param format 输出日志
* @param ...
*/
void PRINTF_RTT_LOGW(const char *func, const long line, const char *format, ...)
{
va_list args;
va_start(args, format);
printf_rtt(RTT_CTRL_TEXT_BRIGHT_MAGENTA); // 字体颜色:紫色
printf_rtt("(%s:%ld) ", func, line);
vsnprintf(log_buf, sizeof(log_buf), format, args);
printf_rtt("%s\r\n", log_buf);
printf_rtt(RTT_CTRL_RESET); // 恢复默认字体颜色
va_end(args);
}
/**
* @brief 类型(LOGV)黄色字体
*
* @param func 当前函数名
* @param line 当前函数位置
* @param format 输出日志
* @param ...
*/
void PRINTF_RTT_LOGV(const char *func, const long line, const char *format, ...)
{
va_list args;
va_start(args, format);
printf_rtt(RTT_CTRL_TEXT_BRIGHT_YELLOW); // 字体颜色:黄色
printf_rtt("(%s:%ld) ", func, line);
vsnprintf(log_buf, sizeof(log_buf), format, args);
printf_rtt("%s\r\n", log_buf);
printf_rtt(RTT_CTRL_RESET); // 恢复默认字体颜色
va_end(args);
}
/**
* @brief 以16进制的方式输出(青色)
*
* @param func 当前函数名
* @param line 当前函数位置
* @param title 标题
* @param len 输出数据长度
* @param format 输出16进制
* @param ...
*/
void PRINTF_RTT_HEX(const char *func, const long line, const char *title, char *format, const int len)
{
printf_rtt(RTT_CTRL_TEXT_BRIGHT_CYAN); // 字体颜色:青色
printf_rtt("(%s:%ld) %s[%d]= ", func, line, title, len);
for (int i = 0; i < len; i++)
{
printf_rtt("%02X ", *(format + i));
}
printf_rtt("\r\n");
printf_rtt(RTT_CTRL_RESET); // 恢复默认字体颜色
}
/**
* @brief 以16进制的方式输出(红色)
*
* @param func 当前函数名
* @param line 当前函数位置
* @param title 标题
* @param len 输出数据长度
* @param format 输出16进制
* @param ...
*/
void PRINTF_RTT_ERR_HEX(const char *func, const long line, const char *title, char *format, const int len)
{
printf_rtt(RTT_CTRL_TEXT_BRIGHT_RED); // 字体颜色:红色
printf_rtt("(%s:%ld) %s[%d]= ", func, line, title, len);
for (int i = 0; i < len; i++)
{
printf_rtt("%02X ", *(format + i));
}
printf_rtt("\r\n");
printf_rtt(RTT_CTRL_RESET); // 恢复默认字体颜色
}
Jlink_RTT.h
#ifndef _JLINK_RTT_H__
#define _JLINK_RTT_H__
#include "SEGGER_RTT.h"
#include <stdio.h>
#include <stdarg.h> // 包含 va_list 相关宏的头文件
/************************************************
通过JLink-RTT输出日志
*************************************************/
#if 1
#define printf_rtt(fmt, ...) \
SEGGER_RTT_SetTerminal(0); \
SEGGER_RTT_printf(0, fmt, ##__VA_ARGS__)
#else
#define printf_rtt(fmt, ...)
#endif
extern void PRINTF_RTT_LOG(const char *format, ...);
extern void PRINTF_RTT_LOGD(const char *func, const long line, const char *format, ...);
extern void PRINTF_RTT_LOGI(const char *func, const long line, const char *format, ...);
extern void PRINTF_RTT_LOGE(const char *func, const long line, const char *format, ...);
extern void PRINTF_RTT_LOGW(const char *func, const long line, const char *format, ...);
extern void PRINTF_RTT_LOGV(const char *func, const long line, const char *format, ...);
extern void PRINTF_RTT_HEX(const char *func, const long line, const char *title, char *format, const int len);
extern void PRINTF_RTT_ERR_HEX(const char *func, const long line, const char *title, char *format, const int len);
#define RTT_LOG(...) PRINTF_RTT_LOG(__VA_ARGS__); // 白色
#define RTT_LOGI(...) PRINTF_RTT_LOGI(__func__, __LINE__, __VA_ARGS__); // 绿色
#define RTT_LOGD(...) PRINTF_RTT_LOGD(__func__, __LINE__, __VA_ARGS__); // 白色
#define RTT_LOGE(...) PRINTF_RTT_LOGE(__func__, __LINE__, __VA_ARGS__); // 红色
#define RTT_LOGW(...) PRINTF_RTT_LOGW(__func__, __LINE__, __VA_ARGS__); // 紫色
#define RTT_LOGV(...) PRINTF_RTT_LOGV(__func__, __LINE__, __VA_ARGS__); // 黄色
#define RTT_HEX(title, data, len) \
PRINTF_RTT_HEX(__func__, __LINE__, title, data, len); // 青色
#define RTT_ERR_HEX(title, data, len) \
PRINTF_RTT_ERR_HEX(__func__, __LINE__, title, data, len); // 红色
#endif
使用示例
char name[]="Test";
RTT_LOGD("delete nfc id:%s\n", name);
uint8_t NFC_ID[10]={1,2,3,4,5};
RTT_HEX("id", NFC_ID, strlen(NFC_ID));