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

STM32F103C8裸机USB代码问题:PC无法识别USB设备

STM32F103C8 USB裸机代码无法被PC识别问题

我尝试通过USB从STM32F103C8发送数据,编写了以下裸机代码,但插入USB后PC无法识别设备。使用HAL库实现相同功能时可以正常运行,因此排除硬件(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;

}

问题排查与修正建议

你的裸机代码缺少STM32 USB设备正常枚举所需的核心初始化与交互逻辑,以下是具体问题和修复方案:

1. 时钟配置完全错误

代码中RCC->APB1ENR |= 0x00000001;是开启TIM2时钟,而非USB所需的48MHz时钟配置。STM32F103的USB模块必须工作在48MHz时钟下,需基于外部晶振正确配置PLL:

void USB_Clock_Init(void) {
    // 开启外部8MHz晶振
    RCC->CR |= RCC_CR_HSEON;
    while(!(RCC->CR & RCC_CR_HSERDY));
    
    // 配置PLL:HSE为源,倍频6得到48MHz,APB1预分频2(保证APB1时钟不超过36MHz)
    RCC->CFGR |= RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL6;
    RCC->CFGR |= RCC_CFGR_HPRE_DIV1 | RCC_CFGR_PPRE1_DIV2;
    
    // 启动PLL并等待就绪
    RCC->CR |= RCC_CR_PLLON;
    while(!(RCC->CR & RCC_CR_PLLRDY));
    
    // 切换系统时钟到PLL
    RCC->CFGR |= RCC_CFGR_SW_PLL;
    while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
    
    // USB时钟不分频,保持48MHz
    RCC->CFGR &= ~RCC_CFGR_USBPRE;
    
    // 开启USB外设时钟
    RCC->APB1ENR |= RCC_APB1ENR_USBEN;
}

2. USB控制器初始化不完整

仅做控制器复位远远不够,还需配置核心寄存器、开启全局中断:

void USB_Init(void) {
    // 复位USB控制器
    USB->CNTR |= USB_CNTR_FRES;
    while(USB->CNTR & USB_CNTR_FRES);
    
    // 开启USB全局中断、复位中断、端点中断
    USB->CNTR |= USB_CNTR_RESETM | USB_CNTR_EPSTM | USB_CNTR_EPEM;
    
    // 配置NVIC中断优先级并使能
    NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 0x02);
    NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
    
    // 配置EP0为双向控制端点,最大包64字节
    USB->EP0R = USB_EP0R_EP_TYPE_CONTROL | USB_EP0R_EP_KIND | USB_EP0R_EP_TX_VALID | USB_EP0R_EP_RX_VALID;
}

3. 端点操作地址与逻辑错误

  • STM32F1的USB端点缓冲区起始地址是0x40006000,而非代码中的0x40000000,地址计算完全错误。
  • 你重复调用USB_Endpoint_Configure(0x00, 0x00, 0x00),属于无效的重复配置。

4. 完全缺失USB枚举流程

PC识别USB设备的核心是枚举交互:PC会发送设备描述符请求、配置描述符请求等,你的代码直接跳过这一步尝试发送数据,PC自然无法识别。必须实现USB中断服务函数处理这些请求:

void USB_LP_CAN1_RX0_IRQHandler(void) {
    uint16_t isr = USB->ISTR;
    
    // 处理USB复位中断
    if(isr & USB_ISTR_RESET) {
        USB->ISTR &= ~USB_ISTR_RESET;
        // 复位后重新初始化EP0
        USB->EP0R = USB_EP0R_EP_TYPE_CONTROL | USB_EP0R_EP_KIND | USB_EP0R_EP_TX_VALID | USB_EP0R_EP_RX_VALID;
        USB->DADDR = 0x00; // 设备地址设为0
    }
    
    // 处理EP0事务
    if(isr & USB_ISTR_EP0) {
        USB->ISTR &= ~USB_ISTR_EP0;
        // 此处需解析SETUP请求,响应设备描述符、配置描述符等枚举请求
        // 这是PC识别设备的核心逻辑,必须实现
    }
}

修正后的主函数示例

int main(void) {
    USB_Clock_Init();
    USB_Init();
    
    // 主循环保持运行,等待枚举完成后再执行数据发送逻辑
    while(1) {
    }
}

关键提示

USB枚举是一套标准的交互流程,必须完整响应PC的所有请求才能被识别。建议参考STM32官方裸机USB示例,重点理解SETUP请求解析、描述符发送的逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 03:12:07