Xin chào mọi người,
Tôi đang gỡ lỗi một vấn đề gián đoạn liên quan đến USB CDC trên bo mạch STM32F103C8T6. Sau khi thức dậy từ chế độ dừng, cổng COM ảo trên máy tính đôi khi bị mất (chỉ có thể khôi phục bằng cách nhấn nút reset cứng). Mong mọi người giúp kiểm tra trình tự khởi tạo clock/USB của tôi có hợp lý không và nếu có giải pháp đã được xác nhận.
Phần cứng và linh kiện
- MCU: STM32F103C8T6, chạy ở 72MHz thông qua PLL nhân 9 từ HSE 8MHz.
- Bo mạch: Phiên bản “Blue Pill (bản ‘tiểu lam bản’)”; IC ổn áp LDO chuyển đổi 5V sang 3.3V, tụ điện 0.1µF + 1µF gắn gần chân VDD, thạch anh 8MHz kiểu HC-49 với tụ tải 22pF.
- USB: Thiết bị tốc độ đầy đủ (Full-speed), chân D+ được kéo lên 3.3V qua điện trở 1.5kΩ; điện trở 22Ω nối tiếp trên đường D+/D-.
Hiện tượng lỗi
- Khởi động lần đầu: USB CDC hoạt động bình thường trên Windows 10 và Ubuntu 22.
- Sau khi thức dậy từ chế độ dừng khoảng 5-30 giây, máy chủ đôi khi không nhận diện được thiết bị CDC (không có sự kiện PID/VID mới, cổng COM biến mất). Tháo/gắn lại cáp USB không luôn hiệu quả, nhưng nhấn nút reset NRST luôn khôi phục được. Tỷ lệ tái hiện khoảng 10%-30%.
Các biện pháp đã thử
- Sau khi thức dậy: Kích hoạt lại HSE/PLL rồi khởi động lại clock USB.
- Chuyển clock ngoại vi USB và thực hiện chuỗi USBD_DeInit() → USBD_Init().
- Gọi SystemCoreClockUpdate() sau khi chuyển đổi clock.
- Thêm độ trễ 1-10ms giữa các bước khởi tạo RCC và USB.
- Đo dao động điện áp 3.3V bằng dao động ký: dao động <20mVpp trong quá trình thức dậy; D+ ở mức cao khoảng 3.3V khi rảnh.
Trình tự cấu hình clock/RCC (dùng thư viện HAL)
// Sau khi thức dậy từ chế độ dừng: kích hoạt lại HSE/PLL và chuyển SYSCLK về PLL
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
HAL_RCC_OscConfig(&(RCC_OscInitTypeDef){
.OscillatorType = RCC_OSCILLATORTYPE_HSE,
.HSEState = RCC_HSE_ON,
.PLL.PLLState = RCC_PLL_ON,
.PLL.PLLSource = RCC_PLLSOURCE_HSE,
.PLL.PLLMUL = RCC_PLL_MUL9
});
HAL_RCC_ClockConfig(&(RCC_ClkInitTypeDef){
.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2,
.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK,
.AHBCLKDivider = RCC_SYSCLK_DIV1,
.APB1CLKDivider = RCC_HCLK_DIV2,
.APB2CLKDivider = RCC_HCLK_DIV1
}, FLASH_LATENCY_2);
SystemCoreClockUpdate();
Đoạn mã khởi tạo lại USB
// Khởi động lại hoàn toàn ngăn xếp thiết bị sau khi clock ổn định
USBD_DeInit(&hUsbDeviceFS);
__HAL_RCC_USB_FORCE_RESET();
HAL_Delay(2);
__HAL_RCC_USB_RELEASE_RESET();
__HAL_RCC_USB_CLK_ENABLE();
USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS);
USBD_RegisterClass(&hUsbDeviceFS, &USBD_CDC);
USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS);
USBD_Start(&hUsbDeviceFS);
Kết quả quan sát
- Nếu bỏ qua chế độ dừng và chỉ chuyển giữa chế độ chạy/ngủ, hệ thống ổn định.
- Nếu giữ SYSCLK ở HSI (không kích hoạt PLL) sau khi thức dậy, hệ thống có vẻ ổn định hơn nhưng tôi cần 72MHz để đáp ứng yêu cầu thời gian.
- Kéo thấp D+ khoảng 10ms qua GPIO (giả lập ngắt kết nối) giúp tăng xác suất khôi phục nhưng không đạt 100%.
Câu hỏi cần hỗ trợ
- Đối với chip F103, sau khi thức dậy từ chế độ dừng, có trình tự/khoảng thời gian chuẩn nào để kích hoạt HSE→PLL→USBFS nhằm tránh lỗi thiết bị CDC không?
- Việc kéo thấp D+ (giả lập ngắt mềm) sau khi thức dậy có phải là thực hành tốt nhất cho dòng F1, hay có phương pháp đặt lại cấp giao thức tốt hơn?
- Có tồn tại lỗi đã biết nào liên quan đến ảnh hưởng của HSE+PLL sau chế độ dừng lên tín hiệu SOF (USB frame sync) hoặc miền clock 48MHz không?
- Nên dùng HSI trước rồi chuyển sang PLL khi khôi phục clock USB, hay luôn ưu tiên ổn định PLL trước tiên?
- Những vấn đề cấp bo mạch nào có thể gây hiện tượng này: dung sai điện trở D+, diode ESD/rò rỉ, tải thạch anh quá mức, đáp ứng quá độ LDO kém?