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
相关产品推荐
相关产品推荐

