1. 背景- 由于项需求,在STM32F072中需实现5个CDC设备,折腾了很久分享出来,希望能帮助别人少踩一些坑.USB2.0全速,该款单片机支持8个输出和8个输入端点
2.1 它是什么?需要使用到什么?- 它是USB设备类型的一种,如U盘插入电脑时,电脑会知道其是U盘,是一个存储设备,那么它就属于USB MCU存储设备。USB协议中对设备进行了分类.它在stm32上叫虚拟串口设备,当将其链接电脑后,可以把它当成串口使用,如使用windows的串口工具打开它,设置波特率等等,这是我的理解。
- InterFace 0:第一个接口,其包括一个端点,和Functional_Destriptcrs,该端点为控制接口(输入型端点(IN)),Functional_Destriptcrs则是一些描述符。先不管这个
- InterFace 1:第二接口,其包括两个端点,endpoint In 和endpoint out端点,IN为stm32数据输出端点,OUT为stm32数据输入端点,随便提下,在STM32 HAL库中,USB的IN,OUT是相对于主机而言的,如:上诉的IN是指主机的输入,主机的输入(IN),对于设备来说就是OUT,数据的输出端点,IN OUT都是相对于主机而言
- 由此可见,正常情况下,一个CDC需要三个端点,其中两个IN端点,一个OUT端点
3.配置过程3.1 配置流程- 在STM32CubeIDE的帮助下,生成一个CDC的代码,这里没什么坑,点几下就成功了,注意的是可能需要将Heap设置大一点
- 修改设备描述符
- 分配端点和修改配置描述符
- 分配PMA端点缓存地址
- 修改HAL库关于CDC部分函数接口的参数,以适配多CDC设备的情况
3.2 使用STM32CubeIDE生成一个CDC程序的代码- 根据下面的文件操作即可,文件会介绍USBCDC的知识,在PDF的最后会说怎么使用cubeIDE配置,很简单没什么坑(注意的是可能需要将Heap设置大一点)
3.3 分配端点和修改配置描述符- 此时问题来了,CDC要求3个端点,其中2个输入1个输出,而stm32F072只支持8个输入端点,8个输出端点,2x5>8,正常情况下是不够的,这里就需要将IN端口中的控制端口省略,也就是第一幅图中的Interface 0中的endpoint IN端点,不分配有效端点给它,在本款单片机中IN端点的有效范围(0x80-0x87),OUT(0x00-0x08)
- #define CDC_IN_EP 0x81U /* CDC*/
- #define CDC_OUT_EP 0x01U
- #define CDC_CMD_EP 0x88U //无效端点
- #define CDC_IN_EP1 0x82U /* CDC1*/
- #define CDC_OUT_EP1 0x02U
- #define CDC_CMD_EP1 0x89U //无效端点
- #define CDC_IN_EP2 0x83U /* CDC2*/
- #define CDC_OUT_EP2 0x03U
- #define CDC_CMD_EP2 0x8AU //无效端点
- #define CDC_IN_EP3 0x84U /* CDC3*/
- #define CDC_OUT_EP3 0x04U
- #define CDC_CMD_EP3 0x8CU //无效端点
- #define CDC_IN_EP4 0x85U /* CDC4*/
- #define CDC_OUT_EP4 0x05U
- #define CDC_CMD_EP4 0x8BU //无效端点
复制代码
- 修改配置描述符号,这里面的内容就很多了,里面涉及到USB方方面面的属性和参数,其中USB_CDC_CONFIG_DESC_SIZ是该结构体的大小,记得修改,或者直接改为最大255。
- 配置描述符的结构如下:
- 配置描述符
- {
- 配置描述符总概括(相当于预览:9字节)
- IAD描述符(标识接下来的接口属于一个设备,一个设备就一个IAD描述符)CDC
- {
- 接口描述符1
- {
- 其他描述符(ACM等)
- 端点描述符(控制端点)
- }
- 接口描述符2
- {
- 端点描述符(IN)
- 端点描述符(OUT)
- }
- }
- IAD描述符(标识接下来的接口属于一个设备,一个设备就一个IAD描述符)CDC1
- {
- 接口描述符1
- {
- 其他描述符(ACM等)
- 端点描述符(控制端点)
- }
- 接口描述符2
- {
- 端点描述符(IN)
- 端点描述符(OUT)
- }
- }
- IAD描述符(标识接下来的接口属于一个设备,一个设备就一个IAD描述符)CDC2
- {
- 接口描述符1
- {
- 其他描述符(ACM等)
- 端点描述符(控制端点)
- }
- 接口描述符2
- {
- 端点描述符(IN)
- 端点描述符(OUT)
- }
- }
- IAD描述符(标识接下来的接口属于一个设备,一个设备就一个IAD描述符)CDC3
- {
- 接口描述符1
- {
- 其他描述符(ACM等)
- 端点描述符(控制端点)
- }
- 接口描述符2
- {
- 端点描述符(IN)
- 端点描述符(OUT)
- }
- }
- IAD描述符(标识接下来的接口属于一个设备,一个设备就一个IAD描述符)CDC4
- {
- 接口描述符1
- {
- 其他描述符(ACM等)
- 端点描述符(控制端点)
- }
- 接口描述符2
- {
- 端点描述符(IN)
- 端点描述符(OUT)
- }
- }
- }
复制代码
- 配置描述符如下,下面有些字段需要修改的,都用casojie标记了,CTRL+F搜索casojie就可以方便显示;
- __ALIGN_BEGIN uint8_t USBD_CDC_CfgFSDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END =
- {
- /*Configuration Descriptor*/
- 0x09, /* bLength: Configuration Descriptor size */
- USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
- USB_CDC_CONFIG_DESC_SIZ, /* wTotalLength:no of returned bytes */
- 0x00,
- 0x0A, /* bNumInterfaces: 10 interface *///总接口大小,5个CDC*2=10=0x0A casojie
- 0x01, /* bConfigurationValue: Configuration value */
- 0x00, /* iConfiguration: Index of string descriptor describing the configuration */
- 0xC0, /* bmAttributes: self powered */
- 0x32, /* MaxPower 0 mA */
- /*------------------------CDC0--------------------------------------------------8+9+5+5+7+9+7+7-*/
- // // // IAD Interface Association Descriptor //IAD描述符需要添加 casojie
- // 0x08, // bLength: Interface Descriptor size //标识IAD描述符的长度,基本都是8,casojie
- // 0x0B, // bDescriptorType: IAD //不用管
- // 0x00, // bFirstInterface //第一个接口的序号
- // 0x02, // bInterfaceCount //本IDA的接口数量
- // 0x02, // bFunctionClass: CDC //表明该IAD是一个CDC设备
- // 0x02, // bFunctionSubClass //不用管
- // 0x01, // bFunctionProtocol //控制协议等其他我也不懂,默认就行
- // 0x02, // iFunction
- /*Interface Descriptor */
- 0x09, /* bLength: Interface Descriptor size */
- USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
- /* Interface descriptor type */
- 0x00, /* bInterfaceNumber: Number of Interface */ //接口编号,从0开始 casojie
- 0x00, /* bAlternateSetting: Alternate setting */
- 0x01, /* bNumEndpoints: One endpoints used */ //接口内有多少个端点被使用 1个 casojie
- 0x02, /* bInterfaceClass: Communication Interface Class */
- 0x02, /* bInterfaceSubClass: Abstract Control Model */
- 0x01, /* bInterfaceProtocol: Common AT commands */
- 0x00, /* iInterface: */
- // /*Header Functional Descriptor*/
- // 0x05, /* bLength: Endpoint Descriptor size */
- // 0x24, /* bDescriptorType: CS_INTERFACE */
- // 0x00, /* bDescriptorSubtype: Header Func Desc */
- // 0x10, /* bcdCDC: spec release number */
- // 0x01,
- // /*Call Management Functional Descriptor*/
- // 0x05, /* bFunctionLength */
- // 0x24, /* bDescriptorType: CS_INTERFACE */
- // 0x01, /* bDescriptorSubtype: Call Management Func Desc */
- // 0x00, /* bmCapabilities: D0+D1 */
- // 0x01, /* bDataInterface: 1 */
- /*ACM Functional Descriptor*/
- 0x04, /* bFunctionLength */
- 0x24, /* bDescriptorType: CS_INTERFACE */
- 0x02, /* bDescriptorSubtype: Abstract Control Management desc */
- 0x0F, /* bmCapabilities */
- /*Union Functional Descriptor*/
- 0x05, /* bFunctionLength */
- 0x24, /* bDescriptorType: CS_INTERFACE */
- 0x06, /* bDescriptorSubtype: Union func desc */
- 0x00, /* bMasterInterface: Communication class interface */ //联合描述符,与IAD功能类似,标识哪两个接口是属于一个设备的casojie
- 0x01, /* bSlaveInterface0: Data Class Interface */ //与上面一样,上面是0,这里是1,标识0号,1号接口是属于一个CDC设备的 casojie
- /*Endpoint 2 Descriptor*/
- 0x07, /* bLength: Endpoint Descriptor size *///控制端点描述符,虽然端点号是无效的,但是这个描述符不可省略
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
- CDC_CMD_EP, /* bEndpointAddress */
- 0x03, /* bmAttributes: Interrupt */
- LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
- HIBYTE(CDC_CMD_PACKET_SIZE),
- 0x10, /* bInterval: */
- /*---------------------------------------------------------------------------*/
- /*Data class interface descriptor*/ //第二个接口描述符 casojie
- 0x09, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */
- 0x01, /* bInterfaceNumber: Number of Interface */ //接口的序号,上一个接口是0,那这里就是1 casojie
- 0x00, /* bAlternateSetting: Alternate setting */
- 0x02, /* bNumEndpoints: Two endpoints used */ //标识此接口有两个端点(IN)(OUT) casojie
- 0x0A, /* bInterfaceClass: CDC */
- 0x00, /* bInterfaceSubClass: */
- 0x00, /* bInterfaceProtocol: */
- 0x00, /* iInterface: */
- /*Endpoint OUT Descriptor*/ //OUT端点
- 0x07, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
- CDC_OUT_EP, /* bEndpointAddress */
- 0x02, /* bmAttributes: Bulk */
- LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
- HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
- 0x00, /* bInterval: ignore for Bulk transfer */
- /*Endpoint IN Descriptor*/ //IN端点
- 0x07, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
- CDC_IN_EP, /* bEndpointAddress */
- 0x02, /* bmAttributes: Bulk */
- LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
- HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
- 0x00, /* bInterval: ignore for Bulk transfer */
- /*---------------------------------------------------------------------------*/
- // IAD Interface Association Descriptor //这里就是CDC1了,重复CDC0的配置过程,注意有些字段的序号 casojie
- // 0x08, // bLength: Interface Descriptor size
- // 0x0B, // bDescriptorType: IAD
- // 0x02, // bFirstInterface //太多了,写不下去了,意思和第一个IDA一样的
- // 0x02, // bInterfaceCount
- // 0x02, // bFunctionClass: CDC
- // 0x02, // bFunctionSubClass
- // 0x01, // bFunctionProtocol
- // 0x02, // iFunction
- /*Interface Descriptor */
- 0x09, /* bLength: Interface Descriptor size */
- USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
- /* Interface descriptor type */
- 0x02, /* bInterfaceNumber: Number of Interface */
- 0x00, /* bAlternateSetting: Alternate setting */
- 0x01, /* bNumEndpoints: One endpoints used */
- 0x02, /* bInterfaceClass: Communication Interface Class */
- 0x02, /* bInterfaceSubClass: Abstract Control Model */
- 0x01, /* bInterfaceProtocol: Common AT commands */
- 0x00, /* iInterface: */
- // /*Header Functional Descriptor*/
- // 0x05, /* bLength: Endpoint Descriptor size */
- // 0x24, /* bDescriptorType: CS_INTERFACE */
- // 0x00, /* bDescriptorSubtype: Header Func Desc */
- // 0x10, /* bcdCDC: spec release number */
- // 0x01,
- // /*Call Management Functional Descriptor*/
- // 0x05, /* bFunctionLength */
- // 0x24, /* bDescriptorType: CS_INTERFACE */
- // 0x01, /* bDescriptorSubtype: Call Management Func Desc */
- // 0x00, /* bmCapabilities: D0+D1 */
- // 0x01, /* bDataInterface: 1 */
- /*ACM Functional Descriptor*/
- 0x04, /* bFunctionLength */
- 0x24, /* bDescriptorType: CS_INTERFACE */
- 0x02, /* bDescriptorSubtype: Abstract Control Management desc */
- 0x0F, /* bmCapabilities */
- /*Union Functional Descriptor*/
- 0x05, /* bFunctionLength */
- 0x24, /* bDescriptorType: CS_INTERFACE */
- 0x06, /* bDescriptorSubtype: Union func desc */
- 0x02, /* bMasterInterface: Communication class interface */
- 0x03, /* bSlaveInterface0: Data Class Interface */
- // /*Endpoint 2 Descriptor*/
- 0x07, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
- CDC_CMD_EP1, /* bEndpointAddress */
- 0x03, /* bmAttributes: Interrupt */
- LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
- HIBYTE(CDC_CMD_PACKET_SIZE),
- 0x10, /* bInterval: */
- /*---------------------------------------------------------------------------*/
- /*Data class interface descriptor*/
- 0x09, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */
- 0x03, /* bInterfaceNumber: Number of Interface */
- 0x00, /* bAlternateSetting: Alternate setting */
- 0x02, /* bNumEndpoints: Two endpoints used */
- 0x0A, /* bInterfaceClass: CDC */
- 0x00, /* bInterfaceSubClass: */
- 0x00, /* bInterfaceProtocol: */
- 0x00, /* iInterface: */
- /*Endpoint OUT Descriptor*/
- 0x07, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
- CDC_OUT_EP1, /* bEndpointAddress */
- 0x02, /* bmAttributes: Bulk */
- LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
- HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
- 0x00, /* bInterval: ignore for Bulk transfer */
- /*Endpoint IN Descriptor*/
- 0x07, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
- CDC_IN_EP1, /* bEndpointAddress */
- 0x02, /* bmAttributes: Bulk */
- LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
- HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
- 0x00, /* bInterval: ignore for Bulk transfer */
- /*---------------------------------------------------------------------------*/
- // IAD Interface Association Descriptor
- // 0x08, // bLength: Interface Descriptor size
- // 0x0B, // bDescriptorType: IAD
- // 0x04, // bFirstInterface
- // 0x02, // bInterfaceCount
- // 0x02, // bFunctionClass: CDC
- // 0x02, // bFunctionSubClass
- // 0x01, // bFunctionProtocol
- // 0x02, // iFunction
- /*Interface Descriptor */
- 0x09, /* bLength: Interface Descriptor size */
- USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
- /* Interface descriptor type */
- 0x04, /* bInterfaceNumber: Number of Interface */
- 0x00, /* bAlternateSetting: Alternate setting */
- 0x01, /* bNumEndpoints: One endpoints used */
- 0x02, /* bInterfaceClass: Communication Interface Class */
- 0x02, /* bInterfaceSubClass: Abstract Control Model */
- 0x01, /* bInterfaceProtocol: Common AT commands */
- 0x00, /* iInterface: */
- // /*Header Functional Descriptor*/
- // 0x05, /* bLength: Endpoint Descriptor size */
- // 0x24, /* bDescriptorType: CS_INTERFACE */
- // 0x00, /* bDescriptorSubtype: Header Func Desc */
- // 0x10, /* bcdCDC: spec release number */
- // 0x01,
- // /*Call Management Functional Descriptor*/
- // 0x05, /* bFunctionLength */
- // 0x24, /* bDescriptorType: CS_INTERFACE */
- // 0x01, /* bDescriptorSubtype: Call Management Func Desc */
- // 0x00, /* bmCapabilities: D0+D1 */
- // 0x01, /* bDataInterface: 1 */
- /*ACM Functional Descriptor*/
- 0x04, /* bFunctionLength */
- 0x24, /* bDescriptorType: CS_INTERFACE */
- 0x02, /* bDescriptorSubtype: Abstract Control Management desc */
- 0x0F, /* bmCapabilities */
- /*Union Functional Descriptor*/
- 0x05, /* bFunctionLength */
- 0x24, /* bDescriptorType: CS_INTERFACE */
- 0x06, /* bDescriptorSubtype: Union func desc */
- 0x04, /* bMasterInterface: Communication class interface */
- 0x05, /* bSlaveInterface0: Data Class Interface */
- // /*Endpoint 2 Descriptor*/
- 0x07, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
- CDC_CMD_EP2, /* bEndpointAddress */
- 0x03, /* bmAttributes: Interrupt */
- LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
- HIBYTE(CDC_CMD_PACKET_SIZE),
- 0x10, /* bInterval: */
- /*---------------------------------------------------------------------------*/
- /*Data class interface descriptor*/
- 0x09, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */
- 0x05, /* bInterfaceNumber: Number of Interface */
- 0x00, /* bAlternateSetting: Alternate setting */
- 0x02, /* bNumEndpoints: Two endpoints used */
- 0x0A, /* bInterfaceClass: CDC */
- 0x00, /* bInterfaceSubClass: */
- 0x00, /* bInterfaceProtocol: */
- 0x00, /* iInterface: */
- /*Endpoint OUT Descriptor*/
- 0x07, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
- CDC_OUT_EP2, /* bEndpointAddress */
- 0x02, /* bmAttributes: Bulk */
- LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
- HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
- 0x00, /* bInterval: ignore for Bulk transfer */
- /*Endpoint IN Descriptor*/
- 0x07, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
- CDC_IN_EP2, /* bEndpointAddress */
- 0x02, /* bmAttributes: Bulk */
- LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
- HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
- 0x00, /* bInterval: ignore for Bulk transfer */
- /*---------------------------------------------------------------------------*/
- // IAD Interface Association Descriptor
- // 0x08, // bLength: Interface Descriptor size
- // 0x0B, // bDescriptorType: IAD
- // 0x06, // bFirstInterface
- // 0x02, // bInterfaceCount
- // 0x02, // bFunctionClass: CDC
- // 0x02, // bFunctionSubClass
- // 0x01, // bFunctionProtocol
- // 0x02, // iFunction
- /*Interface Descriptor */
- 0x09, /* bLength: Interface Descriptor size */
- USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
- /* Interface descriptor type */
- 0x06, /* bInterfaceNumber: Number of Interface */
- 0x00, /* bAlternateSetting: Alternate setting */
- 0x01, /* bNumEndpoints: One endpoints used */
- 0x02, /* bInterfaceClass: Communication Interface Class */
- 0x02, /* bInterfaceSubClass: Abstract Control Model */
- 0x01, /* bInterfaceProtocol: Common AT commands */
- 0x00, /* iInterface: */
- // /*Header Functional Descriptor*/
- // 0x05, /* bLength: Endpoint Descriptor size */
- // 0x24, /* bDescriptorType: CS_INTERFACE */
- // 0x00, /* bDescriptorSubtype: Header Func Desc */
- // 0x10, /* bcdCDC: spec release number */
- // 0x01,
- // /*Call Management Functional Descriptor*/
- // 0x05, /* bFunctionLength */
- // 0x24, /* bDescriptorType: CS_INTERFACE */
- // 0x01, /* bDescriptorSubtype: Call Management Func Desc */
- // 0x00, /* bmCapabilities: D0+D1 */
- // 0x01, /* bDataInterface: 1 */
- /*ACM Functional Descriptor*/
- 0x04, /* bFunctionLength */
- 0x24, /* bDescriptorType: CS_INTERFACE */
- 0x02, /* bDescriptorSubtype: Abstract Control Management desc */
- 0x0F, /* bmCapabilities */
- /*Union Functional Descriptor*/
- 0x05, /* bFunctionLength */
- 0x24, /* bDescriptorType: CS_INTERFACE */
- 0x06, /* bDescriptorSubtype: Union func desc */
- 0x06, /* bMasterInterface: Communication class interface */
- 0x07, /* bSlaveInterface0: Data Class Interface */
- /*Endpoint 2 Descriptor*/
- 0x07, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
- CDC_CMD_EP3, /* bEndpointAddress */
- 0x03, /* bmAttributes: Interrupt */
- LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
- HIBYTE(CDC_CMD_PACKET_SIZE),
- 0x10, /* bInterval: */
- /*---------------------------------------------------------------------------*/
- /*Data class interface descriptor*/
- 0x09, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */
- 0x07, /* bInterfaceNumber: Number of Interface */
- 0x00, /* bAlternateSetting: Alternate setting */
- 0x02, /* bNumEndpoints: Two endpoints used */
- 0x0A, /* bInterfaceClass: CDC */
- 0x00, /* bInterfaceSubClass: */
- 0x00, /* bInterfaceProtocol: */
- 0x00, /* iInterface: */
- /*Endpoint OUT Descriptor*/
- 0x07, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
- CDC_OUT_EP3, /* bEndpointAddress */
- 0x02, /* bmAttributes: Bulk */
- LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
- HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
- 0x00, /* bInterval: ignore for Bulk transfer */
- /*Endpoint IN Descriptor*/
- 0x07, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
- CDC_IN_EP3, /* bEndpointAddress */
- 0x02, /* bmAttributes: Bulk */
- LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
- HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
- 0x00, /* bInterval: ignore for Bulk transfer */
- // /*---------------------------------------------------------------------------*/
- // // IAD Interface Association Descriptor
- // // 0x08, // bLength: Interface Descriptor size
- // // 0x0B, // bDescriptorType: IAD
- // // 0x08, // bFirstInterface
- // // 0x02, // bInterfaceCount
- // // 0x02, // bFunctionClass: CDC
- // // 0x02, // bFunctionSubClass
- // // 0x01, // bFunctionProtocol
- // // 0x02, // iFunction
- /*Interface Descriptor */
- 0x09, /* bLength: Interface Descriptor size */
- USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
- /* Interface descriptor type */
- 0x08, /* bInterfaceNumber: Number of Interface */
- 0x00, /* bAlternateSetting: Alternate setting */
- 0x01, /* bNumEndpoints: One endpoints used */
- 0x02, /* bInterfaceClass: Communication Interface Class */
- 0x02, /* bInterfaceSubClass: Abstract Control Model */
- 0x01, /* bInterfaceProtocol: Common AT commands */
- 0x00, /* iInterface: */
- // /*Header Functional Descriptor*/
- // 0x05, /* bLength: Endpoint Descriptor size */
- // 0x24, /* bDescriptorType: CS_INTERFACE */
- // 0x00, /* bDescriptorSubtype: Header Func Desc */
- // 0x10, /* bcdCDC: spec release number */
- // 0x01,
- // /*Call Management Functional Descriptor*/
- // 0x05, /* bFunctionLength */
- // 0x24, /* bDescriptorType: CS_INTERFACE */
- // 0x01, /* bDescriptorSubtype: Call Management Func Desc */
- // 0x00, /* bmCapabilities: D0+D1 */
- // 0x01, /* bDataInterface: 1 */
- /*ACM Functional Descriptor*/
- 0x04, /* bFunctionLength */
- 0x24, /* bDescriptorType: CS_INTERFACE */
- 0x02, /* bDescriptorSubtype: Abstract Control Management desc */
- 0x0F, /* bmCapabilities */
- /*Union Functional Descriptor*/
- 0x05, /* bFunctionLength */
- 0x24, /* bDescriptorType: CS_INTERFACE */
- 0x06, /* bDescriptorSubtype: Union func desc */
- 0x08, /* bMasterInterface: Communication class interface */
- 0x09, /* bSlaveInterface0: Data Class Interface */
- /*Endpoint 2 Descriptor*/
- 0x07, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
- CDC_CMD_EP4, /* bEndpointAddress */
- 0x03, /* bmAttributes: Interrupt */
- LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
- HIBYTE(CDC_CMD_PACKET_SIZE),
- 0x10, /* bInterval: */
- /*---------------------------------------------------------------------------*/
- /*
- /*Data class interface descriptor*/
- 0x09, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */
- 0x09, /* bInterfaceNumber: Number of Interface */
- 0x00, /* bAlternateSetting: Alternate setting */
- 0x02, /* bNumEndpoints: Two endpoints used */
- 0x0A, /* bInterfaceClass: CDC */
- 0x00, /* bInterfaceSubClass: */
- 0x00, /* bInterfaceProtocol: */
- 0x00, /* iInterface: */
- /*Endpoint OUT Descriptor*/
- 0x07, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
- CDC_OUT_EP4, /* bEndpointAddress */
- 0x02, /* bmAttributes: Bulk */
- LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
- HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
- 0x00, /* bInterval: ignore for Bulk transfer */
- /*Endpoint IN Descriptor*/
- 0x07, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
- CDC_IN_EP4, /* bEndpointAddress */
- 0x02, /* bmAttributes: Bulk */
- LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
- HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
- 0x00 /* bInterval: ignore for Bulk transfer */
- }
复制代码
- 为什么上面有些描述符被注释掉了?
- 配置5个CDC如果描述符都写上了,这个结构体就超过了255个字节大小,超过了USB2.0最大配置描述符长度
- 在我的项目中,IAD和联合描述符(/Union Functional Descriptor/)功能一样,我这里注释IAD描述符,不会影响USB,但在windows平台下,它认的是IAD描述符,这个时候就可以注释联合描述符,打开IAD描述符,总之,省略一些非必要描述,减少长度
- 这里的坑很多很多,需要很耐心,配错一个字段,千奇百怪的问题就来了
3.4 配置PMA---USB硬件缓存3.4.1 PMA是啥?- 此部分感谢一个大佬的帖子,理解来源与它
- 这个PMA的作用就是USB设备模块用来实现MCU与主机进行数据通信的一个专门的数据缓冲区,我们称之为USB硬件缓冲区。说得具体点就是USB模块把来自主机的数据接收进来后先放到PMA,然后再被拷贝到用户数据缓存区;或者MCU要发送到主机的数据,先从用户数据缓存区拷贝**MA,再通过USB模块负责发送给主机。
3.4.2 5个CDC的PMA怎么配置?
- u_int16_t ep_addR=0x70; //起始分配地址: 这里是大坑 casojie
- u_int8_t ep_addr_size=64;
- HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x00 , PCD_SNG_BUF, ep_addR);ep_addR+=ep_addr_size; //此端点是用于USB枚举和基本的通信使用,默认就有,且不可省去
- HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x80 , PCD_SNG_BUF, ep_addR);ep_addR+=ep_addr_size;
- /* USER CODE END EndPoint_Configuration */
- //CDC0 每个端点都分配了64B
- HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_IN_EP , PCD_SNG_BUF, ep_addR);ep_addR+=ep_addr_size;
- HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_OUT_EP , PCD_SNG_BUF, ep_addR);ep_addR+=ep_addr_size;
- //CDC1 //+5
- HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_IN_EP1, PCD_SNG_BUF, ep_addR);ep_addR+=ep_addr_size; //192 258
- HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_OUT_EP1 , PCD_SNG_BUF, ep_addR);ep_addR+=ep_addr_size; //272 2A8
- //CDC2
- HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_IN_EP2 , PCD_SNG_BUF, ep_addR);ep_addR+=ep_addr_size; //0x270
- HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_OUT_EP2 , PCD_SNG_BUF, ep_addR);ep_addR+=ep_addr_size;//0x2C0
- //CDC3
- HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_IN_EP3, PCD_SNG_BUF, ep_addR);ep_addR+=ep_addr_size; //192 258 0x1E0
- HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_OUT_EP3 , PCD_SNG_BUF, ep_addR);ep_addR+=ep_addr_size;
- //CDC4
- HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_IN_EP4, PCD_SNG_BUF, ep_addR);ep_addR+=ep_addr_size;
- HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_OUT_EP4 , PCD_SNG_BUF, ep_addR);ep_addR+=ep_addr_size;
复制代码
- 在PMA的开头部分,是用于记录信息的,这些信息就是:端点缓存区的起始地址:说这简单点,里面写了端点0的缓存区的起始地址是X,端点1的缓冲区起始地址是Y等等
- 由此可见,当使用的端点数量比较多时,信息记录区索要的空间就比较大,反之,就小,需要动态调整
- 由大佬的帖子可知,记录一个端点的起始地址信息需要8字节,那么我们五个CDC,则5x2(IN,OUT)=10,再加上0x80 ,0x00端点,一共12个.则12*8=76字节
- 因此,端点(0x00)分配的地址需要往后偏移,默认的不行,如这次需要76字节的大小,换成16进制,0x4C,我设置了0x70>0x4C,默认的太小了,会导致部分信息记录区被端点0的数据覆盖,而导致部分端点无法使用.
4.修改USB初始化代码,新增对新端点的初始化
- static uint8_t USBD_CDC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
- {
- uint8_t ret = 0U;
- USBD_CDC_HandleTypeDef *hcdc;
- if (pdev->dev_speed == USBD_SPEED_HIGH)
- {
- /* Open EP IN */
- USBD_LL_OpenEP(pdev, CDC_IN_EP, USBD_EP_TYPE_BULK,
- CDC_DATA_HS_IN_PACKET_SIZE);
- pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 1U;
- /* Open EP OUT */
- USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK,
- CDC_DATA_HS_OUT_PACKET_SIZE);
- pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 1U;
- }
- else
- {
- /* Open EP IN */
- USBD_LL_OpenEP(pdev, CDC_IN_EP, USBD_EP_TYPE_BULK,
- CDC_DATA_FS_IN_PACKET_SIZE);
- pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 1U;
- /* Open EP OUT */
- USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK,
- CDC_DATA_FS_OUT_PACKET_SIZE);
- pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 1U;
- /* Open EP IN */
- USBD_LL_OpenEP(pdev, CDC_IN_EP1, USBD_EP_TYPE_BULK,
- CDC_DATA_FS_IN_PACKET_SIZE);
- pdev->ep_in[CDC_IN_EP1 & 0xFU].is_used = 1U;
- /* Open EP OUT */
- USBD_LL_OpenEP(pdev, CDC_OUT_EP1, USBD_EP_TYPE_BULK,
- CDC_DATA_FS_OUT_PACKET_SIZE);
- pdev->ep_out[CDC_OUT_EP1 & 0xFU].is_used = 1U;
- //--------------------------------------------------------------------
- /* Open EP IN */
- USBD_LL_OpenEP(pdev, CDC_IN_EP2, USBD_EP_TYPE_BULK,
- CDC_DATA_FS_IN_PACKET_SIZE);
- pdev->ep_in[CDC_IN_EP2 & 0xFU].is_used = 1U;
- /* Open EP OUT */
- USBD_LL_OpenEP(pdev, CDC_OUT_EP2, USBD_EP_TYPE_BULK,
- CDC_DATA_FS_OUT_PACKET_SIZE);
- pdev->ep_out[CDC_OUT_EP2 & 0xFU].is_used = 1U;
- //-------------------------5---------------------
- /* Open EP IN */
- USBD_LL_OpenEP(pdev, CDC_IN_EP3, USBD_EP_TYPE_BULK,
- CDC_DATA_FS_IN_PACKET_SIZE);
- pdev->ep_in[CDC_IN_EP3 & 0xFU].is_used = 1U;
- /* Open EP OUT */
- USBD_LL_OpenEP(pdev, CDC_OUT_EP3, USBD_EP_TYPE_BULK,
- CDC_DATA_FS_OUT_PACKET_SIZE);
- pdev->ep_out[CDC_OUT_EP3 & 0xFU].is_used = 1U;
- //-------------------------5---------------------
- /* Open EP IN */
- USBD_LL_OpenEP(pdev, CDC_IN_EP4, USBD_EP_TYPE_BULK,
- CDC_DATA_FS_IN_PACKET_SIZE);
- pdev->ep_in[CDC_IN_EP4 & 0xFU].is_used = 1U;
- /* Open EP OUT */
- USBD_LL_OpenEP(pdev, CDC_OUT_EP4, USBD_EP_TYPE_BULK,
- CDC_DATA_FS_OUT_PACKET_SIZE);
- pdev->ep_out[CDC_OUT_EP4 & 0xFU].is_used = 1U;
- }
- /* Open Command IN EP *///控制端口无效,不要取消注释
- // USBD_LL_OpenEP(pdev, CDC_CMD_EP, USBD_EP_TYPE_INTR, CDC_CMD_PACKET_SIZE);
- // pdev->ep_in[CDC_CMD_EP & 0xFU].is_used = 1U;
- // USBD_LL_OpenEP(pdev, CDC_CMD_EP1, USBD_EP_TYPE_INTR, CDC_CMD_PACKET_SIZE);
- // pdev->ep_in[CDC_CMD_EP1 & 0xFU].is_used = 1U;
- // USBD_LL_OpenEP(pdev, CDC_CMD_EP2, USBD_EP_TYPE_INTR, CDC_CMD_PACKET_SIZE);
- // pdev->ep_in[CDC_CMD_EP2 & 0xFU].is_used = 1U;
- // USBD_LL_OpenEP(pdev, CDC_CMD_EP3, USBD_EP_TYPE_INTR, CDC_CMD_PACKET_SIZE);
- // pdev->ep_in[CDC_CMD_EP3 & 0xFU].is_used = 1U;
- pdev->pClassData = USBD_malloc(sizeof(USBD_CDC_HandleTypeDef));
- if (pdev->pClassData == NULL)
- {
- ret = 1U;
- }
- else
- {
- hcdc = (USBD_CDC_HandleTypeDef *) pdev->pClassData;
- /* Init physical Interface components */
- ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Init();
- /* Init Xfer states */
- hcdc->TxState = 0U;
- hcdc->RxState = 0U;
- if (pdev->dev_speed == USBD_SPEED_HIGH)
- {
- /* Prepare Out endpoint to receive next packet */
- USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer,
- CDC_DATA_HS_OUT_PACKET_SIZE);
- }
- else
- {
- /* Prepare Out endpoint to receive next packet */
- USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer,
- CDC_DATA_FS_OUT_PACKET_SIZE);
- /* Prepare Out endpoint to receive next packet */
- USBD_LL_PrepareReceive(pdev, CDC_OUT_EP1, hcdc->RxBuffer,
- CDC_DATA_FS_OUT_PACKET_SIZE);
- /* Prepare Out endpoint to receive next packet */
- USBD_LL_PrepareReceive(pdev, CDC_OUT_EP2, hcdc->RxBuffer,
- CDC_DATA_FS_OUT_PACKET_SIZE);
- /* Prepare Out endpoint to receive next packet */
- USBD_LL_PrepareReceive(pdev, CDC_OUT_EP3, hcdc->RxBuffer,
- CDC_DATA_FS_OUT_PACKET_SIZE);
- /* Prepare Out endpoint to receive next packet */
- USBD_LL_PrepareReceive(pdev, CDC_OUT_EP4, hcdc->RxBuffer,
- CDC_DATA_FS_OUT_PACKET_SIZE);
- USBD_LL_PrepareReceive(pdev, CDC_OUT_EP5, hcdc->RxBuffer,
- CDC_DATA_FS_OUT_PACKET_SIZE);
- }
- }
- return ret;
- }
复制代码
5.修改HAL默认USB CDC相关接口函数- 现在是多CDC的状况了,但默认的HAL库的函数均只是针对一个CDC的情况,我们需要将端口的参数引出来
- USBD_CDC_DataOut:USB接收函数回调,修改提供端口参数
- CDC_Receive_FS(uint8_t *Buf, uint32_t *Len):USB CDC接收函数
- CDC_Transmit_FS(uint8_t* Buf, uint16_t Len):USB CDC发送函数
- USBD_CDC_TransmitPacket(USBD_HandleTypeDef *pdev)
- USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev)
- 先说说这几个函数的关系:CDC_Transmit_FS---调用--->USBD_CDC_TransmitPacket(USBD_HandleTypeDef *pdev)
- USBD_CDC_DataOut(数据接收回调)---调用--->CDC_Receive_FS
CDC_Receive_FS(uint8_t *Buf, uint32_t *Len)修改为CDC_Receive_FS(uint8_t* Buf, uint32_t *Len,uint8_t epnum): 此后所有OUT端点来的数据都将进入此函数,可通过参数epnum区分
- static uint8_t USBD_CDC_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum)
- {
- USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *) pdev->pClassData;
- /* Get the received data length */
- hcdc->RxLength = USBD_LL_GetRxDataSize(pdev, epnum);
- /* USB data will be immediately processed, this allow next USB traffic being
- NAKed till the end of the application Xfer */
- // printf("USBD_CDC_DataOut.1294:Len:%d :EP:%02X\n",hcdc->RxLength,epnum);
- if (pdev->pClassData != NULL)
- {
- ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Receive(hcdc->RxBuffer, &hcdc->RxLength,epnum);//此处修改,加入端点,相关的函数声明也跟这这样改
- return USBD_OK;
- }
- else
- {
- return USBD_FAIL;
- }
- }
复制代码
- CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)该为CDC_Transmit_FS(uint8_t* Buf, uint16_t Len,uint8_t epnum):只是增加一个参数,函数体不用动
- uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len,uint8_t epnum)
- {
- uint8_t result = USBD_OK;
- /* USER CODE BEGIN 7 */
- USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceFS_CDC.pClassData;
- if (hcdc->TxState != 0){
- return USBD_BUSY;
- }
- USBD_CDC_SetTxBuffer(&hUsbDeviceFS_CDC, Buf, Len);
- result = USBD_CDC_TransmitPacket(&hUsbDeviceFS_CDC,epnum); //将epnum的端点传到这个函数,这个函数也需要改
- /* USER CODE END 7 */
- return result;
- }
复制代码
- USBD_CDC_TransmitPacket的修改
- uint8_t USBD_CDC_TransmitPacket(USBD_HandleTypeDef *pdev,uint8_t epnum)
- {
- USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *) pdev->pClassData;
- if (pdev->pClassData != NULL)
- {
- if (hcdc->TxState == 0U)
- {
- /* Tx Transfer in progress */
- hcdc->TxState = 1U;
- /* Update the packet total length */
- pdev->ep_in[epnum & 0xFU].total_length = hcdc->TxLength;//将原本的CDC_IN_EP更改为我们传入的端点
- /* Transmit next packet */
- USBD_LL_Transmit(pdev, epnum, hcdc->TxBuffer,
- (uint16_t)hcdc->TxLength);//此处也是
- return USBD_OK;
- }
- else
- {
- return USBD_BUSY;
- }
- }
- else
- {
- return USBD_FAIL;
- }
- }
复制代码
该函数的作用:当端口接收一包数据后,需要执行此函数来接收下一包数据,简单的说:告诉主机我可以接收下一包数据了
- uint8_t USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev,uint8_t epnum)
- {
- USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *) pdev->pClassData;
- /* Suspend or Resume USB Out process */
- if (pdev->pClassData != NULL)
- {
- if (pdev->dev_speed == USBD_SPEED_HIGH)
- {
- /* Prepare Out endpoint to receive next packet */
- USBD_LL_PrepareReceive(pdev,
- CDC_OUT_EP,
- hcdc->RxBuffer,
- CDC_DATA_HS_OUT_PACKET_SIZE);
- }
- else
- {
- /* Prepare Out endpoint to receive next packet */
- USBD_LL_PrepareReceive(pdev,
- epnum,
- hcdc->RxBuffer,
- CDC_DATA_FS_OUT_PACKET_SIZE);
- }
- return USBD_OK;
- }
- else
- {
- return USBD_FAIL;
- }
- }
复制代码
对以后而言,我们就使用CDC_Receive_FS(uint8_t* Buf, uint32_t *Len,uint8_t epnum)和CDC_Transmit_FS(uint8_t* Buf, uint16_t Len,uint8_t epnum) 向CDCx发送数据,就填写CDCx的IN端口 * 接收CDCx数据时,CDC_Receive_FS函数会传入该数据来源(OUT)端口,就可知道哪个CDCx了
6.后续- 此时就完成了,不过不排除有些深层的问题。
- 如:当五个CDC同时在Read时,其中一个close,有可能会导致其他四个都无法再Read出数据,需要重新Open,但在电脑的深度depin系统上并没有这个问题,在项目中的板子上就有,希望大佬们提供下线索。感激不尽
转载自:casojie
|