前言
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)
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{S} | Rd, Rm | 传送 R~d~ 数据到 R~m~ | N,Z | movs r0, #0x1 movs R1,R0 mov R1,R0 |
MVN{S} | Rd, Rm | R~m~ 位取反后,传送到 R~d~ | N,Z | mvns r7, r6 |
MRS(略) | ||||
MSR(略) |
- 2)算术运算指令
助记符 | 操作数 | 操作含义 | 影响标志位 | 测试 | |
---|---|---|---|---|---|
ADD{S} | {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, r3 |
ADCS | {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, r3 |
SBC{S} | {Rd,} Rn, Rm | 带符号减 | N,Z,C,V | ||
RSB{S} | {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, r34)移位指令
助记符 操作数 操作含义 影响标志位 测试 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, r35)比较与测试指令
助记符 操作数 操作含义 影响标志位 测试 CMP Rn, <Rm\ #imm> 比较 N,Z,C,V cmp r2, #1
cmp r2, r3CMN Rn, Rm 比较负值 N,Z,C,V TST{S} Rn, Rm 逻辑与测试 N,Z tst r2, r3
tsts r2, r3- 6)位域操作指令(无)
7)跳转指令
助记符 操作数 操作含义 影响标志位 测试 B{cc} 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 其它指令
助记符 | 操作数 | 操作含义 | 影响标志位 | 测试 |
---|---|---|---|---|
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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。