十分钟快速自制CMSIS_DAP仿真器,将ST-LINK-V2变身DAP仿真器!
(一)简介
说到单片机仿真器(下载器)首先想到的就是J-LINK和ST-LINK,很多人可能还是第一次听说DAP仿真器,那么就先介绍一下。
CMSIS DAP是ARM官方推出的开源仿真器,支持所有的Cortex-A/R/M器件,支持JTAG/SWD接口。有以下特点:
- 完全开源,没有版权限制,所以相应的价格会很便宜
- 不需要安装驱动,即插即用
- 在新版本的DAP里集成了串口,除了下载调试外还能充当USB转串口模块,一机两用
- 性能方面已经可以满足一般用户的需求
市面上基本所有的离线下载器基本都是基于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.h 和USBD_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 文件:
#ifndef __DAP_CONFIG_H__
#define __DAP_CONFIG_H__
#include "main.h"
#define CPU_CLOCK 72000000U
#define IO_PORT_WRITE_CYCLES 2U
#define DAP_SWD 1
#define DAP_JTAG 1
#define DAP_JTAG_DEV_CNT 8U
#define DAP_DEFAULT_PORT 1U
#define DAP_DEFAULT_SWJ_CLOCK 10000000U
#define DAP_PACKET_SIZE 64U
#define DAP_PACKET_COUNT 8U
#define SWO_UART 0
#define SWO_UART_MAX_BAUDRATE 10000000U
#define SWO_MANCHESTER 0
#define SWO_BUFFER_SIZE 4096U
#define SWO_STREAM 0
#define TIMESTAMP_CLOCK 72000000U
#define TARGET_DEVICE_FIXED 0
#if TARGET_DEVICE_FIXED
#define TARGET_DEVICE_VENDOR "ARM"
#define TARGET_DEVICE_NAME "Cortex-M4"
#endif
__STATIC_INLINE uint8_t DAP_GetVendorString (char *str)
{
(void)str;
return (0U);
}
__STATIC_INLINE uint8_t DAP_GetProductString (char *str)
{
(void)str;
return (0U);
}
__STATIC_INLINE uint8_t DAP_GetSerNumString (char *str)
{
(void)str;
return (0U);
}
__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);
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);
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);
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);
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);
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);
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);
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);
}
__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);
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);
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);
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);
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);
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);
}
__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);
}
__STATIC_FORCEINLINE uint32_t PIN_SWCLK_TCK_IN (void)
{
return JTAG_TCK_GPIO_Port->ODR & JTAG_TCK_Pin ? 1 : 0;
}
__STATIC_FORCEINLINE void PIN_SWCLK_TCK_SET (void)
{
JTAG_TCK_GPIO_Port->BSRR = JTAG_TCK_Pin;
}
__STATIC_FORCEINLINE void PIN_SWCLK_TCK_CLR (void)
{
JTAG_TCK_GPIO_Port->BRR = JTAG_TCK_Pin;
}
__STATIC_FORCEINLINE uint32_t PIN_SWDIO_TMS_IN (void)
{
return JTAG_TMS_GPIO_Port->ODR & JTAG_TMS_Pin ? 1 : 0;
}
__STATIC_FORCEINLINE void PIN_SWDIO_TMS_SET (void)
{
JTAG_TMS_GPIO_Port->BSRR = JTAG_TMS_Pin;
}
__STATIC_FORCEINLINE void PIN_SWDIO_TMS_CLR (void)
{
JTAG_TMS_GPIO_Port->BRR = JTAG_TMS_Pin;
}
__STATIC_FORCEINLINE uint32_t PIN_SWDIO_IN (void)
{
return JTAG_TMS_GPIO_Port->IDR & JTAG_TMS_Pin ? 1 : 0;
}
__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;
}
}
__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);
}
__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);
}
__STATIC_FORCEINLINE uint32_t PIN_TDI_IN (void)
{
return JTAG_TDI_GPIO_Port->ODR & JTAG_TDI_Pin ? 1 : 0;
}
__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;
}
}
__STATIC_FORCEINLINE uint32_t PIN_TDO_IN (void)
{
return JTAG_TDO_GPIO_Port->IDR & JTAG_TDO_Pin ? 1 : 0;
}
__STATIC_FORCEINLINE uint32_t PIN_nTRST_IN (void)
{
return JTAG_nTRST_GPIO_Port->ODR & JTAG_nTRST_Pin ? 1 : 0;
}
__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;
}
}
__STATIC_FORCEINLINE uint32_t PIN_nRESET_IN (void)
{
return JTAG_nRESET_GPIO_Port->ODR & JTAG_nRESET_Pin ? 1 : 0;
}
__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;
}
}
__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);
}
}
__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);
}
}
__STATIC_INLINE uint32_t TIMESTAMP_GET (void)
{
return (DWT->CYCCNT);
}
__STATIC_INLINE void DAP_SETUP (void)
{
PORT_JTAG_SETUP();
}
__STATIC_INLINE uint8_t RESET_TARGET (void)
{
return (1U);
}
#endif
USBD_User_HID_0.c 文件:
#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;
static volatile uint16_t USB_RequestIndexO;
static volatile uint16_t USB_RequestCountI;
static volatile uint16_t USB_RequestCountO;
static volatile uint16_t USB_ResponseIndexI;
static volatile uint16_t USB_ResponseIndexO;
static volatile uint16_t USB_ResponseCountI;
static volatile uint16_t USB_ResponseCountO;
static volatile uint8_t USB_ResponseIdle;
static volatile uint32_t USB_EventFlags;
static uint8_t USB_Request [DAP_PACKET_COUNT][DAP_PACKET_SIZE];
static uint8_t USB_Response[DAP_PACKET_COUNT][DAP_PACKET_SIZE];
extern USBD_HandleTypeDef hUsbDeviceFS;
void USBD_HID0_Initialize (void)
{
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;
}
void USBD_HID0_Uninitialize (void)
{
}
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:
case USBD_HID_REQ_PERIOD_UPDATE:
break;
case USBD_HID_REQ_EP_INT:
if (USB_ResponseCountI != USB_ResponseCountO)
{
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);
}
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;
}
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);
}
}
__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);
while (USB_RequestCountI != USB_RequestCountO)
{
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;
}
}
}
DAP_ExecuteCommand(USB_Request[USB_RequestIndexO], USB_Response[USB_ResponseIndexI]);
USB_RequestIndexO++;
if (USB_RequestIndexO == DAP_PACKET_COUNT)
{
USB_RequestIndexO = 0U;
}
USB_RequestCountO++;
USB_ResponseIndexI++;
if (USB_ResponseIndexI == DAP_PACKET_COUNT)
{
USB_ResponseIndexI = 0U;
}
USB_ResponseCountI++;
if (USB_ResponseIdle)
{
if (USB_ResponseCountI != USB_ResponseCountO)
{
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);
}
}
}
}
}
现在其实就是完成了第二步,DAP通过SWD协议将固件下载进目标单片机FLASH中的步骤,那么下面就开始修改USB的配置,KEIL能通过USB和DAP通信。
修改usbd_custom_hid_if.c 文件,找到CUSTOM_HID_ReportDesc_FS 数组,改为:
__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END =
{
0x06,0x00,0xFF,
0x09,0x01,
0xA1,0x01,
0x15,0x00,
0x25,0xFF,
0x75,0x08,
0x95,64,
0x09,0x01,
0x81,0x02,
0x95,64,
0x09,0x01,
0x91,0x02,
0x95,64,
0x09,0x01,
0xB1,0x02,
0xC0
};
导入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…
|