Jlink-RTT扫频

Jlink-RTT扫频

Porcovsky Lv3

可提供操作系统级别的采样速度,远远大于jscope和vofa正常的串口通信,用于参数辨识+控制设计

1.啁啾信号

定义:频率随着时间增加或减小的信号。

image-20250207092323502

使用C实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
float chirp = 0;//啁啾信号
float chirp_zero_phase= 0;//初始相位
float chirp_f0 = 0;//初始频率
float chirp_k = 0.0025;//频率变化率
float chirp_t = 0;
float chirp_f = 0;//信号频率
float Chrip_Amplitude = 6000;//信号幅值
void Make_Chirp(int t)
{
chirp_f = chirp_f0 + chirp_k * t;
chirp = Chrip_Amplitude*sinf(chirp_zero_phase + 2 * PI*
(chirp_f0+chirp_k*0.5f*t)* t/1000);
}

使用1ms定时器中断运行产生啁啾信号函数,运行120s,产生0-300Hz的幅值为6000的啁啾信号,图像如图所示。

image-20250207092448336

香农采样定理:要想根据采样准确的再现信号,信号中必须不含有高于1/2采样速率的频率成分。

2.Jlink RTT

系统运行频率为1kHz,频率最大值可以根据香农采样定理初步确定为500Hz以下,然后可以根据系统具 体情况确定扫频最大频率。

Jlink RTT耗费系统资源少,不影响系统的实时性。RTT的性能明显高于其他将数据输出到主机的模式。平均一行文本可以在1微秒或更短的时间内输出。基本上相当于做一个memcopy()的时间。RTT速度由调用发送函数的速度决定,速度可以达到很高。局限在于无法通过曲线直观观察数据,无法用于在线调试。但用来做参数辨识及更细的分析很合适

2.1 移植RTT

RTT源码包在JLINK驱动的目录中。

image-20250207092648751

将RTT文件夹复制到工程文件夹中,并在工程中新建RTT分组,将RTT文件中两个.c文件添加进来,并在魔术棒中添加RTT的头文件路径。之后可以正常使用Jlink RTT

2.2 使用RTT

常用函数 SEGGER_RTT_SetTerminal(unsigned char TerminalId) 该函数传入参数为终端ID,在Jlink RTT Viewer中可以打开不同终端窗口查看数据,同时在初始窗口中会 显示所有终端数据。 SEGGER_RTT_printf(unsigned BufferIndex, const char * sFormat, …) 该函数用于打印数据,常用参数格式为

1
2
3
4
5
6
7
8
9
10
11
12
%[flags][Fieldwidth][.Precision]ConversionSpecifier
Supported flags:
-: 字段宽度内左对齐
+: 为有符号参数打印符号
0: 用0代替空格. 使用“-”或精度时忽略
Supported conversion specifiers:
c: 将参数打印为一个字符
d: 将参数打印为有符号整数
u: 将参数打印为无符号整数
x: 将参数打印为十六进制整数
s: 打印参数指向的字符串
p: 将参数打印为 8 位十六进制整数.(参数应该是一个指向 void 的指针)

打印整型(u8 u16 u32 s8 s16 s32)时使用“d” 打印字符串时使用“s” 打印浮点数时,使用out_float函数将float型转换为字符串后打印。或通过在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
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
#ifdef SEGGER_RTT_PRINT_FLOAT_ENABLE
case 'f':
case 'F':
{
float fv;
fv = (float)va_arg(*pParamList, double); //取出输入的浮点数值
v = (int)fv; //取整数部分
_PrintInt(&BufferDesc, v, 10u, NumDigits, FieldWidth, FormatFlags);
//显示整数,支持负数
_StoreChar(&BufferDesc, '.');
//显示小数点
v = abs((int)(fv * 100));
v = v % 100;
_PrintInt(&BufferDesc, v, 10u, 2, FieldWidth, FormatFlags);
//显示小数点后两位
}
break;
#endif
//para:
//value:the floating number
//decimal_digit: Fractional part length
//output_length: length of output data
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.0, 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.0,
_length_buff - i - 1) % 10 + 0x30;
integer_buff = integer_buff % (unsigned long)pow(10.0, _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.0,
_length_buff-i-1) % 10 + 0x30;
decimal_buff = decimal_buff % (unsigned long)pow(10.0, _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;
}

3.使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void SEGGERRTTTask(void)
{
timestamp = TIM5->CNT;
if(RTT_Send_flag)
{
SEGGER_RTT_SetTerminal(0);//设置终端ID
SEGGER_RTT_printf(0,"Des = %s \n", out_float(g_stGM_YawSpeedPID.fpDes,4,&length));//r
SEGGER_RTT_printf(0,"U = %s \n", out_float(g_stGM_YawSpeedPID.fpU,4,&length));//pidu

SEGGER_RTT_SetTerminal(1);
SEGGER_RTT_printf(0,"FB = %s \n", out_float(g_stGM_YawSpeedPID.fpFB,4,&length));//y_flt
SEGGER_RTT_printf(0,"YY = %s \n", out_float(g_stGM_YawSpeedPID.fpFB,4,&length));//y_raw
}
}

一般使用JLinkRTTViewer观看数据

过低版本的jlink可能不自带RTT Viewer的安装包,如果在jlink的安装文件夹里面找不到RTTViewer的话升个版本就行

image-20250207092827230

将Specify Target Device选为对应的型号 Target Interface & Speed选为对应的接口。

关于最后的RTT Control Block的Address,可以通过工程编译生成的.map文件(双击HITCRT_ENGINEER_2024即可打开)查看

[]: https://blog.csdn.net/qq_38295600/article/details/129331670 “查看MAP”

image-20250207092940265

在文件中查询_SEGGER_RTT得到类似下面的代码

1
_SEGGER_RTT  0x20002f50  Data  168  segger_rtt.o(.bss)

其中0x20002f50即为the address of the RTT Control block。

连接后就可以观看JLink RTT发送的数据。

image-20250207093006207

使用JLinkRTTViewer导出数据时,可以点击工具栏中Logging->Start Terminals Logging,之后Terminals中的数据就会被存储在.log文件中,之后再次点击Logging->Stop Terminals Logging终止数据记录。

如果数据更新不正常可以修改浮点数精度

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
0>  42466  0.0  0.0  0.0  0.0 
0> 42468 0.0 0.0 0.0 0.0
0> 42470 0.0 0.0 0.0 0.0
0> 42472 0.0 0.0 0.0 0.0
0> 42474 0.0 0.0 0.0 0.0
0> 42476 0.5 11.600 299.899 5803.935
0> 42478 0.29 34.793 299.700 5800.202
0> 42480 0.98 81.148 299.500 5784.846
0> 42482 0.284 173.781 299.299 5746.227
0> 42484 0.747 358.877 299.100 5661.67
0> 42486 1.859 728.712 298.899 5482.806
0> 42488 4.452 1467.655 298.700 5118.334
0> 42492 10.384 2946.81 298.300 4387.412
0> 42496 53.297 11787.771 297.900 -68.46
0> 42500 118.456 23579.550 297.500 -6031.500
0> 42504 567.645 94098.648 297.100 -41811.968
0> 42508 1230.136 188146.62 296.500 -185136.546
0> 42514 5672.13 751090.0 295.900 -758900.125
0> 42520 25687.847 2998271.750 295.300 -3055603.0
0> 42528 241244.750 23896540.0 294.300 -24520456.0
0> 42536 1061137.875 190583856.0 293.500 -98301464.0
0> 42546 9632197.0 1519956224.0 292.300 -1574043008.0
0> 42558 178292192.0 3540067328.0 291.100 -4037248000.0
0> 42570 1572560768.0 4164190208.0 289.899 -321142784.0
0> 42582 2459584512.0 3717332992.0 288.700 -2777284608.0
0> 42594 3926392832.0 3791650816.0 287.500 -4118806528.0
0> 42606 1244397568.0 369098752.0 286.299 -1610612736.0
0> 42618 3019898880.0 2684354560.0 285.100 -0.0
0> 42628 2415919104.0 2147483648.0 284.100 -0.0
0> 42638 3489660928.0 0.0 283.299 -0.0
0> 42644 3221225472.0 0.0 282.699 -0.0
  • 标题: Jlink-RTT扫频
  • 作者: Porcovsky
  • 创建于 : 2025-02-07 09:20:45
  • 更新于 : 2025-06-12 11:12:02
  • 链接: https://pocro.github.io/2025/02/07/Jlink-RTT扫频/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
 评论