嵌入式mcu开发:OpenOCD的使用及Telnet和GDB服务进行MCU内存读写详解

openocd官方文档连接:https://openocd.org/doc/html/General-Commands.html#Memory-access-commands

openocd源码库和exe文件:https://github.com/openocd-org/openocd/releases

一、使用openocd烧录固件到MCU

用法:
"path_to\\openocd.exe" -f [interface.cfg] -f [target.cfg] -c "program [path_to/app.bin] [start_addr] verify" -c "reset run" -c "exit"

解释:
interface.cfg:接口配置文件
target.cfg:目标MCU的配置文件
program:表示进行烧录
path_to/app.bin:固件路径
start_addr:开始地址,如0x08020000
verify:表示烧录结束后进行校验
reset run:表示重置并运行MCU
exit:表示退出

注意:用双引号""包括路径,可以防止路径中有空格,导致路径识别失败

# 例子
"path_to\\openocd.exe" -f interface/cmsis-dap.cfg -f target/stm32f4x.cfg -c "program path_to/app.bin 0x08020000 verify" -c "reset run" -c "exit"

如何获取.cfg配置文件?
在openocd的固件库中获取。

位置:
1、接口配置文件路径:
xxx\openocd-133dd9d66-i686-w64-mingw32\share\openocd\scripts\interface

2、设备配置文件路径:
xxx\openocd-133dd9d66-i686-w64-mingw32\share\openocd\scripts\target

二、启动openocd服务

启动openocd服务后,即可使用tcl、telnet、gdb服务。
需要根据通信设备和MCU型号来选择接口文件。

# 例子
C:\Users\15010\.eide\tools\openocd_7a1adfbec_mingw32\bin\openocd.exe -f interface/cmsis-dap.cfg -f target/stm32f4x.cfg

1、tcl服务:
host:localhost;port:6666

2、telnet服务:
host:localhost;port:4444

3、gdb服务
host:localhost;port:3333

启动openopc成功后输出:

Open On-Chip Debugger 0.12.0-rc2-g7a1adfbec (2022-11-06-10:08)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "swd". To override use 'transport select <transport>'.
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : CMSIS-DAP: SWD supported
Info : CMSIS-DAP: JTAG supported
Info : CMSIS-DAP: SWO-UART supported
Info : CMSIS-DAP: Atomic commands supported
Info : CMSIS-DAP: FW Version = 2.0.0
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : SWCLK/TCK = 1 SWDIO/TMS = 1 TDI = 1 TDO = 1 nTRST = 1 nRESET = 1
Info : CMSIS-DAP: Interface ready
Info : clock speed 2000 kHz
Info : SWD DPIDR 0x2ba01477
Info : [stm32f4x.cpu] Cortex-M4 r0p1 processor detected
Info : [stm32f4x.cpu] target has 6 breakpoints, 4 watchpoints
Info : starting gdb server for stm32f4x.cpu on 3333
Info : Listening on port 3333 for gdb connections
Info : accepting 'telnet' connection on tcp/4444

三、使用telnet方法连接openocd和命令操作

1、开启openocd服务

参考步骤二

2、通过telnet登录

在telnet客户端输入host和port登录,登录成功后即可使用命令行进行控制。
host:localhost;port:4444

telnet localhost 4444

3、特殊寄存器操作命令

# 1、列出当前目标的所有可用寄存器,显示编号、名称、大小、值和缓存状态
reg

# 2、获取寄存器值  例子:get_reg {pc sp} 获取寄存器 pc和sp的值
get_reg [-force] list

# 3、设置寄存器值 例子:set_reg {pc 0 sp 0x1000}  # 设置寄存器pc值为0,sp寄存器值为0x1000
set_reg dict


4、读取寄存器命令

# 方法一:
mdd [phys] addr [count]  # 按64bit方式读取:addr为开始寄存器地址(如:0x08020000);count为按序读取的个数;phys标明为物理内容地址而非虚拟内存
mdw [phys] addr [count]  # 按32bit方式读取
mdh [phys] addr [count]  # 按16bit方式读取
mdb [phys] addr [count]  # 按8bit方式读取

# 例子:mdw  0x200039d8 2

# 方法二:
read_memory addr width count ['phys'] # addr为开始寄存器地址(如:0x08020000); width为读取的位宽度(32-按32位读取); count为按序读取的个数

# 例子:read_memory 0x200039dC 32 2

5、设置寄存器命令

# 方法一:
mwd [phys] addr doubleword [count]  # doubleword 为64bit值; count为从起始地址开始连续写入的个数
mww [phys] addr word [count]        # doubleword 为32bit值
mwh [phys] addr halfword [count]    # doubleword 为16bit值
mwb [phys] addr byte [count]        # doubleword 为8bit值


# 方法二:
write_memory addr width data ['phys']  # width为位宽度(32-按32位)

# 例子: write_memory 0x20000000 32 {0xdeadbeef 0x00230500} 从起始地址开始写入2个32bit值

6、读取一段寄存器地址,并保存到文件中

dump_image filename address size  # filename为文件路径; address为开始地址 size为读取长度

7、更多命令

help -- 打印所有控制命令
help [command] -- 查看某个命令的使用方法

三、使用gdb方法连接openocd和命令操作

需要使用gdb工具,我使用的是gcc工具链提供的arm-none-eabi-gdb.exe,其它的gdb工具亦可。

1、开启openocd服务

参考步骤二

2、运行gdb工具

path/project.elf 为当前mcu运行固件对应的elf文件,该文件记录了固件的详细信息。

path/arm-none-eabi-gdb.exe path/project.elf

3、连接openocd服务器

target remote localhost:3333

连接成功后即可进行命令控制

4、print命令

按特定数据类型,来打印某个内存地址的数据

Usage: print [[OPTION]... --] [/FMT] [EXP]
数据格式:默认10进制;u-10进制无符号格式;x-16进制;o-8进制;

print *(int*)0x8000000   # 按整形的方式获取数据,显示为10进制
print /x *(int*)0x8000000   # 按整形的方式获取数据,显示为16进制

演示结果:

(gdb) print *(int*)0x8000000
$1 = 537031880
(gdb) print/x *(int*)0x8000000
$2 = 0x200274c8
(gdb) print/o *(int*)0x8000000
$3 = 04000472310

5、x 命令

打印内存数据,x命令获取内存数据比print命令更方便。

Usage: x /<count><format><width> <addr>

数据格式:
o(octal), x(hex), d(decimal), u(unsigned decimal), t(binary), f(float), 
a(address), i(instruction), c(char), s(string), and z(hex, zero padded on the left)

数据宽度:
b(byte), h(halfword), w(word), g(giant, 8 bytes)

# 例子
x /db 0x20000000  # 获取1个内存数据,单个大小为1字节,10进制显示
x /xw 0x20000000  # 获取1个内存数据,单个大小为4字节,16进制显示
x /10xw 0x20000000  # 获取10个内存数据,单个大小为4字节,16进制显示

演示结果:

(gdb) x /xw 0x20000000
0x20000000 <adc1_conv_to_val_index_switch_a>:   0x0a100f0e

(gdb) x /db 0x20000000
0x20000000 <adc1_conv_to_val_index_switch_a>:   14

(gdb) x /10xw 0x20000000
0x20000000 <adc1_conv_to_val_index_switch_a>:   0x0a100f0e      0x1211030c      0x00001301      0x0a100f0d
0x20000010 <adc1_conv_to_val_index_switch_b+4>: 0x1211030b      0x00001301      0x04050908      0x07000206
0x20000020 <table>:     0x0e070e1e      0x0dd70def

(gdb) x /10xb 0x20000000
0x20000000 <adc1_conv_to_val_index_switch_a>:   0x0e    0x0f    0x10    0x0a    0x0c    0x03    0x11    0x12
0x20000008 <adc1_conv_to_val_index_switch_a+8>: 0x01    0x13

注意:
1、使用x命令之后,按下回车键,将会显示下一个内存地址的数据,移动的宽度为上一次设置的宽度;
2、再次使用x命令,且不设置显示格式,则沿用上一次设置的格式;

6、更多命令

help -- 打印所有控制命令
help [command] -- 查看某个命令的使用方法
quit --退出gdb

作者:文子杰

物联沃分享整理
物联沃-IOTWORD物联网 » 嵌入式mcu开发:OpenOCD的使用及Telnet和GDB服务进行MCU内存读写详解

发表回复