OpenOCD 添加 Air001
前言
-
1)AIR001 简介:
-
(1)采用 PY32F003 的 TSSOP20 封装的 MCU
-
(2)采用 Cortex-M0+ 内核
-
(3)内置 32KB 的 Flash、4KB 的 RAM
-
(4)芯片集成多路 USART、IIC、SPI 等通讯外设
-
(5)5 个 16 位定时器、1 路 ADC、2 路比较器。
-
(6)最重要的是只要 7 毛多一片。
-
-
2)SWD 下载接口:
- SWDIO:PA13
- SWCLK:PA14
-
3)引脚图:
-
4)本文介绍了两种烧录算法来实现 OpenOCD 对 Air001 芯片的支持。
1 烧录算法——寄存器
-
1)所谓寄存器烧录,即是使用 OpenOCD 通过 MEM-AHP 控制 FLASH 所在的 AHB 总线,进而控制 FLASH 读写相关寄存器来进行烧录。
-
2)优点就是实现比较简单,但一个 32KB 的 image 烧录用了 90 多秒,呵呵。该方式可以用来熟悉 OpenOCD 代码,以及在不了解 ARM 汇编指令下使用。当对 OpenOCD 代码以及汇编指令有一定了解后,就可以实现同/异步烧录算法了。
1.1 air001.cfg 文件说明
-
1)在 /tcl/target 目录下,添加 air001.cfg 文件。
-
2)因为 AIR001 采用 Cortex-M0+ 内核,所以这里复制 tcl/target/stm32l0.cfg 一份,并重命名为 air001.cfg
-
3)修改:
# 第 14 行,修改芯片名称。 # set _CHIPNAME stm32l0 set _CHIPNAME air001 # 第 38 行,修改芯片 ID。(可能需要等首次下载时获取到) # set _CPUTAPID 0x0bc11477 set _CPUTAPID 0x0bc11477 # 第 51 行,设置 FLASH 大小。air001 芯片只有 32KB 大小,即 0x00008000 # flash bank $_FLASHNAME stm32lx 0x08000000 0 0 0 $_TARGETNAME flash bank $_FLASHNAME air001 0x00008000 0 0 0 $_TARGETNAME # 有一个 stm32l0_enable_HSI16() 的函数,不知道干嘛的,删掉 # 最下边有一个配置 DEBUG 寄存器的功能,这个就需要根据芯片的寄存器手册来进行修改了 $_TARGETNAME configure -event examine-end { # DBGMCU_CR |= DBG_STANDBY | DBG_STOP | DBG_SLEEP mmw 0x40015804 0x00000002 0 # Stop watchdog counters during halt # DBGMCU_APB1_FZ |= DBG_IWDG_STOP | DBG_WWDG_STOP mmw 0x40015808 0x00001800 0 }
-
4)关于修改 air001.cfg 文件最后的 DBGMCU_CR 配置:
- (1)DBGMCU_CR 寄存器(地址偏移 0x04)的地址:
-
由此可得,DBGMCU_CR = 0x4001 5804
- (2)air001 芯片只有 DEBUG_CR 的 DBG_STOP(位于 bit1)
- (1)DBGMCU_CR 寄存器(地址偏移 0x04)的地址:
-
5)关于修改 air001.cfg 文件最后的 DBGMCU_APB1_FZ 配置:
- (1)DBGMCU_APB1_FZ 寄存器偏移地址为 0x08,DBG_IWDG_STOP 和 DBG_WWDG_STOP 分别位于其 bit12 和 bit11 位。
1.2 air001.c 文件说明
-
1)在 /src/flash/nor 目录下,添加 air001.c 文件。用于告知 openocd 芯片的 flash 烧录流程。
-
2)air001.c 文件中的宏定义、函数等均围绕以下内容来建立:
const struct flash_driver air001_flash = { .name = "air001", .commands = air001_command_handlers, .flash_bank_command = air001_flash_bank_command, .erase = air001_erase, .write = air001_write, .read = default_flash_read, .probe = air001_probe, .auto_probe = air001_auto_probe, .erase_check = default_flash_blank_check, .protect_check = air001_protect_check, .info = air001_get_info, .free_driver_priv = default_flash_free_driver_priv, };
-
3)接下来,按照 air001_flash 结构中定义的函数顺序,依次实现
1.3 drivers.c 文件说明
- 1)drivers.c 文件位于 /src/flash/nor 目录下,该文件主要用于根据驱动名称查找驱动。 这里根据 1.2 中的驱动名称添加一下就可以了:
...... extern const struct flash_driver air001_flash; ...... static const struct flash_driver * const flash_drivers[] = { ...... &air001_flash, ......
1.4 Makefile.am 文件说明
- 1)这里的 Makefile.am 文件位于 /src/flash/nor 目录下,指明了该目录下源文件的编译规则。因此这里需要修改让 openocd 编译时引用到上述新增的文件。
...... NOR_DRIVERS = \ %D%/air001.c \ ......
2 烧录算法——异步烧录
2.1 异步烧录算法
-
1)烧录程序:主要就是在 RAM 中分配两块工作空间
- 一块用来运行 air001.inc 文件内的汇编代码(该代码实现 image 从 RAM 到 FLASH 的搬运工作)
- 一块尽可能大,用来作为一个循环的 FIFO,可以在 OpenOCD 将 image 写进来的同时,AIR001 通过上述的汇编代码读取数据并写入 FLASH。
-
参考:https://zhuanlan.zhihu.com/p/593389551
- 大佬这篇文章写的非常非常好,清晰明了地说明异常烧录的原理。
2.2 关于汇编代码
-
1)汇编代码位于 /contrib/loaders/flash/airm2m/air001.inc 文件中。
-
2)这里可以参考 stm32f1x.S 文件,不同点在于:
- (1)stm32f1x.s 中,每次只能写入半字(2 个字节)
- (2)air001 芯片需要在每 31 个字写入后,将 FLASH_CR->PGSTRT(bit19) 置 1,最后再写入第 32 个字。
-
3)汇编文件编写完成后,在 air001.S 文件所在目录直接 make,即可生成 air001.inc 文件。前提是需要仿照 /contrib/loaders/flash/stm32/Makefile创建自已的 Makefile
3 编译与测试
3.1 编译 OpenOCD
# 启动项测试
./bootstrap
# 创建编译目录
mkdir openocd-clion-build
cd openocd-clion-build
# 配置
../configure --enable-ftdi
# 编译
make -j4 && make install
3.2 测试日志
-
1)首先在 MSYS2 中输入:openocd -d3 -f interface/cmsis-dap.cfg -f target/air001.cfg 以启动 OpenOCD。
-
2)通过 telnet 127.0.0.1 4444 连接到 OpenOCD 服务端:
$ telnet 127.0.0.1 4444 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. Open On-Chip Debugger > halt [air001.cpu] halted due to debug-request, current mode: Thread xPSR: 0x21000000 pc: 0x080071f8 msp: 0x20000ff0 > flash write_image erase "D:\\_Workspace\\11_MCU\\07_LuatOS\\Air001\\Air001_001_GCCTemplate\\cmake-build-debug\\Air001_000_GCCTemplate.hex" device id = 0x60001000 AIR001 flash size is 32kb, base address is 0x08000000 auto erase enabled wrote 32768 bytes from file D:\_Workspace\11_MCU\07_LuatOS\Air001\Air001_001_GCCTemplate\cmake-build-debug\Air001_000_GCCTemplate.hex in 3.767980s (8.493 KiB/s) > reset > halt [air001.cpu] halted due to debug-request, current mode: Thread xPSR: 0x01000000 pc: 0x080071f4 msp: 0x20000ff0 > flash write_image erase "D:\\_Workspace\\11_MCU\\07_LuatOS\\Air001\\Air001_001_GCCTemplate\\cmake-build-debug\\Air001_000_GCCTemplate.hex" auto erase enabled wrote 16384 bytes from file D:\_Workspace\11_MCU\07_LuatOS\Air001\Air001_001_GCCTemplate\cmake-build-debug\Air001_000_GCCTemplate.hex in 2.048985s (7.809 KiB/s) > reset > shutdown shutdown command invoked Connection closed by foreign host.
- 可以发现,32 KB 写入只需要 3.767980s (8.493 KiB/s);16 KB 写入则需要 2.048985s (7.809 KiB/s)。
4 附录1:ARM Cortex-M0+ 汇编指令
-
1)以下测试结果基于 arm-none-eabi-gcc 和 Cortex-M0+ 平台。根据《STM32F0系列Cortex-M0原理与实践》、《嵌入式系统设计基础及应用——基于ARM Cortex-M4微处理器》整理。
-
2)ARM Cortex-M0+ Thumb 指令集只能使用前8个(R0-R7)寄存器。
-
3)N、Z、C、V 标志位是什么
- Negative:表示结果为负数时 N 标志位置 1。如 cmp 指令相减时小于 0
- Zero:表示结果为零时 Z 标志位置 1。如 cmp 指令比较两个数相等时,可通过 beq 跳转;比较两个数不相等时,可通过 bne 跳转。
- Carry:表示加减法运算导致最高位有进位或借位时 C 标志位置 1。而 bcc 指令在 C = 0 时跳转
- oVerflow:表示有符号数运算时超出表示范围则 V 标志位置 1。
-
4)通用寄存器:
4.1 数据处理指令
-
1)数据传输指令
助记符 操作数 操作含义 影响标志位 测试 MOV Rd, Rm 传送 Rd 数据到 Rm N,Z movs r0, #0x1
movs R1,R0
mov R1,R0MVN Rd, Rm Rm 位取反后,传送到 Rd N,Z mvns r7, r6 MRS(略) MSR(略) -
2)算术运算指令
助记符 操作数 操作含义 影响标志位 测试 ADD {Rd,} Rn, <Rm|#imm> 加 N,Z,C,V add r2, r3
add r2, r2, r3
adds r2, #1
adds r2, r3
adds r2, r2, #1
adds r2, r2, r3ADCS {Rd,} Rn, Rm 带进位加 N,Z,C,V SUBS {Rd,} Rn, <Rm|#imm> 减法 N,Z,C,V subs r2, #1
subs r2, r3
subs r2, r2, #1
subs r2, r2, r3SBC {Rd,} Rn, Rm 带符号减 N,Z,C,V RSB {Rd,} Rn, #0 逆向减法 N,Z,C,V MULS Rd, Rn, Rm 乘法 N,Z -
3)逻辑运算指令
助记符 操作数 操作含义 影响标志位 测试 ANDS {Rd,} Rn, Rm 位与计算 N,Z ands r2, r3
ands r2, r2, r3ORRS {Rd,} Rn, Rm 逻辑或 N,Z orrs r2, r3
orrs r2, r2, r3EORS {Rd,} Rn, Rm 异或 N,Z eors r2, r3
eors r2, r2, r3BICS {Rd,} Rn, Rm 位清除 N,Z bics r2, r3
bics r2, r2, r3 -
4)移位指令
助记符 操作数 操作含义 影响标志位 测试 ASRS {Rd,} Rm, <Rs|#imm> 算术右移 N,Z,C asrs r2, #1
asrs r2, r3
asrs r2, r2, #1
asrs r2, r2, r3LSLS {Rd,} Rn, <Rs|#imm> 逻辑左移 N,Z,C 同上 LSRS {Rd,} Rn, <Rs|#imm> 逻辑右移 N,Z,C 同上 RORS {Rd,} Rn, Rs 循环右移 N,Z,C rors r2, r3
rors r2, r2, r3 -
5)比较与测试指令
助记符 操作数 操作含义 影响标志位 测试 CMP Rn, <Rm|#imm> 比较 N,Z,C,V cmp r2, #1
cmp r2, r3CMN Rn, Rm 比较负值 N,Z,C,V TST Rn, Rm 逻辑与测试 N,Z tst r2, r3
tsts r2, r3 -
6)位域操作指令(无)
-
7)跳转指令
助记符 操作数 操作含义 影响标志位 测试 B label 跳转 — 跳转, BL label 带链接的分支跳转 — 跳转,且保存 PC 到 R14 中。 BLX Rm 带链接的间接跳转 — 跳转,并将处理器的工作状态在 ARM 状态和 Thumb 状态之前切换,且保存 PC 到 R14 中。 BX Rm 间接跳转 — 跳转,并将处理器的工作状态在 ARM 状态和 Thumb 状态之前切换。 BEQ Rm 间接跳转 — BNE Rm 间接跳转 —
4.2 存储器访问指令
-
1)存储器访问常用指令
助记符 操作数 操作含义 影响标志位 测试 ADR Rd, label 将基于PC相对偏移的地址读到寄存器 — LDR Rt, label 从基于PC相对偏移地址上加载寄存器 — LDR Rt, [Rn, <Rm|#imm>] 以字加载寄存器 — LDRB Rt, [Rn, <Rm|#imm>] 以字节加载寄存器 — LDRH Rt, [Rn, <Rm|#imm>] 以半字加载寄存器 — LDRSB Rt, [Rn, <Rm|#imm>] 以有符号字节加载寄存器 — LDRSH Rt, [Rn, <Rm|#imm>] 以有符号半字加载寄存器 — STR Rt, [Rn, <Rm|#imm>] 将寄存器作为字来存储 — STRB Rt, [Rn, <Rm|#imm>] 将寄存器作为字节来存储 — STRH Rt, [Rn, <Rm|#imm>] 将寄存器作为半字来存储 — -
2)批量加载/存储数据指令
助记符 操作数 操作含义 影响标志位 测试 LDM Rn{!}, reglist 加载多个寄存器,访问之后会递增地址 — STM Rn!, reglist 批量存储寄存器, Rn递减 — -
3)进栈/出栈指令
助记符 操作数 操作含义 影响标志位 测试 POP reglist 寄存器出栈 — PUSH reglist 寄存器压栈 —
4.3 其它指令
- 1)
助记符 操作数 操作含义 影响标志位 测试 BKPT #imm 断点 — NOP — 空操作 — REV Rd, Rm 按字节反转 — REV16 Rd, Rm 按半字反转 — REVSH Rd, Rm 按有符号半字反转 — WFE — 等待事件 — WFI — 等待中断 —
5 附录2:OpenOCD 命令
- 1)probe 以及 info 命令:获取芯片的大致信息
> halt
[air001.cpu] halted due to debug-request, current mode: Thread
xPSR: 0x21000000 pc: 0x08000418 msp: 0x20000ff0
> flash probe 0
device id = 0x60001000
AIR001 flash size is 32kb, base address is 0x08000000
flash 'air001' found at 0x08000000
> flash info 0
#0 : air001 at 0x08000000, size 0x00008000, buswidth 0, chipwidth 0
# 0: 0x00000000 (0x1000 4kB) protected
# 1: 0x00001000 (0x1000 4kB) protected
# 2: 0x00002000 (0x1000 4kB) protected
# 3: 0x00003000 (0x1000 4kB) protected
# 4: 0x00004000 (0x1000 4kB) protected
# 5: 0x00005000 (0x1000 4kB) protected
# 6: 0x00006000 (0x1000 4kB) protected
# 7: 0x00007000 (0x1000 4kB) protected
AIR001 (Cat.1 - Low/Medium Density) - Rev: A
- 2)除了可以通过 flash write_image erase {"/path/to/image.hex" | "/path/to/image.bin" address} 命令可以烧录镜像外,我们还可以使用以下命令来测试自已的驱动是否正常:
> flash erase_check 0
successfully checked erase state
# 0: 0x00000000 (0x1000 4kB) not erased
> flash fillw 0x08003000 0xcafebaba 32
wrote 128 bytes to 0x08003000 in 0.602882s (0.207 KiB/s)
> flash mdw 0x08003000 48
0x08003000: cafebaba cafebaba cafebaba cafebaba cafebaba cafebaba cafebaba cafebaba
0x08003020: cafebaba cafebaba cafebaba cafebaba cafebaba cafebaba cafebaba cafebaba
0x08003040: cafebaba cafebaba cafebaba cafebaba cafebaba cafebaba cafebaba cafebaba
0x08003060: cafebaba cafebaba cafebaba cafebaba cafebaba cafebaba cafebaba cafebaba
0x08003080: ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
0x080030a0: ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
> flash erase_sector 0 3 4
erased sectors 3 through 4 on flash bank 0 in 0.199937s
> flash mdw 0x08003000 48
0x08003000: ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
0x08003020: ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
0x08003040: ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
0x08003060: ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
0x08003080: ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
0x080030a0: ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
- 实际测试过程中,fillw 偶尔会出现失败的情况,未找到原因。
6 附录3:项目源码
- dev-air 分支:https://gitee.com/luyaoCode/openocd.git