关于DAPLink
DAPLink是ARM推出的一款调试器方案,支持SWD接口的Cortex-M系列MCU,或JTAG接口的Cortex-A系列MPU,软件代码使用Apache2.0许可,因此可以自由地用在个人和商业项目上。
CMSIS-DAP项目主页:
https://www.keil.com/pack/doc/CMSIS/DAP/html/index.html
CMSIS-DAP Github主页:
https://github.com/ARMmbed/DAPLink
DAPLinkv1和v2区别
由于DAPLink目前支持的功能很多也比较复杂,比如使用USB-CDC虚拟串口,USB-MSC虚拟U盘,USB Audio Streaming音频输入/输出实现的ADC/DAC功能等,这里只说明调试器部分的区别。
DAPLinkv1调试器使用的是HID驱动,在win8.1/win10下可以做到免安装驱动程序,使用UsbTreeView
软件可以查看到用于调试器的端点描述符和端点信息,下图中可以看到使用两个分别为IN/OUT方向的中断(interrupt)端点来完成数据传输,轮循时间间隔设置为1ms,由于这是个USB全速设备(12Mbps),最大包长是64字节。
在usb全速设备中,中断端点在每个微帧只能安排一次传输事务(高速设备可以放3个中断传输事务,见下图),
由于上面配置的轮询间隔是1ms,最大包长是64字节,减去协议开销13字节,因此每1ms能携带51字节用户数据,那么中断传输的带宽 = 51 * 1000 / 1024 = 49.804KB/s
,因此USB中断端点(HID模式)拿来做数据传输带宽还是比较低的。
在DAPLinkv2中,调试器部分使用了winusb驱动,设备端在USB描述符中报告自身支持winusb特性,并且提供上层应用程序访问的GUID,连接 WinUSB设备时,win8.1/win10主机系统会读取设备信息并自动加载Winusb.sys。
有了winusb驱动,就可以直接读写USB设备的端点了,它的功能实际上和libusb类似,不过libusb是将驱动上半部提供给用户空间完成的。那么在winusb驱动下,就可以不局限于中断端点,转而使用高速的批量传输(bulk)端点完成数据交互了。使用UsbTreeView查看DAPLinkv2设备的设备描述符和端点信息,相比与HID模式,WINUSB模式使用两个方向的批量传输端点替代了中断传输端点。
在一个微帧时间内,不会限制批量传输事务的传输数,总是利用剩余带宽安排传输,因此在全速设备中,批量传输每个包最大为64字节,一个微帧内,最大传输数是19个批量事务,因此批量传输的带宽 = (64 - 13) * 19 * 1000 / 1024 = 946.289KB/s
,比起全速设备的中断传输,速度有了很大的提升。
如果设备端芯片换成CH568
这种支持高速USB(480Mbps)的MCU(也可以是F1C100S,AM1808,AM335X这类MPU),那么速度会更快一些,不过最终的编程速度还受到目标板Flash擦写速度的限制;如果是调试阶段直接载入RAM执行,那么选择支持高速USB的设备就可以提高开发效率了。
USB描述符和枚举过程
本项目主要移植CMSIS-DAPv2 WinUSB
部分,选用的MCU型号为GD32F303RC,Cortex-M4F核心,256KB FLASH + 48KB SRAM,带全速USB@12Mbps。
关于usb2.0具体的枚举过程,可以查看usb中文网的相关资料,
USB设备的枚举过程分析
Windows和Linux不同主机下USB设备枚举过程中的差别
这里主要针对枚举过程中,设备端需要返回的描述符进行适配,下面给出实际调试过程中的USB枚举过程:
USB描述符的配置主要参考DAPLink项目DAPLink-main\source\usb
文件夹的以下文件:
usb_def.h
,usb_lib.c
,usbd_desc.h
,usb_winusb.h
其中重点是usb_lib.c
,包含了DAPLink所有的usb描述符配置信息。
推荐使用集成开发环境阅读usb_lib.c
,并且需要先定义宏:
__USB_CONFIG__
,USBD_ENABLE 1
,USBD_WINUSB_ENABLE 1
,USBD_BULK_ENABLE 1
,USBD_BOS_ENABLE 1
虽然usb_lib.c内容很多,但这样借助集成开发环境的宏高亮功能,也能看的比较清楚了,下面是Eclipse的示例。
USB设备描述符:唯一需要注意的是bcdUSB
需要填0x0210
或0x0201
,这样在windows下主机才会发起请求二进制设备对象存储描述符(BOS Descriptor),填0x0200
,0x0110
我测试下来不行。
不过通过0xEE
返回MSFT100
倒是可以制作自己的USB应用程序,而无需借助libusb驱动。
使用微软系统描述符1.0制作免驱动自定义USB设备
/* USB standard device descriptor */
#define USBD_BOS_ENABLE
#define USBD_EP0_MAX_SIZE 64
#define USB_CLASS_MISC 0xEF
#define USBD_VID 0xC251U
#define USBD_PID 0xF000U
const usb_desc_dev dap_dev_desc = {
.header = {
.bLength = USB_DEV_DESC_LEN,
.bDescriptorType = USB_DESCTYPE_DEV
},
#if (USBD_BOS_ENABLE)
// 通过二进制设备对象存储描述符(BOS Descriptor)向主机报告支持winusb
.bcdUSB = 0x0210U,
#else
// 通过 操作系统字符串描述符->扩展兼容ID描述符->扩展属性描述符 向主机报告支持winusb
// 暂未调试通过,windows未请求wIndex为5的主机扩展属性(Extend Properties),无法报告Keil-MDK使用DAPLink系统接口的GUID
.bcdUSB = 0x0200U,
#endif
.bDeviceClass = USB_CLASS_MISC,
.bDeviceSubClass = 0x02u,
.bDeviceProtocol = 0x01U,
.bMaxPacketSize0 = USBD_EP0_MAX_SIZE,
.idVendor = USBD_VID,
.idProduct = USBD_PID,
.bcdDevice = 0x0110U,
.iManufacturer = STR_IDX_MFC, // 1 厂商字符串位置
.iProduct = STR_IDX_PRODUCT, // 2 产品字符串位置
.iSerialNumber = STR_IDX_SERIAL, // 3 序列号字符串位置
.bNumberConfigurations = USBD_CFG_MAX_NUM // 1 配置描述符的数量
};
配置描述符:需要注意的是Bulk OUT端点在前,Bulk IN端点在后。
/* USB device configuration descriptor */
const usb_desc_config_set dap_config_desc = {
.config = {
.header = {
.bLength = USB_CFG_DESC_LEN,
.bDescriptorType = USB_DESCTYPE_CONFIG
},
// no vcom
.wTotalLength = (USB_CFG_DESC_LEN + USB_ITF_DESC_LEN + 2 * USB_EP_DESC_LEN),
.bNumInterfaces = 0x01U,
.bConfigurationValue = 0x01U,
.iConfiguration = 0x00U,
// bus powered, no remote wakeup
.bmAttributes = (USB_CONFIG_BUS_POWERED),
// 500mA
.bMaxPower = 0xFAu
},
// descriptor: winusb
.dap_itf = {
.header = {
.bLength = USB_ITF_DESC_LEN,
.bDescriptorType = USB_DESCTYPE_ITF
},
.bInterfaceNumber = 0x00U,
.bAlternateSetting = 0x00U,
.bNumEndpoints = 0x02U,
.bInterfaceClass = USB_CLASS_VEND_SPECIFIC,
.bInterfaceSubClass = 0x00,
.bInterfaceProtocol = 0x00,
// The string index for this interface.
.iInterface = 0x04
},
// CMSIS-DAP v2 WinUSB 要求第一个端点是 Bulk OUT,第二个端点是 Bulk IN
.dap_winusb_epout = {
.header = {
.bLength = USB_EP_DESC_LEN,
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = WINUSB_OUT_EP,
.bmAttributes = USB_EP_ATTR_BULK,
.wMaxPacketSize = WINUSB_PACKET_SIZE,
// bInterval: ignore for Bulk transfer
.bInterval = 0x00U
},
.dap_winusb_epin = {
.header = {
.bLength = USB_EP_DESC_LEN,
.bDescriptorType = USB_DESCTYPE_EP
},
.bEndpointAddress = WINUSB_IN_EP,
.bmAttributes = USB_EP_ATTR_BULK,
.wMaxPacketSize = WINUSB_PACKET_SIZE,
// bInterval: ignore for Bulk transfer
.bInterval = 0x00U
}
};
产品字符串:必须包含CMSIS-DAP
字符串
/* USB product string */
static const usb_desc_str product_string = {
.header = {
.bLength = USB_STRING_LEN(14U),
.bDescriptorType = USB_DESCTYPE_STR,
},
.unicode_string = {'C', 'M', 'S', 'I', 'S', '-', 'D', 'A', 'P', ' ','v','2','.','1'}
};
接口字符串:对于组合设备(DAPLink + USBCDC虚拟串口),必须包含CMSIS-DAP
字符串
static const usb_desc_str dap_itf_string = {
.header = {
.bLength = USB_STRING_LEN(17U),
.bDescriptorType = USB_DESCTYPE_STR,
},
.unicode_string = {'C', 'M', 'S', 'I', 'S', '-', 'D', 'A', 'P',' ','v','2','.','1','-', 'G','D'}
};
其它的字符串信息,按照usb_lib.c的作为模板,可以自行编写。
经过这样操作,USB设备就可以被主机成功枚举,由于我使用的是win10LTSC版本的系统,不能自动安装winusb驱动,因此出现代码28
表示驱动探测失败。
设备管理器也是感叹号
对于这种现象,只需要手动安装inf驱动描述文件就行,先关闭win10驱动签名验证,再到DAPLink网站下载inf文件,
USB Driver and *.inf file
直接复制即可,新建个记事本,粘贴,需要修改inf文件内的VID PID和USB设备一致,最后另存为.inf
文件,并放入单独的文件夹。
然后到设备管理器里,手动安装驱动。
最后USB就调通了。
在keil-MDK也能识别到CMSIS DAPlink工具,只是由于当前还未移植DAPLink SWD接口IO操作部分,显示SWD/JTAG通信失败。