rtt使用与配置指南

  • ~12.45K 字
  1. 1. 调试信息输出RTT
    1. 1.1. 引入RTT
    2. 1.2. 添加原生flaot输出支持
    3. 1.3. 预定义技巧
    4. 1.4. J-scope 调试
    5. 1.5. pyocd查看rtt
    6. 1.6. rtt数据转发:UART

调试信息输出RTT

RTT(real time terminal)的优势在于1us的即时输出和可调缓冲区。目前pyocd(ARM)和openocd均已支持RTT读取。不过,pyocd提供了python api可以调用,目前可以通过脚本实现RTT的内容多向转发出去。openocd是通过tcp服务器进行转发。

引入RTT

参考资料:

详细过程:

  1. 下载jlink驱动,建议7.92n及以上,支持gd最新出(2019)的gd32c10x系列芯片
  2. 会提示clone的盗版jlink可以不用官方的rtt viewer和j-scope,只用驱动+用pyocd转发到串口查看rtt日志
    1. 原理:RTT输出快,rtt被上位机读取后,下位机就不占用内存了,上位机就算串口转发,效率也比下位机效率高。对高速传输非常有利(数据传输间隔小于3ms)。
    2. 没有jlink也没事,使用daplink或者各种link(国内外的各自link除了私有加密的均是以dap协议为基础的,而pyocd和dap就是arm公司开发的)都可以用pyocd或者openocd直接读取rtt,无需jlink支持。
  3. jlink安装目录内有sample目录Samples\RTT内就是rtt库的路径,将次目录内压缩报解压到项目目录内引入即可
  4. 需要引入的文件
    1. SEGGER_RTT.c
    2. SEGGER_RTT_printf.c
    3. 非keil的ide需要引入SEGGER_RTT_ASM_ARMv7M.S
    4. Syscall.c (暂时不需要)
  5. 需要include的目录
    1. \RTT
    2. \Config
    3. \Syscalls (暂时不需要里面的文件)
  6. 在main文件中,引入头文件#include "SEGGER_RTT.h"
  7. 在main函数中,初始化rtt,SEGGER_RTT_Init()
  8. 此后,在其他任意需要rtt输出的地方,引入头文件#include "SEGGER_RTT.h",并调用SEGGER_RTT_printf()即可输出数据
  9. 以下是示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "SEGGER_RTT.h"

int main() {
// 初始化rtt
SEGGER_RTT_Init();

// 输出数据
SEGGER_RTT_printf(0, "Hello, world!\n");

// 其他操作
//...
while(1);
}

添加原生flaot输出支持

参考资料:

以下是流程:

  1. 先添加一个宏开关用于控制浮点数输出
    1
    2
    3
    4
    5
        //! 浮点数的支持(需要420 BYTE的flash空间)
    #ifndef SEGGER_RTT_PRINT_FLOAT_ENABLE
    #define SEGGER_RTT_PRINT_FLOAT_ENABLE (1)
    #endif

  2. 然后在SEGGER_RTT_vprintf()函数中添加浮点数的处理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    int SEGGER_RTT_vprintf(unsigned BufferIndex, const char * sFormat, va_list * pParamList) {
    /// ......
    switch (c) {
    // ......
    //! ============================================================
    //! 支持浮点数
    #if (SEGGER_RTT_PRINT_FLOAT_ENABLE != 0)
    case 'f':
    case 'F':
    {
    float fv;
    fv = (float)va_arg(*pParamList, double); //取出输入的浮点数值

    if(fv < 0) _StoreChar(&BufferDesc, '-'); // 判断正负,用来显示负号

    v = abs((int)fv); //取整数部分

    _PrintInt(&BufferDesc, v, 10u, NumDigits, FieldWidth, FormatFlags); //显示整数
    _StoreChar(&BufferDesc, '.'); //显示小数点

    v = abs((int)(fv * 1000));
    v = v % 1000;
    _PrintInt(&BufferDesc, v, 10u, 3, FieldWidth, FormatFlags); //显示小数点后三位
    }
    break;
    #endif
    //! ============================================================
    default:
    break;
    }
    // ......
    }
  3. 用户函数定义,可以对RTT_printf()函数进行各种封装
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
     #if RTT_DBG_ENABLE
    #include "SEGGER_RTT.h"
    #define DEBUG_INIT() SEGGER_RTT_Init()

    #define RTT_DBG_PORT 0
    #define LOG_PROTO(type,color,format,...) \
    SEGGER_RTT_printf(RTT_DBG_PORT,"%s%s"format"\r\n%s", \
    color, \
    type, \
    ##__VA_ARGS__, \
    RTT_CTRL_RESET)

    #define log_clear() SEGGER_RTT_WriteString(RTT_DBG_PORT, " "RTT_CTRL_CLEAR)

    #define log_debug(format,...) LOG_PROTO("D:", RTT_CTRL_TEXT_BRIGHT_GREEN, format,##__VA_ARGS__)
    #define log_info(format,...) LOG_PROTO("I:", RTT_CTRL_TEXT_BRIGHT_WHITE, format, ##__VA_ARGS__)
    #define log_warn(format,...) LOG_PROTO("W:", RTT_CTRL_TEXT_BRIGHT_YELLOW, format, ##__VA_ARGS__)
    #define log_error(format,...) LOG_PROTO("E:", RTT_CTRL_TEXT_BRIGHT_RED , format, ##__VA_ARGS__)

    #else
    #define DEBUG_INIT(format,...)
    #define log_clear(format,...)
    #define log_debug(format,...)
    #define log_info(format,...)
    #define log_warn(format,...)
    #define log_error(format,...)
    #endif

    // 打印文件和数组
    #if defined(USE_HAL_DRIVER)
    extern uint32_t HAL_GetTick(void);
    extern uint32_t HAL_GetUs(void);
    #define SYS_MS HAL_GetTick()
    #define SYS_US HAL_GetUs()
    #else

    #define SYS_MS log_getTick() //log_getUs() // 如果不是使用HAL库 则使用自定义的时间源 ///-mdfc_40325
    #define SYS_US log_getTick()
    #endif

    #define USE_LOG_DEBUG 1
    #define PRINT_TIMESTAMP 1

    typedef enum {
    LOG_TERMINAL0,
    LOG_TERMINAL1,
    LOG_TERMINAL2,
    LOG_TERMINAL3,
    LOG_TERMINAL4,
    LOG_TERMINAL5,
    } LogTerminal_e;

    typedef enum {
    LOG_ARR_BYTE1,
    LOG_ARR_BYTE2,
    LOG_ARR_BYTE4,
    LOG_ARR_FLOAT,
    LOG_ARR_INT,
    } LogArrayType_e;

    #if USE_LOG_DEBUG
    #if PRINT_TIMESTAMP ///-mdfc_40325
    #if !APP
    #define LOG_PROTO(type, color, format, ...) SEGGER_RTT_printf(0, "%s""[%02d:%03d:%03d]" "%s"format,color,(SYS_MS / 1000) % 60, SYS_MS % 1000, SYS_US%1000,type,##__VA_ARGS__);
    #else
    #define LOG_PROTO(type, color, format, ...) \
    SEGGER_RTT_printf(0, "%s" "[%02d:%02d:%02d:%03d]" "%s"format,color,(Sys_Time.hour),(Sys_Time.min),(SYS_MS%60),(SYS_MS%1000),type,##__VA_ARGS__);
    #endif

    #if PRINT_TIMESTAMP
    #define LOG_PROTO(type, color, format, ...) \
    SEGGER_RTT_printf(0, "[%02d:%03d:%03d] %s%s" format "%s\r\n", (SYS_MS / 1000) % 60, SYS_MS % 1000, SYS_US % 1000, color, type, \
    ##__VA_ARGS__, RTT_CTRL_RESET);

    #define LOG_PROTO(type, color, format, ...) \
    SEGGER_RTT_printf(0, "[%02d:%02d:%02d:%03d:%03d] %s%s" format "%s\r\n", (SYS_MS / (60 * 60 * 1000)) % 24, (SYS_MS / (60 * 1000)) % 60, \
    (SYS_MS / 1000) % 60, SYS_MS % 1000, SYS_US % 1000, color, type, ##__VA_ARGS__, RTT_CTRL_RESET);
    #else
    #define LOG_PROTO(type, color, format, ...) SEGGER_RTT_printf(0, "%s%s" format "%s\r\n", color, type, ##__VA_ARGS__, RTT_CTRL_RESET);
    #endif

    #if 1
    #define LOG_I(format, ...) LOG_PROTO("[I]:",RTT_CTRL_TEXT_BRIGHT_WHITE, format, ##__VA_ARGS__) // 白色日志输出
    #define LOG_D(format, ...) LOG_PROTO("[D]:",RTT_CTRL_TEXT_BRIGHT_GREEN,format, ##__VA_ARGS__) // 绿色日志输出
    #define LOG_W(format, ...) LOG_PROTO("[W]:", RTT_CTRL_TEXT_BRIGHT_YELLOW,format, ##__VA_ARGS__) // 黄色日志输出
    #define LOG_E(format, ...) LOG_PROTO("[E]:", RTT_CTRL_TEXT_BRIGHT_RED, format, ##__VA_ARGS__) // 红色日志输出
    #define LOG_CLEAR() SEGGER_RTT_WriteString(0, "\r\n" RTT_CTRL_CLEAR) // 清屏
    #else
    #define LOG_I(format, ...) LOG_PROTO("[INFO]:", "", format, ##__VA_ARGS__) // 无颜色日志输出
    #define LOG_D(format, ...) LOG_PROTO("[DEBUG]:", RTT_CTRL_TEXT_BRIGHT_GREEN, format, ##__VA_ARGS__) // 绿色日志输出
    #define LOG_W(format, ...) LOG_PROTO("[WARN]:", RTT_CTRL_TEXT_BRIGHT_YELLOW, format, ##__VA_ARGS__) // 黄色日志输出
    #define LOG_E(format, ...) LOG_PROTO("[ERROR]:", RTT_CTRL_TEXT_BRIGHT_RED, format, ##__VA_ARGS__) // 红色日志输出
    #define LOG_CLEAR() SEGGER_RTT_WriteString(0, "\r\n" RTT_CTRL_CLEAR) // 清屏
    #endif

    // 打印数组
    #define LOG_ARRAY(pArr, len, logArrayType, terminal) \
    do { \
    SEGGER_RTT_SetTerminal(terminal); \
    for (int i = 0; i < len; i++) { \
    switch (logArrayType) { \
    case LOG_ARR_BYTE1: \
    SEGGER_RTT_printf(0, "%02x ", *(uint8_t *)(pArr + i)); \
    break; \
    case LOG_ARR_BYTE2: \
    SEGGER_RTT_printf(0, "%04x ", *(uint16_t *)(pArr + i)); \
    break; \
    case LOG_ARR_BYTE4: \
    SEGGER_RTT_printf(0, "%08x ", *(uint32_t *)(pArr + i)); \
    break; \
    case LOG_ARR_FLOAT: \
    SEGGER_RTT_printf(0, "%f ", *(float *)(pArr + i)); \
    break; \
    case LOG_ARR_INT: \
    SEGGER_RTT_printf(0, "%d ", *(int *)(pArr + i)); \
    break; \
    default: \
    break; \
    } \
    } \
    SEGGER_RTT_printf(0, "\r\n"); \
    SEGGER_RTT_SetTerminal(0); \
    } while (0)

    // 初始化
    #define LOG_INIT() \
    do { \
    SEGGER_RTT_Init(); \
    LOG_CLEAR(); \
    LOG_I("RTT Inited!"); \
    } while (0)

    #else
    #define LOG_INFO(format, ...)
    #define LOG_DEBUG(format, ...)
    #define LOG_WARN(format, ...)
    #define LOG_ERROR(format, ...)
    #define LOG_CLEAR()
    #define LOG_ARRAY(pArr, len, logArrayType, terminal)
    #define LOG_INIT()
    #endif
    #endif

预定义技巧

参考如下:

  • 目前可定义的有:
    • 入口函数
    • 小数点位数等
    • 预设值颜色
    • 分类输出err,info
    • 分类输出到j-scope
  • 小数点位数控制需要在上文vprintf函数中修改_PrintInt(&BufferDesc, v, 10u, 3, FieldWidth, FormatFlags); //显示小数点后三位
    • 或者将3改为宏定义数字,在用户配置头文件里控制小数点长度。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//! 调试输出配置
#if RTT_DBG_ENABLE
#include "SEGGER_RTT.h"
#define DEBUG_INIT() SEGGER_RTT_Init()

#define RTT_DBG_PORT 0
#define LOG_PROTO(type,color,format,...) \
SEGGER_RTT_printf(RTT_DBG_PORT,"%s%s"format"\r\n%s", \
color, \
type, \
##__VA_ARGS__, \
RTT_CTRL_RESET)

#define log_clear() SEGGER_RTT_WriteString(RTT_DBG_PORT, " "RTT_CTRL_CLEAR)

#define log_debug(format,...) LOG_PROTO("D:", RTT_CTRL_TEXT_BRIGHT_GREEN, format,##__VA_ARGS__)
#define log_info(format,...) LOG_PROTO("I:", RTT_CTRL_TEXT_BRIGHT_WHITE, format, ##__VA_ARGS__)
#define log_warn(format,...) LOG_PROTO("W:", RTT_CTRL_TEXT_BRIGHT_YELLOW, format, ##__VA_ARGS__)
#define log_error(format,...) LOG_PROTO("E:", RTT_CTRL_TEXT_BRIGHT_RED , format, ##__VA_ARGS__)

#else
#define DEBUG_INIT(format,...)
#define log_clear(format,...)
#define log_debug(format,...)
#define log_info(format,...)
#define log_warn(format,...)
#define log_error(format,...)
#endif

J-scope 调试

pyocd查看rtt

目的:通过pyocd或openocd等第三方ocd软件不依赖jlink,从而绕过SEGGER实现rtt日志查看。使各种link都能实现rtt日志查看。
实现方法:以使用daplink或gdlink查看rtt日志为例进行配置

  1. 下载pyocd或openocd软件,安装并配置好环境
  2. 下载daplink或gdlink驱动,安装并配置好环境

rtt数据转发:UART

目的:将rtt数据转发到串口,方便上位机查看。能够使用vofa+实现更多功能的画图。

实现方法:以vofa+显示图像为例进行配置

1.

打赏
打赏提示信息
分享
分享提示信息