OpenOCD基础篇(三)——擦除和读取

前言:
  本篇文章介绍使用OpenOCD烧录和调试。

1 全片擦除

  通过OpenOCD可以对MCU的Flash进行全片擦除,本文以STM32F103ZET6单片机为例,创建一个OpenOCD的脚本文件 jlink_swd_stm32f103_erase_flash.cfg ,内容如下:

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
# ============================================
# STM32F103ZET6 全片擦除配置文件(J-Link + SWD)
# 支持无 nSRST 连接情况
# ============================================

# 使用 J-Link 调试器
source [find interface/jlink.cfg]

# 选择 SWD 传输模式
transport select swd

# 设置 SWD 时钟频率(单位:kHz),STM32F1 支持最高约 4000 kHz
adapter speed 4000

# 目标芯片配置
source [find target/stm32f1x.cfg]

# ==================================================================================
# 复位配置说明:
# - 如果你没有将 J-Link 的 nSRST 接到 STM32 的 NRST 引脚,请使用 `reset_config none`
# - 否则可使用 `reset_config srst_only srst_open_drain` 等
# ==================================================================================
reset_config none

# 可选:启用软复位支持(通过 AIRCR 触发 Cortex-M 复位)
adapter srst delay 100
adapter srst pulse_width 100

# ==================================================================================
# 擦除主程序函数
# ==================================================================================
proc erase_chip {} {
echo "开始擦除 STM32F103ZET6 ..."

# 初始化调试会话
init

# 尝试复位并进入 halt 状态
echo "尝试复位并暂停 CPU..."
catch {reset init} err
if {[string first "timed out" $err] != -1 || [string first "Error" $err] != -1} {
echo "reset init 失败,尝试手动 halt..."

# 如果 reset init 失败,尝试强制 halt
catch {halt}
if {[target cur_state] != "halted"} {
echo "错误:无法暂停 CPU,请检查供电、BOOT 模式或保护位!"
shutdown
return -1
}
}

# 等待稳定
sleep 100

# 执行全片擦除(mass erase)
echo "执行全片擦除..."
set res [catch {stm32f1x mass_erase 0} result]
if {$res == 0} {
echo "全片擦除成功!"
} else {
echo "全片擦除失败: $result"
echo "尝试逐扇区擦除..."
for {set i 0} {$i < 64} {incr i} {
echo "擦除扇区 $i..."
flash erase_sector 0 $i $i
}
}

# 最终复位并运行
echo "复位并启动芯片..."
reset run

echo "擦除完成!"
}

# 执行擦除操作
erase_chip

# 关闭连接
shutdown

  通过JLink连接单片机,并使用命令行运行该脚本:

1
openocd -f jlink_swd_stm32f103_erase_flash.cfg

  擦除成功后可看到如下打印:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
╰─ openocd -f jlink_swd_stm32f103_erase_flash.cfg
Open On-Chip Debugger 0.11.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
开始擦除 STM32F103ZET6 ...
Info : J-Link V9 compiled May 7 2021 16:26:12
Info : Hardware version: 9.70
Info : VTarget = 3.300 V
Info : clock speed 1000 kHz
Info : SWD DPIDR 0x1ba01477
Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints
Info : starting gdb server for stm32f1x.cpu on 3333
Info : Listening on port 3333 for gdb connections
尝试复位并暂停 CPU...
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x1ffff020 msp: 0x200001fc
执行全片擦除...
Info : device id = 0x10036414
Info : flash size = 512kbytes
全片擦除成功!
复位并启动芯片...
擦除完成!
shutdown command invoked

  通常为了安全,产线烧录后会对芯片进行加锁,但开发调试阶段为了方便,一般不加锁。

2 读取全片

  通过OpenOCD读取芯片全片Flash,同样以STM32F103ZET6单片机为例,创建文件 jlink_swd_stm32f103_dump_flash.cfg ,内容如下:

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
# ============================================
# STM32F103 Flash读取
# ============================================

source [find interface/jlink.cfg]
transport select swd
adapter speed 4000
source [find target/stm32f1x.cfg]
reset_config none

# 设置输出文件名
set output_file "read_flash.bin"

# 主程序
proc read_flash_clean {} {
global output_file

echo "开始读取STM32F103 Flash..."

# 初始化
init
reset init
halt
sleep 100

# 直接读取整个Flash(512KB)
echo "正在读取Flash到: $output_file"
echo "大小: 512KB (0x08000000 - 0x0807FFFF)"

# 删除旧文件(如果存在)
catch {file delete $output_file}

# 读取整个Flash
dump_image $output_file 0x08000000 0x80000

# 验证读取
if {[file exists $output_file]} {
set file_size [file size $output_file]
echo "读取完成!"
echo "文件大小: $file_size 字节 ([expr $file_size/1024] KB)"

if {$file_size == 0x80000} {
echo "读取成功: $output_file"
echo ""

# 使用外部命令查看文件头(兼容性更好)
echo "使用外部命令检查文件头:"
catch {
# 使用hexdump命令(如果可用)
exec sh -c "hexdump -C -n 64 $output_file 2>/dev/null || xxd -g 1 -l 64 $output_file 2>/dev/null || od -x -N 64 $output_file 2>/dev/null"
}

} else {
echo "警告: 文件大小不正确"
}
} else {
echo "错误: 文件创建失败"
}

# 恢复芯片运行
reset run
echo "芯片已复位运行"
}

# 执行读取
read_flash_clean

# 关闭连接
shutdown

  执行以下命令读取全片,保存到bin文件。

1
openocd -f jlink_swd_stm32f103_dump_flash.cfg

  可以看到打印如下。

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
╰─ openocd -f jlink_swd_stm32f103_dump_flash.cfg
Open On-Chip Debugger 0.11.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
开始读取STM32F103 Flash...
Info : J-Link V9 compiled May 7 2021 16:26:12
Info : Hardware version: 9.70
Info : VTarget = 3.300 V
Info : clock speed 1000 kHz
Info : SWD DPIDR 0x1ba01477
Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints
Info : starting gdb server for stm32f1x.cpu on 3333
Info : Listening on port 3333 for gdb connections
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x1ffff020 msp: 0x200001fc
检查Flash状态...
Info : device id = 0x10036414
Info : flash size = 512kbytes
Flash已解锁,需要复位...
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x1ffff020 msp: 0x200001fc
正在读取Flash到: read_flash.bin
大小: 512KB (0x08000000 - 0x0807FFFF)
读取完成!
文件大小: 524288 字节 (512 KB)
读取成功: read_flash.bin

使用外部命令检查文件头:
芯片已复位运行
shutdown command invoked

  脚本会先解锁Flash的读保护,然后将读取的内容写入bin文件,比如STM32F103ZET6的Flash大小为512KB,即为 $512 * 1024 = 524288$ 字节。注意,如果芯片被加锁读保护,会导致读取失败。