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

使用PyUSB读取STM32F103C8T6 Flash时接收空列表问题排查

问题分析与解决方案

核心问题定位

交替空接收+数据错误,本质是USB CDC传输的同步逻辑缺陷,结合STM32端发送时序不严谨、PyUSB接收逻辑无等待机制共同导致。


一、STM32从机侧修复

  1. 强制等待CDC发送完成
    CDC_Transmit_FS是非阻塞函数,直接连续调用会导致数据丢包。必须等待上一次发送完成后再触发下一包发送:

    uint8_t CDC_TxComplete = 1;
    
    // 发送完成回调函数(需在USB CDC回调中实现)
    void USBD_CDC_TransmitCpltCallback(USBD_HandleTypeDef *hUsbDeviceFS)
    {
      CDC_TxComplete = 1;
    }
    
    // 封装可靠的发送函数
    void Send_Flash_Block(uint8_t *buf, uint32_t len)
    {
      while(!CDC_TxComplete); // 等待上一包发送完成
      CDC_TxComplete = 0;
      CDC_Transmit_FS(buf, len);
    }
    

    调用此函数发送两包8192字节的Flash数据,确保时序同步。

  2. Flash读取边界校验
    确保读取的Flash地址在STM32F103C8T6的有效范围(0x08000000~0x0800FFFF),避免越界读取垃圾数据:

    // 示例:读取指定地址的Flash数据
    void Read_Flash_Data(uint32_t start_addr, uint8_t *buf, uint32_t len)
    {
      if(start_addr + len > 0x08010000) return; // 超过64KB Flash上限直接返回
      for(uint32_t i=0; i<len; i++)
      {
        buf[i] = *(uint8_t*)(start_addr + i);
      }
    }
    
  3. 命令触发的可靠性校验
    原中断仅判断字节数就触发发送,易受干扰误触发。建议给9字节命令加校验位(比如最后1字节为前8字节的异或和),中断中先验证校验再设置Flash_Data_Transmission_Flag。


二、PC主机侧PyUSB代码修复

  1. 加入等待与超时机制
    原循环无等待直接读取,从机还未准备好数据就会返回空列表。发送命令后给从机预留处理时间,读取时设置合理超时:

    import usb.core
    import usb.util
    import time
    
    # 匹配Blue Pill CDC设备(默认VID/PID)
    dev = usb.core.find(idVendor=0x0483, idProduct=0x5740)
    if not dev:
        raise ValueError("设备未找到")
    
    dev.set_configuration()
    # 直接使用端点地址(通常OUT为0x01,IN为0x81)
    EP_OUT = 0x01
    EP_IN = 0x81
    
    Main_List = []
    trigger_cmd = b'\x01\x00\x00\x00\x00\x00\x00\x00\x01' # 示例9字节命令(含校验)
    
    # 发送触发命令
    dev.write(EP_OUT, trigger_cmd)
    time.sleep(0.02) # 给从机20ms处理时间
    
    for _ in range(2):
        try:
            # 读取8192字节,超时1秒
            data = dev.read(EP_IN, 8192, timeout=1000)
            Main_List.append(list(data))
        except usb.core.USBError as e:
            # 读取失败时重试一次
            dev.write(EP_OUT, trigger_cmd)
            time.sleep(0.02)
            data = dev.read(EP_IN, 8192, timeout=1000)
            Main_List.append(list(data))
    
    print(f"接收结果长度: {[len(lst) for lst in Main_List]}")
    
  2. 处理USB端点缓存
    若仍有空读,可在每次读取前尝试清空端点缓存:

    # 读取前清空缓存
    try:
        dev.read(EP_IN, 0, timeout=100)
    except usb.core.USBError:
        pass
    
  3. 增加数据校验
    在从机每包数据末尾加入CRC16或字节和校验,PC端接收后验证,若校验失败则请求从机重发对应包,确保数据正确性。


三、额外稳定性优化

  • STM32中断优先级配置:将USB_LP_CAN1_RX0_IRQn的中断优先级设为较高级别(比如0或1),避免USB传输被其他低优先级中断打断。
  • PC端权限处理:Linux/macOS下需配置udev规则或用sudo运行脚本,避免USB权限不足导致传输异常。
  • 拆分数据包:若8192字节传输不稳定,可拆分为1024字节小包多次发送,PC端拼接后得到完整16KB数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 09:57:42