嵌入式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
作者:文子杰