十分钟快速自制CMSIS_DAP仿真器,将ST-LINK-V2变身DAP仿真器!


目录

  • 十分钟快速自制CMSIS_DAP仿真器,将ST-LINK-V2变身DAP仿真器!
    • (一)简介
    • (二)调试器的原理
    • (三)工程配置
    • (四)移植DAP源码
    • (五)测试CMSIS_DAP仿真器
    • (六)补充内容:软启动的实现

本章博客涉及代码,关注文章尾部公众号,回复关键字cmsis-dap获取下载链接!


(一)简介

说到单片机仿真器(下载器)首先想到的就是J-LINK和ST-LINK,很多人可能还是第一次听说DAP仿真器,那么就先介绍一下。

CMSIS DAP是ARM官方推出的开源仿真器,支持所有的Cortex-A/R/M器件,支持JTAG/SWD接口。有以下特点:

  1. 完全开源,没有版权限制,所以相应的价格会很便宜
  2. 不需要安装驱动,即插即用
  3. 在新版本的DAP里集成了串口,除了下载调试外还能充当USB转串口模块,一机两用
  4. 性能方面已经可以满足一般用户的需求

市面上基本所有的离线下载器基本都是基于CMSIS_DAP方案来的,例如正点原子的离线下载器、无线下载器等,还有就是国产单片机厂家做的调试器,例如GD32出的GD-LINK,都是基于CMSIS DAP方案改的。

而ST-LINK是ST官方出的,目前有V1、V2、V3版本,并且闭源,正版的很贵!你买到的很便宜,可能也就十几三十几块钱得样子,这是因为这都是盗版的!网上早就有人把ST-LINK-V2的固件给破解出来的,而且原理图也有,所以市面上的便宜货都是根据官方的原理图做出来的板子,然后下载进固件就完成了一个ST-LINK调试器!但是他们是没有ST-LINK的固件源代码的!

什么是在线调试下载?

在线调试器就是用keil或者iar等软件对目标MCU进行调试和下载程序,适用于开发阶段。像是ST-LINK、J-LINK就是在线调试器。

什么是离线下载?

当项目开发中已经接近尾声或者已经成熟后,那么每生产一块板子就需要刷程序,但是刷程序就需要连接到PC机上通过软件刷,这样的话就非常的麻烦,那么有没有一种方式是做一个小板子,把下载程序的功能集成到这个小板子中,这样的话刷程序就是用这个小板子给目标MCU刷程序,就是手持式的。

有!就是离线下载器!

离线下载器的功能说简单点就是Keil下载程序的功能!

关于本篇

教程持续更新,最终的目的是做一个在线调试+离线下载完整功能的仿真器。

本篇教程就先实现用keil能够在线调试下载程序和调试的功能,这是实现完整功能最简单的一步。


(二)调试器的原理

在教程开始之前,我觉得有必要说一下调试器的原理以及他是怎么工作的,怎么把程序下载到目标MCU的,这里以KEIL+ST-LINK为例进行说明。

我们使用keil下载程序的时候,必须要选定一个下载算法,如下所示:

当你使用STM32的时候必须要安装pack包,而下载算法就在pack包内,安装好后就在keil软件的安装目录下了,具体在这里:

Keil会根据你当前工程使用的芯片,自动去识别应该用哪一个下载算法,例如我目前这个工程是用的STM32F103C8这个芯片,FLASH容量是64K的,属于这个系列的小容量,那么KEIL就自动给我识别了STM32F103x_128.FLM这个下载算法。

那么这个下载算法是什么东西?

进入这个目录后,他有一个工程,而这个工程就是下载算法的模板工程,我们打开看一下:

打开工程后很简单的,就只有两个文件,函数也很简单都是对FLASH的操作,编译一下看看:

AXF(ARM Executable File)是ARM芯片使用的文件格式,它除了包含bin代码外,还包括了输出给调试器的调试信息

看到没!编译完了之后生成了STM32F103x_16.axf文件,然后只是做了一个文件复制并且重命名成了STM32F10x_16.FLM文件!

现在对下载算法就明确了,下载算法其实就是对目标MCU的FLASH的一系列操作函数!

那么KEIL给目标MCU下载程序的时候,其实就是解析出编译好的.axf文件,然后通过USB连接线经过ST-LINK先将下载算法加载进目标MCU的内存中并运行,由于.axf文件中包含的信息很多,其中就有当程序加载进内存后函数在内存中的地址,这个地址也可以通过.map文件查看(.map文件也是keil在编译完工程后生成的),知道函数在内存中的地址,就可以在外部通过特定的进行调用,所以STLINK就是接收来自KEIL的固件程序,然后操控目标单片机内存中的FLASH的操作函数,在通过SWD协议将固件下载进目标MCU的FLASH中,就这样实现了程序下载,后面我们会做离线下载器那么也就明确了,可以将接收KEIL的下发固件这一个步骤变成本地SD卡存储固件,这样不就实现了离线~

现在keil整个下载程序的流程清楚了后,我们的CMSIS_DAP就可以分成两步骤进行:

  • 第一步:实现KEIL通过USB和DAP的通信
  • 第二步:DAP通过SWD协议将收到固件下载进目标MCU

对于第一步,我们使用STM32CubeMX软件生成工程,然后对源码进行一个简单的修改就可以完成。如果你对USB没有一个充分的了解,本章教程可以先不用管为什么这么修改源代码,跟着步骤来即可,我的另一篇博客有对USB相关知识的扫盲,可以帮助你快速了解:https://blog.csdn.net/qq153471503/article/details/116053851

对于第二步,我们需要移植自ARM官方的CMSIS_DAP源码,源代码在Keil软件的安装目录下,ARM官方的代码是基于LPC单片机的,但是不妨碍我们移植使用:

我们需要的就是上图中这三个文件夹中的源文件以及USBD_User_HID_0.c文件即可,这个文件在这里:


(三)工程配置

紧接上文,现在开始实现第一步,由于我目前手头没有现成的硬件,然后我想起ST-LINK其实也是个STM32F103C8单片机,那么我用STLINK的板子不就行了,而且在网上还能找到原理图,省去了自己做板子验证的步骤,等软件调试完毕后,在做板子把它做得小巧精致些。


它的原理图是这样的:

从原理图上可知,使用的引脚分布如下:

  • JTAG_nTRST(PB1)
  • JTAG_nRESET(PB0)
  • JTAG_TDI(PA7)
  • JTAG_TMS(PB14,这个引脚也是SWD模式下的SWDIO引脚)
  • JTAG_TCK(PB13/PA5,这个引脚也是SWD模式下的SWCLK引脚)
  • JTAG_TDO(PA6/PA10)
  • LED灯(PA9,低电平为红灯,高电平为绿灯)

对于JTAG_TCK,这是时钟信号引脚,官方用了PB13和PA5两个引脚,我猜测是因为为了提高抗干扰能力,JTAG和SWD两种模式下,使用不同的引脚当做时钟信号。JTAG_TMS引脚同理。

对于JTAG_TDO,当JTAG模式时,这是JTAG的数据输入引脚,当使用SWD模式时,这个引脚可以作为调试输出的作用,所以也是分成了两个引脚来用。

在本教程中,我们就各只用一个IO就可以,不搞那么麻烦。

下面开始贴一下我的配置,时钟配置到72M,并开启USB配置成48M,如下:

下载程序的调试接口配置为SWD模式:


USB的配置如下:



IO引脚的配置如下:

这就是全部的配置内容,然后生成代码就可以了!


(四)移植DAP源码

将DAP加入我们的工程,如下所示:

对于DAP源码的移植,我们主要就是修改DAP_config.hUSBD_User_HID_0.c俩文件,其中DAP_config.h是修改的GPIO以及DAP的一些默认配置参数,USBD_User_HID_0.c就是USB与PC的函数接口了,由于原CMSIS_DAP工程是基于LCP单片机并且跑了系统的,而我没有跑系统,所以就需要将USBD_User_HID_0.c文件中使用系统了的API换成轮训方式即可,下面贴一下我的配置:

DAP_config.h文件:

/** Copyright (c) 2013-2017 ARM Limited. All rights reserved.** SPDX-License-Identifier: Apache-2.0** Licensed under the Apache License, Version 2.0 (the License); you may* not use this file except in compliance with the License.* You may obtain a copy of the License at** www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an AS IS BASIS, WITHOUT* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.** ----------------------------------------------------------------------** $Date:        1. December 2017* $Revision:    V2.0.0** Project:      CMSIS-DAP Configuration* Title:        DAP_config.h CMSIS-DAP Configuration File (Template)**---------------------------------------------------------------------------*/#ifndef __DAP_CONFIG_H__
#define __DAP_CONFIG_H__//**************************************************************************************************
/**
\defgroup DAP_Config_Debug_gr CMSIS-DAP Debug Unit Information
\ingroup DAP_ConfigIO_gr
@{
Provides definitions about the hardware and configuration of the Debug Unit.This information includes:- Definition of Cortex-M processor parameters used in CMSIS-DAP Debug Unit.- Debug Unit Identification strings (Vendor, Product, Serial Number).- Debug Unit communication packet size.- Debug Access Port supported modes and settings (JTAG/SWD and SWO).- Optional information about a connected Target Device (for Evaluation Boards).
*/#include "main.h"/// Processor Clock of the Cortex-M MCU used in the Debug Unit.
/// This value is used to calculate the SWD/JTAG clock speed.
#define CPU_CLOCK               72000000U       ///< Specifies the CPU Clock in Hz./// Number of processor cycles for I/O Port write operations.
/// This value is used to calculate the SWD/JTAG clock speed that is generated with I/O
/// Port write operations in the Debug Unit by a Cortex-M MCU. Most Cortex-M processors
/// require 2 processor cycles for a I/O Port Write operation.  If the Debug Unit uses
/// a Cortex-M0+ processor with high-speed peripheral I/O only 1 processor cycle might be
/// required.
#define IO_PORT_WRITE_CYCLES    2U              ///< I/O Cycles: 2=default, 1=Cortex-M0+ fast I/0./// Indicate that Serial Wire Debug (SWD) communication mode is available at the Debug Access Port.
/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
#define DAP_SWD                 1               ///< SWD Mode:  1 = available, 0 = not available./// Indicate that JTAG communication mode is available at the Debug Port.
/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
#define DAP_JTAG                1               ///< JTAG Mode: 1 = available, 0 = not available./// Configure maximum number of JTAG devices on the scan chain connected to the Debug Access Port.
/// This setting impacts the RAM requirements of the Debug Unit. Valid range is 1 .. 255.
#define DAP_JTAG_DEV_CNT        8U              ///< Maximum number of JTAG devices on scan chain./// Default communication mode on the Debug Access Port.
/// Used for the command \ref DAP_Connect when Port Default mode is selected.
#define DAP_DEFAULT_PORT        1U              ///< Default JTAG/SWJ Port Mode: 1 = SWD, 2 = JTAG./// Default communication speed on the Debug Access Port for SWD and JTAG mode.
/// Used to initialize the default SWD/JTAG clock frequency.
/// The command \ref DAP_SWJ_Clock can be used to overwrite this default setting.
#define DAP_DEFAULT_SWJ_CLOCK   10000000U       ///< Default SWD/JTAG clock frequency in Hz./// Maximum Package Size for Command and Response data.
/// This configuration settings is used to optimize the communication performance with the
/// debugger and depends on the USB peripheral. Typical vales are 64 for Full-speed USB HID or WinUSB,
/// 1024 for High-speed USB HID and 512 for High-speed USB WinUSB.
#define DAP_PACKET_SIZE         64U             ///< Specifies Packet Size in bytes./// Maximum Package Buffers for Command and Response data.
/// This configuration settings is used to optimize the communication performance with the
/// debugger and depends on the USB peripheral. For devices with limited RAM or USB buffer the
/// setting can be reduced (valid range is 1 .. 255).
#define DAP_PACKET_COUNT        8U              ///< Specifies number of packets buffered./// Indicate that UART Serial Wire Output (SWO) trace is available.
/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
#define SWO_UART                0               ///< SWO UART:  1 = available, 0 = not available./// Maximum SWO UART Baudrate.
#define SWO_UART_MAX_BAUDRATE   10000000U       ///< SWO UART Maximum Baudrate in Hz./// Indicate that Manchester Serial Wire Output (SWO) trace is available.
/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
#define SWO_MANCHESTER          0               ///< SWO Manchester:  1 = available, 0 = not available./// SWO Trace Buffer Size.
#define SWO_BUFFER_SIZE         4096U           ///< SWO Trace Buffer Size in bytes (must be 2^n)./// SWO Streaming Trace.
#define SWO_STREAM              0               ///< SWO Streaming Trace: 1 = available, 0 = not available./// Clock frequency of the Test Domain Timer. Timer value is returned with \ref TIMESTAMP_GET.
#define TIMESTAMP_CLOCK         72000000U       ///< Timestamp clock in Hz (0 = timestamps not supported)./// Debug Unit is connected to fixed Target Device.
/// The Debug Unit may be part of an evaluation board and always connected to a fixed
/// known device.  In this case a Device Vendor and Device Name string is stored which
/// may be used by the debugger or IDE to configure device parameters.
#define TARGET_DEVICE_FIXED     0               ///< Target Device: 1 = known, 0 = unknown;#if TARGET_DEVICE_FIXED
#define TARGET_DEVICE_VENDOR    "ARM"           ///< String indicating the Silicon Vendor
#define TARGET_DEVICE_NAME      "Cortex-M4"     ///< String indicating the Target Device
#endif/** Get Vendor ID string.
\param str Pointer to buffer to store the string.
\return String length.
*/
__STATIC_INLINE uint8_t DAP_GetVendorString (char *str)
{(void)str;return (0U);
}/** Get Product ID string.
\param str Pointer to buffer to store the string.
\return String length.
*/
__STATIC_INLINE uint8_t DAP_GetProductString (char *str)
{(void)str;return (0U);
}/** Get Serial Number string.
\param str Pointer to buffer to store the string.
\return String length.
*/
__STATIC_INLINE uint8_t DAP_GetSerNumString (char *str)
{(void)str;return (0U);
}///@}//**************************************************************************************************
/**
\defgroup DAP_Config_PortIO_gr CMSIS-DAP Hardware I/O Pin Access
\ingroup DAP_ConfigIO_gr
@{Standard I/O Pins of the CMSIS-DAP Hardware Debug Port support standard JTAG mode
and Serial Wire Debug (SWD) mode. In SWD mode only 2 pins are required to implement the debug
interface of a device. The following I/O Pins are provided:JTAG I/O Pin                 | SWD I/O Pin          | CMSIS-DAP Hardware pin mode
---------------------------- | -------------------- | ---------------------------------------------
TCK: Test Clock              | SWCLK: Clock         | Output Push/Pull
TMS: Test Mode Select        | SWDIO: Data I/O      | Output Push/Pull; Input (for receiving data)
TDI: Test Data Input         |                      | Output Push/Pull
TDO: Test Data Output        |                      | Input
nTRST: Test Reset (optional) |                      | Output Open Drain with pull-up resistor
nRESET: Device Reset         | nRESET: Device Reset | Output Open Drain with pull-up resistorDAP Hardware I/O Pin Access Functions
-------------------------------------
The various I/O Pins are accessed by functions that implement the Read, Write, Set, or Clear to
these I/O Pins.For the SWDIO I/O Pin there are additional functions that are called in SWD I/O mode only.
This functions are provided to achieve faster I/O that is possible with some advanced GPIO
peripherals that can independently write/read a single I/O pin without affecting any other pins
of the same I/O port. The following SWDIO I/O Pin functions are provided:- \ref PIN_SWDIO_OUT_ENABLE to enable the output mode from the DAP hardware.- \ref PIN_SWDIO_OUT_DISABLE to enable the input mode to the DAP hardware.- \ref PIN_SWDIO_IN to read from the SWDIO I/O pin with utmost possible speed.- \ref PIN_SWDIO_OUT to write to the SWDIO I/O pin with utmost possible speed.
*/// Configure DAP I/O pins ------------------------------/** Setup JTAG I/O pins: TCK, TMS, TDI, TDO, nTRST, and nRESET.
Configures the DAP Hardware I/O pins for JTAG mode:- TCK, TMS, TDI, nTRST, nRESET to output mode and set to high level.- TDO to input mode.
*/
__STATIC_INLINE void PORT_JTAG_SETUP (void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();__HAL_RCC_GPIOC_CLK_ENABLE();HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(JTAG_TCK_GPIO_Port, JTAG_TCK_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(JTAG_TMS_GPIO_Port, JTAG_TMS_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(JTAG_nRESET_GPIO_Port, JTAG_nRESET_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(JTAG_TDI_GPIO_Port, JTAG_TDI_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(JTAG_TDO_GPIO_Port, JTAG_TDO_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(JTAG_nTRST_GPIO_Port, JTAG_nTRST_Pin, GPIO_PIN_SET);// LEDGPIO_InitStruct.Pin = LED_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct);// TCKGPIO_InitStruct.Pin = JTAG_TCK_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(JTAG_TCK_GPIO_Port, &GPIO_InitStruct);// TMSGPIO_InitStruct.Pin = JTAG_TMS_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(JTAG_TMS_GPIO_Port, &GPIO_InitStruct);// TDIGPIO_InitStruct.Pin = JTAG_TDI_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(JTAG_TDI_GPIO_Port, &GPIO_InitStruct);// nRESETGPIO_InitStruct.Pin = JTAG_nRESET_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;GPIO_InitStruct.Pull = GPIO_PULLUP;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(JTAG_nRESET_GPIO_Port, &GPIO_InitStruct);// nTRSTGPIO_InitStruct.Pin = JTAG_nTRST_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;GPIO_InitStruct.Pull = GPIO_PULLUP;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(JTAG_nTRST_GPIO_Port, &GPIO_InitStruct);// TDOGPIO_InitStruct.Pin = JTAG_TDO_Pin;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_NOPULL;HAL_GPIO_Init(JTAG_TDO_GPIO_Port, &GPIO_InitStruct);
}/** Setup SWD I/O pins: SWCLK, SWDIO, and nRESET.
Configures the DAP Hardware I/O pins for Serial Wire Debug (SWD) mode:- SWCLK, SWDIO, nRESET to output mode and set to default high level.- TDI, nTRST to HighZ mode (pins are unused in SWD mode).
*/
__STATIC_INLINE void PORT_SWD_SETUP (void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};__HAL_RCC_GPIOA_CLK_ENABLE();__HAL_RCC_GPIOB_CLK_ENABLE();__HAL_RCC_GPIOC_CLK_ENABLE();HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(JTAG_TCK_GPIO_Port, JTAG_TCK_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(JTAG_TMS_GPIO_Port, JTAG_TMS_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(JTAG_nRESET_GPIO_Port, JTAG_nRESET_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(JTAG_TDI_GPIO_Port, JTAG_TDI_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(JTAG_TDO_GPIO_Port, JTAG_TDO_Pin, GPIO_PIN_SET);HAL_GPIO_WritePin(JTAG_nTRST_GPIO_Port, JTAG_nTRST_Pin, GPIO_PIN_SET);// LEDGPIO_InitStruct.Pin = LED_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct);// TCKGPIO_InitStruct.Pin = JTAG_TCK_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(JTAG_TCK_GPIO_Port, &GPIO_InitStruct);// TMSGPIO_InitStruct.Pin = JTAG_TMS_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(JTAG_TMS_GPIO_Port, &GPIO_InitStruct);// nRESETGPIO_InitStruct.Pin = JTAG_nRESET_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;GPIO_InitStruct.Pull = GPIO_PULLUP;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(JTAG_nRESET_GPIO_Port, &GPIO_InitStruct);// TDI TDO nTRSTHAL_GPIO_DeInit(JTAG_TDI_GPIO_Port, JTAG_TDI_Pin);HAL_GPIO_DeInit(JTAG_TDO_GPIO_Port, JTAG_TDO_Pin);HAL_GPIO_DeInit(JTAG_nTRST_GPIO_Port, JTAG_nTRST_Pin);
}/** Disable JTAG/SWD I/O Pins.
Disables the DAP Hardware I/O pins which configures:- TCK/SWCLK, TMS/SWDIO, TDI, TDO, nTRST, nRESET to High-Z mode.
*/
__STATIC_INLINE void PORT_OFF (void)
{HAL_GPIO_DeInit(JTAG_TCK_GPIO_Port, JTAG_TCK_Pin);HAL_GPIO_DeInit(JTAG_TMS_GPIO_Port, JTAG_TMS_Pin);HAL_GPIO_DeInit(JTAG_nRESET_GPIO_Port, JTAG_nRESET_Pin);HAL_GPIO_DeInit(JTAG_TDI_GPIO_Port, JTAG_TDI_Pin);HAL_GPIO_DeInit(JTAG_TDO_GPIO_Port, JTAG_TDO_Pin);HAL_GPIO_DeInit(JTAG_nTRST_GPIO_Port, JTAG_nTRST_Pin);
}// SWCLK/TCK I/O pin -------------------------------------/** SWCLK/TCK I/O pin: Get Input.
\return Current status of the SWCLK/TCK DAP hardware I/O pin.
*/
__STATIC_FORCEINLINE uint32_t PIN_SWCLK_TCK_IN (void)
{return JTAG_TCK_GPIO_Port->ODR & JTAG_TCK_Pin ? 1 : 0;
}/** SWCLK/TCK I/O pin: Set Output to High.
Set the SWCLK/TCK DAP hardware I/O pin to high level.
*/
__STATIC_FORCEINLINE void PIN_SWCLK_TCK_SET (void)
{JTAG_TCK_GPIO_Port->BSRR = JTAG_TCK_Pin;
}/** SWCLK/TCK I/O pin: Set Output to Low.
Set the SWCLK/TCK DAP hardware I/O pin to low level.
*/
__STATIC_FORCEINLINE void PIN_SWCLK_TCK_CLR (void)
{JTAG_TCK_GPIO_Port->BRR = JTAG_TCK_Pin;
}// SWDIO/TMS Pin I/O --------------------------------------/** SWDIO/TMS I/O pin: Get Input.
\return Current status of the SWDIO/TMS DAP hardware I/O pin.
*/
__STATIC_FORCEINLINE uint32_t PIN_SWDIO_TMS_IN (void)
{return JTAG_TMS_GPIO_Port->ODR & JTAG_TMS_Pin ? 1 : 0;
}/** SWDIO/TMS I/O pin: Set Output to High.
Set the SWDIO/TMS DAP hardware I/O pin to high level.
*/
__STATIC_FORCEINLINE void PIN_SWDIO_TMS_SET (void)
{JTAG_TMS_GPIO_Port->BSRR = JTAG_TMS_Pin;
}/** SWDIO/TMS I/O pin: Set Output to Low.
Set the SWDIO/TMS DAP hardware I/O pin to low level.
*/
__STATIC_FORCEINLINE void PIN_SWDIO_TMS_CLR (void)
{JTAG_TMS_GPIO_Port->BRR = JTAG_TMS_Pin;
}/** SWDIO I/O pin: Get Input (used in SWD mode only).
\return Current status of the SWDIO DAP hardware I/O pin.
*/
__STATIC_FORCEINLINE uint32_t PIN_SWDIO_IN (void)
{return JTAG_TMS_GPIO_Port->IDR & JTAG_TMS_Pin ? 1 : 0;
}/** SWDIO I/O pin: Set Output (used in SWD mode only).
\param bit Output value for the SWDIO DAP hardware I/O pin.
*/
__STATIC_FORCEINLINE void PIN_SWDIO_OUT (uint32_t bit)
{if(bit & 0X01){JTAG_TMS_GPIO_Port->BSRR = JTAG_TMS_Pin;}else{JTAG_TMS_GPIO_Port->BRR = JTAG_TMS_Pin;}
}/** SWDIO I/O pin: Switch to Output mode (used in SWD mode only).
Configure the SWDIO DAP hardware I/O pin to output mode. This function is
called prior \ref PIN_SWDIO_OUT function calls.
*/
__STATIC_FORCEINLINE void PIN_SWDIO_OUT_ENABLE (void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.Pin = JTAG_TMS_Pin;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(JTAG_TMS_GPIO_Port, &GPIO_InitStruct);
}/** SWDIO I/O pin: Switch to Input mode (used in SWD mode only).
Configure the SWDIO DAP hardware I/O pin to input mode. This function is
called prior \ref PIN_SWDIO_IN function calls.
*/
__STATIC_FORCEINLINE void PIN_SWDIO_OUT_DISABLE (void)
{GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.Pin = JTAG_TMS_Pin;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(JTAG_TMS_GPIO_Port, &GPIO_InitStruct);
}// TDI Pin I/O ---------------------------------------------/** TDI I/O pin: Get Input.
\return Current status of the TDI DAP hardware I/O pin.
*/
__STATIC_FORCEINLINE uint32_t PIN_TDI_IN (void)
{return JTAG_TDI_GPIO_Port->ODR & JTAG_TDI_Pin ? 1 : 0;
}/** TDI I/O pin: Set Output.
\param bit Output value for the TDI DAP hardware I/O pin.
*/
__STATIC_FORCEINLINE void PIN_TDI_OUT (uint32_t bit)
{if(bit & 0X01){JTAG_TDI_GPIO_Port->BSRR = JTAG_TDI_Pin;}else{JTAG_TDI_GPIO_Port->BRR = JTAG_TDI_Pin;}
}// TDO Pin I/O ---------------------------------------------/** TDO I/O pin: Get Input.
\return Current status of the TDO DAP hardware I/O pin.
*/
__STATIC_FORCEINLINE uint32_t PIN_TDO_IN (void)
{return JTAG_TDO_GPIO_Port->IDR & JTAG_TDO_Pin ? 1 : 0;
}// nTRST Pin I/O -------------------------------------------/** nTRST I/O pin: Get Input.
\return Current status of the nTRST DAP hardware I/O pin.
*/
__STATIC_FORCEINLINE uint32_t PIN_nTRST_IN (void)
{return JTAG_nTRST_GPIO_Port->ODR & JTAG_nTRST_Pin ? 1 : 0;
}/** nTRST I/O pin: Set Output.
\param bit JTAG TRST Test Reset pin status:- 0: issue a JTAG TRST Test Reset.- 1: release JTAG TRST Test Reset.
*/
__STATIC_FORCEINLINE void PIN_nTRST_OUT (uint32_t bit)
{if(bit & 0X01){JTAG_nTRST_GPIO_Port->BSRR = JTAG_nTRST_Pin;}else{JTAG_nTRST_GPIO_Port->BRR = JTAG_nTRST_Pin;}
}// nRESET Pin I/O------------------------------------------/** nRESET I/O pin: Get Input.
\return Current status of the nRESET DAP hardware I/O pin.
*/
__STATIC_FORCEINLINE uint32_t PIN_nRESET_IN (void)
{return JTAG_nRESET_GPIO_Port->ODR & JTAG_nRESET_Pin ? 1 : 0;
}/** nRESET I/O pin: Set Output.
\param bit target device hardware reset pin status:- 0: issue a device hardware reset.- 1: release device hardware reset.
*/
__STATIC_FORCEINLINE void PIN_nRESET_OUT (uint32_t bit)
{if(bit & 0X01){JTAG_nRESET_GPIO_Port->BSRR = JTAG_nRESET_Pin;}else{JTAG_nRESET_GPIO_Port->BRR = JTAG_nRESET_Pin;}
}///@}//**************************************************************************************************
/**
\defgroup DAP_Config_LEDs_gr CMSIS-DAP Hardware Status LEDs
\ingroup DAP_ConfigIO_gr
@{CMSIS-DAP Hardware may provide LEDs that indicate the status of the CMSIS-DAP Debug Unit.It is recommended to provide the following LEDs for status indication:- Connect LED: is active when the DAP hardware is connected to a debugger.- Running LED: is active when the debugger has put the target device into running state.
*//** Debug Unit: Set status of Connected LED.
\param bit status of the Connect LED.- 1: Connect LED ON: debugger is connected to CMSIS-DAP Debug Unit.- 0: Connect LED OFF: debugger is not connected to CMSIS-DAP Debug Unit.
*/
__STATIC_INLINE void LED_CONNECTED_OUT (uint32_t bit)
{if(bit & 0X01){HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);  // 拉低是亮黄灯}else{HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);    // 拉高是亮红灯}
}/** Debug Unit: Set status Target Running LED.
\param bit status of the Target Running LED.- 1: Target Running LED ON: program execution in target started.- 0: Target Running LED OFF: program execution in target stopped.
*/
__STATIC_INLINE void LED_RUNNING_OUT (uint32_t bit)
{if(bit & 0X01){HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);  // 拉低是亮黄灯}else{HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);    // 拉高是亮红灯}
}///@}//**************************************************************************************************
/**
\defgroup DAP_Config_Timestamp_gr CMSIS-DAP Timestamp
\ingroup DAP_ConfigIO_gr
@{
Access function for Test Domain Timer.The value of the Test Domain Timer in the Debug Unit is returned by the function \ref TIMESTAMP_GET. By
default, the DWT timer is used.  The frequency of this timer is configured with \ref TIMESTAMP_CLOCK.*//** Get timestamp of Test Domain Timer.
\return Current timestamp value.
*/
__STATIC_INLINE uint32_t TIMESTAMP_GET (void)
{return (DWT->CYCCNT);
}///@}//**************************************************************************************************
/**
\defgroup DAP_Config_Initialization_gr CMSIS-DAP Initialization
\ingroup DAP_ConfigIO_gr
@{CMSIS-DAP Hardware I/O and LED Pins are initialized with the function \ref DAP_SETUP.
*//** Setup of the Debug Unit I/O pins and LEDs (called when Debug Unit is initialized).
This function performs the initialization of the CMSIS-DAP Hardware I/O Pins and the
Status LEDs. In detail the operation of Hardware I/O and LED pins are enabled and set:- I/O clock system enabled.- all I/O pins: input buffer enabled, output pins are set to HighZ mode.- for nTRST, nRESET a weak pull-up (if available) is enabled.- LED output pins are enabled and LEDs are turned off.
*/
__STATIC_INLINE void DAP_SETUP (void)
{PORT_JTAG_SETUP();
}/** Reset Target Device with custom specific I/O pin or command sequence.
This function allows the optional implementation of a device specific reset sequence.
It is called when the command \ref DAP_ResetTarget and is for example required
when a device needs a time-critical unlock sequence that enables the debug port.
\return 0 = no device specific reset sequence is implemented.\n1 = a device specific reset sequence is implemented.
*/
__STATIC_INLINE uint8_t RESET_TARGET (void)
{return (1U);             // change to '1' when a device reset sequence is implemented
}///@}#endif /* __DAP_CONFIG_H__ */

USBD_User_HID_0.c文件:

/*------------------------------------------------------------------------------* MDK Middleware - Component ::USB:Device* Copyright (c) 2004-2017 ARM Germany GmbH. All rights reserved.*------------------------------------------------------------------------------* Name:    USBD_User_HID_0.c* Purpose: USB Device Human Interface Device class (HID) User module* Rev.:    V6.2.3*----------------------------------------------------------------------------*/
/*** \addtogroup usbd_hidFunctions** USBD_User_HID_0.c implements the application specific functionality of the* HID class and is used to receive and send data reports to the USB Host.** The implementation must match the configuration file USBD_Config_HID_0.h.* The following values in USBD_Config_HID_0.h affect the user code:**  - 'Endpoint polling Interval' specifies the frequency of requests*    initiated by USB Host for \ref USBD_HIDn_GetReport.**  - 'Number of Output Reports' configures the values for \em rid of*    \ref USBD_HIDn_SetReport.**  - 'Number of Input Reports' configures the values for \em rid of*    \ref USBD_HIDn_GetReport and \ref USBD_HID_GetReportTrigger.**  - 'Maximum Input Report Size' specifies the maximum value for:*       - return of \ref USBD_HIDn_GetReport*       - len of \ref USBD_HID_GetReportTrigger.**  - 'Maximum Output Report Size' specifies the maximum value for \em len*    in \ref USBD_HIDn_SetReport for rtype=HID_REPORT_OUTPUT**  - 'Maximum Feature Report Size' specifies the maximum value for \em len*    in \ref USBD_HIDn_SetReport for rtype=HID_REPORT_FEATURE**///! [code_USBD_User_HID]#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include "DAP_config.h"
#include "DAP.h"
#include "usbd_custom_hid_if.h"#define HID_REPORT_INPUT            0x81
#define HID_REPORT_OUTPUT           0x91
#define HID_REPORT_FEATURE          0xB1#define USBD_HID_REQ_EP_CTRL        0x01
#define USBD_HID_REQ_PERIOD_UPDATE  0x02
#define USBD_HID_REQ_EP_INT         0x03static volatile uint16_t USB_RequestIndexI;     // Request  Index In
static volatile uint16_t USB_RequestIndexO;     // Request  Index Out
static volatile uint16_t USB_RequestCountI;     // Request  Count In
static volatile uint16_t USB_RequestCountO;     // Request  Count Outstatic volatile uint16_t USB_ResponseIndexI;    // Response Index In
static volatile uint16_t USB_ResponseIndexO;    // Response Index Out
static volatile uint16_t USB_ResponseCountI;    // Response Count In
static volatile uint16_t USB_ResponseCountO;    // Response Count Out
static volatile uint8_t  USB_ResponseIdle;      // Response Idle  Flag
static volatile uint32_t USB_EventFlags;static uint8_t  USB_Request [DAP_PACKET_COUNT][DAP_PACKET_SIZE];  // Request  Buffer
static uint8_t  USB_Response[DAP_PACKET_COUNT][DAP_PACKET_SIZE];  // Response Bufferextern USBD_HandleTypeDef hUsbDeviceFS;// Called during USBD_Initialize to initialize the USB HID class instance.
void USBD_HID0_Initialize (void)
{// Initialize variablesUSB_RequestIndexI  = 0U;USB_RequestIndexO  = 0U;USB_RequestCountI  = 0U;USB_RequestCountO  = 0U;USB_ResponseIndexI = 0U;USB_ResponseIndexO = 0U;USB_ResponseCountI = 0U;USB_ResponseCountO = 0U;USB_ResponseIdle   = 1U;USB_EventFlags     = 0U;
}// Called during USBD_Uninitialize to de-initialize the USB HID class instance.
void USBD_HID0_Uninitialize (void)
{}// \brief Prepare HID Report data to send.
// \param[in]   rtype   report type:
//                - HID_REPORT_INPUT           = input report requested
//                - HID_REPORT_FEATURE         = feature report requested
// \param[in]   req     request type:
//                - USBD_HID_REQ_EP_CTRL       = control endpoint request
//                - USBD_HID_REQ_PERIOD_UPDATE = idle period expiration request
//                - USBD_HID_REQ_EP_INT        = previously sent report on interrupt endpoint request
// \param[in]   rid     report ID (0 if only one report exists).
// \param[out]  buf     buffer containing report data to send.
// \return              number of report data bytes prepared to send or invalid report requested.
//              - value >= 0: number of report data bytes prepared to send
//              - value = -1: invalid report requested
int32_t USBD_HID0_GetReport (uint8_t rtype, uint8_t req, uint8_t rid, uint8_t *buf)
{(void)rid;switch (rtype){case HID_REPORT_INPUT:switch (req){case USBD_HID_REQ_EP_CTRL:        // Explicit USB Host request via Control OUT Endpointcase USBD_HID_REQ_PERIOD_UPDATE:  // Periodic USB Host request via Interrupt OUT Endpointbreak;case USBD_HID_REQ_EP_INT:         // Called after USBD_HID_GetReportTrigger to signal data obtained.if (USB_ResponseCountI != USB_ResponseCountO){// Load data from response buffer to be sent backmemcpy(buf, USB_Response[USB_ResponseIndexO], DAP_PACKET_SIZE);USB_ResponseIndexO++;if (USB_ResponseIndexO == DAP_PACKET_COUNT){USB_ResponseIndexO = 0U;}USB_ResponseCountO++;return ((int32_t)DAP_PACKET_SIZE);}else{USB_ResponseIdle = 1U;}break;}break;case HID_REPORT_FEATURE:break;}return (0);
}// \brief Process received HID Report data.
// \param[in]   rtype   report type:
//                - HID_REPORT_OUTPUT    = output report received
//                - HID_REPORT_FEATURE   = feature report received
// \param[in]   req     request type:
//                - USBD_HID_REQ_EP_CTRL = report received on control endpoint
//                - USBD_HID_REQ_EP_INT  = report received on interrupt endpoint
// \param[in]   rid     report ID (0 if only one report exists).
// \param[in]   buf     buffer that receives report data.
// \param[in]   len     length of received report data.
// \return      true    received report data processed.
// \return      false   received report data not processed or request not supported.
bool USBD_HID0_SetReport (uint8_t rtype, uint8_t req, uint8_t rid, const uint8_t *buf, int32_t len)
{(void)req;(void)rid;switch (rtype){case HID_REPORT_OUTPUT:if (len == 0){break;}if (buf[0] == ID_DAP_TransferAbort){DAP_TransferAbort = 1U;break;}if ((uint16_t)(USB_RequestCountI - USB_RequestCountO) == DAP_PACKET_COUNT){USB_EventFlags |= 0X80;break;  // Discard packet when buffer is full}// Store received data into request buffermemcpy(USB_Request[USB_RequestIndexI], buf, (uint32_t)len);USB_RequestIndexI++;if (USB_RequestIndexI == DAP_PACKET_COUNT){USB_RequestIndexI = 0U;}USB_RequestCountI++;USB_EventFlags |= 0X01;break;case HID_REPORT_FEATURE:break;}return true;
}void USBD_HID0_OutEvent_FS(void)
{USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef *)hUsbDeviceFS.pClassData;USBD_HID0_SetReport(HID_REPORT_OUTPUT, 0, 0, hhid->Report_buf, USBD_CUSTOMHID_OUTREPORT_BUF_SIZE);
}void USBD_HID0_InEvent_FS(void)
{int32_t len;USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef *)hUsbDeviceFS.pClassData;if ((len=USBD_HID0_GetReport(HID_REPORT_INPUT, USBD_HID_REQ_EP_INT, 0, hhid->Report_buf)) > 0){USBD_HID_GetReportTrigger(0, 0, hhid->Report_buf, len);}
}// DAP Thread.
__NO_RETURN void DAP_Thread (void *argument)
{uint32_t n;uint32_t flags = 0;(void)argument;for (;;){while((USB_EventFlags & 0X81) == 0){;}USB_EventFlags &= (~0X81);// Process pending requestswhile (USB_RequestCountI != USB_RequestCountO){// Handle Queue Commandsn = USB_RequestIndexO;while (USB_Request[n][0] == ID_DAP_QueueCommands){USB_Request[n][0] = ID_DAP_ExecuteCommands;n++;if (n == DAP_PACKET_COUNT){n = 0U;}if (n == USB_RequestIndexI){while((USB_EventFlags & 0X81) == 0){;}flags = USB_EventFlags;USB_EventFlags &= (~0X81);if(flags & 0X80){break;}}}// Execute DAP Command (process request and prepare response)DAP_ExecuteCommand(USB_Request[USB_RequestIndexO], USB_Response[USB_ResponseIndexI]);// Update Request Index and CountUSB_RequestIndexO++;if (USB_RequestIndexO == DAP_PACKET_COUNT){USB_RequestIndexO = 0U;}USB_RequestCountO++;// Update Response Index and CountUSB_ResponseIndexI++;if (USB_ResponseIndexI == DAP_PACKET_COUNT){USB_ResponseIndexI = 0U;}USB_ResponseCountI++;if (USB_ResponseIdle){if (USB_ResponseCountI != USB_ResponseCountO){// Load data from response buffer to be sent backn = USB_ResponseIndexO++;if (USB_ResponseIndexO == DAP_PACKET_COUNT){USB_ResponseIndexO = 0U;}USB_ResponseCountO++;USB_ResponseIdle = 0U;USBD_HID_GetReportTrigger(0U, 0U, USB_Response[n], DAP_PACKET_SIZE);}}}}
}//! [code_USBD_User_HID]

现在其实就是完成了第二步,DAP通过SWD协议将固件下载进目标单片机FLASH中的步骤,那么下面就开始修改USB的配置,KEIL能通过USB和DAP通信。

修改usbd_custom_hid_if.c文件,找到CUSTOM_HID_ReportDesc_FS数组,改为:

/** Usb HID report descriptor. */
__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END =
{/* USER CODE BEGIN 0 */0x06,0x00,0xFF,         /*  Usage Page (vendor defined) ($FF00) global */0x09,0x01,              /*  Usage (vendor defined) ($01) local */0xA1,0x01,              /*  Collection (Application) */0x15,0x00,              /*   LOGICAL_MINIMUM (0) */0x25,0xFF,              /*   LOGICAL_MAXIMUM (255) */0x75,0x08,              /*   REPORT_SIZE (8bit) */// Input Report0x95,64,                /*   Report Length (64 REPORT_SIZE) */0x09,0x01,              /*   USAGE (Vendor Usage 1) */0x81,0x02,              /*   Input(data,var,absolute) */// Output Report0x95,64,                /*   Report Length (64 REPORT_SIZE) */0x09,0x01,              /*   USAGE (Vendor Usage 1) */0x91,0x02,              /*   Output(data,var,absolute) */// Feature Report0x95,64,                /*   Report Length (64 REPORT_SIZE) */0x09,0x01,              /*   USAGE (Vendor Usage 1) */0xB1,0x02,              /*   Feature(data,var,absolute) *//* USER CODE END 0 */0xC0                    /*  END_COLLECTION                */
};

导入USBD_User_HID_0.c文件中的这四个函数,并新写一个CUSTOM_HID_InEvent_FS函数:

修改以下几个函数添加调用:


修改usbd_custom_hid_if.h文件,导出新增的USBD_HID_GetReportTrigger这个函数声明:

然后修改usbd_customhid.h文件的这几个宏:

然后在改一下USBD_CUSTOM_HID_ItfTypeDef这个结构,需要新增一个函数指针:

然后在修改一下这个函数:USBD_CUSTOM_HID_DataIn,内容如下:

static uint8_t  USBD_CUSTOM_HID_DataIn(USBD_HandleTypeDef *pdev,uint8_t epnum)
{/* Ensure that the FIFO is empty before a new transfer, this condition couldbe caused by  a new transfer before the end of the previous transfer */USBD_CUSTOM_HID_HandleTypeDef     *hhid = (USBD_CUSTOM_HID_HandleTypeDef *)pdev->pClassData;hhid->state = CUSTOM_HID_IDLE;/* I add a new interface func in the structure USBD_CUSTOM_HID_ItfTypeDef. */((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->InEvent(hhid->Report_buf[0],hhid->Report_buf[1]);return USBD_OK;
}

最后修改main.c文件,添加头文件包含:

main函数内容如下:

至此,就已经完成了一个CMSIS_DAP仿真器!

下载编译工程,将程序下载进STLINK的板子中去试试吧!

注:STLINK可能设置了写保护,需要先解除写保护才能用KEIL给它下载程序,解除办法见我的另一篇博客:STM32下载程序问题解决:Can not read memory! Disable Read Out Proyection and try.


(五)测试CMSIS_DAP仿真器

将程序下载进板子后,USB插入电脑,设备管理器->人体学输入设备会多出一个USB输入设备符合HID标准的供应商定义设备,如下图:

然后我们打开KEIL,调试器选择CMSIS_DAP:

给目标板下载程序:

没问题!再试试在线调试:

也没问题!成功!


(六)补充内容:软启动的实现

通过以上教程后,此时的DAP仿真器已经能够正常使用了,但是还是有缺陷的,就是无法像ST-LINK那样通过软件复位的形式运行程序,换句话说就是用keil下载程序后是不运行的,必须手动复位一下,或者必须将目标单片机的复位引脚接到DAP的复位控制引脚上,我们最初的设计目的就是四脚的DAP,而不是5脚的(5脚是多了一个硬件RESET),所以下面就需要修改一下SWD的源码,在对下载目标MCU下载完程序后,使用软件复位的形式给他复位一下。

这一部分内容牵扯到离线下载器的源码内容,离线下载器中的源码主要是移植自ARM官方的DAP-LINK中的源码,我们现在还未实现离线下载的功能,所以只需要SWD_host.c文件,这个文件实现的是怎么通过SWD协议控制目标MCU的功能。

需要修改的是DAP.c文件中的函数DAP_ProcessCommand

if(DAP_Data.debug_port == DAP_PORT_SWD)  /* +++lakun:SWD模式下需要软启动,JTAG模式下不需要 */
{swd_init_debug();                    /* +++lakun:使目标MCU进入调试模式 */swd_set_target_reset(0);             /* +++lakun:写入命令实现软复位启动 */
}

这里就是在使用keil下载完程序后执行断开的操作,我们在断开连接之前软件复位一下目标MCU就实现了软启动,swd_set_target_reset函数内容如下:

原理上其实就是用过SWD协议向目标MCU的SCB->AIRCR寄存器执行一次写入操作,执行一次软件复位,这个寄存器描述如下:

通过以上设置,就完成了完整版的DAP调试器!


ends…

十分钟快速自制CMSIS_DAP仿真器~将ST-LINK-V2变身DAP仿真器~相关推荐

  1. python新手教程 从零开始-Python零基础从零开始学习Python十分钟快速入门

    原标题:Python零基础从零开始学习Python十分钟快速入门 学习Python的,都知道Python 是一个高层次的结合了解释性.编译性.互动性和面向对象的脚本语言.Python是一种动态解释型的 ...

  2. 十分钟快速了解 ES6 Promise

    转载自 十分钟快速了解 ES6 Promise 什么是Promise Promise最早由社区提出并实现,典型的一些库有Q,when, bluebird等:它们的出现是为了更好地解决JavaScrip ...

  3. 十分钟快速DIY简易FM电台和收音机

    十分钟快速DIY简易FM电台和收音机 FM简介 实现功能 使用前准备 器件连接 频率调节 广播音乐 无线话筒 总结 原文链接:https://www.yourcee.com/newsinfo/2923 ...

  4. 【Microsoft Azure 的1024种玩法】五十四. 十分钟快速上手创建部署Azure speech服务

    [简介] Azure语音服务是Microsoft提供稳定可靠的云通信服务,其在单个 Azure 订阅中统合了语音转文本.文本转语音以及语音翻译功能,我们可以通过各种方式(语音 CLI.语音 SDK.S ...

  5. 用整站程序(网站源代码)十分钟快速建站

     用整站程序(网站源代码)十分钟快速建站 悬赏分:0 - 解决时间:2007-2-2 18:20 怎么做 提问者: guolibao888 - 试用期 一级 最佳答案 现在提起做网站,特别是一些做一些 ...

  6. Linux一键部署duckchat,DuckChat 1.0.7发布,十分钟快速搭建聊天系统

    DuckChat 1.0.7发布,十分钟快速搭建聊天系统 2018年09月28日 11:55作者:黄页编辑:黄页 分享 DuckChat是一款安全的私有聊天软件,基于PHP环境,可运行在Docker. ...

  7. 整理ST Link V2 与stm32连接经过

    整理ST Link V2 与stm32连接经过 一.购买合适的ST Link V2仿真器: 二.下载ST Link V2的驱动并安装: 本人过程并没有修改安装路径,直接下一步,对于驱动最好为默认安装路 ...

  8. STM32 ST link V2 固件 ST-LinkV2.J16.S4.hex

    STM32 ST link V2 固件 将一个STM32F103C8T6变成ST link V2 STM32 ST link V2 固件 ST-LinkV2.J16.S4.hex https://do ...

  9. Python语言十分钟快速入门

    假设你希望学习Python这门语言,却苦于找不到一个简短而全面的入门教程.那么本教程将花费十分钟的时间带你走入Python的大门.本文的内容介于教程(Toturial)和速查手册(CheatSheet ...

  10. 梓晨教你十分钟快速搭建安装属于自己的网站

    其实搭建网站很简单,对老手来说方法很多,不过还是有些新手不明白,今天梓晨就为大家简单的讲解一下快速搭建网站的方法. 1:环境的选择 搭建网站有很多种方法,不过现在asp网站不多了,基本都是php的,一 ...

最新文章

  1. 技术分享:几种常见的JavaScript混淆和反混淆工具分析实战【转】
  2. 阿里云系列——7.阿里云IIS系列详解(过程+通用+最新)
  3. R语言心得-分词包的安装
  4. There is no tracking information for the current branch
  5. android rtsp协议转http协议_网络协议HTTP 协议(一)
  6. 数据库---查询(详细)
  7. C++堆和栈详解(转)
  8. 科研人的暑假:学长下地中暑,博士小姐姐留校养鱼......
  9. pycharm遇到的小问题
  10. 选择与Git进行提交意味着什么?
  11. [25年后的统计系会是什么样?
  12. windows下配置NGINX实现内网穿透并配置开机自启动
  13. 四旋翼无人机飞控系统设计(输出分配)
  14. MAX96706开发板POC电路分析
  15. c 语言 农历,C++算法系列之中国农历的算法
  16. 同一个基站连接两个核心网AMF POOL的场景分析及带AMF重选的注册流程
  17. 7-24 猜数字游戏
  18. 饥荒服务器搭建运行,《饥荒》专用服务器搭建图文教程
  19. JVM如何识别“到底谁才是垃圾“?
  20. Docker概述、安装及基础命令

热门文章

  1. 商淘软件五周年:着力打造S2B2C及电商一站式服务专家
  2. VS2013 应用程序无法正常启动0xc0150002
  3. 用甘特图控件VARCHART XGantt搞定项目管理
  4. Python读写修改Shapefile
  5. 计算机主板过热报警,利用电脑主板BIOS的报警声音辨别电脑故障
  6. 从输入URL到页面展现的全过程
  7. 剑网3:指尖江湖手游脚本哪个好呢? 剑网3:指尖江湖手游自动采集IOS脚本
  8. 计算机科学的endnote格式,基于国家标准的 EndNote 输出样式模板
  9. 334个地级市名单_中国各级行政区划数量,统计到乡镇一级
  10. orl_faces数据集分享