踩坑记录——USB键盘睡眠唤醒

news2024/11/20 3:25:26

踩坑记录——USB键盘睡眠唤醒

目录

  • 踩坑记录——USB键盘睡眠唤醒
    • 前言
    • 1 USB远程睡眠唤醒要注意的几个点
    • 2 MCU唤醒之后引起USB异常的几个点
    • 结束语

前言

前段时间我用一个国产MCU实现了雷蛇键盘的效果,按键支持十键无冲,RGB灯支持单控任意一个灯任意一种颜色,但是这个过程还是比较曲折的,原本以为键盘功能是最难搞的,低功耗处理是最简单的,没想到前面这么顺利,最后才翻车了,所以特意出一期记录一下我踩过的坑。

1 USB远程睡眠唤醒要注意的几个点

1、配置描述符(Configuration Descriptor)要打开远程唤醒(Remote Wakeup)功能。

Configuration Descriptor

OffsetFieldSizeValueDescriptionremark
0bLength1Number以字节为单位的描述符大小bLength以字节为单位的描述符大小(0x09)
1bDescriptorType1Constant配置描述符类型一般为CONFIGURATION (0x02)
2wTotalLength2Number配置返回的数据总长度包括该配置返回的所有描述符(配置、接口、端点、和专用的类型或者专用的厂商描述符)的总长度
4bNumInterfaces1Number配置支持的接口数量最小值为0x01
5bConfigurationValue1NumberGet Configuration 和Set Configuration请求的配置值必须为0x01或者更高值。取值为0的Set Configuration请求会使设备进入未配置状态(Not Configured state)
6iConfiguration1Index字符串描述符索引若没有字符串描述符,这个字段的值为0
7bmAttributes1Bitmap配置特性Bit7: USB1.0协议中表示总线供电(Bus Powered),设置bit7=1表示由总线供电(Bus Powered),其他协议该位保留(Reserved),必须设置为1
Bit6: 自供电(Self-powered),bits6=1时,设备自供电(Self-powered)
Bit5: 远程唤醒(Remote Wakeup),bit5=1时,设备支持远程唤醒
Bit4…0: 未使用,保留,必须为0
8bMaxPower1mA设备从总线获取的最大功耗当设备完全运行时,特定配置的USB设备从总线取得的最大功耗

bmAttributes属性的Bit5要置1,这样才能打开远程唤醒(Remote Wakeup)功能,另外PC端也要在相应的USB设备上打开“允许此设备唤醒计算机(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的倍数。
时钟频率要设对,否则会导致通讯异常。这个点原本我是知道的,但没想到的是MCU在进入休眠之后自动切换到了内部时钟,而且在唤醒之后没有切换回来,因此导致唤醒之后USB通讯异常。
所以,在上电初始化的时候以及休眠唤醒之后都需要配置好系统时钟。

3、USB Device唤醒PC需要发送唤醒序列。
PC在进入睡眠之后会主动发送SetDeviceFeature,设备端收到以后进入挂起状态(SUSPend)并且USB进入低功耗模式,如果设备需要唤醒PC的话则需要发送唤醒序列,先使用RESUME_INTERNAL唤醒设备本身,然后进入远程唤醒状态RESUME_START,远程唤醒的操作就是把USB控制寄存器的第4位置1,然后等待10ms把USB控制寄存器的第4位置为0,最后进入RESUME_OFF状态,设备的一次远程唤醒请求完成。
注:USB总线由SUSPend状态切换回CONFIGURED状态实际上是由Host决定的,Device只能发送唤醒序列,然后等Host返回ClearFeature之后才能真正的唤醒USB,回到正常的CONFIGURED状态

RESUME函数参考示例:

/*******************************************************************************
 * @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异常的几个点

我在调试好键盘功能之后就开始着手做MCU的休眠,但是在调试的过程中发现了一些新的问题。
注:我用的MCU是ch32v203,这个MCU是一款国产IC,应该是参考了stm32设计的,无论是硬件还是软件都极其相似,因此,如果改用stm32或者其他stm32的替代方案可能也会有类似的问题。

1、不能在USB中断服务函数里面让MCU进入休眠。
收到PC端传过来的休眠信号之后,会进USB中断,然后进入挂起状态(SUSPend),我测试的时候图方便直接在中断里面让MCU进入了停机模式,结果MCU唤不醒了,可能是因为唤醒之后要从睡眠那行代码继续往后跑,但是因为睡眠是在中断服务函数里面的,唤醒之后进不了这个中断了,也就没法继续往下跑了。

2、睡眠之前要失能窗口看门狗。
这个问题有点莫名其妙,窗口看门狗是挂在APB1时钟上面的,MCU进入休眠的时候会关闭APB1时钟,所以看门狗是不会影响休眠和唤醒的,实际上也是MCU休眠和唤醒的功能也是正常的,休眠之前USB和看门狗也是正常的,但是如果休眠时不先关闭看门狗时钟,唤醒之后就会出现USB通讯异常的情况,我一时间也没搞懂是什么原因,唯一有关联的是USB和看门狗都是挂在APB1下面的,有大神可以解答一下我的疑惑吗?

3、MCU休眠只能选择WFE,选择WFI的话USB无法唤醒MCU。
普通外部中断唤醒(EXTI0-15)不管用WFI还是WFE都是可以正常使用的,USB中断(EXTI18)在MCU休眠之前也是可以正常使用,但是一旦MCU通过WFI进入休眠之后,就无法通过USB中断唤醒了,这个时候哪怕通过其他外部中断唤醒了MCU,USB也还是无法恢复正常通讯。
如果是用WFE则没有这个问题,这就很奇怪了,中断配置我也检查过很多次了,并没有发现什么问题,最后没办法就只能用WFE了。

结束语

关于USB远程睡眠唤醒的坑就讲到这,这里其实只是列举了一部分,因为这只是总结我遇到的新坑,有些以前踩过的坑这里就没写了,我也是第一次做USB的低功耗,没想到会遇到这么多奇怪的问题。虽然最后问题都解决了,但是有些疑惑还是没想明白,有大神知道的话还望不吝赐教!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/453.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringAMQP (RabbitMQ五种模式 消息转换器)

一、简化Hello World模型实现步骤演示 代码步骤演示如下所示: 消息提供者publisher代码简化: 消息消费者consumer代码简化: 二、Work Queue 工作队列模型 消息提供者代码如下所示: 消息消费者代码如下所示: 消息…

SpringCloudAlibaba【六】微服务架构下的秒杀案例

背景 分布式微服务中的秒杀是怎么实现的呢?接着看下去吧 我们实现一个秒杀微服务,流程逻辑如下 项目搭建 MySQL create database if not exists demo;use demo;drop table if exists skill_goods;create table if not exists skill_goods (id bigint…

1024程序员节|【MySQL从入门到精通】【高级篇】(二十七)外连接和内连接如何进行查询优化呢?join的原理了解一波

您好,我是码农飞哥,感谢您阅读本文,欢迎一键三连哦。 💪🏻 1. Python基础专栏,基础知识一网打尽,9.9元买不了吃亏,买不了上当。 Python从入门到精通 ❤️ 2.网上优质的Python题库很少…

[面试宝典] Linux常见命令及面试题

前言: 😄作者简介:小曾同学.com,小伙伴们也可以叫我小曾,一个致力于测试开发的博主⛽️ 如果文章知识点有错误的地方,还请大家指正,让我们一起学习,一起进步。😊 座右铭:…

C语言 - 你一定能看懂的扫雷万字详解(加入了递归展开和手动标雷的功能)

C语言之扫雷详解(包含递归展开和手动标雷功能,非常强大!) 文章目录一.写在前面二.效果展示三.整体逻辑四.详解1.进入主函数,打印菜单,玩家作出选择2.定义棋盘的数组并进行赋值3.棋盘的展示4.随机布雷5.开始…

前端开发入门--html--Flask

快速开发网站 pip install flaskfrom flask import Flaskapp Flask(__name__)# 创建了网址 /show/info 和 函数index 的对应关系 # 以后用户在浏览器上访问 /show/info,网站自动执行 index app.route("/show/info") def index():return "中国联通&…

TPH-YOLOv5 | 基于Transformer的YOLOv5小目标检测器 | 四头加注意力

论文地址:https://arxiv.org/pdf/2108.11539.pdf 项目地址:https://github.com/cv516Buaa/tph-yolov5 在无人机捕获的场景中进行对象检测是最近的一项热门任务。由于无人机总是在不同的高度航行,物体尺度变化剧烈,给网络优化带来…

NMEA协议解析

文章目录一、NMEA0183协议1、NMEA基本框架2、常用语句1)GNGGA2)GNGLL3)GNGSA4)GPGSV5)GNRMC6)GNVTG7)GNZDA8)PAIRCLK等二、异或校验和代码1、网址在线计算BCC2、BCC校验和代码一、NM…

Java语言中的异常处理

异常处理 在java语言中,很机智的将异常作为对象来处理,而且定义一个基类java.lang.Throwable作为所有异常类的父类。在这许多类中一般分为两大类: 错误类(Error)和异常类(Expception)。 如图: 注&#xf…

iNOF在现实网络中的运用,以带反射器的iONF为例

定义 iNOF(Intelligent Lossless NVMe Over Fabric,智能无损存储网络)是指通过对接入主机的快速管控,将智能无损网络应用到存储系统,实现计算和存储网络融合的技术。 目的 网络转发设备用于传输流量,不同类…

竞争不是内卷,用头脑学习,而非时间

文章目录 用头脑学习,而非时间 前言 一、自由竞争不是内卷 二、内卷都在哪些行业 三、高效学习来大数据梦想联盟 用头脑学习,而非时间 前言 大多数人不懂,不会,不做,才是你的机会,你得行动&#xff…

【Queue】- 从源码分析ArrayDeque及其常用方法

文章目录概述ArrayDeque基础知识ArrayDeque内部结构ArrayDeque的构造方法ArrayDeque的扩容操作ArrayDeque常用方法将ArrayDeque作为双端队列使用时public void addFirst(E e)public void addLast(E e)public boolean offerFirst(E e)public boolean offerLast(E e)public E pol…

动态SLAM论文归纳

持续更新,持续更新 2022 Multi-modal Semantic SLAM for Complex Dynamic Environments 作者:Han Wang, Jing Ying Ko and Lihua Xie, Fellowcode:https://github.com/wh200720041/MMS_SLAM视频:https://www.youtube.com/watch…

web自动化测试——入门篇01

😏作者简介:博主是一位测试管理者,同时也是一名对外企业兼职讲师。 📡主页地址:【Austin_zhai】 🙆目的与景愿:旨在于能帮助更多的测试行业人员提升软硬技能,分享行业相关最新信息。…

并发编程中的原子性,可见性,有序性问题

前言:大家好,我是小威,24届毕业生,在一家满意的公司实习。本篇文章是关于并发编程中出现的原子性,可见性,有序性问题。 本篇文章记录的基础知识,适合在学Java的小白,也适合复习中&am…

PyTorch(三)TensorBoard 与 Transforms

文章目录Log一、TensorBoard1. TensorBoard 的安装2. SummaryWriter 的使用① add_scalar() 的使用a. 参数说明b. 函数使用c. 使用 Tensorboard② add_image() 的使用a. 参数说明b. 使用 numpy.array() 对 PIL 图片进行转换c. 使用函数d. 改变 global_step二、Transforms1. Tra…

数据结构 | 时间复杂度与空间复杂度

… 🌳🌲🌱本文已收录至:数据结构 | C语言 更多知识尽在此专栏中! 🎉🎉🎉欢迎点赞、收藏、关注 🎉🎉🎉文章目录🌳前言🌳正…

【C++初阶】类和对象(二)

大家好我是沐曦希💕 类和对象1.类的6个默认成员函数2.构造函数2.1 概念2.2 特性3.析构函数3.1 概念3.2 特性4.拷贝构造函数4.1 概念4.2 特征1.类的6个默认成员函数 空类:类中一个成员都没有 可是空类真的什么都没有吗? 并不是,任…

STM32关于UART的接收方式

STM32的 UART 一般分为定长接收和不定长接收 定长接收: HAL_UART_Receive():只能接收固定长度的数据,如果超过固定长度的数据只能接收对应长度,如果小于固定长度则不会接收 HAL_UART_Receive_IT():中断方式接收,每接收一个字节…

CSS 2 CSS 选择器 - 5 2.8 伪选择器 2.8.1 伪类选择器【根据特定状态选取元素】

CSS 文章目录CSS2 CSS 选择器 - 52.8 伪选择器2.8.1 伪类选择器【根据特定状态选取元素】2 CSS 选择器 - 5 2.8 伪选择器 2.8.1 伪类选择器【根据特定状态选取元素】 【什么是伪类】 伪类用于定义元素的特殊状态。 例如,它可以用于: 设置鼠标悬停在…