USB端点不够用?不存在的!满血USB端点的MCU助你一臂之力!
一、容易被误导的USB2.0端点问题
在USB2.0规范文档中,对于设备模式来说,USB端点地址的描述以下,可知,低四位是USB2.0端点号,最大可到16个端点,最高位(第八位)是端点方向,可以是输入也可以是输出。
这里有两层意思:
1、每个端点可以是单向数据传输的,也就是说该端点要么是输入要么是输出。
2、在USB2.0中,每个端点都可以支持同时双向数据传输,也就是说最大支持的16个端点,数据传输可以同时输入或者输出。
但同时也带来了一些误导:
1、但有些厂家MCU文档介绍的时候,会有说明具有几个USB端点,支持不支持双向数据传输,比如"具有8个USB端点,支持双向数据传输",这里可能的意思是端点0支持双向,但其他端点可能是单向,也有可能的意思是,支持双向但是不支持同时双向数据传输。
2、开发者在使用的时候,特别USB作为设备模式,需要复合设备,由于上述的误导,可能会存在端点不足引起的一系列问题。
本文的端点指的是:端点能同时支持输入和输出双向数据传输。
二、先楫USB外设介绍
先楫目前的所有MCU系列,USB外设都是480Mbps的高速USB2.0,且设备模式下每个端点都是支持同时双向,且是满血的最大16个端点。
比如HPM5300系列的USB外设介绍:
不过需要注意的是,并不是所有的MCU系列都支持满血最大16个同时双向端点,比如最早的HPM6700系列、HPM6300系列、HPM6200系列仅支持8个同时双向端点。
在之后HPM5300系列、HPM6800系列、HPM6E00系列等后续的产品,都是满血的最大16个同时双向端点。可以在hpm sdk中可以看到IP的特性宏定义,如下:
三、验证先楫的最大端点实现
为了验证是否达到16个端点,可以使用CDC类设备来实现,以HPM5300EVK作为平台,当然HPM5301EVKlite同样也可以。
还是那句话,源码链接:
https://github.com/RCSN/hpm_sdk_extra.git
路径在demos/multi_cdc_acm_vcom中。
实现USB CDC类设备,需要两个接口,一个接口需要有INT端点,一个接口需要IN端点和OUT端点。这可以概括是需要两个输入IN端点和一个输出OUT端点。
如果每个CDC需要带上一个INT端点,最大能实现7个USB CDC类设备(2 * 7 = 14个端点),还剩下一个端点未使用。
效果如下,实现了7个USB CDC类设备。
七个cdc设备同时打开进行收发
四、实现原理步骤
这里实现多个CDC的复合设备,使用的是cherryusb协议栈,这个协议栈在本公众号已经提及很多次了,可以参考以下文章:
《 使用cherryusb的vendor class驱动实现USB副屏 》
对于该协议栈,hpm sdk也提供了十分丰富的例子,涵盖了主机,设备,多设备三大类
本文的多路CDC也是参考hpm_sdk的cherryusb例子
主要是cdc_acm和composite部分:
samples/cherryusb/cdc_acm/cdc_acm_vcom
该例子提供cdc设备的实现
samples/cherryusb/composite/cdc_acm_hid_msc
该例子提供复合设备的实现
本文不过多阐述USB原理以及cherryusb协议栈内部实现,更多配合例子进行讲解。
要实现一个USB设备,首先必须与之主机进行枚举过程,所谓的枚举过程其实就是主机不断发送命令请求从机回应,比如USB描述符(设备描述符,配置描述符,接口描述符,端点描述符,字符串描述符等等)
在cherryusb协议栈中,并不需要太多关心相关描述符的字段描述,而是定义了个相关宏定义,开放用户只需要关心的字段。比如以下的设备描述符,定义了USB_DEVICE_DESCRIPTOR_INIT
cherryusb的USB描述符设置实现,使用了回调方式获取,当USB请求是USB描述符时,对应的会进来相关描述符请求回调。下面依次介绍USB描述符的设置
1、USB设备描述符
参考复合设备例子可知:
该描述符封装使用的是USB_DEVICE_DESCRIPTOR_INIT,对应需要赋值USB版本号,USB类和子类,协议代码编号(复合设备的话,该字段为0xEF 0x02 0x01)厂商的VID PID编号等。
回调注册,其他描述符回调类似实现。
从USB抓包看,可知道主机请求获取设备描述符,从机回应。
2、在cherryusb中,以上的配置描述符,接口描述符,端点描述符都可以集中在一个描述符数组中,对应的描述符也有宏定义实现。
配置描述符 USB_CONFIG_DESCRIPTOR_INIT
接口描述符,端点描述符 CDC_ACM_DESCRIPTOR_INIT
设备描述符需要告诉总的接口数量和端点描述符长度多少
这里提示描述符长度为(9 + (CDC_ACM_DESCRIPTOR_LEN * MAX_CDC_COUNT))
9代表配置描述符占用的固定长度,cdc所需要的接口和端点描述符长度需要66字节,而实现的7个cdc,总共需要的长度为471
从USB抓包上看,获取的描述符实现没有错误
在端点实现分配上,cdc的in和out使用1到7号的双向端点,而INT端点使用8到14号端点
在cdc类的描述符中(包括IAD,接口描述符,端点描述符等)都是用CDC_ACM_DESCRIPTOR_INIT宏定义,用户只需要赋值开始接口偏移地址,三个端点号,端点BULK长度即可。这里第一个参数代表起始偏移接口号,因为一个cdc占用两个接口,所以每个CDC需要偏移两个。
在USB抓包同样也可以看到端点的分配。
接下来就是USB Device初始化相关
1、需要把USB描述符回调实现注册,API为usbd_desc_register
2、加入两个接口,以及IN和OUT端点地址注册以及操作回调
3、USB设备初始化 usbd_initialize
IN和OUT数据收发回调函数
需要注意的是,注册的USB事件,发生USBD_EVENT_CONFIGURED需要启动第一次接收传输
以上就是大概的实现思路,具体可参考查阅提供的源码。
五、总结
1、先楫的USB外设都是480Mbps的高速USB2.0,且设备模式下每个端点都是支持同时双向,且是满血的最大16个端点,还内置DMA,可满足并且最大效率实现USB2.0大部分应用场景。
2、得益于cherryUSB协议栈的开源,让USB在MCU有了无限可能发挥。