ARM 调试工具 UINIO-DAP-Link 应用指南

ARM Mbed OS 是一款开源免费的物联网操作系统,包含有基于 ARM Cortex-M 微控制器开发智能连接产品所需的全部基础架构。其推出的 DAP-Link 同样是一个开源软件项目,它支持编程和调试运行在 ARM Cortex 微控制器上面的代码,主要运行于拥有 SWD 或者 JTAG 接口的微控制器当中,并且通过 USB 接口在计算机与 ARM Cortex 微控制器之间创建一个用于调试仿真的桥梁,为开发人员提供了下载调试串口通信拖拽烧录等实用功能。

DAPLink 主要由 Mbed 硬件开发工具包 以及 DAPLink 固件 两个开源项目构成,而 UINIO-DAP-Link 则是由博主设计的一款开源 DAPLink 仿真器实现,相比于官方原版的硬件电路设计,在引出有 SWD 调试接口(由 ARM 制订)的同时,还引出了 JTAG 接口(IEEE 1149 国际标准)以及5V3.3V 电源,并且附带有 SWDJTAG 的转接板 PCB 设计,而固件部分则是基于 ARM 官方的原版固件移植而来,全部的原理图与固件程序都开源在博主的 GitHub

DAPLink 总体上可以为开发人员提供如下一系列功能:

  • MSC - 通过拖拽编程 Flash 存储器;
  • CDC - 用于日志、跟踪和终端仿真的虚拟通信端口;
  • CMSIS-DAPv2 WinUSB - 兼容 CMSIS 的调试通道;
  • CMSIS-DAPv1 HID - 兼容 CMSIS 的调试通道;
  • WebUSB CMSIS-DAP HID - 兼容 CMSIS 的调试通道;

目前 DAPLink 已经取代 mbed CMSIS-DAP 接口固件项目。

如下品牌的 ARM 硬件接口电路(HIC,Hardware Interface Circuits)与 DAPLink 的固件完全兼容:

DAPLink 主要提供了拖放编程串口通信调试三种接口:

  • 拖拽烧录:通过将 .bin 或者 .hex 格式的文件复制或者保存到 DAPLink 驱动器,从而实现对目标微控制器的编程。完成以后,驱动器将会重新进行挂载。如果下载出现问题,则驱动器上将会自动生成一个包含有故障信息的 FAIL.TXT 文件;
  • 串口通信:可以直接连接至目标 MCU 微控制器进行双向通信,支持 9600144001920028800384005600057600115200 等常见波特率;
  • 下载调试:可以使用任意支持 CMSIS-DAP 协议的 IDE 集成开发环境进行调试,例如 pyOCDuVisionIAR

更新设备固件时,只需要连接上 USB 并且按住重置键,让设备进入 Bootloader 引导模式,从而开始下载固件。如果下载成功,那么设备将会离开引导模式,并且开始运行新的固件。如果下载失败,则设备将会自动生成显示有错误原因的 FAIL.TXT 文件。

DAPLink 的固件源代码使用 project-generator 项目下的 progen 命令进行构建,除此之外,编译过程当中还需要使用到如下一系列组件:

  • 安装 Python 3 并将其加入环境变量;
  • 安装 Git 并将其加入环境变量;
  • 安装相关的编译器,可以选择标识为 gcc_armGNU ARM Embedded Toolchain 、标识为 armclangARM Compiler 6,以及标识为 armccKeil MDK 或者 ARM Compiler 5 编译器,它们当中的大部分都只支持 Linux 和 Windows 操作系统环境;
  • 安装 GNU Make 构建工具,可以组合使用 CMake 以及 ninja
  • 通过 pip Install virtualenv 命令全局安装 Python 虚拟环境 virtualenv

准备虚拟环境

获取源代码,并且创建 Python 虚拟环境:

1
2
3
4
$ git clone https://github.com/mbedmicro/DAPLink
$ cd DAPLink
$ pip install virtualenv
$ virtualenv venv

激活 Python 虚拟环境,并且更新关联依赖:

1
2
3
$ venv/Scripts/activate   (For Linux)
$ venv/Scripts/activate.bat (For Windows)
(venv) $ pip install -r requirements.txt

构建与编译

接下来开始构建 DAPLink 工程,可以使用 project-generator 中的 progen 命令或者 tools/progen_compile.py 脚本。

通过 progen_compile.py 脚本编译

1
(venv) $ python tools/progen_compile.py [-t <tool>] [--clean] [-v] [--parallel] [<project> [<project> ...]]
  • -t <tool>:选择要构建的工具链,默认为 make_gcc_arm,其它的可选项分别为 make_gcc_armmake_armclangmake_armcccmake_gcc_armcmake_armclangcmake_armcc
  • --clean: 清除现有的编译结果,并且强制重新编译所有文件;
  • -v: 列出所有命令以及参数,将会使得编译过程更加冗长;
  • --parallel: 启用并行编译;
  • <project>: 待编译的目标项目(例如 stm32f103xb_bl, lpc11u35_if),如果不指定将会编译所有工程;

通过 progen 命令和 Make 编译

如下的命令组合了生成与编译操作:

1
(venv) $ progen generate -t make_gcc_arm -p <project> -b

当然也可以将生成与编译操作分开执行:

1
2
(venv) $ progen generate -t make_gcc_arm -p <project>
(venv) $ make -C projectfiles/make_gcc_arm/<project> [<target>] [VERBOSE=1]
  • <project>: 等待编译的目标工程(例如 stm32f103xb_bllpc11u35_if);
  • <target>: 编译目标,可以选择为 allcleanhelp
  • VERBOSE=1: 显示额外的编译信息;

通过 progen 命令和 CMake 编译

下面的命令同样组合了生成与编译两个步骤:

1
(venv) $ progen generate -t cmake_gcc_arm -o generator=<generator> -p <project> -b
  • <generator>: 指定使用如下的 CMake 生成器:
    • make (Unix Makefiles);
    • mingw-make (MinGW Makefiles);
    • msys-make (MSYS Makefiles, untested);
    • ninja (Ninja);
    • nmake (NMake Makefiles);
  • <project>: 等待编译的目标项目(例如 stm32f103xb_bl, lpc11u35_if);

通过 progen 命令和 MDK 编译

通过如下命令在 projectfiles/uvision 目录下生成 MDK 工程文件:

1
$ progen generate -t uvision

也可以使用如下的命令,仅仅生成一个指定的工程:

1
progen generate -f projects.yaml -p stm32f103xb_stm32f746zg_if -t uvision
  • -f 指定输入工程文件;
  • -p 指定工程名称;
  • -t 指定 IDE 名称;

安装 arm-eabi 工具链

STM32CubeIDE 当中运用 DAPLink 调试与下载程序,需要使用到 ARM 嵌入式应用程序二进制接口,也就是 ARM EABI。包括了 Windows Toolchain for ARMOpenOCD 两个工具库:

  1. 首先,需要调用 openocd.exe 开启一个连接到 DAP-Link 与目标微控制器的 GDB 调试服务;
  2. 然后,STM32CubeIDE 就可以通过 arm-none-eabi-gdb.exe 访问这个 GDB 服务;

下载并且解压两个工具库之后,分别将它们的 bin 目录添加到操作系统的环境变量,再分别执行如下两个命令,测试其是否已经正确的进行安装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
λ openocd

Open On-Chip Debugger 0.11.0 (2021-11-18) [https://github.com/sysprogs/openocd]
Licensed under GNU GPL v2
libusb1 09e75e98b4d9ea7909e8837b7a3f00dda4589dc3
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
embedded:startup.tcl:26: Error: Can't find openocd.cfg
in procedure 'script'
at file "embedded:startup.tcl", line 26
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Error: Debug Adapter has to be specified, see "adapter driver" command
embedded:startup.tcl:26: Error:
in procedure 'script'
at file "embedded:startup.tcl", line 26
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
λ arm-none-eabi-gdb

GNU gdb (GDB) 10.2.90.20210621-git
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "--host=i686-w64-mingw32 --target=arm-none-eabi".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word".
(gdb)

以命令行方式进行调用

配置 OpenOCD

OpenOCD 命令的调用格式如下面的代码所示,其中的 -f 参数表示当前使用的是配置文件:

1
openocd.exe -f interface\调试器接口配置文件 -f target\目标微控制器配置文件

下面例出了 OpenOCD\share\openocd\scripts\interface 目录下的配置文件,由于这里使用的是 DAP-Link 作为调试器,所以通常都是以 cmsis-dap.cfg 作为参数:

1
2
3
4
5
6
7
8
9
10
11
12
λ ls OpenOCD\share\openocd\scripts\interface

altera-usb-blaster.cfg dummy.cfg jtag_vpi.cfg raspberrypi2-native.cfg ti-icdi.cfg
altera-usb-blaster2.cfg estick.cfg kitprog.cfg raspberrypi-native.cfg ti-icdi-auto.cfg
arm-jtag-ew.cfg flashlink.cfg nds32-aice.cfg rlink.cfg ulink.cfg
at91rm9200.cfg ft232r/ nulink.cfg rshim.cfg usb-jtag.cfg
buspirate.cfg ft232r.cfg opendous.cfg stlink.cfg usbprog.cfg
calao-usb-a9260.cfg ftdi/ openjtag.cfg stlink-dap.cfg vsllink.cfg
cc3200lp.cfg imx-native.cfg osbdm.cfg stlink-v1.cfg xds110.cfg
chameleon.cfg jlink.cfg parport.cfg stlink-v2.cfg
cmsis-dap.cfg jtag_dpi.cfg parport_dlc5.cfg stlink-v2-1.cfg
dln-2-gpiod.cfg jtag_hat_rpi2.cfg picoprobe.cfg sysfsgpio-raspberrypi.cfg

下面例出的是 OpenOCD\share\openocd\scripts\target 目录下的全部配置文件,这里需要根据目标微控制器的型号,酌情选择相应的配置文件作为参数:

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
82
λ ls OpenOCD\share\openocd\scripts\target

adsp-sc58x.cfg bluenrg-x.cfg lpc13xx.cfg samsung_s3c2440.cfg
aduc702x.cfg c100.cfg lpc1549.cfg samsung_s3c2450.cfg
aducm3027.cfg c100config.tcl lpc15xx.cfg samsung_s3c4510.cfg
aducm3029.cfg c100helper.tcl lpc17xx.cfg samsung_s3c6410.cfg
aducm302x.tcl c100regs.tcl lpc1850.cfg sharp_lh79532.cfg
aducm360.cfg cc2538.cfg lpc1xxx.cfg sim3x.cfg
aducm4050.cfg cs351x.cfg lpc2103.cfg smp8634.cfg
aducm4x50.tcl davinci.cfg lpc2124.cfg snps_em_sk_fpga.cfg
allwinner_v3s.cfg dragonite.cfg lpc2129.cfg snps_hsdk.cfg
alphascale_asm9260t.cfg dsp56321.cfg lpc2148.cfg spear3xx.cfg
altera_fpgasoc.cfg dsp568013.cfg lpc2294.cfg stellaris.cfg
altera_fpgasoc_arria10.cfg dsp568037.cfg lpc2378.cfg stm32f0x.cfg
am335x.cfg efm32.cfg lpc2460.cfg stm32f1x.cfg
am437x.cfg em357.cfg lpc2478.cfg stm32f2x.cfg
amdm37x.cfg em358.cfg lpc2900.cfg stm32f3x.cfg
ampere_emag.cfg eos_s3.cfg lpc2xxx.cfg stm32f4x.cfg
ar71xx.cfg epc9301.cfg lpc3131.cfg stm32f7x.cfg
arm_corelink_sse200.cfg esi32xx.cfg lpc3250.cfg stm32g0x.cfg
armada370.cfg exynos5250.cfg lpc40xx.cfg stm32g0x_alt.cfg
at32ap7000.cfg faux.cfg lpc4350.cfg stm32g4x.cfg
at91r40008.cfg feroceon.cfg lpc4357.cfg stm32g4x_alt.cfg
at91rm9200.cfg fm3.cfg lpc4370.cfg stm32h7x.cfg
at91sam3ax_4x.cfg fm4.cfg lpc546xx.cfg stm32h7x_dual_bank.cfg
at91sam3ax_8x.cfg fm4_mb9bf.cfg lpc55sxx.cfg stm32h7x_dual_core.cfg
at91sam3ax_xx.cfg fm4_s6e2cc.cfg lpc84x.cfg stm32l0.cfg
at91sam3nXX.cfg gd32e23x.cfg lpc8nxx.cfg stm32l0_dual_bank.cfg
at91sam3sXX.cfg gd32vf103.cfg lpc8xx.cfg stm32l1.cfg
at91sam3u1c.cfg gp326xxxa.cfg ls1012a.cfg stm32l1x_dual_bank.cfg
at91sam3u1e.cfg hi3798.cfg marvell/ stm32l4x.cfg
at91sam3u2c.cfg hi6220.cfg max32620.cfg stm32l5x.cfg
at91sam3u2e.cfg hilscher_netx10.cfg max32625.cfg stm32mp15x.cfg
at91sam3u4c.cfg hilscher_netx50.cfg max3263x.cfg stm32u5x.cfg
at91sam3u4e.cfg hilscher_netx500.cfg mc13224v.cfg stm32w108xx.cfg
at91sam3uxx.cfg icepick.cfg mdr32f9q2i.cfg stm32wbx.cfg
at91sam3XXX.cfg imx.cfg nds32v2.cfg stm32wlx.cfg
at91sam4c32x.cfg imx21.cfg nds32v3.cfg stm32xl.cfg
at91sam4cXXX.cfg imx25.cfg nds32v3m.cfg stm8l.cfg
at91sam4lXX.cfg imx27.cfg nds32v5.cfg stm8l152.cfg
at91sam4sd32x.cfg imx28.cfg nhs31xx.cfg stm8s.cfg
at91sam4sXX.cfg imx31.cfg npcx.cfg stm8s003.cfg
at91sam4XXX.cfg imx35.cfg nrf51.cfg stm8s103.cfg
at91sam7a2.cfg imx51.cfg nrf52.cfg stm8s105.cfg
at91sam7se512.cfg imx53.cfg nuc910.cfg str710.cfg
at91sam7sx.cfg imx6.cfg numicro.cfg str730.cfg
at91sam7x256.cfg imx6_dual.cfg omap2420.cfg str750.cfg
at91sam7x512.cfg imx6_quad.cfg omap3530.cfg str912.cfg
at91sam9.cfg imx6sx.cfg omap4430.cfg swj-dp.tcl
at91sam9260.cfg imx6ul.cfg omap4460.cfg swj-dp-legacy.tcl
at91sam9260_ext_RAM_ext_flash.cfg imx7.cfg omap5912.cfg swm050.cfg
at91sam9261.cfg imx7ulp.cfg omapl138.cfg test_reset_syntax_error.cfg
at91sam9263.cfg imx8m.cfg or1k.cfg test_syntax_error.cfg
at91sam9g10.cfg imx8qm.cfg pic32mm.cfg ti_calypso.cfg
at91sam9g20.cfg imxrt.cfg pic32mx.cfg ti_cc13x0.cfg
at91sam9g45.cfg infineon/ psoc4.cfg ti_cc13x2.cfg
at91sam9rl.cfg is5114.cfg psoc5lp.cfg ti_cc26x0.cfg
at91sama5d2.cfg ixp42x.cfg psoc6.cfg ti_cc26x2.cfg
at91samdXX.cfg k1921vk01t.cfg pxa255.cfg ti_cc3220sf.cfg
at91samg5x.cfg k40.cfg pxa270.cfg ti_cc32xx.cfg
atheros_ar2313.cfg k60.cfg pxa3xx.cfg ti_dm355.cfg
atheros_ar2315.cfg ke02.cfg qualcomm_qca4531.cfg ti_dm365.cfg
atheros_ar9331.cfg ke04.cfg quark_d20xx.cfg ti_dm6446.cfg
atheros_ar9344.cfg ke06.cfg quark_x10xx.cfg ti_k3.cfg
atmega128.cfg ke0x.cfg raspberry.cfg ti_msp432.cfg
atmega128rfa1.cfg ke1xf.cfg raspberrypi2.cfg ti_rm4x.cfg
atsame5x.cfg ke1xz.cfg raspberrypi3.cfg ti_tms570.cfg
atsaml1x.cfg kex.cfg raspberrypi3_single.cfg ti_tms570ls1224.cfg
atsamv.cfg kinetis_128k.cfg readme.txt ti_tms570ls20xxx.cfg
avr32.cfg kinetis_1m.cfg renesas_r7s72100.cfg ti_tms570ls3137.cfg
bcm2711.cfg kinetis_256k.cfg renesas_rcar_gen2.cfg ti-ar7.cfg
bcm281xx.cfg kinetis_32k.cfg renesas_rcar_gen3.cfg ti-cjtag.cfg
bcm2835.cfg kinetis_512k.cfg renesas_rcar_reset_common.cfg tmpa900.cfg
bcm2836.cfg kinetis_64k.cfg renesas_rz_g2.cfg tmpa910.cfg
bcm2837.cfg kinetis_generic.cfg renesas_s7g2.cfg tnetc4401.cfg
bcm4706.cfg kl25.cfg rk3308.cfg u8500.cfg
bcm4718.cfg kl46.cfg rk3399.cfg vybrid_vf6xx.cfg
bcm47xx.cfg klx.cfg rm57x.cfg xilinx_zynqmp.cfg
bcm5352e.cfg ks869x.cfg rp2040.cfg xmc1xxx.cfg
bcm6348.cfg kx.cfg rp2040-core0.cfg xmc4xxx.cfg
bluefield.cfg lpc11xx.cfg rs14100.cfg xmos_xs1-xau8a-10_arm.cfg
bluenrg-lp.cfg lpc12xx.cfg samsung_s3c2410.cfg zynq_7000.cfg

这里以 STM32L0STM32F1STM32F4 系列微控制器为例,需要分别使用到如下几条 OpenOCD 命令:

1
2
3
openocd.exe -f interface\cmsis-dap.cfg -f target\stm32l0.cfg
openocd.exe -f interface\cmsis-dap.cfg -f target\stm32f1x.cfg
openocd.exe -f interface\cmsis-dap.cfg -f target\stm32f4x.cfg

接下来,具体以 STM32L051K8U6 微控制器作为示例,正确执行 OpenOCD 命令之后的效果如下所示,命令行会提示当前提供的 GDB 服务端口为 3333

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
λ openocd.exe -f interface\cmsis-dap.cfg -f target\stm32l0.cfg

Open On-Chip Debugger 0.11.0 (2021-11-18) [https://github.com/sysprogs/openocd]
Licensed under GNU GPL v2
libusb1 09e75e98b4d9ea7909e8837b7a3f00dda4589dc3
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
Warn : could not read product string for device 0x058f:0x9540: Entity not found
Info : CMSIS-DAP: SWD supported
Info : CMSIS-DAP: JTAG supported
Info : CMSIS-DAP: FW Version = 1.0
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : SWCLK/TCK = 1 SWDIO/TMS = 1 TDI = 1 TDO = 1 nTRST = 0 nRESET = 0
Info : CMSIS-DAP: Interface ready
Info : clock speed 300 kHz
Info : SWD DPIDR 0x0bc11477
Info : stm32l0.cpu: Cortex-M0+ r0p1 processor detected
Info : stm32l0.cpu: target has 4 breakpoints, 2 watchpoints
Info : starting gdb server for stm32l0.cpu on 3333
Info : Listening on port 3333 for gdb connections

简便起见,也可以将上述命令编写为 Windows 下一个名为 OpenOCD stm32l0.bat 的批处理文件,方便调试的时候快速进行调用:

1
2
3
echo Openocd Runing ... ... ...
openocd.exe -f interface\cmsis-dap.cfg -f target\stm32l0.cfg
pause

配置 STM32CubeIDE

启动 OpenOCD 服务之后,接着在 STM32CubeIDE 当中新建一个名称为 Test-STM32L051K8U6 的工程:

首先,选中 STM32CubeIDE 左侧的工程名称,点击鼠标右键菜单上的 【Build Project】, 编译生成出一个 Test-STM32L051K8U6.elf 二进制文件。点击工具栏上【Debug】图标右侧的箭头,点击弹出菜单上的 Debug Configrations... 打开调试配置窗口,鼠标双击窗口上的 GDB Hardware Debugging 新建一条名为 Test-STM32L051K8U6 Debug 的 GDB 硬件调试配置,并在自动打开的【Main】选项卡界面进行如下一系列配置:

切换至【Debugger】选项卡,将 GDB 命令修改为 D:\Software\Tech\ARMEabi\bin\arm-none-eabi-gdb.exe,并且将连接地址设置为 localhost:3333

经过上述步骤的配置,鼠标点击【Debug】按钮,就已经可以让 STM32CubeIDE 通过 UINIO-DAP-Link 实现固件下载,如果需要进入单步调试模式,则可以在【Startup】选项卡当中,手动在 main 函数位置设置一个断点:

以 External Tools 方式使用

首先,需要在 STM32CubeIDEExternal Tool 当中添加 OpenOCD,依次点击顶部菜单栏当中的 【RUN -> External Tools -> External Tools Configrations...】,新建一个名称为 Test-STM32L051K8U6 Run 的配置,并且将 OpenOCD 命令所在的位置指定为 D:\Software\Tech\ARMEabi\OpenOCD\bin\openocd.exe,相应的参数设置为 -f interface\cmsis-dap.cfg -f target\stm32l0.cfg

打开 STM32CubeIDEDebugger Configration 窗口,鼠标双击左侧的【Launch Group】新建一个名为 UINIO-DAP-Link运行组,然后点击右侧的【Add...】按钮:

此时添加的执行配置策略为,首先执行 OpenOCD 命令开启 GDB 服务,然后再通过 arm-none-eabi-gdb 执行调试任务:

最后,配置完成之后的 Launch Group 顺序如下图所示:

接下来,就可以在 STM32CubeIDE 顶部工具栏上【Run】和【Debug】按钮的下面,发现上面的配置的 UINIO-DAP-Link 运行组,点击之后就可以愉快的使用 UINIO-DAP-Link 执行 STM32 微控制器的程序调试工作了。