USB设备的休眠挂起及远程唤醒RemoteWakeUp
USB协议的第9章讲到USB可见设备状态,分为连接(Attached),上电(Powered),默认(Default),地址(Address),配置(Configured)和挂起(Suspended)6个状态。所谓可见,即USB系统和主机可见的状态,其他状态属于USB设备内部而不可见。其中有关电源的,大致可分下面三类:
- 连接状态(Attached):设备连接,但未提供电源。
- 上电状态(Powered):设备被复位(Reset),或者说处于地址、配置状态。
- 挂起状态(Suspended):3 ms未见总线活动而处于省电状态,设备不可用,但仍然保持原有的USB地址和配置。

USB设备的挂起
我们知道,在USB系统中,正常状态下hub或root hub会一直周期性地发送SOF包(Start Of Frame,全速USB每1ms发送一个,高速USB则是125µs发送一个)。根据USB协议,如果USB线上一直处于空闲(Idle)状态超过3ms,设备应该把它当作一个挂起(Suspended)信号,要求设备在10ms内进入挂起状态,并把设备所需的电流大小降到规定的值(对于low-power设备,要求是500 µA,而对于high-power或支持远程唤醒(remote wakeup)功能的设备是2.5mA。
- 所在USB设备都应该支持挂起状态(Suspending State)。
- USB设备可以从任意供电状态进入挂起状态。
- USB设备从上游主机端口接收到持续的IDLE信号超过3.0ms时,USB设备应当认为这是一个挂起的信号,然后开始进入挂起状态。当所有端口无总线活动时,要求设备在10ms内进入挂起状态,并把设备所需的电流大小降到规定的值,即挂起电流值(对于low-power设备,要求是500 µA,而对于high-power或支持远程唤醒(remote wakeup)功能的设备是2.5mA。
- USB设备面向上游的端口上的任何总线活动都将使其退出挂起状态。
- 在没有任何其他总线流量的情况下,SOF令牌每(微)帧将发生一次,以防止全速/高速设备挂起。 在没有任何低速流量的情况下,低速设备将在每一帧中看到至少一个保持活动状态发生 SOF,使它们无法挂起。
- 当处于挂起状态时,设备必须继续为其 D+(全速/高速)或 D-(低速)上拉电阻供电,以保持IDLE状态,以便上游集线器可以保持正确的设备连接状态 。
- USB设备挂起时,可以停止设备的时钟,以进入低功耗模式。而当USB设备可以通过外部中断唤醒自身,然后再由USB设备唤醒主机。
在挂起状态中,设备必须继续向数据项D+/D-的上拉电阻提供电压以维持Idle状态。而对于USB2.0高速设备,还有些额外的要求:
- 高速设备在收到挂起信号(3ms空闲)后,应在0.125ms内切换到全速状态,也就是说要把终端电阻移除,并在D+数据线上重新挂上1.5k上拉电阻。
设备在随后的100-875µs内检测数据线上的状态。如果该状态是一个Full speed J,那么说明host发下来的是一个挂起信号;如果此时该状态是SE0,说明是host drive数据线D+到0,这是一个复位信号(复位信号会持续至少10ms时间)。
要注意的是高速设备在挂起状态时处于高速模式,只是所用的是全速信号。一旦从挂起状态恢复回来,会马上进入高速工作状态而无需进行复位操作。USB主机使使用周期性的SOF或Keep alive(低速)阻止设备进入挂起状态。
- For High-speed, SOF is sent every micro-frame 125us +/- 65ns
- For Full-speed, SOF is sent every frame 1ms +/- 500ns
- For Low-speed, Keep Alive (End of Packet) is sent every 1ms in absence of low-speed data
USB设备要退出【挂起模式】 可以由Host唤醒,或者自己唤醒。

- 如果设备配置为高功率(高达 500 mA),并启用了远程唤醒功能,则在挂起期间允许消耗高达 2.5 mA 的电流。
设备的唤醒
设备处于挂起状态时,任何总线上的活动(非空闲信号)都可以把设备唤醒/恢复,从而退出低功耗模式。
因为设备挂起时处于全速信号,在当host需要把将设备退出suspend状态时,需要先发送一个持续时间超过20ms的Fulll Speed K状态。设备看到K状态结束的1.3us内醒过来,而host需要在3ms内发送uSOF信号以维持正常的高速信号模式,否则设备又将进入suspend。
当主机想要在挂起后唤醒设备时,它通过将数据线上的信号极性反转至少 20ms 来实现。该信号以分组信号的低速端完成。
具有远程唤醒功能集的设备也可以自行启动恢复。它必须处于空闲状态至少 5ms,并且必须在 1 到 15ms 之间应用唤醒 K 条件。主机在 1 ms 内接管恢复信号的驱动。

设备的远程唤醒
同样,设备也可以换醒host,比如电脑待机时通过USB键盘来换醒主机,这种功能称之为“远程唤醒”(remote wakeup)。
在配置描述符中,USB设备会将是否支持远程唤醒的能力报告给主机。
GetStatus返回请求
GetStatus()设备请求返回的信息,包含是否允许远程唤醒的信息。
详见:USB读取状态GET_STATUS
在配置描述符中,偏移第7个字节的描述属性项,包含了设备是否支持远程的功能位。Bit5代表是否支持远程唤醒。
------------------ Configuration Descriptor -------------------
bLength : 0x09 (9 bytes)
bDescriptorType : 0x02 (Configuration Descriptor)
wTotalLength : 0x010B (267 bytes)
bNumInterfaces : 0x04 (4 Interfaces)
bConfigurationValue : 0x01 (Configuration 1)
iConfiguration : 0x00 (No String Descriptor)
bmAttributes : 0xA0
D7: Reserved, set 1 : 0x01
D6: Self Powered : 0x00 (no)
D5: Remote Wakeup : 0x01 (yes)
D4..0: Reserved, set 0 : 0x00
MaxPower : 0x32 (100 mA)
禁用USB设备的远程唤醒
当然,也可通过 USB清除特性请求CLEAR_FEATURE 来禁止USB的远程唤醒功能。
USB远程唤醒的编程
设备要唤醒主机,需要先将写寄存器RESUME信号,这样由设备驱动总线,将总线状态变为K状态(1~15ms),然后再清空RESUMEP寄存器,释放总线控制权,总线会回到J状态.主机收到resume信号之后,发出SOF,这样设备就知道主机唤醒了。
- 情况1:主机没有在1ms内发出SOF
原因:主机可能正在忙,或者USB控制器响应慢。
设备该怎么做:重新发一次Resume信号(再次置位RESUME,延时,清空),如果重试几次仍无响应,设备应重新进入挂起模式。 - 情况2:主机发的是复位信号(Reset),而不是SOF
原因:有些主机的驱动策略是“醒来后先复位设备,重新枚举”。
设备该怎么做:必须能正确处理复位信号(清空所有端点状态,重新等待主机枚举)。如果你的设备不支持这种场景,可能会卡住。
| 动作 | 描述 |
|---|---|
| 写寄存器RESUME信号 | 设备发起远程唤醒(Device Initiated Remote Wakeup) |
| 由设备驱动总线,变为K状态 | 设备驱动K状态(Device-Driven K State) |
| 保持1~15ms | 唤醒信号持续时间(Resume Duration) |
| 清空RESUME寄存器,释放总线 | 设备释放总线(Device Releases Bus) |
| 总线回到J状态 | 总线回到空闲状态(Idle / J State) |
| 主机收到信号后发出SOF | 主机恢复帧起始包(Host Resumes with SOF) |
| 设备收到SOF,确认唤醒 | 设备检测到总线活动(Bus Activity Detected) |
USB规格书P156)(7.1.7.7)A device with remote wakeup capability may not generate resume signaling unless the bus has been continuously in the Idle state for 5 ms (TWTRSM). This allows the hubs to get into their Suspend state andprepare for propagating resume signaling. The remote wakeup device must hold the resume signaling for atleast 1 ms but for no more than 15 ms (TDRSMUP). At the end of this period, the device stops driving the bus(puts its drivers into the high-impedance state and does not drive the bus to the J state).
如我们有一个STM32做的USB设备,通过外接GPIO按键触发实现远程唤醒PC主机。
Resume的代码其实是一个状态机,是将STM32远程唤醒的寄存器置高10ms,即10个EOF,然后再置低,以使USB设备通过D+/D-的电信号进行远程唤醒。
STM32寄存器定义将此位置高1-15ms,将进行远程唤醒操作。

stm32f407提供了远程唤醒的函数
/**
* @brief USB_OTG_RemoteWakeup : active remote wakeup signalling
* @param None
* @retval : None
*/
void USB_OTG_ActiveRemoteWakeup(USB_OTG_CORE_HANDLE *pdev)
{
if (pdev->dev.DevRemoteWakeup) // ① 主机允许远程唤醒吗?
{
dsts.d32 = USB_OTG_READ_REG32(&pdev->regs.DREGS->DSTS);
if (dsts.b.suspsts == 1) // ② 当前确实挂起了吗?
{
// ③ 低功耗模式:先恢复时钟
if (pdev->cfg.low_power) {
power.d32 = USB_OTG_READ_REG32(&pdev->regs.PCGCCTL);
power.b.gatehclk = 0; // 取消 AHB 时钟门控
power.b.stoppclk = 0; // 取消 PHY 时钟停止
USB_OTG_WRITE_REG32(pdev->regs.PCGCCTL, power.d32);
}
// ④ 发出远程唤醒信号
dctl.d32 = 0;
dctl.b.rmtwkupsig = 1; // 置位远程唤醒信号位
USB_OTG_MODIFY_REG32(&pdev->regs.DREGS->DCTL, 0, dctl.d32);
USB_OTG_BSP_mDelay(5); // ⑤ 保持 5ms
// ⑥ 清除信号
USB_OTG_MODIFY_REG32(&pdev->regs.DREGS->DCTL, dctl.d32, 0);
}
}
}
当然,支持远程唤醒也是需要PC机的配合的,一般情况下是在BIOS中的电源管理进行配置。
主机唤配设备
- 起始状态:主机和设备都处于挂起(Suspend)状态,总线空闲,处于 J 状态。
- 主机发起唤醒:主机会驱动总线进入 K 状态。
- 保持 K 状态:这个 K 状态会持续至少 20ms(这是关键差异点!)。
- 释放总线:主机停止驱动总线,总线回到 J 状态。
- 发送 SOF:紧接着,主机开始发送帧起始包(SOF),恢复正常通信。
设备端如何响应主机唤醒:
- 设备检测到总线从 J→K→J 的变化后,应该:
- 退出挂起模式
- 恢复内部时钟
- 重新使能USB收发器
- 等待接收主机发来的SOF包
- 设备检测到总线从 J→K→J 的变化后,应该:
设备不需要主动做任何事:
- 主机唤醒设备时,设备不需要像“设备唤醒主机”那样操作RESUME寄存器,只需要被动检测总线活动,然后自己退出挂起即可。
如果设备检测到K状态但收不到SOF:
- 如果K状态消失后超过3ms还没收到SOF,设备应该重新进入挂起状态(认为唤醒失败或总线异常)。

K状态的时间长度约为50ms
唤醒对比
| 唤醒方向 | K状态持续时间 | 后续动作 |
|---|---|---|
| 设备唤醒主机 | 1ms ~ 15ms | 主机发SOF |
| 主机唤醒设备 | 至少 20ms(典型值20ms~100ms) | 主机发SOF |
USB2.0物理层的电信号





