为什么我的电脑通过USB向STM32发送数据时无法将其识别为USB设备?

问题描述 投票:0回答:1

我想通过USB从STM32发送数据。我编写了以下裸机代码来执行此操作。但是我的电脑在插入 USB 设备时无法识别。

#include "stm32f10x.h"

void USB_Endpoint_Configure(uint8_t endpoint, uint8_t type, uint8_t dir) {

  // Check the parameters.
  if (endpoint >= 16) {
    return;
  }

  // Set the endpoint type.
  *((volatile unsigned int *)0x40005C00 + endpoint * 4 + 0x00) = type;

  // Set the endpoint direction.
  *((volatile unsigned int *)0x40005C00 + endpoint * 4 + 0x01) = dir;

  // Set the endpoint max packet size.
  *((volatile unsigned int *)0x40005C00 + endpoint * 4 + 0x02) = 64;

  // Enable the endpoint.
  *((volatile unsigned int *)0x40005C00 + endpoint * 4 + 0x04) |= 0x00000001;
}

int USB_Endpoint_Write(uint8_t endpoint, const void *data, int size) {
  // Check if the endpoint is valid.
  if (endpoint >= 0x80 || endpoint >= 0xE0) {
    return -1;
  }

  // Check if the data is large enough to fill the endpoint buffer.
  if (size > 64) {
    return -1;
  }

  // Write the data to the endpoint buffer.
  for (int i = 0; i < size; i++) {
    *((uint8_t *)(0x40000000 + (endpoint << 7) + i)) = *((const uint8_t *)data + i);
  }

  // Send the data to the device.
  return 0;
}


int main(void){
    //USB_Init()
    // Reset the USB controller.
  RCC->APB1ENR |= RCC_APB1ENR_USBEN;
  *((volatile unsigned int *)0x40005C00) |= 0x00000001;
  while (*((volatile unsigned int *)0x40005C00) & 0x00000001);
    
    // Initialize the USB clock.
  RCC->APB1ENR |= 0x00000001;

  // Initialize the USB interrupts.
  NVIC_EnableIRQ(5);

    USB_Endpoint_Configure(0x00, 0x00, 0x00);
  USB_Endpoint_Configure(0x80, 0x00, 0x40);
    
    
    
    
    USB_Endpoint_Configure(0x00, 0x00, 0x00);
    
    
    char data[]="Hello WOrld!!!";
    
     USB_Endpoint_Write(0x00, data, sizeof(data));
     
     //Enable endpoint EP0
     *((uint8_t *)(0x40000000 + (0x00 << 7) + 0)) |= 1;
     
        // Wait for the data to be transferred.
        while (!*((uint8_t *)(0x40000000 + (0x00 << 7) + 1)) & 0x80) {
    // Do nothing.
        }
        
        //Disable endpoint EP0
     *((uint8_t *)(0x40000000 + (0x00 << 7) + 0)) |= 0;

}


我可以在通过 HAL 库编码时做同样的事情,并且成功。因此,USB 电缆或设备不存在任何物理问题。那么有人可以帮助在裸机中做到这一点吗? (我使用的是STM32F103C8芯片)

stm32 bare-metal stm32f1
1个回答
0
投票

USB 恰好比这更复杂。您至少应该从主机接收 SETUP 数据包,用一些合理的内容回答它,在操作系统认为您的设备工作之前继续进行总线枚举 - 一旦您出现,您就可以开始交换数据。

不幸的是,这意味着一些额外的“聊天”,这就是您了解描述您的设备、其 EP 等的“USB 描述符”的地方。参见例如linux下wireshark其他设备如何做枚举阶段和做同样的事情。仅供参考,USB 设备无法自行发送数据,除非主机要求它这样做并且设备用数据应答。这就是 EP 缓冲区的真正用途 - 当主机询问时,硬件将从缓冲区中排序正确的答案。

您可以尝试查看类似 https://github.com/eddyem/stm32samples - 它具有一些 USB、裸机风格,甚至伪造一些 USB 串行桥接 IC 以避免编写操作系统驱动程序。很少有其他类似的例子。这个使用(Cortex M3 端)中断来服务端点,我还遇到了一些在“轮询模式”下执行 EP 服务的东西,但是执行 SETUP 等操作并不完全是养眼的代码,因为您基本上应该包含一些 USB 协商逻辑.

另外:USB 对时序相当严格。最值得注意的是,它在 D+ 或 D- 线上有“下拉电阻”的概念,并且最好由软件控制。大多数支持 USB 的主板都会有 USB 下拉控制电路,您可以知道它在主板上的位置。

流程为:

  1. 在启动时尽早停用下拉电阻。
  2. 初始化系统做任何你想做的事。
  3. 一旦您认为您的 USB 已准备好进入黄金时段,请启用下拉电阻。
  4. 主机会发现设备并尝试枚举它发送设置数据包等。

建议这样做,因为如果您的系统初始化无法满足严格的 USB 时序(IIRC 自总线上检测到下拉电阻以来大约 10 毫秒),主机可能/会认为您的设备有故障,并且可能会忽略它或执行各种奇怪的“恢复” “东西(例如总线重置/失速/......你显然还没有处理)。因此,一旦您能够真正承受严格的 USB 超时计时,完成所有操作并启用下拉会更安全。

顺便说一句,Linux 使事情变得更加容易,因为您至少可以看到操作系统是否检测到您的设备以及它在“dmesg”输出中得到了什么,给出了上拉是否有效、操作系统如何看到您的 USB 描述符、枚举是否有效以及像wireshark这样的工具可以帮助立即查看USB I/O数据包流(它的字面意思是“modprobe usbmon”,然后只运行wireshark,选择USB作为源 - 只需2步即可进行低级USB调试)。

© www.soinside.com 2019 - 2024. All rights reserved.