基于STM32Cube MX的USB复合设备开发:MSC+CDC双功能整合实战

张开发
2026/4/16 10:40:29 15 分钟阅读

分享文章

基于STM32Cube MX的USB复合设备开发:MSC+CDC双功能整合实战
1. 为什么需要USB复合设备开发在嵌入式系统开发中USB接口的应用越来越广泛。传统的做法是为每个功能单独使用一个USB接口比如一个用于虚拟串口(CDC)另一个用于虚拟U盘(MSC)。但实际项目中我们常常会遇到硬件资源紧张的情况特别是引脚数量有限的小型开发板。这时候就需要通过一个USB接口同时实现多个功能。我最近在一个智能硬件项目中就遇到了这种情况。设备需要通过USB与PC通信既要能传输调试日志CDC功能又要能存储配置文件MSC功能。如果使用两个USB接口不仅占用宝贵的硬件资源还会增加成本。经过多次尝试最终通过STM32Cube MX成功实现了MSCCDC双功能整合。USB复合设备的优势很明显节省硬件资源只需一个USB接口降低成本减少外围电路简化设计统一管理多个功能提升用户体验PC端可以同时使用多个功能2. 开发前的准备工作2.1 硬件选型与准备要实现USB复合设备功能首先需要选择合适的硬件平台。我使用的是STM32F407ZGT6开发板它内置了全速USB OTG控制器性能足够满足我们的需求。开发板上还集成了W25Q128 Flash芯片可以用来模拟U盘存储。如果你手头没有这款开发板也可以选择其他支持USB Device模式的STM32系列芯片比如F103、F429等。关键是要确认芯片的USB外设支持我们要实现的功能。硬件连接很简单将开发板的USB_OTG_FS_DM(PA11)和USB_OTG_FS_DP(PA12)连接到USB接口确保USB供电正常可以通过开发板的USB接口供电连接调试器ST-Link等用于下载程序2.2 软件环境搭建在开始编码前需要准备好开发环境安装STM32CubeMX最新版本即可安装Keil MDK或IAR等IDE下载对应芯片的HAL库准备USB驱动库通常包含在HAL库中我建议使用STM32CubeMX的最新版本因为它对USB功能的支持越来越好配置起来也更方便。安装完成后记得更新对应的芯片支持包。3. STM32CubeMX基础配置3.1 创建新工程与时钟配置打开STM32CubeMX点击New Project创建一个新工程。在芯片选择界面输入你的芯片型号比如STM32F407ZGTx然后双击选中。第一步是配置系统时钟。对于USB设备时钟配置很重要因为USB模块对时钟精度有要求。我通常这样设置在Pinout Configuration选项卡中选择RCC将HSE设置为Crystal/Ceramic Resonator在Clock Configuration选项卡中配置时钟树确保USB时钟为48MHz这是USB全速设备的要求时钟配置完成后系统时钟可以设置为最高频率对于F407是168MHz但要注意USB时钟必须精确为48MHz。3.2 USB外设基本设置接下来配置USB外设在Connectivity下找到USB_OTG_FS将Mode设置为Device_Only在Configuration选项卡中选择USB_DEVICE在Class For FS IP中选择Custom Human Device这里选择Custom Human Device而不是特定的CDC或MSC是因为我们要实现复合设备需要自定义描述符。这个选择会影响生成的代码结构。4. MSC功能配置详解4.1 存储介质接口配置MSC功能需要一个存储介质作为后端。在我的项目中使用的是板载的SPI FlashW25Q128。首先需要在CubeMX中配置SPI接口在Connectivity下找到对应的SPI接口我的是SPI1设置Mode为Full-Duplex Master配置合适的时钟分频不要超过Flash支持的最高频率设置数据宽度为8bit配置NSS信号为Hardware Output然后需要实现Flash的底层驱动。虽然CubeMX可以生成SPI初始化代码但Flash的操作指令需要自己实现。我创建了一个单独的驱动文件w25qxx.c实现了以下基本功能芯片识别扇区擦除页编程数据读取4.2 MSC中间件配置在CubeMX的Middleware选项卡中选择USB_DEVICE然后勾选MSC支持。这里有几个关键配置BOT Only选择这个选项因为我们不需要UASP协议Max LUN设置为1因为我们只有一个存储设备Storage Interface选择FLASH需要自己实现接口函数配置完成后CubeMX会生成MSC相关的框架代码但我们需要自己实现几个关键的回调函数STORAGE_Init初始化存储设备STORAGE_GetCapacity获取存储容量STORAGE_IsReady检查设备是否就绪STORAGE_IsWriteProtected检查写保护状态STORAGE_Read读取数据STORAGE_Write写入数据这些函数需要根据实际的存储设备来实现。在我的项目中它们都调用了w25qxx.c中的相应功能。5. CDC功能配置详解5.1 通信参数配置CDC功能配置相对简单一些。在CubeMX的Middleware选项卡中选择USB_DEVICE然后勾选CDC支持。主要配置项包括CDC Only选择这个选项Interface number保持默认值0Endpoint settings使用默认的端点配置可以稍后调整CDC功能需要实现几个关键的回调函数CDC_Init初始化CDC设备CDC_DeInit反初始化CDC_Control处理控制请求CDC_Receive接收数据回调CDC_TransmitCplt发送完成回调5.2 数据收发实现CDC的数据收发基于端点中断。在生成的代码框架中已经包含了基本的收发功能但我们需要根据实际需求进行调整。在我的项目中我实现了以下功能环形缓冲区管理用于缓存接收到的数据数据回显将接收到的数据原样发回用于测试流量控制防止缓冲区溢出关键是要处理好CDC_Receive_FS回调函数。当PC端发送数据时这个函数会被调用。我的实现如下static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len) { // 将数据存入环形缓冲区 if(ring_buffer_put(rx_buffer, Buf, *Len) RING_BUFFER_OK) { // 通知应用层有新数据到达 usb_rx_notify(); } // 准备接收下一包数据 USBD_CDC_SetRxBuffer(hUsbDeviceFS, Buf[0]); USBD_CDC_ReceivePacket(hUsbDeviceFS); return (USBD_OK); }6. 复合设备关键整合步骤6.1 描述符合并与修改实现复合设备最关键的步骤是合并和修改USB描述符。USB描述符定义了设备的功能和特性包括设备描述符、配置描述符、接口描述符和端点描述符等。在单独实现MSC或CDC时CubeMX会自动生成对应的描述符。但要做复合设备我们需要手动合并这些描述符。具体步骤创建一个新的描述符文件如usbd_msccdc_desc.c合并MSC和CDC的描述符结构调整接口编号和端点地址避免冲突添加IADInterface Association Descriptor描述符IAD描述符特别重要它告诉主机多个接口属于同一个功能组。对于我们的MSCCDC设备需要两个IAD一个关联CDC的两个接口另一个关联MSC的接口。6.2 驱动文件整合除了描述符还需要整合MSC和CDC的驱动文件。我的做法是创建一个新的类处理文件usbd_msccdc.c合并MSC和CDC的类回调函数实现一个切换机制根据请求的接口选择对应的处理函数修改端点初始化代码确保所有端点正确配置关键是要处理好USBD_MC_Setup函数它负责路由各种USB请求。我的实现中使用了接口号来判断请求应该由MSC还是CDC处理uint8_t USBD_MC_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) { if (req-wIndex MSC_INTERFACE) { // 交给MSC处理 MC_Switch_MSC(pdev); return USBD_MSC_Setup(pdev, req); } else { // 交给CDC处理 MC_Switch_CDC(pdev); return USBD_CDC_Setup(pdev, req); } }7. 常见问题与调试技巧7.1 枚举失败问题排查在开发USB复合设备时最常遇到的问题就是枚举失败。表现为设备管理器中出现未知设备或者设备反复连接断开。这类问题通常由以下原因引起描述符错误长度不对或内容有误端点冲突MSC和CDC使用了相同的端点地址缓冲区大小不足FIFO设置太小时钟不准确USB需要精确的48MHz时钟我的调试经验是使用USB分析仪捕获通信过程如果有条件在USBD_SetupStage和USBD_DataOutStage等关键函数中添加调试输出逐步简化描述符先确保基本功能正常检查CubeMX生成的端点配置是否正确7.2 性能优化建议当MSC和CDC功能同时工作时可能会遇到性能问题。特别是在大量数据传输时可能会出现丢包或速度慢的情况。以下是我总结的优化建议合理分配端点确保MSC和CDC使用不同的端点调整FIFO大小在USBD_LL_Init中增加FIFO分配优化中断处理减少中断服务程序中的处理时间使用DMA如果硬件支持启用USB DMA传输例如在F407上我这样配置FIFOHAL_PCDEx_SetRxFiFo(hpcd_USB_OTG_FS, 0x80); // RX FIFO HAL_PCDEx_SetTxFiFo(hpcd_USB_OTG_FS, 0, 0x40); // EP0 HAL_PCDEx_SetTxFiFo(hpcd_USB_OTG_FS, 1, 0x40); // MSC OUT HAL_PCDEx_SetTxFiFo(hpcd_USB_OTG_FS, 2, 0x40); // CDC OUT8. 实际项目应用与扩展8.1 在智能硬件中的应用案例在我的一个智能家居网关项目中这个MSCCDC的复合设备方案发挥了重要作用。具体应用场景如下CDC功能用于设备调试和日志输出支持AT指令配置设备参数提供固件升级通道配合BootloaderMSC功能存储设备配置文件记录运行日志和历史数据提供用户可访问的存储空间这种设计大大简化了硬件结构同时提供了丰富的功能。用户可以通过USB接口同时进行调试和数据交换而不需要切换连接方式。8.2 功能扩展思路基于这个MSCCDC的基础框架还可以进一步扩展更多实用功能添加HID设备实现键盘、鼠标等输入设备功能支持多配置根据主机需求切换不同功能组合实现WinUSB提供更高效的专有通信通道增加音频类支持音频输入输出扩展时需要注意USB规范对端点数量有限制全速设备最多6个数据端点不同功能可能有特定的描述符要求主机驱动支持情况需要考虑我在另一个项目中就尝试过MSCCDCHID的三重组合用来实现一个带存储功能的键盘设备。虽然实现过程复杂一些但最终效果很好证明了这种架构的灵活性。

更多文章