主頁 >  其他 > 十分鐘快速自制CMSIS_DAP仿真器,將ST-LINK-V2變身DAP仿真器!

十分鐘快速自制CMSIS_DAP仿真器,將ST-LINK-V2變身DAP仿真器!

2021-09-19 11:57:11 其他

十分鐘快速自制CMSIS_DAP仿真器,將ST-LINK-V2變身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 resistor


DAP 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);

	// LED
    GPIO_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);
	
	// TCK
    GPIO_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);
	
	// TMS
    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);
	
	// TDI
    GPIO_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);
	
	// nRESET
    GPIO_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);
	
	// nTRST
    GPIO_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);

	// TDO
    GPIO_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);

	// LED
    GPIO_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);
	
	// TCK
    GPIO_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);
	
	// TMS
    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);
	
	// nRESET
    GPIO_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 nTRST
	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);
}

/** 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.\n
        1 = 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         0x03

static 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 Out

static 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 Buffer

extern USBD_HandleTypeDef hUsbDeviceFS;

// Called during USBD_Initialize to initialize the USB HID class instance.
void USBD_HID0_Initialize (void)
{
    // Initialize variables
    USB_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 Endpoint
        case USBD_HID_REQ_PERIOD_UPDATE:  // Periodic USB Host request via Interrupt OUT Endpoint
            break;
        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 back
                memcpy(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 buffer
        memcpy(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 requests
        while (USB_RequestCountI != USB_RequestCountO)
        {
            // Handle Queue Commands
            n = 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 Count
            USB_RequestIndexO++;
            if (USB_RequestIndexO == DAP_PACKET_COUNT)
            {
                USB_RequestIndexO = 0U;
            }
            USB_RequestCountO++;

            // Update Response Index and Count
            USB_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 back
                    n = 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 Report
  0x95,64,                /*   Report Length (64 REPORT_SIZE) */
  0x09,0x01,              /*   USAGE (Vendor Usage 1) */
  0x81,0x02,              /*   Input(data,var,absolute) */
 
  // Output Report
  0x95,64,                /*   Report Length (64 REPORT_SIZE) */
  0x09,0x01,              /*   USAGE (Vendor Usage 1) */
  0x91,0x02,              /*   Output(data,var,absolute) */
 
  // Feature Report
  0x95,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這個結構,需要新增一個函式指標:

然后修改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:

給目標板下載程式:

沒問題!再試試在線除錯:

也沒問題!成功!


ends…

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/301352.html

標籤:其他

上一篇:位元組一面:“為什么網路要分層?每一層的職責、包含哪些協議?”

下一篇:維特WT931 姿態角度傳感器(陀螺儀)使用記錄

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more