900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > STM32CubeMX | 基于STM32使用HAL库实现USB组合设备之多路CDC

STM32CubeMX | 基于STM32使用HAL库实现USB组合设备之多路CDC

时间:2021-01-10 04:51:41

相关推荐

STM32CubeMX | 基于STM32使用HAL库实现USB组合设备之多路CDC

STM32CubeMX | 基于STM32使用HAL库实现USB组合设备之多路CDC

本博客完整代码下载地址:/download/qq153471503/18169362

或关注以下公众号,回复关键字MultiCDC获取下载链接!

目录

STM32CubeMX | 基于STM32使用HAL库实现USB组合设备之多路CDC第一步:基础工程生成第二步:USB设备描述符的修改第三步:修改PMA端点分布第四步:修改配置描述符第五步:修改函数接口注意事项及问题补充:3路CDC实现

1、本篇文章已经默认您已经会使用STM32CUBEMX生成CDC工程并测试通过了,如果你还不会可参考我的另一篇博客:STM32快速实现USB虚拟串口+回环测试+USB转TTL的功能

2、USB组合设备的编写需要具备一定的USB相关知识,如果你不了解,那么请先看一下我的这篇博客:STM32 USB相关知识扫盲

工程环境:

STM32F103RCSTM32CubeIDE 1.5.1

第一步:基础工程生成

~~~~~~~~首先先用STM32CUBEMX生成CDC工程,并测试通过没有问题后,就可以着手开始下一步的修改,如果你还不了解CDC虚拟串口,那么可以参考文章开头链接第一条说明里的博客内容。

第二步:USB设备描述符的修改

~~~~~~~~这一步很简单的,就是修改usbd_desc.c中的设备描述符数组USBD_FS_DeviceDesc,将设备类型改为组合设备类型:

第三步:修改PMA端点分布

然后进入usbd_conf.c文件中,找到USBD_LL_Init函数,修改PMA端点初始化:

说一下PMA为什么这么改,目前我们用到的端点有:

0X80、0X00为USB所必须的端点0X81、0X01为CDC1的输入输出端点0X82、0X02为CDC2的输入输出端点0X83为CDC1的命令控制端点(0X03端点未使用,但是占用空间)0X84为CDC2的命令控制端点(0X04端点未使用,但是占用空间)

所以一共10个端点,10x8=80字节,十六进制为0X50,所以端点的缓存地址就是从PMA偏移0X50地址处开始。

输入输出端点最大可配置为64字节的缓存,所以是自增0X40。

命令端点的缓存数据大小是8字节缓存,自增16字节是因为光用到了输入端点,输出端点没用但是占用PMA空间,所以是自增0X10。

如下图指示是8字节:

接下修改端点初始化,找到USBD_CDC_Init函数,添加CDC2的端点初始化操作:

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;/ +++lakun /* Open EP IN */USBD_LL_OpenEP(pdev, CDC2_IN_EP, USBD_EP_TYPE_BULK,CDC_DATA_FS_IN_PACKET_SIZE);pdev->ep_in[CDC2_IN_EP & 0xFU].is_used = 1U;/* Open EP OUT */USBD_LL_OpenEP(pdev, CDC2_OUT_EP, USBD_EP_TYPE_BULK,CDC_DATA_FS_OUT_PACKET_SIZE);pdev->ep_out[CDC2_OUT_EP & 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;/ +++lakun /* Open Command IN EP */USBD_LL_OpenEP(pdev, CDC2_CMD_EP, USBD_EP_TYPE_INTR, CDC_CMD_PACKET_SIZE);pdev->ep_in[CDC2_CMD_EP & 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);/ +++lakun /* Prepare Out endpoint to receive next packet */USBD_LL_PrepareReceive(pdev, CDC2_OUT_EP, hcdc->RxBuffer,CDC_DATA_FS_OUT_PACKET_SIZE);}}return ret;}

注:这一步骤的修改都非常挂关键,好多人组合设备修改出错的原因就是在修改PMA这里出的问题!在本文章起始处的蓝色链接《STM32 USB知识扫盲》文中有对PAM的详细讲解,一定要仔细看一下并理解!

第四步:修改配置描述符

贴一下我修改好的两路CDC配置,修改过的地方都有+++lakun样的标记:

/* USB CDC device Configuration Descriptor */__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,0x04, /* bNumInterfaces: 4 interface */ /* +++lakun:一个CDC用到了连个接口,两个CDC就是4个接口 */0x01, /* bConfigurationValue: Configuration value */0x00, /* iConfiguration: Index of string descriptor describing the configuration */0xC0, /* bmAttributes: self powered */0x32, /* MaxPower 0 mA *//*---------------------------------------------------------------------------*///// +++lakun: IAD(Interface Association Descriptor)//0X08, // bLength: Interface Descriptor size,固定值0X0B, // bDescriptorType: IAD,固定值0X00, // bFirstInterface,第一个接口的起始序号0X02, // bInterfaceCount,本IAD下的接口数量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 */ /* +++lakun:接口编号,从0开始 */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 */0x02, /* bmCapabilities *//*Union Functional Descriptor*/0x05, /* bFunctionLength */0x24, /* bDescriptorType: CS_INTERFACE */0x06, /* bDescriptorSubtype: Union func desc */0x00, /* bMasterInterface: Communication class interface */ /* +++lakun:这里指示的是本CDC的通信接口编号 */0x01, /* bSlaveInterface0: Data Class Interface *//* +++lakun:这里指示的是本CDC的数据接口编号 *//*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),CDC_FS_BINTERVAL, /* bInterval: *//*---------------------------------------------------------------------------*//*Data class interface descriptor*/0x09, /* bLength: Endpoint Descriptor size */USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */0x01, /* bInterfaceNumber: Number of Interface *//* +++lakun:CDC1的数据接口编号 */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_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*/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 *///// +++lakun: IAD(Interface Association Descriptor)//0X08, // bLength: Interface Descriptor size,固定值0X0B, // bDescriptorType: IAD,固定值0X02, // bFirstInterface,第一个接口的起始序号(第0、1编号的接口用于CDC1,现在是第二个CDC了,所以从2开始)0X02, // bInterfaceCount,本IAD下的接口数量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 */0x02, /* bInterfaceNumber: Number of Interface */ /* +++lakun:这里就是第二个CDC了,第0、1编号的接口给CDC1使用了,所以是2开始的 */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 */0x02, /* bmCapabilities *//*Union Functional Descriptor*/0x05, /* bFunctionLength */0x24, /* bDescriptorType: CS_INTERFACE */0x06, /* bDescriptorSubtype: Union func desc */0x02, /* bMasterInterface: Communication class interface *//* +++lakun:这里指示的是本CDC的通信接口编号 */0x03, /* bSlaveInterface0: Data Class Interface */ /* +++lakun:这里指示的是本CDC的数据接口编号 *//*Endpoint 2 Descriptor*/0x07, /* bLength: Endpoint Descriptor size */USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */CDC2_CMD_EP, /* bEndpointAddress */0x03, /* bmAttributes: Interrupt */LOBYTE(CDC_CMD_PACKET_SIZE),/* wMaxPacketSize: */HIBYTE(CDC_CMD_PACKET_SIZE),CDC_FS_BINTERVAL, /* bInterval: *//*---------------------------------------------------------------------------*//*Data class interface descriptor*/0x09, /* bLength: Endpoint Descriptor size */USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */0x03, /* bInterfaceNumber: Number of Interface */ /* +++lakun:CDC2的数据接口编号 */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 */CDC2_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*/0x07, /* bLength: Endpoint Descriptor size */USB_DESC_TYPE_ENDPOINT,/* bDescriptorType: Endpoint */CDC2_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 */} ;

第五步:修改函数接口

默认的HAL库函数是只针对一路CDC的情况,所以我们需要修改成多路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_CDC_DataOut->

~~~~~~~~~~~~CDC_Receive_FS

USBD_CDC_DataOut函数有一个参数是epnum,这个是端点号,我们就可以使用该参数来区分是哪一路CDC!

所以USBD_CDC_DataOut函数添加上epnum参数,改为:

USBD_CDC_TransmitPacket函数改为:

USBD_CDC_ReceivePacket函数改为:

CDC_Transmit_FS函数改为:

CDC_Receive_FS函数改为:

到此就已经修改完毕了,将USB接入电脑后,设备管理区会多出现一个USB复合设备和两个端口,这样就是成功了:

下面进行测试,使用串口助手同时对两路CDC进行收发测试效果如下:

互不影响,USB两路CDC成功!

注意事项及问题

如果你移植的时候将USB接入电脑后,设备管理器里面只会出现1路串口,那么可能是因为你先用的CDC单独的工程测试的,电脑已经默认枚举为了VCP,那么此时有两种办法解决:

设备管理器卸载掉出现的虚拟串口,重新拔插USB修改程序里的USBD_VID,换个其他编号

我是用第二个办法解决的!

补充:3路CDC实现

实现3路CDC修改步骤跟实现2路一样,首先是PMA端点的配置:

为什么要把CDC_DATA_FS_MAX_PACKET_SIZE改成32呢?

现在我们算一下使用到的端点:

0X80、0X00用于USB必须的0X81、0X01用于CDC1输入输出端点0X82、0X02用于CDC2输入输出端点0X83、0X03用于CDC3输入输出端点0X84用于CDC1命令控制端点、0X04未用但占用空间0X85用于CDC2命令控制端点、0X05未用但占用空间0X86用于CDC3命令控制端点、0X06未用但占用空间

可以看到我们用到的端点很多,光算输入输出端点一共8个,每个端点最大占用64,那么就是512字节,而PMA一共才512字节,所以如果将端点缓冲设置为最大64字节的话,3路CDC串口缓存空间不够用!不改成32的话你会发现第1和2路串口收发没问题,而第三路串口返回的都是0!

如下图:

改成32后,效果如下:

三路虚拟串口收发数据互不影响,到这里就成功了!

ends…

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。