基于SEGGER-RTT的调试模块

SEGGER-RTT简介:

如果在MCU开发调试时使用的是jlink,同时SEGGER-RTT支持当前MCU时,我们可以使用SEGGER-RTT代码串口进行打印输出及命令输入,可参考文章:

https://blog.csdn.net/qlexcel/article/details/126633180

需求背景:

SEGGER-RTT提供的接口函数:
1、int SEGGER_RTT_printf(unsigned BufferIndex, const char * sFormat, …)
格式化输出,支持字符、字符串、hex字符打印,但不支持浮点数及hex字符串。

2、unsigned SEGGER_RTT_Read(unsigned BufferIndex, void* pBuffer, unsigned BufferSize)
读取上位机RTT客户端输入字符串,但不支持直接读取hex字符串。

基于以上两点原因,决定对SEGGER-RTT原有接口函数进行封装,增加缺失的功能。

新的接口函数如下:
1、DebugPrintf(Format, …)
格式化打印输出,支持C语言所有格式化数据。
2、DebugHexPrint(const uint8_t aBuf[], const uint16_t Len)
hex字符串打印,在通讯调试时非常实用。
3、uint16_t DebugHexRecv(uint8_t *pBuf,uint16_t BufLen)
读取hex字符串读取,方便调试带参数的函数。
4、uint16_t DebugStrRecv(uint8_t *pBuf,uint16_t BufLen)
字符串读取
5、void JscopeDataUpLoad(uint32_t Data)
Jscope绘制曲线


头文件:

/********************************************************************
  FileName : debug.h
  Author   : 
  Version  : 
  Date     : 
  Note     : 使用打印功能时,必须连接Jlink仿真器,通过J-Link RTT Viewer查看输出
             使用J-Scope功能时,必须连接Jlink仿真器,通过J-Scope 上位机查看输出
  History  :  
********************************************************************/

#ifndef __DEBUG_H__
#define __DEBUG_H__

#ifdef __cplusplus
 extern "C" {
#endif

/* Includes ------------------------------------------------------*/
#include <stdint.h>

/* Public define -------------------------------------------------*/


//<<< Use Configuration Wizard in Context Menu >>>

// <h>Debug cfg

//<c1>Enable Debug
//<i>Enable Debug
#define D_ENABLE_DEBUG
//</c>

// <o>debug buffer length
//  <i>Default: 256
//  <1-1024>
#define D_PRINT_BUF_LEN 1024

//<c1>Enable debug time
//<i>Enable debug time
#define D_PRINT_TIME
//</c>

//<c1>Enable Hex Print
//<i>Enable Hex Print
#define D_ENABLE_HEX_PRINT
//</c>

// <o>hex buffer length
//  <i>Default: 255
//  <1-255>
#define D_HEX_BUF_LEN 200


//<c1>Enable jscope
//<i>Enable jscope
//#define D_ENABLE_JSCOPE
//</c>

// <o>jscope buffer length
//  <i>Default: 2048
//  <1-65535>
#define D_JSCOPE_BUF_LEN 4096

//<c1>Enable jscope time
//<i>Enable jscope  time
//#define D_JSCOPE_TIME
//</c>

//<c1>Enable receive
//<i>Enable receive
#define D_ENABLE_RECV
//</c>

// </h>

//<<< end of configuration section >>>


#ifdef D_ENABLE_DEBUG

    #include "SEGGER_RTT.h"
    extern void RTT_Printf(const void *pFormat,...);
    
    #define  DebugPrintf(Format, ...)   RTT_Printf(Format, ##__VA_ARGS__)
    
    #define  DebugErr(Format, ...)    do {\
                                            SEGGER_RTT_WriteString(0,RTT_CTRL_TEXT_BRIGHT_RED);\
                                            SEGGER_RTT_WriteString(0,__FILE__);\
                                            RTT_Printf("line:%d\n",__LINE__);\
                                            RTT_Printf(Format, ##__VA_ARGS__);\
                                            SEGGER_RTT_WriteString(0,RTT_CTRL_TEXT_BRIGHT_CYAN);\
                                          }while(0)
#else
    #define DebugPrintf(format, ...) 
    #define DebugErr(Format, ...)
#endif

/* Public typedef ------------------------------------------------*/
/* Public constants ----------------------------------------------*/
/* Public variables ----------------------------------------------*/
/* Public function prototypes ------------------------------------*/
extern void DebugInit(void);
extern void DebugHexPrint(const uint8_t aBuf[], const uint16_t Len);
extern void JscopeDataUpLoad(uint32_t Data);
extern uint16_t DebugHexRecv(uint8_t *pBuf,uint16_t BufLen);
extern uint16_t DebugStrRecv(uint8_t *pBuf,uint16_t BufLen);

#ifdef __cplusplus
 }
#endif

#endif

/*************************** END OF FILE ***************************/


如果你使用的keil5,可以通过configuration Wizard进行开关、buffer等配置,如图所示:
keil


源文件:

/********************************************************************
  FileName : debug.c
  Author   : 
  Version  : 
  Date     : 
  Note     : 使用打印功能时,必须连接Jlink仿真器,通过J-Link RTT Viewer查看输出
             使用J-Scope功能时,必须连接Jlink仿真器,通过J-Scope 上位机查看输出
  History  :

********************************************************************/

/* Includes ------------------------------------------------------*/
#include "debug.h"
#include <string.h>
#include <stdio.h>

/* Private define ------------------------------------------------*/
/* Private typedef -----------------------------------------------*/
/* Private constants ---------------------------------------------*/
/* Private variables ---------------------------------------------*/

#ifdef D_ENABLE_DEBUG
static uint8_t s_aPrintStrBuf[D_PRINT_BUF_LEN] = {0};

#ifdef D_ENABLE_HEX_PRINT
static uint8_t s_aPrintHexBuf[D_HEX_BUF_LEN] = {0};
#endif

#ifdef D_ENABLE_JSCOPE
static uint8_t s_aJscopeBuf[D_JSCOPE_BUF_LEN] = {0};
#endif

#endif

/* Private function prototypes -----------------------------------*/
/* Public constants ----------------------------------------------*/
/* Public variables ----------------------------------------------*/

/********************************************************************
* name         : DebugInit
* description  : RTT 初始化
* Input        : none
* Output       : none
* Return       : none
********************************************************************/
void DebugInit(void)
{
#ifdef D_ENABLE_DEBUG

    SEGGER_RTT_Init();
    SEGGER_RTT_WriteString(0, RTT_CTRL_CLEAR);
    SEGGER_RTT_WriteString(0, RTT_CTRL_TEXT_BRIGHT_CYAN);

#ifdef D_ENABLE_JSCOPE
    /* J-Scope上行配置,通道1,格式:uint_32*/
#ifdef D_JSCOPE_TIME
    SEGGER_RTT_ConfigUpBuffer(1, "JScope_t4u4", s_aJscopeBuf, sizeof(s_aJscopeBuf), SEGGER_RTT_MODE_NO_BLOCK_SKIP);
#else
    SEGGER_RTT_ConfigUpBuffer(1, "JScope_u4", s_aJscopeBuf, sizeof(s_aJscopeBuf), SEGGER_RTT_MODE_NO_BLOCK_SKIP);
#endif
#endif

#endif
}

/********************************************************************
* name         : RTT_Printf
* description  : RTT 格式化打印
* Input        : Format 格式化字符串
* Output       : RTT print
* Return       : none
********************************************************************/
#ifdef D_ENABLE_DEBUG
void RTT_Printf(const void *pFormat, ...)
{
    uint16_t Index = 0;
    memset(s_aPrintStrBuf, 0, sizeof(s_aPrintStrBuf));
#ifdef D_PRINT_TIME
    extern uint32_t HAL_GetTick(void);

    //时间戳
    Index += sprintf((char *)s_aPrintStrBuf + Index, "(%d)", HAL_GetTick());
#endif
    //数据
    va_list arg;
    va_start(arg, pFormat);
    Index += vsprintf((char *)&s_aPrintStrBuf[Index], (char *)pFormat, arg);
    va_end(arg);
    Index = (Index < D_PRINT_BUF_LEN ? Index : D_PRINT_BUF_LEN);
    SEGGER_RTT_Write(0, s_aPrintStrBuf, Index);
}

#endif

/********************************************************************
* name         : DebugHexPrint
* description  : Hex 数据打印
* Input        : aBuf Hex数组
* Input        : Len Hex长度
* Output       : RTT print
* Return       : none
********************************************************************/
void DebugHexPrint(const uint8_t aBuf[], const uint16_t Len)
{
#ifdef D_ENABLE_DEBUG
#ifdef D_ENABLE_HEX_PRINT

    uint16_t Index = 0;
    memset(s_aPrintHexBuf, 0, sizeof(s_aPrintHexBuf));
    Index += sprintf((char *)&s_aPrintHexBuf[Index], "HEX:");
    for(uint16_t i = 0; (i < Len) && (Index < D_HEX_BUF_LEN - 5); i++)
    {
        Index += sprintf((char *)&s_aPrintHexBuf[Index], "%02X ", aBuf[i]);
    }

    Index += sprintf((char *)&s_aPrintHexBuf[Index], "\n");
    SEGGER_RTT_Write(0, s_aPrintHexBuf, Index);

#endif
#endif
}

/********************************************************************
* name         : JscopeDataUpLoad
* description  : Jscope数据上传
* Input        : Data
* Output       : none
* Return       : none
********************************************************************/
void JscopeDataUpLoad(uint32_t Data)
{
#ifdef D_ENABLE_DEBUG
#ifdef D_ENABLE_JSCOPE
#ifdef D_JSCOPE_TIME
    extern uint32_t HAL_GetTick(void);

    //注意字节顺序
    uint64_t TickUs = HAL_GetTick() * 1000;
    uint64_t Temp = (uint64_t)Data << 32;
    Temp += TickUs;
    SEGGER_RTT_Write(1, &Temp, sizeof(uint64_t));
#else
    SEGGER_RTT_Write(1, &Data, sizeof(uint32_t));
#endif
#endif
#endif
}

#ifdef D_ENABLE_RECV
#ifdef D_ENABLE_DEBUG
/********************************************************************
* name         : CharToHex
* description  : 字符转Hex
* Input        : Ch 字符
* Output       : Hex
* Return       : Hex
********************************************************************/
static int8_t CharToHex(int8_t Ch)
{
    if((Ch >= '0') && (Ch <= '9'))
    {
        return  Ch - 0x30;
    }
    else   if((Ch >= 'A') && (Ch <= 'F'))
    {
        return  Ch - 'A' + 10;
    }
    else   if((Ch >= 'a') && (Ch <= 'f'))
    {
        return  Ch - 'a' + 10;
    }
    else
    {
        return (0xFF);
    }
}

/********************************************************************
* name         : Str2HexData
* description  : ASCII字符串转hex字符串
* Input        : Str ASCII字符串
* Input        : Len ASCII字符串长度
* Input        : Tag 字符串分隔符
* Output       : pHex hex字符串
* Return       : hex字符串长度
********************************************************************/
static int32_t Str2HexData(const uint8_t *pStr, uint16_t StrLen, uint8_t *pHex, uint8_t Tag)
{
    int32_t  HexData = 0;
    int32_t  LowHexData = 0;
    uint16_t  HexLen = 0;

    for(uint16_t i = 0; i < StrLen; i++)
    {
        //跳过分隔符
        if(!memcmp(&pStr[i], &Tag, 1))
        {
            continue;
        }

        int8_t StrH = pStr[i];
        i++;

        if(i >= StrLen)
        {
            break;
        }
        uint8_t strL = pStr[i];
        HexData = CharToHex(StrH);
        LowHexData = CharToHex(strL);

        if((HexData == 16) || (LowHexData == 16))
        {
            break;
        }
        else
        {
            HexData = HexData * 16 + LowHexData;
        }
        pHex[HexLen] = (char)HexData;
        HexLen++;
    }

    return HexLen;
}
#endif
#endif
/********************************************************************
* name         : DebugHexRecv
* description  : 从RTT接收HEX数据
* Input        : pBuf
* Input        : BufLen
* Output       : pBuf
* Return       : 接收数据长度
********************************************************************/
uint16_t DebugHexRecv(uint8_t *pBuf, uint16_t BufLen)
{
    uint16_t DataLen = 0;
#ifdef D_ENABLE_DEBUG
#ifdef D_ENABLE_RECV
    uint8_t aDataBuf[128] = {0};
    uint16_t StrLen = 0;
    uint8_t aStrBuf[256] = {0};

    StrLen = (int)SEGGER_RTT_Read(0u, aStrBuf, sizeof(aStrBuf));
    if(StrLen > 0)
    {
        DataLen = Str2HexData(aStrBuf, StrLen, aDataBuf, ' ');
        if(DataLen > 0)
        {
            DataLen = DataLen < BufLen ? DataLen : BufLen;
            //黄色字体显示输入
            SEGGER_RTT_WriteString(0, RTT_CTRL_TEXT_BRIGHT_YELLOW);
            aStrBuf[StrLen] = '\n';
            SEGGER_RTT_WriteString(0, (const char *)aStrBuf);
            SEGGER_RTT_WriteString(0, RTT_CTRL_TEXT_BRIGHT_CYAN);
            memcpy(pBuf, aDataBuf, DataLen);
        }
    }

#endif
#endif
    return DataLen;
}

/********************************************************************
* name         : DebugStrRecv
* description  : 从RTT接收字符串
* Input        : pBuf
* Input        : BufLen
* Output       : pBuf
* Return       : 接收数据长度
********************************************************************/
uint16_t DebugStrRecv(uint8_t *pBuf, uint16_t BufLen)
{
    uint16_t StrLen = 0;
#ifdef D_ENABLE_DEBUG
#ifdef D_ENABLE_RECV
    static uint8_t aStrBuf[256] = {0};
    StrLen = (int)SEGGER_RTT_Read(0u, aStrBuf, sizeof(aStrBuf));
    if(StrLen > 0)
    {
        if(StrLen <= BufLen)
        {
            //显示输入
            SEGGER_RTT_WriteString(0, RTT_CTRL_TEXT_BRIGHT_MAGENTA);
            DebugPrintf("%s\n", aStrBuf);
            SEGGER_RTT_WriteString(0, RTT_CTRL_TEXT_BRIGHT_CYAN);
            memcpy(pBuf, aStrBuf, StrLen);
        }
        else
        {
            SEGGER_RTT_WriteString(0, RTT_CTRL_TEXT_BRIGHT_RED);
            DebugPrintf("input string is too long!\n");
            SEGGER_RTT_WriteString(0, RTT_CTRL_TEXT_BRIGHT_CYAN);
            StrLen = 0;
        }
    }
#endif
#endif
    return StrLen;
}

/*************************** END OF FILE ***************************/

如有问题及优化建议,欢迎留言指正,谢谢!

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值