調試備忘錄-J-Link RTT的使用(原理 + 教程 + 應用 + 代碼)


MCU:STM32F407VE

MDK:5.29

IAR:8.32

目錄--點擊可快速直達

寫在前面

本文介紹了J-Link RTT的部分使用內容,很多地方參考和使用了J-Link的官方資料,有的地方可能翻譯的不太准確,請見諒。

如果想了解更加准確詳細的內容,請點此處


什么是RTT?

RTT(Real Time Transfer)是一種用於嵌入式中與用戶進行交互的技術,它結合了SWO和半主機的優點,具有極高的性能。

使用RTT可以從MCU非常快速輸出調試信息和數據,且不影響MCU實時性。這個功能可以用於很多支持J-Link的設備和MCU,兼容性強。

RTT支持兩個方向的多個通道,上到主機,下到目標,它可以用於不同的目的,為用戶提供盡可能多的自由。默認實現每個方向使用一個通道,用於可打印終端輸入和輸出。

使用J-Link RTT Viewer,可用於“虛擬”終端,允許打印到多個窗口(例如,一個用於標准輸出,一個對於錯誤輸出,一個用於調試輸出)。

RTT的工作原理

RTT在MCU的存儲器中使用SEGGER RTT控制塊結構管理數據讀寫。控制塊對於每個可用的信道都在內存中包含了一個ID,通過J-Link或者環形緩沖結構區(鏈表)都可以通過ID找到對應的控制塊。

可用信道的最大數目可以在編譯時配置,並且每個緩沖區都可以在MCU運行時配置和使用。上下緩沖區可以分開處理。每個通道都可以配置為阻塞或非阻塞。

在阻塞模式下,應用程序將等待緩沖區寫滿,直到可以寫入所有內存為止,這將導致應用程序處於阻塞狀態,但可以防止數據丟失。

在非阻塞模式下,只會寫入適合緩沖區的數據,或完全不寫入緩沖區,其余的數據將被丟棄。這樣即使沒有連接調試器,也可以實時運行。開發人員不必創建特殊的調試版本,並且代碼可以保留在發布應用程序中。

RTT的性能

RTT的性能明顯高於其他任何用於將數據輸出到主機PC的方式。平均一行文本可以在1微秒或更短的時間內輸出。基本上相當於做一個memcopy()的時間。

RTT實現代碼使用大約500字節的ROM和(n(通道數) * (24字節ID+24字節))的RAM。推薦的大小是1 kByte(上行信道)和16到32字節(下行信道),這取決於輸入/輸出的負載。

快速使用教程

1.首先安裝J-Link的軟件驅動

2.安裝完成后,打開J-Link的安裝目錄(開始->SEGGR->J-Link RTT Viewer->右鍵打開文件所在位置->然后繼續右鍵打開文件所在位置->此時就是安裝目錄了),

找到如下路徑SEGGER\JLink_V632f\Samples\RTT,解壓路徑里面的壓縮包SEGGER_RTT_V632f.zip(不同的版本,V后面的數字可能不一樣)。

3.將解壓完的文件拷貝到代碼工程目錄中。

4.在項目工程中加入SEGGER_RTT_V632f\RTT目錄下的全部四個文件。工程添加文件方法請自行百度。

5.工程加入文件后,在想要用到RTT的文件中包含#include "SEGGER_RTT.h",然后直接調用SEGGER_RTT_printf()就好了,

例如SEGGER_RTT_printf(0,"hello world!")這個和C語言的printf的格式差不多,就是前面加了一個端口0的參數。(詳細信息請看高級使用教程

6.然后點擊開始->SEGGR->J-Link RTT Viewer,打開J-Link RTT Viewer 選擇好你的芯片型號后,點擊確認。

7.然后就能看到我們打印的內容了。

高級使用教程

1.部分函數介紹:

(1)void SEGGER_RTT_Init (void) RTT初始化函數,應放於程序開始階段。


(2)int SEGGER_RTT_GetKey (void); 從RTT終端獲取一個按鍵字符。
Return Value

Value Meaning
>=0 返回按鍵字符(0-255)
< 0 緩存區中沒有有效的字符

示例代碼:

    int c;
    c = SEGGER_RTT_GetKey();
    if (c == 'q') {
        exit();
    }

(3)int SEGGER_RTT_HasKey (void);檢測緩存區中是否還有字符。
Return Value

Value Meaning
1 緩存區中至少有一個字符是有效的
0 緩存區中沒有有效的字符

示例代碼:

   if (SEGGER_RTT_HasKey()) {
      int c = SEGGER_RTT_GetKey();
   }

(4)int SEGGER_RTT_printf (unsigned BufferIndex, const char * sFormat, …)格式化輸出字符串
Return Value

Value Meaning
>=0 已經發送的字符數
< 0 發生錯誤

附加信息:

 轉換規范具有以下語法:

 %[標志][字段寬度][.精度]轉換指定程序

 支持的標志:

 -:在字段寬度內左對齊

 +:始終打印有符號轉換的符號擴展名

 0:用0代替空格。使用“-”標志或精度時忽略

 支持的轉換說明符:

 c:將參數打印為一個字符

 d:將參數打印為有符號整數

 u:將參數打印為無符號整數

 x:將參數打印為十六進制整數

 s:打印參數指向的字符串

 p:將參數打印為8位十六進制整數。

 ps.似乎官方沒有給float類型格式化輸出方式。

示例代碼:

SEGGER_RTT_printf(0, "SEGGER RTT Sample. Uptime: %.10dms.", /*OS_Time*/ 890912);

同時,可以使用SEGGER_RTT_printf()來設置字體顏色還背景顏色:

例如:

SEGGER_RTT_printf(0,RTT_CTRL_BG_WHITE);
SEGGER_RTT_printf(0,RTT_CTRL_TEXT_BLUE);

(5)void SEGGER_RTT_SetTerminal(char TerminalId);設置虛擬終端ID。
Return Value

Parameter Meaning
TerminalId 虛擬終端的ID

示例代碼:

//
// Send a string to terminal 1 which is used as error out.
//
SEGGER_RTT_SetTerminal(1); // Select terminal 1
SEGGER_RTT_WriteString(0, "ERROR: Buffer overflow");
SEGGER_RTT_SetTerminal(0); // Reset to standard terminal

SEGGER_RTT_WriteString中的0參數,是通道號,不是終端號。


(6)int SEGGER_RTT_WaitKey (void);檢測緩存區中是否還有字符。
Return Value

Value Meaning
≥0 等待返回一個按鍵值

示例代碼:

   int c = 0;
    do {
        c = SEGGER_RTT_WaitKey();
    } while (c != 'c');

附上測試代碼

/*terminal 0: if you press any key in the keyboard ,terminal 0 will show the key value witch you press.
  terminal 1: show the date
  terminal 2: show the time
*/		
if (SEGGER_RTT_HasKey()) 
{
	int c = SEGGER_RTT_GetKey();
	SEGGER_RTT_SetTerminal(0); 
	SEGGER_RTT_Write (0, &c, 1);
	SEGGER_RTT_printf(0,"\n");
}
//GET DATA
HAL_RTC_GetTime(&hrtc,&_current_time,RTC_FORMAT_BIN);
//GET TIME
HAL_RTC_GetDate(&hrtc,&_current_date,RTC_FORMAT_BIN);
//Printf
SEGGER_RTT_SetTerminal(1); 
SEGGER_RTT_printf(0,"%d . %d . %d \n",_current_date.Year,_current_date.Month,_current_date.Date);
SEGGER_RTT_SetTerminal(2); 
SEGGER_RTT_printf(0,"%d : %d : %d \n\n",_current_time.Hours,_current_time.Minutes,_current_time.Seconds);

代碼的下載鏈接:https://download.csdn.net/download/xue745146527/12045381 (工程包含了Keil 和 IAR )

2019年12月27日更新--增加打印float的功能

因為官方的RTT View不能打印出float類型的數據,因此我簡單寫了個float轉字符串的函數。

unsigned char *out_float(double value, unsigned char decimal_digit, unsigned char *output_length)
{
	unsigned char _output[20];
	unsigned long integer;
	unsigned long decimal;
	unsigned char _output_length = 0;
	unsigned char _length_buff = 0;
	static unsigned char *return_pointer;
	unsigned char signal_flag;
	if (value < 0)
		signal_flag = 1;
	else
		signal_flag = 0;
	value = fabs(value);
	integer = (unsigned long)value;
	decimal = (unsigned long)((value - integer) * pow(10, decimal_digit));

	unsigned long integer_buff = integer;
	unsigned long decimal_buff = decimal;

	while (1)
	{
		if (integer / 10 != 0)
			_length_buff++;
		else
		{
			_length_buff++;
			break;
		}
		integer = integer / 10;
	}
	for (int i = 0; i < _length_buff; i++)
	{
		if (i == _length_buff - 1)
			_output[_output_length] = integer_buff % 10 + 0x30;
		else
		{
			//_output[_output_length] = integer_buff / 10 % 10 + 0x30;
			_output[_output_length] = integer_buff / (unsigned long)pow(10, _length_buff - i - 1) % 10 + 0x30;
			integer_buff = integer_buff % (unsigned long)pow(10, _length_buff - i - 1);
			//integer_buff = integer_buff % 10;
		}
		_output_length++;
	}
	_output[_output_length] = '.';
	_output_length++;
	_length_buff = 0;
	while (1)
	{
		if (decimal / 10 != 0)
			_length_buff++;
		else
		{
			_length_buff++;
			break;
		}
		decimal = decimal / 10;
	}
	for (int i = 0; i < _length_buff; i++)
	{
		if (i == _length_buff - 1)
			_output[_output_length] = decimal_buff % 10 + 0x30;
		else
		{
			_output[_output_length] = decimal_buff / (unsigned long)pow(10, _length_buff-i-1) % 10 + 0x30;
			decimal_buff = decimal_buff % (unsigned long)pow(10, _length_buff - i - 1);
		}
			
		_output_length++;
	}
	_output[_output_length] = 0x00;
	_output_length++;
	return_pointer = (unsigned char *)realloc(return_pointer,_output_length);

	*output_length = _output_length - 1;
	if (return_pointer == 0)
		return 0;
	else
	{
		if (signal_flag == 1)
		{
			return_pointer[0] = '-';
			memcpy(return_pointer+1, _output, _output_length);
		}
		else
			memcpy(return_pointer, _output, _output_length);
	}
	return return_pointer;
}

Parameter

Value Meaning
value 想要打印的數據
decimal_digit 數字小數部分的位數
_output_length 輸出字符串的長度

Return Value

Value Meaning
unsigned char* 返回一個字符串指針

示例代碼:

   float value = 3.1415;
   unsigned char length;
   SEGGER_RTT_printf(0,"value = %s \n",out_float(value,4,&length));


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM