• 正文
    • 1 USB遠程睡眠喚醒要注意的幾個點
    • 2 MCU喚醒之后引起USB異常的幾個點
  • 推薦器件
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

踩坑記錄——USB鍵盤睡眠喚醒

2024/07/18
3654
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

前段時間我用一個國產(chǎn)MCU實現(xiàn)了雷蛇鍵盤的效果,按鍵支持十鍵無沖,RGB燈支持單控任意一個燈任意一種顏色,但是這個過程還是比較曲折的,原本以為鍵盤功能是最難搞的,低功耗處理是最簡單的,沒想到前面這么順利,最后才翻車了,所以特意出一期記錄一下我踩過的坑。

1 USB遠程睡眠喚醒要注意的幾個點

1、配置描述符(Configuration Descriptor)要打開遠程喚醒(Remote Wakeup)功能。

Configuration Descriptor

Offset Field Size Value Description remark
0 bLength 1 Number 以字節(jié)為單位的描述符大小 bLength以字節(jié)為單位的描述符大小(0x09)
1 bDescriptorType 1 Constant 配置描述符類型 一般為CONFIGURATION (0x02)
2 wTotalLength 2 Number 配置返回的數(shù)據(jù)總長度 包括該配置返回的所有描述符(配置、接口、端點、和專用的類型或者專用的廠商描述符)的總長度
3 bNumInterfaces 1 Number 配置支持的接口數(shù)量 最小值為0x01
4 bConfigurationValue 1 Number Get Configuration 和Set Configuration請求的配置值 必須為0x01或者更高值。取值為0的Set Configuration請求會使設(shè)備進入未配置狀態(tài)(Not Configured state)
5 iConfiguration 1 Index 字符串描述符索引 若沒有字符串描述符,這個字段的值為0
6 bmAttributes 1 Bitmap 配置特性 Bit7: USB1.0協(xié)議中表示總線供電(Bus Powered),設(shè)置bit7=1表示由總線供電(Bus Powered),其他協(xié)議該位保留(Reserved),必須設(shè)置為1
Bit6: 自供電(Self-powered),bits6=1時,設(shè)備自供電(Self-powered)
Bit5: 遠程喚醒(Remote Wakeup),bit5=1時,設(shè)備支持遠程喚醒
Bit4…0: 未使用,保留,必須為0
7 bMaxPower 1 mA 設(shè)備從總線獲取的最大功耗 當(dāng)設(shè)備完全運行時,特定配置的USB設(shè)備從總線取得的最大功耗

bmAttributes屬性的Bit5要置1,這樣才能打開遠程喚醒(Remote Wakeup)功能,另外PC端也要在相應(yīng)的USB設(shè)備上打開“允許此設(shè)備喚醒計算機(O)”。

在這里插入圖片描述

Configuration Descriptor 部分配置參考示例:

0x09,                   //bLength(9);               配置描述符
0x02,                   //bDescriptorType(Configuration);
0x29,0x00,              //wTotalLength(41);
0x01,                   //bNumInterfaces(1);
0x01,                   //bConfigurationValue(1);
0x00,                   //iConfiguration(0);
0xA0,                   //bmAttributes(BUSPower); //支持遠程喚醒
0x32,                   //MaxPower(100mA);

2、USB的時鐘頻率不能低于48MHz且必須是48的倍數(shù)。

時鐘頻率要設(shè)對,否則會導(dǎo)致通訊異常。這個點原本我是知道的,但沒想到的是MCU在進入休眠之后自動切換到了內(nèi)部時鐘,而且在喚醒之后沒有切換回來,因此導(dǎo)致喚醒之后USB通訊異常。

所以,在上電初始化的時候以及休眠喚醒之后都需要配置好系統(tǒng)時鐘。

3、USB Device喚醒PC需要發(fā)送喚醒序列。

PC在進入睡眠之后會主動發(fā)送SetDeviceFeature,設(shè)備端收到以后進入掛起狀態(tài)(SUSPend)并且USB進入低功耗模式,如果設(shè)備需要喚醒PC的話則需要發(fā)送喚醒序列,先使用RESUME_INTERNAL喚醒設(shè)備本身,然后進入遠程喚醒狀態(tài)RESUME_START,遠程喚醒的操作就是把USB控制寄存器的第4位置1,然后等待10ms把USB控制寄存器的第4位置為0,最后進入RESUME_OFF狀態(tài),設(shè)備的一次遠程喚醒請求完成。

注:USB總線由SUSPend狀態(tài)切換回CONFIGURED狀態(tài)實際上是由Host決定的,Device只能發(fā)送喚醒序列,然后等Host返回ClearFeature之后才能真正的喚醒USB,回到正常的CONFIGURED狀態(tài)

RESUME函數(shù)參考示例:

/*******************************************************************************
 * @fn       Resume
 *
 * @brief    This is the state machine handling resume operations and
 *                 timing sequence. The control is based on the Resume structure
 *                 variables and on the ESOF interrupt calling this subroutine
 *                 without changing machine state.
 *
 * @param    a state machine value (RESUME_STATE)
 *                  RESUME_ESOF doesn't change ResumeS.eState allowing
 *                  decrementing of the ESOF counter in different states.
 *
 * @return  None.
 */
void Resume(RESUME_STATE eResumeSetVal)
{
  uint16_t wCNTR;

  if (eResumeSetVal != RESUME_ESOF)
	{
   ResumeS.eState = eResumeSetVal;
	}
	
  switch (ResumeS.eState)
  {
    case RESUME_EXTERNAL:
      if (remotewakeupon ==0)
      {
        Resume_Init();
        ResumeS.eState = RESUME_OFF;
      }
      else 
      {
        ResumeS.eState = RESUME_ON;
      }
      break;
			
    case RESUME_INTERNAL:
      Resume_Init();
      ResumeS.eState = RESUME_START;
      remotewakeupon = 1;
      break;
		
    case RESUME_LATER:
      ResumeS.bESOFcnt = 2;
      ResumeS.eState = RESUME_WAIT;
      break;
		
    case RESUME_WAIT:
      ResumeS.bESOFcnt--;
      if (ResumeS.bESOFcnt == 0)
        ResumeS.eState = RESUME_START;
      break;
			
    case RESUME_START:
      wCNTR = _GetCNTR();
      wCNTR |= CNTR_RESUME;
      _SetCNTR(wCNTR);
      ResumeS.eState = RESUME_ON;
      ResumeS.bESOFcnt = 10;
      break;
		
    case RESUME_ON:    
      ResumeS.bESOFcnt--;
      if (ResumeS.bESOFcnt == 0)
      {
        wCNTR = _GetCNTR();
        wCNTR &= (~CNTR_RESUME);
        _SetCNTR(wCNTR);
        ResumeS.eState = RESUME_OFF;
        remotewakeupon = 0;
      }
      break;
			
    case RESUME_OFF:
			
    case RESUME_ESOF:
			
    default:
      ResumeS.eState = RESUME_OFF;
      break;
  }
}

2 MCU喚醒之后引起USB異常的幾個點

我在調(diào)試好鍵盤功能之后就開始著手做MCU的休眠,但是在調(diào)試的過程中發(fā)現(xiàn)了一些新的問題。

注:我用的MCU是ch32v203,這個MCU是一款國產(chǎn)IC,應(yīng)該是參考了stm32設(shè)計的,無論是硬件還是軟件都極其相似,因此,如果改用stm32或者其他stm32的替代方案可能也會有類似的問題。

1、不能在USB中斷服務(wù)函數(shù)里面讓MCU進入休眠。

收到PC端傳過來的休眠信號之后,會進USB中斷,然后進入掛起狀態(tài)(SUSPend),我測試的時候圖方便直接在中斷里面讓MCU進入了停機模式,結(jié)果MCU喚不醒了,可能是因為喚醒之后要從睡眠那行代碼繼續(xù)往后跑,但是因為睡眠是在中斷服務(wù)函數(shù)里面的,喚醒之后進不了這個中斷了,也就沒法繼續(xù)往下跑了。

2、睡眠之前要失能窗口看門狗。

這個問題有點莫名其妙,窗口看門狗是掛在APB1時鐘上面的,MCU進入休眠的時候會關(guān)閉APB1時鐘,所以看門狗是不會影響休眠和喚醒的,實際上也是MCU休眠和喚醒的功能也是正常的,休眠之前USB和看門狗也是正常的,但是如果休眠時不先關(guān)閉看門狗時鐘,喚醒之后就會出現(xiàn)USB通訊異常的情況,我一時間也沒搞懂是什么原因,唯一有關(guān)聯(lián)的是USB和看門狗都是掛在APB1下面的,有大神可以解答一下我的疑惑嗎?

3、MCU休眠只能選擇WFE,選擇WFI的話USB無法喚醒MCU。

普通外部中斷喚醒(EXTI0-15)不管用WFI還是WFE都是可以正常使用的,USB中斷(EXTI18)在MCU休眠之前也是可以正常使用,但是一旦MCU通過WFI進入休眠之后,就無法通過USB中斷喚醒了,這個時候哪怕通過其他外部中斷喚醒了MCU,USB也還是無法恢復(fù)正常通訊。

如果是用WFE則沒有這個問題,這就很奇怪了,中斷配置我也檢查過很多次了,并沒有發(fā)現(xiàn)什么問題,最后沒辦法就只能用WFE了。

結(jié)束語

關(guān)于USB遠程睡眠喚醒的坑就講到這,這里其實只是列舉了一部分,因為這只是總結(jié)我遇到的新坑,有些以前踩過的坑這里就沒寫了,我也是第一次做USB的低功耗,沒想到會遇到這么多奇怪的問題。雖然最后問題都解決了,但是有些疑惑還是沒想明白,有大神知道的話還望不吝賜教!

推薦器件

更多器件
器件型號 數(shù)量 器件廠商 器件描述 數(shù)據(jù)手冊 ECAD模型 風(fēng)險等級 參考價格 更多信息
FOD4208 1 Fairchild Semiconductor Corporation Triac Output Optocoupler, 1-Element, 5000V Isolation, LEAD FREE, DIP-6
$4.1 查看
HFBR-2525EZ 1 Avago Technologies FIBER OPTIC RECEIVER, THROUGH HOLE MOUNT, ROHS COMPLIANT, PLASTIC, PACKAGE-8/6

ECAD模型

下載ECAD模型
暫無數(shù)據(jù) 查看
CY62148ESL-55ZAXIT 1 Cypress Semiconductor Standard SRAM, 512KX8, 55ns, CMOS, PDSO32, 8 X 13.40 MM, LEAD FREE, STSOP-32
$64.78 查看

相關(guān)推薦