日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2023/12/20 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 STM32CubeMX | 基于STM32使用HAL库实现USB组合设备之多路CDC 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

STM32CubeMX | 基于STM32使用HAL庫實現USB組合設備之多路CDC


本博客完整代碼下載地址:https://download.csdn.net/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相關知識掃盲


工程環境:

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

注:這一步驟的修改都非常掛關鍵,好多人組合設備修改出錯的原因就是在修改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…

    總結

    以上是生活随笔為你收集整理的STM32CubeMX | 基于STM32使用HAL库实现USB组合设备之多路CDC的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。