当前位置:   article > 正文

STM32CubeMX | 基于STM32使用HAL库实现USB组合设备之多路CDC_usbd_cdc_init()函数

usbd_cdc_init()函数

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


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

或关注以下公众号,回复关键字MultiCDC获取下载链接!
在这里插入图片描述



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

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


工程环境:

  • STM32F103RC
  • STM32CubeIDE 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;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94

注:这一步骤的修改都非常挂关键,好多人组合设备修改出错的原因就是在修改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 */

} ;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199

第五步:修改函数接口

默认的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,那么此时有两种办法解决:

  1. 设备管理器卸载掉出现的虚拟串口,重新拔插USB
  2. 修改程序里的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…

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/482208
推荐阅读
相关标签
  

闽ICP备14008679号