STM32 USB 程序将BULK EP改成双缓冲机制后,接收OUT数据的速度从原先的500KB/S,

本文分享了STM32 USB驱动程序的优化经验,通过调整参数及采用双缓冲机制,显著提高了USB数据传输速率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前天测试自己编写的USB驱动程序时候发现从主机到STM32的OUT传输(主机到设备)速率竟然只有最高33KB/S,实在是晕死了。经过研究后发现是驱动程序中设置的PIPE MaxTransferSize参数的关系,原先设置64只能33KB/S,后参考其他USB设备驱动程序的值,设置成了65535,再测试USB OUT的速度,达到了500KB/S,终于解决了驱动程序的瓶颈。不过算下USB 2.0全速的通讯速率是12Mb/S,排除掉CRC、令牌、SOF等等开销怎么也应该不止最大500KB/S啊。到网上看了看,基本上应该能达到600KB/S~700KB/S以上,我现在的速度应该还有很大的提升才是。
  看看程序,发现
void EP3_OUT_Callback(void)//EP3 OUT的回调函数,当EP3接收到数据时候中断调用该函数
{
count_out = GetEPRxCount(ENDP3);//获得接收到的数据长度
PMAToUserBufferCopy(buffer_out, ENDP3_RXADDR, count_out);//将数据从USB EP3 RX的缓冲区拷贝到用户指定的数组中
SetEPRxValid(ENDP3); //完成拷贝后置有效状态,从而EP3发送ACK主机可以进行下一个数据包的发送
}
  试着将PMAToUserBufferCopy这句注释掉(这样STM32就不处理接收到的数据了)后再测试速度,惊奇地发现速度竟然达到了997KB/S!晚上仔细想了想,数据肯定是要使用的,这个数据拷贝的过程的时间消费总是少不了的;由于通常情况下USB设备BULK数据接收的步骤就是:接收到数据,置NAK->将缓冲区数据拷贝到用户区(用户处理过程)->发ACK通知主机完成了完整的接收可以发送下一个->主机发送下一个,按照以上的步骤USB接收一步步的进行,只要STM32不完成数据处理,状态就一直是NAK,主机就会不停地发送该数据包,浪费了带宽,因此就会导致我上面最大速度500KB/S难以再增加的情况!不甘心啊~~
  昨天晚上又仔细研究了STM32的技术参考手册的USB章节内容,里面提到BULK可以采用双缓冲机制(PING-PONG)进行处理,正好可以解决上面的情况。双缓冲机制的原理就是分配2块接收缓冲,STM32的用户处理和USB接口可以分别交替占用2个缓冲区,当USB端点接收数据写其中一个缓冲区的时候,用户的应用程序可以同时处理另一个缓冲区,这样缓冲区依次交换占有者,只要用户处理程序在USB端点接收的时间片段内完成处理,就能够完全不影响USB的通讯速度!
  程序部分修改
一、EP3_OUT的设置修改,
//ZYP:修改EP3为BULK双缓冲方式-------------------------
SetEPType(ENDP3, EP_BULK);
SetEPDoubleBuff(ENDP3);
SetEPDblBuffAddr(ENDP3, ENDP3_BUF0Addr, ENDP3_BUF1Addr);
SetEPDblBuffCount(ENDP3, EP_DBUF_OUT, VIRTUAL_COM_PORT_DATA_SIZE);
ClearDTOG_RX(ENDP3);
ClearDTOG_TX(ENDP3);
ToggleDTOG_TX(ENDP3);
SetEPRxStatus(ENDP3, EP_RX_VALID);
SetEPTxStatus(ENDP3, EP_TX_DIS);
//------------------------------------------------------
二、EP3_OUT回调函数的修改
void EP3_OUT_Callback(void)
{
//ZYP:以下是修改成EP3双缓冲OUT后的处理函数
if (GetENDPOINT(ENDP3) & EP_DTOG_TX)//先判断本次接收到的数据是放在哪块缓冲区的
{
  FreeUserBuffer(ENDP3, EP_DBUF_OUT); //先释放用户对缓冲区的占有,这样的话USB的下一个接收过程可以立刻进行,同时用户并行进行下面处理
  count_out = GetEPDblBuf0Count(ENDP3);//读取接收到的字节数
  PMAToUserBufferCopy(buffer_out, ENDP3_BUF0Addr, count_out);
}
else
{
  FreeUserBuffer(ENDP3, EP_DBUF_OUT);
  count_out = GetEPDblBuf1Count(ENDP3);
  PMAToUserBufferCopy(buffer_out, ENDP3_BUF1Addr, count_out);
}
}
  经过上面的修改,终于解决了STM32在处理接收数据时导致主机等待的情况,用BUS HOUND软件测试了下
  哈哈,这下终于爽了。



PS:上面的FreeUserBuffer(ENDP3, EP_DBUF_OUT); 这句话的上下位置是关键,如果放到函数的后面,则仍旧会有主机等待STM32处理数据的情况,速度仍然是500KB/S!
  把这句话放在拷贝函数的前面的话就真正把双缓冲PING-PONG机制用起来了。大致算了下PMAToUserBufferCopy(buffer_out, ENDP3_BUF1Addr, count_out);这句话当count_out为最大值64的时候STM32执行需要302个周期,72MHZ情况下约4.2微秒执行时间,而USB传输按照12Mb/s的线速度传输64字节的数据至少也得40微秒,因此只要PMAToUserBufferCopy的时间不超过40微秒,就不会导致缓冲区竞争的情况。
<think>好的,我现在需要帮助用户找到关于如何在STM32F407IG上使用HAL库配置USB CDC双DMA的教程或示例代码。首先,我需要回忆一下STM32USB CDC配置和DMA相关的内容。用户提到了双DMA,这可能指的是同时使用发送和接收的DMA通道,或者多通道的DMA传输。 首先,我应该考虑STM32CubeMX的配置步骤。通常,配置USB CDC需要启用USB设备库,选择CDC类,然后配置相关的端点。DMA部分可能需要在DMA设置中添加对USB的请求。不过,STM32USB外设通常使用专用的缓冲区,不一定需要DMA,但用户明确要求使用双DMA,所以需要确认是否可行。 接下来,我需要查阅HAL库的相关文档。HAL库中关于USB CDC的函数可能包括发送和接收的函数,例如CDC_Transmit_FS和CDC_Receive_FS。默认情况下,这些函数可能使用轮询或中断方式,而不是DMA。因此,用户需要手动启用DMA,并配置相关的中断和回调函数。 然后,我应该检查是否有官方示例或社区资源。ST的官方例程库中可能有USB CDC的例子,但不确定是否包含双DMA配置。用户可能需要自行修改代码,添加DMA初始化,并在发送和接收函数中使用HAL_PCD_StartRequest等函数结合DMA传输。 另外,DMA的配置需要注意通道和流的分配,确保不与其他外设冲突。USB的双DMA可能涉及IN和OUT端点分别使用不同的DMA通道,这需要在CubeMX中正确配置每个端点的DMA设置。 可能遇到的问题包括DMA传输的缓冲区管理、数据对齐以及如何触发DMA传输完成的中断。用户需要实现相应的回调函数,如HAL_PCD_DataOutStageCallback和HAL_PCD_DataInStageCallback,来处理数据接收和发送完成事件。 最后,我应该建议用户参考ST的官方文档和社区论坛,如STM32CubeF4的USB例程,以及相关的应用笔记,如AN4879,这些资源可能提供更多的细节和示例代码。</think>### STM32F407IG USB CDC双DMA配置步骤 #### 1. CubeMX基础配置 - **USB OTG FS模式**:选择为`Device Only`,并启用`USB_DEVICE`库 - **USB CDC类配置**:在Middleware中激活`USB_DEVICE -> Communication Device Class (Virtual Port Com)` - **DMA设置**: - 为`USB_OTG_FS`的RX/TX端点分别添加DMA流 - 确保DMA方向与端点匹配:RX用`PERIPH_TO_MEM`,TX用`MEM_TO_PERIPH` - 建议使用双缓冲模式(配置DMA为Circular模式) #### 2. 关键代码实现 ```c /* DMA接收初始化 */ HAL_PCD_StartRequest(&hpcd, EP1_OUT_ADDR, (uint8_t*)usb_rx_buffer, USB_MAX_PACKET_SIZE, USB_OTG_FS_DMA_STREAM); /* 发送函数改造 */ void CDC_Transmit_FS(uint8_t* Buf, uint16_t Len) { HAL_PCD_EP_Transmit_DMA(&hpcd, EP1_IN_ADDR, Buf, Len); } /* DMA传输完成回调 */ void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd) { USBD_CDC_ReceivePacket(&hUsbDeviceFS); // 重新启动接收 } ``` #### 3. 注意事项 - **内存对齐**:DMA缓冲区需要4字节对齐,建议使用`__attribute__((aligned(4)))` - **双缓冲策略**:推荐使用环形缓冲区管理接收数据 - **时钟配置**:确保USB时钟为48MHz(通过PLLQ分频实现) - **端点配置**:建议使用`BULK`传输类型端点,典型配置: ```c #define EP1_IN_ADDR 0x81 #define EP1_OUT_ADDR 0x01 ``` ### 关键API参考 1. **DMA启动函数**: ```c HAL_StatusTypeDef HAL_PCD_EP_Transmit_DMA(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len); ``` 2. **接收回调注册**: ```c USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS); ``` ### 相关资源推荐 1. ST官方参考手册:**RM0090** (F4系列参考手册)第10章DMA控制器,第33章USB OTG 2. ST应用笔记:**AN4879**《USB hardware and PCB guidelines using STM32 MCUs》 3. ST官方示例:`STM32Cube_FW_F4_V1.27.0\Projects\STM324xG_EVAL\Applications\USB_Device\CDC_Standalone` [^1]: STM32F4 HAL库USB设备驱动实现参考手册第33.4节 [^2]: STM32CubeMX DMA配置指南AN4031应用笔记
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值