Open imuncle opened 4 years ago
点赞👍
tql
win10有设备插入提示音但是找不到这个设备2333
@LIGHT1213 这情况我没遇到过,有可能是Windows识别成了其他类型的设备,可以试试这个文章中的办法:keil无法识别CMSIS-DAP的问题,希望对你有帮助
我仔细看了下,我应该不是这个问题,我这边别识别成了usb输入设备,程序卡在那个USBD_Configured地方了,我再看看把,不过还是谢了
@imuncle 我知道怎么回事了,现在我这边keil识别cmsis dap 他的define USBD_PRODUCT_STRING_FS这个东西的字符串里必须带有“CMSIS-DAP”这一部分
@LIGHT1213 噢对对对,原文作者没写我也就忘加了,这一点在官方手册里就有写:Configure USB Peripheral
The String Settings - Product String must contain "CMSIS-DAP" somewhere in the string. This is used by the debuggers to identify a CMSIS-DAP compliant Debug Unit that is connected to a host computer.
上周末硬件组的人说想搞个无线下载器,就用ARM开源的CMSIS-DAP协议,结果就这一句话我自闭了四五天,终于在一篇非常棒的文章里找到了方法,因为写的太好了,所以这里我直接把原文翻译成中文,英文原文见:usb_express:cmsis-dap [Geniekits]
在STM32F103C8上实现CMSIS-DAP
如果你想在主流的MCU上进行嵌入式开发,一般来说你需要一个JTAG来连接单片机和电脑。对于ARM的MCU来说,J-Link是一个非常有名的设备,它可以提供JTAG协议将单片机个电脑连接起来。CMSIS-DAP是一个由ARM公司发布的一款类似的设备。DAP和J-Link最主要的区别在于DAP使开源的,而J-Link是一个付费产品。另外,CMSIS-DAP是基于USB HID的,所以你不需要额外安装一个固件驱动就能使用。
CMSIS-DAP 2.0.0的源码在CMSIS V5的GitHub仓库里。在源码里还有两个基于LPC-Link-II的示例。这两个示例使用的是Keil MDK的RTE组件和USB库。在源码中还有一个叫做
USB_CM3.lib
的库文件。正因为这些,官方提供的这个示例很不方便(即非ARM的平台就没法用)。作为一个开源的狂热粉丝,我决定以完全自由的方式实现CMSIS-DAP。STM32F103是一款非常受欢迎,非常便宜的MCU,它有着ARM Cotex-M3的内核,由STMicroelectronics设计。正巧我手里有一些叫做Bluepill的最小系统板,它上面搭载了一片小巧的STM32F103C8T6芯片,并且上面也没有多余的无用的外设组件。这块开发板的唯一不足是连接在USB D+引脚的上拉电阻是10K,但按照标准它应该是1.5K,不过它的USB依然能够正常工作,没有出什么问题。
给STM32创建一个完整的嵌入式开发环境是非常简单的。在ST的官网上,我们可以下载一些免费软件,其中最重要的是STM32CubeMX。这是一个图形化配置工具,帮助用户选择单片机引脚的功能,并自动生成外设初始化代码。我配置了USB、SPI1和USART1,并选择了USB的Custom HID middleware模式。GPIOB10到GPIO15被配置为JTAG调试需要的引脚。GPIOC13用于驱动单片机上的LED灯。但是我不知道怎么在STM32CubeMX中配置CoreDebug和DWT,它能在软件里面配置吗?
ST公司也开发了他们自己的JTAG调试器——STLink。当然它并不是必要的,你也可以使用J-Link或者其他种类的调试器。STLink的驱动和程序可以在ST官网上下载。在网站里还有一个基于Eclipse开发环境开发的IDE,STM32CubeMX也被包含其中。我选择的IDE是基于CodeBlocks的Embitz,IDE中的arm_none_eabi_gcc版本是5.4.1。在我完成我的CMSIS-DAP之前,我必须使用STLink来调试我的代码。现在我在使用新做出来的CMSIS-DAP结合OpenOCD进行日常的开发。
从CMSIS-DAP的源码开始
CMSIS V4 和 V5 中都有CMSIS-DAP的源码,我从Github上下载了CMSIS_5。CMSIS-DAP的核心文件有五个,但示例其实比核心代码更重要。我将从示例V1的头文件开始分析。
我选择LPC-Link-II V1作为我的参考是因为它是通过USB HID实现的(V2是通过WinUSB实现)。我分析的第一个文件是
DAP_Config.h
。第一个关键位置如下:我想抛弃RTE的相关文件,所以
_RTE_
是不会被定义的,并且我必须创建我自己的device.h
。我将参数
CPU_CLOCK
重定义为72000000
(72MHz)。根据文件里的注释,参数DAP_PACKET_SIZE
必须重新定义为64U
。我把SWO_UART
改为0,这让我的工作轻松不少。参数TIMESTAMP_CLOCK
也要重定义为72000000
。LPC-Link-II使用Cortex-M3 的 DWT模块实现时间戳(TIMESTAMP),这也是为什么我想在CubeMX中尝试配置STM32F103的DWT。最后我自己写了一小段代码来实现这个功能(在device.c
中):我定义的引脚和NXP LPCxx的完全不同。我为STM32F103重写了所有的引脚的操作代码。在
DAP_Config.h
这个文件中还有一些奇怪的地方,比如:我不明白为什么DAP的代码里需要读取
SWCLK/TCK
引脚的电平,这个引脚明明是被配置为推挽输出来产生时钟信号输送给JTAG从机的。从上面列出来的代码可以看出,它是希望返回当前引脚的电平值,我的实现方式稍微有点不同:我返回的是当前引脚的输出值。我不确定这是否正确。在整个代码中,这句话只被
DAP.c
中的一个叫DAP_SWJ_Pins
的函数调用了两次。我猜测DAP_SWJ_Pins
这个函数是用来测试IO口是否工作正常的。另一个奇怪的地方是
PIN_nRESET_OUT
:为猜测
release
的意思可能是将nRESET
引脚重新配置为一个失能的引脚。我的代码如下:include "cmsis_os2.h"
ifdef osObjectsExternal
extern osThreadId_t DAP_ThreadId;
else
extern osThreadId_t DAP_ThreadId; osThreadId_t DAP_ThreadId;
endif
extern void DAP_Thread (void *argument);
extern void app_main (void *argument);
endif / __osObjects_h__ /
我直接“跳转”到本需要被创建的线程函数中,这就意味着
main.c
中的osKernelGetState
&osKernelStart
&osDelay
三个函数永远不会被执行。下一个重要的函数是USBD_Configured
,我将在使用STM32CubeMX生成初始化代码那一节解释这个函数。我移除了
RTE\USB\USBD_Config_HID_0.h
并在我自己的rl_usb.h
中重新定义了USBD_HID0_OUT_REPORT_MAX_SZ
&USBD_HID0_IN_REPORT_MAX_SZ
两个参数。USB HID通信的核心是由两个接口中断函数管理的两个循环队列:
当上位机向DAP发送
OUTPUT REPORT
报文后,DAP会调用USBD_HID0_SetReport
函数,该参数的输入形参rtype
必须为HID_REPORT_OUTPUT
。当DAP成功向上位机发送INPUT REPORT
报文时,函数USB_HID0_GetReport
被调用,该函数的输入形参rtype
必须为HID_REPORT_INPUT
,并且形参req
必须为USBD_HID_REQ_EP_INT
。这意味着我们所有的报文必须通过64B数据包大小的USB中断端点传输。线程
DAP_Thread
只是一个命令判断选择器。在这个函数中有一个很重要的语句:我们必须实现一个叫做
USBD_HID_GetReportTrigger
的函数来想上位机发送INPUT REPORT
。使用STM32CubeMX生成初始化代码
在我的单片机上有一个8MHz的晶振,所以我选择HSE为时钟信号源。
PLLMul
配置为x9
,得到72MHz的PLLCLK
,提供给CPU和AHB/APB2总线,提供给APB1总线的PCLK1
配置为36MHz。USB预分频配置为1.5分频,得到48MHz的USB时钟。SPI1配置为Full-Duplex Master
,舍去NSS
信号,USART1配置为Asynchronous
。USB设备进一步配置为Custom HID Class
,USBD_CUSTOMHID_OUTREPORT_BUF_SIZE
设置为64 Bytes。如果你发现你的软件不能识别我这个CMSIS-DAP,或许你需要恰当的VID和PID。可以试试示例代码中的VID/PID,它在一个叫做
USBD_Config_0.c
的文件中,我的工程中没有这个文件。有STM32CubeMX生成的代码需要一些修改。在
usbd_customhid.h
中,CUSTOM_HID_EPIN_SIZE
和CUSTOM_HID_EPOUT_SIZE
必须设置为0x40U
。我把CUSTOM_HID_FS_BINTERVAL
改为0x01
来尝试提升HID的通信速度。在
_USBD_CUSTOM_HID_Itf
结构体中,我新增了一个成员:当
INPUT REPORT
报文成功发给上位机时,InEvent
函数应当被调用,所以usbd_customhid.c
中的USBD_CUSTOM_HID_DataIn
函数需要修改如下:设备描述符
CUSTOM_HID_ReportDesc_FS
被定义在usbd_suctom_hid_if.c
中,我定义了一个简单的描述符:可能
Feature Report
在CMSIS-DAP中不是必要的,就留着它吧。我在这个C文件中还实现了一个新的接口函数
CUSTOM_HID_InEvent_FS
:CUSTOM_HID_Init_FS
和CUSTOM_HID_DeInit_FS
两个函数被实现为使能/失能DAP功能:编写将所有源代码关联起来的桥梁
为了移除CMSIS RTOS,我写了一些函数来模拟RTOS:
函数
USBD_Configured
和USBD_HID_GetReportTrigger
实现如下:函数
USBD_CUSTOM_HID_SendReport
是由STM32CubeMX生成的,它被定义在usbd_customhid.c
中,我自己的事件句柄如下:count = delay; while (--count); }
__STATIC_FORCEINLINE void PIN_DELAY_SLOW (uint32_t delay) { uint32_t count;
count = delay; while (--count) { /**
如何使用这个CMSIS-DAP
硬件连接非常简单。我使用一块STM32F103C8T6作为CMSIS-DAP,另一块相同的开发板作为被调试的目标板,下图是两块开发板之间的连线,只需要四根线。
一些有用的链接
结语
翻译完了,接下来的是我自己的一些话。
真的非常感谢这位大大,过去的四五天我看了很多份代码,基本都是基于X893大神的代码修改的,但毕竟X893的代码历史太悠久了,已经是七年前写的代码了,使用的又是MDK Keil的RL-USB库,核心代码居然是在一个库文件中,根本看不到内核,导致我一直不知道
usbd_hid_get_report
和usbd_hid_set_report
和usbd_hid_get_report_trigger
这三个函数应该怎么实现,是在什么情况下被调用的,功能具体是啥。只能说能照搬着用,但是一旦换成其他系列的芯片就不好办了。后来我看到了这篇文章:高速USB 2.0的CMSIS-DAP调试器:CMSIS-DAP正确打开方式(3月18日更新速度和稳定性),跟着走了一遍,但不知道为什么下载进去后,USB插电脑上一点反应都没有,电脑既没有识别这个HID设备也没有提示请求描述符失败,至今还是感觉很迷。
还有一些代码把X893代码里的库文件替换成了可以打开的C文件和H文件,比如xjtuecho/CMSIS-DAP,但是还是看不懂,最后的最后,在今天,我遇见了这篇好文章,跟着它的步骤走,只用了两个小时不到,就自己从零实现了一个CMSIS-DAP,当时别提多激动了。
不过就像这篇文章最开始说的那样,我的最终目标是实现无线的CMSIS-DAP,也就是想正点原子的无线调参盒那种,正点原子的无线调试器要卖两百多,而我自己做的话成本可以压到三四十块,用起来很随心所欲的那种。
关于无线的CMSIS-DAP,我找到了三个开源的代码:
我也想尝试着实现一个,但目前还卡在一个问题中。等我彻底实现了无线CMSIS-DAP再把相关的东西写出来。