一直没搞明白EZ-USB的远程唤醒到底是个什么鬼,今天就集中极力搞一搞。我不明白的主要集中在以下两点:
(1)、远程唤醒是由谁发起的?既然是叫“远程”,那么这个远程唤醒是由谁发起的呢?是Host吗?还是EZ-USB得WAKEUP / WU2引脚引起的中断吗?
(2)、远程唤醒的一整套的响应机制是怎样的呢?
同样,我才用的依然是“追踪研读法”,就是在固件源代码和芯片手册中搜索相关的关键字,详细的研读,追踪每一个细节,不放过能够捕捉到的任何蛛丝马迹。没办法,谁叫俺笨呢?哈哈,这就是笨人的本方法。
在这里,我将追踪“remote wakeup”、“WU2”等关键词。
远程唤醒在固件源码中的体现
固件源码中与远程唤醒有关的,是一个Rwuen变量,搜索到有8处这个变量的痕迹:
(1)声明Rwuen为BOOL类型的变量:
BOOL Rwuen;
(2)在main函数的刚开始先关闭远程唤醒:
Rwuen = FALSE; // Disable remote wakeup
(3)在主函数中处理睡眠状态时,用于判断远程唤醒是不是被使能了:
while(!Rwuen && EZUSB_EXTWAKEUP());
// above. Must continue to go back into suspend if the host has disabled remote wakeup
// *and* the wakeup was caused by the external wakeup pin.
(4)在设备请求的解析函数SetupCommand中,用于返回给Host设备状态:远程唤醒和自供电。
case SC_GET_STATUS: // *** Get Status
if(DR_GetStatus())
switch(SETUPDAT[0])
{
case GS_DEVICE: // Device
EP0BUF[0] = ((BYTE)Rwuen << 1) | (BYTE)Selfpwr;
EP0BUF[1] = 0;
EP0BCH = 0;
EP0BCL = 2;
break;
(5)在设备请求的解析函数SetupCommand中,用于响应清除特性的请求:
case SC_CLEAR_FEATURE: // *** Clear Feature
if(DR_ClearFeature())
switch(SETUPDAT[0])
{
case FT_DEVICE: // Device
if(SETUPDAT[2] == 1)
Rwuen = FALSE; // Disable Remote Wakeup
else
EZUSB_STALL_EP0(); // Stall End Point 0
break;
case FT_ENDPOINT: // End Point
if(SETUPDAT[2] == 0)
{
*(BYTE xdata *) epcs(SETUPDAT[4]) &= ~bmEPSTALL;
EZUSB_RESET_DATA_TOGGLE( SETUPDAT[4] );
}
else
EZUSB_STALL_EP0(); // Stall End Point 0
break;
}
break;
(6)在设备请求的解析函数SetupCommand中,用于响应设置特性的请求:
case SC_SET_FEATURE: // *** Set Feature
if(DR_SetFeature())
switch(SETUPDAT[0])
{
case FT_DEVICE: // Device
if(SETUPDAT[2] == 1)
Rwuen = TRUE; // Enable Remote Wakeup
else if(SETUPDAT[2] == 2)
// Set Feature Test Mode. The core handles this request. However, it is
// necessary for the firmware to complete the handshake phase of the
// control transfer before the chip will enter test mode. It is also
// necessary for FX2 to be physically disconnected (D+ and D-)
// from the host before it will enter test mode.
break;
else
EZUSB_STALL_EP0(); // Stall End Point 0
break;
case FT_ENDPOINT: // End Point
*(BYTE xdata *) epcs(SETUPDAT[4]) |= bmEPSTALL;
break;
default:
EZUSB_STALL_EP0(); // Stall End Point 0
}
break;
(7)变量的声明:
extern BOOL Rwuen;
(8)在初始化程序中使能远程唤醒:
void TD_Init(void) // Called once at startup
{
BREAKPT &= ~bmBPEN;
Rwuen = TRUE; // 使能远程唤醒
那么问题来了:
Rwuen只是一个声明的变量,单纯的变量而已,并没有牵涉到芯片的寄存器,上述固件源码中只看到判断Ruwen的状态,而没有看到响应寄存器的设置,那是怎么直到有“远程唤醒”事件的发生呢?也就是说没有看到类似中断服务程序。
我想这个问题只能到文档中去寻找相关的描述了吧!那么,下面继续:
文档中的相关描述,关键词remote wakeup
↑这里说到:USB总线上所有的数据传输都是有Host发起的,但是只有一种情况是例外的,即当Host将USB Device设置成挂起模式进入低功耗状态后,USB设备可以发起一个“remote wakeup ”信号。
起码明白了一点:远程唤醒信号是由USB设备发送给Host。
那么问题又来了:
(1)固件源码中并没有相应的体现啊!!!即固件源码中并没有发送远程唤醒信号给Host的部分;
(2)设备是怎样知道啥时候该发送这个信号呢???固件源码中也没有说。
继续追踪:
↑ 这里提到:USB设备要想发起恢复信号,则他必须在枚举阶段在配置描述符里表明其具备远程唤醒能力,并且Host使能了该设备的远程唤醒能力。
继续追踪:
↑谢天谢地,这里详细介绍了远程唤醒,其实就是恢复/重新启动。
这段说到,固件通过设置USBCS寄存器的SIGRSUME位,来向Host发送远程唤醒请求,总算解决了一个疑问,那么Host收到远程唤醒请求(即Resume,后面说成恢复)之后,怎样将USB设备恢复呢???从下图里可以看到,8051只需要做到设置USBCS寄存器的SIGRSUME位即可,其他的应该是有Host和USB设备的硬件来做吧:
当然,还有一些细节要注意:固件在收到唤醒中断5ms之后才能设置USBCS寄存器的SIGRSUME位,然后再过10-15ms清除之。现在又多了一个问题:我咋没有看见固件中的唤醒中断呢??
↑这一段又可耻的说了一通恢复程序中应该注意的问题,看来他要吊我的胃口了,就是不肯说这个唤醒中断在哪里,还有Host收到恢复请求之后是怎样告诉USB设备可以恢复了。
在设置USBCS寄存器的SIGRSUME位之前,固件必须检查唤醒源是否来自WAKEUP / WU2引脚,如果两个引脚都没有不是唤醒源,那么唤醒事件来自USB总线被激活,这种情况下就不能通过设置USBCS寄存器的SIGRSUME位来恢复。
↑继续东拉西扯转移话题,WU2/PA3是同时激活状态,只不过WU2只有在设置WU2EN位为1的前提下才有效,这是还需要将PA3设置成输入。
没办法,只得继续追踪了,可奈何文档里关于这个关键词也就这么多内容了。
文档中的相关描述,关键词WU2
之所以选择WU2这个关键词,是因为WU2是芯片的唤醒信号的其中一个引脚。我怀疑远程唤醒是WAKEUP / WU2引脚引起的。
↑ 在这张表中可以看到,Resume的中断优先级是0,是最高的。
↑说到,唤醒中断的标志位(哈哈哈,终于到重点了),RESI = 1 表明有Resume中断发生,中断源可能来自 WAKEUP或WU2引脚,或者USB总线从挂起状态恢复了。
我也终于在fw.c中找到了一段关于RESI 的代码,唤醒中断处理:
// Wake-up interrupt handler
void resume_isr(void) interrupt WKUP_VECT
{
EZUSB_CLEAR_RSMIRQ();
}
↑在这个中断服务程序里,也仅仅是清除了中断标志位,没有对变量Resume处理。还有,这里的WKUP_VECT为啥是6啊???不是应该是0吗???
#define EZUSB_ENABLE_RSMIRQ() (EICON |= 0x20)
// Enable Resume Interrupt (EPFI_)
#define EZUSB_DISABLE_RSMIRQ() (EICON &= ~0x20)
// Disable Resume Interrupt (EPFI_)
#define EZUSB_CLEAR_RSMIRQ() (EICON &= ~0x10)
// Clear Resume Interrupt Flag (PFI_)
↑上面这段话可以比较含混的证明使芯片唤醒的就是WAKEUP或WU2引脚,或者USB总线从挂起状态恢复这三个条件,但并没有明确说明远程唤醒来自这三个货。唉,为啥就不能明明白白的说明呢!!!
↑这里又有说明这三个唤醒源可以分别使能,也可以在这里设置WU和WU2的有效极性。也可以进一步查询是哪个引脚引起的唤醒中断。