STM32F4 USB远程唤醒避坑指南
本篇文章是以STM32F411CEU6为例,使用USB远程唤醒功能去唤醒PC做的一个测试例程,以下是个人成功唤醒PC的一个方法。
1、配置描述符的bmAttributres字节必须配置远程唤醒功能
因为USB host会根据此D5:远程唤醒位来判断设备是否支持远程唤醒,若设备支持远程唤醒,那么PC进入休眠前会发送Set_Feature请求告诉USB device启用远程唤醒功能,此时device知道主机可以支持远程唤醒功能。当device发送唤醒序列唤醒host后,host会发送Clear_Feature清除此位来禁用远程唤醒功能。
2、远程唤醒流程参考
可以参考这两个网站,说的比较详细:
USB设备的休眠挂起及远程唤醒 - USB中文网 (usbzh.com)
USB设备远程唤醒RemoteWakeUp - USB中文网 (usbzh.com)
需要注意的是,当device向电脑端发送完唤醒序列后,电脑端被唤醒后会去读取device的设备HID端点数据,如果没有数据,此时电脑会重新进入休眠,会出现唤醒序列正确但是唤不醒电脑的现象。
所以,当device发送完唤醒序列后,必须还得发送HID report数据才能够成功唤醒电脑端,我这里是唤醒后去打开joystick设备,以此能在发送唤醒序列后再发送HID report数据成功唤醒PC。
3、code例程参考
例程中我是用了一个按键去触发唤醒序列实现的,代码如下:
/* 按键中断函数 */
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0) != RESET) {
#ifdef KEY_REMOTE_WAEUP_ENABLE
usb_drv_remote_wakeup();
#endif
EXTI_IRQn_Handler(EXTI_Line0);
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
/* 唤醒函数 */
void usb_drv_remote_wakeup(void)
{
usb_remote_wakeup_active_action();
usb_drv_device_resume_ind();
}
static void usb_remote_wakeup_active_action(void)
{
if (USB_OTG_dev.dev.DevRemoteWakeup)
{
if (USB_OTG_dev.cfg.low_power)
{
/* Reset SLEEPDEEP and SLEEPONEXIT bits */
SCB->SCR &=
(uint32_t) ~
((uint32_t) (SCB_SCR_SLEEPDEEP_Msk | SCB_SCR_SLEEPONEXIT_Msk));
/* device休眠后唤醒需要重新初始化时钟 */
SystemInit();
/* 重新打开USB clock */
USB_OTG_UngateClock(&USB_OTG_dev);
}
USB_OTG_ActiveRemoteWakeup(&USB_OTG_dev); //启用唤醒序列,1~15ms均可
USB_OTG_dev.dev.device_status = USB_OTG_dev.dev.device_old_status; //此处必须调用
usb_context.remote_wakeup = true; //唤醒标志位
APP_LOG_I("> USB Device woke up.\n");
}
}
void usb_drv_device_resume_ind(void)
{
usb_context.usbIsReady = true;
usb_drv_periph_enable(); //此处打开hid report外设,如果没打开就无法在唤醒后report data,则无法唤醒PC
}
sb_drv_periph_enable(); //此处打开hid report外设,如果没打开就无法在唤醒后report data,则无法唤醒PC
}