You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于STM32F103的USB CDC在Windows7与Ubuntu上的传输差异问题

解决Windows7下STM32 USB CDC高频小数据包传输效率问题

从你的描述和代码来看,问题核心在于Windows7自带的USB CDC驱动对高频小数据包的批量传输处理效率远低于Linux内核驱动,再加上你的代码没有考虑USB传输的异步特性,导致大量发送请求被丢弃或阻塞。以下是针对性的解决方案:

1. 修复STM32端的发送逻辑:避免无等待的高频发送

你当前在4000Hz的Systick回调里直接调用CDC_Transmit_FS,但USB CDC的传输是异步的——如果上一次传输还未完成,新的发送请求会返回USBD_BUSY并直接失败,而你的代码没有检查返回值,导致大量数据包根本没发出去。Windows7驱动响应速度慢,这种失败概率更高,而Linux驱动处理更快,所以能跟上频率。

修改代码,加入传输完成的同步机制:

uint8_t tx_in_progress = 0; // 标记当前是否有传输在进行
uint8_t buff[21]; // 你的数据包缓冲区

void HAL_SYSTICK_Callback(void) //4000Hz触发
{
  if (!tx_in_progress) {
    tx_in_progress = 1;
    // 先更新buff里的新数据(如果需要的话)
    update_tx_data(buff); 
    // 发起传输,传输完成后会触发回调
    CDC_Transmit_FS(buff, 21);
  }
}

// USB CDC传输完成回调函数
void HAL_CDC_TransmitCpltCallback_FS(uint8_t *Buf, uint32_t *Len)
{
  tx_in_progress = 0; // 标记传输完成,允许下一次发送
}

2. 合并小数据包,减少USB传输次数

STM32F103是全速USB设备,批量端点的最大包长为64字节。你每次发21字节的小包,在Windows7下,驱动每1ms(USB帧周期)只能处理一次批量传输请求,自然只能达到1000包/秒。而Linux可以在一个帧内处理多个小数据包。

可以将多个21字节的小包合并成一个接近64字节的大包再发送:

uint8_t tx_buff[63]; // 3*21=63字节,接近最大包长
uint8_t tx_packet_count = 0;
uint8_t single_buff[21];

void HAL_SYSTICK_Callback(void)
{
  // 将当前数据包复制到合并缓冲区
  memcpy(&tx_buff[tx_packet_count * 21], single_buff, 21);
  tx_packet_count++;

  // 攒够3包就发送
  if (tx_packet_count >= 3) {
    tx_packet_count = 0;
    // 等待上一次传输完成再发送
    while (CDC_Transmit_FS(tx_buff, 63) == USBD_BUSY);
  }
}

这样发送频率降到~1333Hz,Windows7驱动能轻松处理,同时总数据量和之前一致(400021=84000字节/秒,133363≈84000字节/秒)。

3. 替换Windows7的USB CDC驱动

Windows7自带的CDC驱动非常老旧,对批量传输的优化很差。可以安装ST官方提供的USB CDC驱动,或者使用通用USB驱动如libusb-win32/libusbK,这些驱动对小数据包高频传输的支持更好,能大幅提升传输效率。

4. 优化PC端的接收逻辑

如果用Python测试,要确保接收代码足够高效:

  • 避免使用过小的缓冲区,或者用read_all()一次性读取所有缓存数据
  • 不要在读取循环里加入不必要的延迟
  • 可以用线程异步读取,避免主线程阻塞

示例Python代码:

import serial
import time

ser = serial.Serial('COM3', 115200, timeout=0, write_timeout=0)
start = time.time()
packet_count = 0

try:
    while time.time() - start < 1.0:
        # 一次性读取所有可用数据,按21字节分包计数
        data = ser.read_all()
        packet_count += len(data) // 21
    print(f"Received {packet_count} packets in 1 second")
finally:
    ser.close()

为什么Ubuntu下正常?

Linux内核的USB CDC驱动对批量传输的调度更灵活,允许在单个USB帧(1ms)内处理多个小数据包请求,同时用户态工具如CuteCOM的读取逻辑也更高效,能及时取走缓冲区的数据,不会因为缓冲区满导致丢包。

内容的提问来源于stack exchange,提问作者Danding

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:34:27