MDK:5.29
IAR:8.32
目錄--點(diǎn)擊可快速直達(dá)
寫在前面
本文介紹了J-Link RTT的部分使用內(nèi)容,,很多地方參考和使用了J-Link的官方資料,,有的地方可能翻譯的不太準(zhǔn)確,,請見諒,。
如果想了解更加準(zhǔn)確詳細(xì)的內(nèi)容,,請點(diǎn)此處,。
什么是RTT?
RTT(Real Time Transfer)是一種用于嵌入式中與用戶進(jìn)行交互的技術(shù),,它結(jié)合了SWO和半主機(jī)的優(yōu)點(diǎn),具有極高的性能,。
使用RTT可以從MCU非??焖佥敵稣{(diào)試信息和數(shù)據(jù),且不影響MCU實(shí)時性,。這個功能可以用于很多支持J-Link的設(shè)備和MCU,,兼容性強(qiáng)。
RTT支持兩個方向的多個通道,,上到主機(jī),,下到目標(biāo),它可以用于不同的目的,,為用戶提供盡可能多的自由,。默認(rèn)實(shí)現(xiàn)每個方向使用一個通道,用于可打印終端輸入和輸出,。
使用J-Link RTT Viewer,,可用于“虛擬”終端,允許打印到多個窗口(例如,,一個用于標(biāo)準(zhǔn)輸出,,一個對于錯誤輸出,一個用于調(diào)試輸出),。
RTT的工作原理
RTT在MCU的存儲器中使用SEGGER RTT控制塊結(jié)構(gòu)管理數(shù)據(jù)讀寫,??刂茐K對于每個可用的信道都在內(nèi)存中包含了一個ID,通過J-Link或者環(huán)形緩沖結(jié)構(gòu)區(qū)(鏈表)都可以通過ID找到對應(yīng)的控制塊,。
可用信道的最大數(shù)目可以在編譯時配置,,并且每個緩沖區(qū)都可以在MCU運(yùn)行時配置和使用。上下緩沖區(qū)可以分開處理,。每個通道都可以配置為阻塞或非阻塞,。
在阻塞模式下,應(yīng)用程序?qū)⒌却彌_區(qū)寫滿,,直到可以寫入所有內(nèi)存為止,,這將導(dǎo)致應(yīng)用程序處于阻塞狀態(tài),但可以防止數(shù)據(jù)丟失,。
在非阻塞模式下,,只會寫入適合緩沖區(qū)的數(shù)據(jù),或完全不寫入緩沖區(qū),,其余的數(shù)據(jù)將被丟棄,。這樣即使沒有連接調(diào)試器,也可以實(shí)時運(yùn)行,。開發(fā)人員不必創(chuàng)建特殊的調(diào)試版本,,并且代碼可以保留在發(fā)布應(yīng)用程序中。
RTT的性能
RTT的性能明顯高于其他任何用于將數(shù)據(jù)輸出到主機(jī)PC的方式,。平均一行文本可以在1微秒或更短的時間內(nèi)輸出,。基本上相當(dāng)于做一個memcopy()的時間,。
RTT實(shí)現(xiàn)代碼使用大約500字節(jié)的ROM和(n(通道數(shù)) * (24字節(jié)ID+24字節(jié)))的RAM,。推薦的大小是1 kByte(上行信道)和16到32字節(jié)(下行信道),這取決于輸入/輸出的負(fù)載,。
快速使用教程
1.首先安裝J-Link的軟件驅(qū)動,。
2.安裝完成后,打開J-Link的安裝目錄(開始->SEGGR->J-Link RTT Viewer->右鍵打開文件所在位置->然后繼續(xù)右鍵打開文件所在位置->此時就是安裝目錄了),,
找到如下路徑SEGGER\JLink_V632f\Samples\RTT
,解壓路徑里面的壓縮包SEGGER_RTT_V632f.zip
(不同的版本,,V后面的數(shù)字可能不一樣)。
3.將解壓完的文件拷貝到代碼工程目錄中,。
4.在項目工程中加入SEGGER_RTT_V632f\RTT
目錄下的全部四個文件,。工程添加文件方法請自行百度,。
5.工程加入文件后,,在想要用到RTT的文件中包含#include "SEGGER_RTT.h"
,然后直接調(diào)用SEGGER_RTT_printf()
就好了,
例如SEGGER_RTT_printf(0,"hello world!")
這個和C語言的printf的格式差不多,,就是前面加了一個端口0
的參數(shù),。(詳細(xì)信息請看高級使用教程)
6.然后點(diǎn)擊開始->SEGGR->J-Link RTT Viewer,打開J-Link RTT Viewer 選擇好你的芯片型號后,點(diǎn)擊確認(rèn),。
7.然后就能看到我們打印的內(nèi)容了,。
高級使用教程
1.部分函數(shù)介紹:
(1)void SEGGER_RTT_Init (void)
RTT初始化函數(shù),應(yīng)放于程序開始階段,。
(2)int SEGGER_RTT_GetKey (void);
從RTT終端獲取一個按鍵字符,。
Return Value
Value |
Meaning |
>=0 |
返回按鍵字符(0-255) |
< 0 |
緩存區(qū)中沒有有效的字符 |
示例代碼:
int c;
c = SEGGER_RTT_GetKey();
if (c == 'q') {
exit();
}
(3)int SEGGER_RTT_HasKey (void);
檢測緩存區(qū)中是否還有字符。
Return Value
Value |
Meaning |
1 |
緩存區(qū)中至少有一個字符是有效的 |
0 |
緩存區(qū)中沒有有效的字符 |
示例代碼:
if (SEGGER_RTT_HasKey()) {
int c = SEGGER_RTT_GetKey();
}
(4)int SEGGER_RTT_printf (unsigned BufferIndex, const char * sFormat, …)
格式化輸出字符串
Return Value
Value |
Meaning |
>=0 |
已經(jīng)發(fā)送的字符數(shù) |
< 0 |
發(fā)生錯誤 |
附加信息:
?轉(zhuǎn)換規(guī)范具有以下語法:
?%[標(biāo)志][字段寬度][.精度]轉(zhuǎn)換指定程序
?支持的標(biāo)志:
?-:在字段寬度內(nèi)左對齊
?+:始終打印有符號轉(zhuǎn)換的符號擴(kuò)展名
?0:用0代替空格,。使用“-”標(biāo)志或精度時忽略
?支持的轉(zhuǎn)換說明符:
?c:將參數(shù)打印為一個字符
?d:將參數(shù)打印為有符號整數(shù)
?u:將參數(shù)打印為無符號整數(shù)
?x:將參數(shù)打印為十六進(jìn)制整數(shù)
?s:打印參數(shù)指向的字符串
?p:將參數(shù)打印為8位十六進(jìn)制整數(shù),。
?ps.似乎官方?jīng)]有給float類型格式化輸出方式。
示例代碼:
SEGGER_RTT_printf(0, "SEGGER RTT Sample. Uptime: %.10dms.", /*OS_Time*/ 890912);
同時,,可以使用SEGGER_RTT_printf()
來設(shè)置字體顏色還背景顏色:
例如:
SEGGER_RTT_printf(0,RTT_CTRL_BG_WHITE);
SEGGER_RTT_printf(0,RTT_CTRL_TEXT_BLUE);
(5)void SEGGER_RTT_SetTerminal(char TerminalId);
設(shè)置虛擬終端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參數(shù),是通道號,,不是終端號,。
(6)int SEGGER_RTT_WaitKey (void);
檢測緩存區(qū)中是否還有字符。
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類型的數(shù)據(jù),,因此我簡單寫了個float轉(zhuǎn)字符串的函數(shù),。
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 |
想要打印的數(shù)據(jù) |
decimal_digit |
數(shù)字小數(shù)部分的位數(shù) |
_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));