Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
CherryUSB
CherryUSB开发笔记(一):FSDEV USB IP核的 HID Remote WakeUp (USB HID 远程唤醒) 2025-01-18 V1.1
发布于 2025-01-20 01:12:40 浏览:405
订阅该版
[tocm] # CherryUSB开发笔记(一):基于FSDEV IP的USB HID device remote wakeup (USB HID 设备远程唤醒) 2025-01-18 V1.1 # CherryUSB开发笔记
基于FSDEV IP的USB HID device remote wakeup
— — FSDEV USB IP核的 USB HID 设备远程唤醒实现
关键词:STM32、FSDEV、USB、Visual Studio 2022、CherryUSB、HID、remote wakeup、远程唤醒 # 前言 前几天给甲方重构项目的时候,发现CherryUSB从v1.4.0开始支持USB remote wakeup,提供了相对应的template,但是,实际发现只是写了API,并没有具体的实现。 看了眼代码,代码没有具体的唤醒序列过程,但是,既然写了接口,那么写个实现应该不难。 CheryyUSB在项目中能用到的东西也挺多的,HID的话可以做鼠标键盘之类的,MSC可以做读卡器、U盘、UF2升级,还可以实现DFU升级,当然前提是对USB的东西有个基础的印象,没有印象的可以通过以下的几个链接获取学习的资源。 1. [CherryUSB使用指南『官方』](https://cherryusb.readthedocs.io/zh-cn/latest/) 或是 [开发者经验/开源项目分享](https://cherryusb.readthedocs.io/zh-cn/latest/share.html#id1)『官方的资源推荐』 2. [CherryUSB移植笔记(一):APM32F407VGT6 DWC2移植 Port.A Full-Speed + Por.B High-Speed](https://club.rt-thread.org/ask/article/3e893614c58da7aa.html)『有打印解析』 3. [CherryUSB移植与远程唤醒实现](https://www.acfun.cn/v/ac46788507)『有字幕』 >为了大家玩的开心,我花了点时间做个视频,有需要的可以看看学学,很简单的,像是STM32、nRF52840几分钟就移植完了,前后只需要改不到10行代码的,还都是复制粘贴。 有手有腳,都能学会的,如果还学不会,那没法了,XD > **CherryUSB耻辱柱(做人不能太唐):** > >  > # 实现过程 STM32的STD库是带有CPU低功耗休眠唤醒的,写的相对复杂,既然只是实现USB唤醒,只专注于USB唤醒即可。 通过百度搜索,查阅到了[STM32 USB设备远程唤醒机制详解](https://blog.csdn.net/weiaipan1314/article/details/113600998)这篇文章,抓住唤醒(Resume)字眼,可知道唤醒是可由Host发起,也可以由设备发起。 那么设备如何发起?搜索通过“wakeup”关键字搜索HAL库,发现了`USB_ActivateRemoteWakeup()`和`USB_DeActivateRemoteWakeup()`这两个函数,一个是设置,一个是清除。  抓住CNTR这个关键字,在手册中查找,发现了这个寄存器的用法。  立马实操,发现如下的操作确实可以唤醒Host: 1. 在配置描述符中添加`USB_CONFIG_REMOTE_WAKEUP` ```c# const uint8_t hid_descriptor[] = { USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0x00, 0x00, 0x00, USBD_VID, USBD_PID, 0x0002, 0x01), USB_CONFIG_DESCRIPTOR_INIT(USB_HID_CONFIG_DESC_SIZ, 0x01, 0x01, USB_CONFIG_REMOTE_WAKEUP | USB_CONFIG_SELF_POWERED, USBD_MAX_POWER), ``` 2. 添加远程唤醒相关的报告描述符,是配置Feature ```c# 0x09, 0x3C, // Usage (Motion Wakeup) 0x05, 0xFF, // Usage Page (Reserved 0xFF) 0x09, 0x01, // Usage (0x01) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x01, // Logical Maximum (1) 0x75, 0x01, // Report Size (1) 0x95, 0x02, // Report Count (2) 0xB1, 0x22, // Feature (Data,Var,Abs,No Wrap,Linear,No Preferred State,No Null Position,Non-volatile) 0x75, 0x06, // Report Size (6) 0x95, 0x01, // Report Count (1) 0xB1, 0x01, // Feature (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 0xC0 // End Collection ``` 3. 按下按键之后,触发唤醒信号 ```c# if (!(GPIOB->IDR & (1 << 9))) //按下鼠标左键,触发唤醒 { USB->CNTR |= (uint16_t)USB_CNTR_RESUME; delay_ms(1); } ``` 但是,问题来了,触发了唤醒之后,系统卡死了,其他HID设备也都失效了。 甚至,拿到其他电脑上测试之后发现,整个根集线器下面的设备全掉了,XD 一翻搜索下发现手册里面的说明:  结合CherryUSB内的API,发现下面的判断条件是有意义的: ```c# if (g_usbd_core[busid].remote_wakeup_support && g_usbd_core[busid].remote_wakeup_enabled && g_usbd_core[busid].is_suspend) ``` 首先,必须触发了`Suspend`(休眠)之后,再进行`Set_Wakeup_Feature`,达成前提条件之后才能触发Resume信号; 其次,Resume信号必须的触发至少1ms但不能多于15ms,1ms内会由Host接管Resume信号的触发; 其三,设备必须具有唤醒功能;  怎么处理? `Suspend`就是中断触发休眠了,直接在USBD_IRQHandler的`if (wIstr & USB_ISTR_SUSP)`分支内进行处理即可,CherryUSB已经写了回调,直接调用即可; ```c# if (wIstr & USB_ISTR_SUSP) { ...... //#INFO 置位 is_suspend 标志位 usbd_event_suspend_handler(busid); } ``` `Set_Wakeup_Feature`就是休眠前电脑下发请求设置这个标志位了,CherryUSB也是已经写好了, ```c# case USB_REQUEST_CLEAR_FEATURE: case USB_REQUEST_SET_FEATURE: if (value == USB_FEATURE_REMOTE_WAKEUP) { if (setup->bRequest == USB_REQUEST_SET_FEATURE)//#SETFEATURE { g_usbd_core[busid].remote_wakeup_enabled = true; g_usbd_core[busid].event_handler(busid, USBD_EVENT_SET_REMOTE_WAKEUP); } else { g_usbd_core[busid].remote_wakeup_enabled = false;//#CLEARFEATURE g_usbd_core[busid].event_handler(busid, USBD_EVENT_CLR_REMOTE_WAKEUP); } } ``` 休眠前:  休眠后:  唤醒后:  休眠/睡眠/关机都时会下发set,将remote_wakeup_enable置1。 # 功能验证 emmmmmmm,功能算是弄完了么?怎么感觉像是差点意思,所以索性测试了点东西: ### 睡眠(S3)和休眠(S4)是否都能唤醒? 睡眠是保存到内存,休眠是保存到硬盘, 睡眠默认是通电的,休眠默认是断电的(但是休眠时可以设置为一直供电,但是此时是可以拔下硬盘的),休眠断USB的供电是没法唤醒的,因此需要打开关机依然保持供电输出的功能。 > 注意,不是所有的电脑都有USB唤醒,也不是所有的设备都有休眠,也不是所有的硬盘都能支持唤醒。 > 手头的**富士通T902笔记本就没有休眠唤醒,长江存储的硬盘就有休眠唤醒卡死或者休眠唤醒睡死情况**,建议谨慎测试远程唤醒之类的功能,尽量避免系统因此损坏。 ### 休眠(S4/S3)状态下,插拔USB后,能否唤醒? 那肯定是不可以,因为掉电后标志位都没了。 ### 休眠(S4/S3)状态下,插拔USB后,唤醒后是否会没法设置标志位? 是的,标志位`g_usbd_core[busid].remote_wakeup_support`只有在枚举时才配置标志位。 休眠后,如果你拔掉设备再插上,所以标志位置位,按开机键重启后,系统只是复位设备,不会重新枚举,所以之后的每一次睡眠/休眠,标志位只会变成011。 所以,CherryUSB的这个机制不够完善,稍微进行修改,可以在休眠后插拔,启动之后依然具有休眠唤醒的标志。 修改前: ```c# if (g_usbd_core[busid].remote_wakeup_support && g_usbd_core[busid].remote_wakeup_enabled && g_usbd_core[busid].is_suspend) ``` 修改后: ```c# bool remote_wakeup_support_flag = (g_usbd_core[0].descriptors[25] & USB_CONFIG_REMOTE_WAKEUP) ? true : false; //if (g_usbd_core[busid].remote_wakeup_support && g_usbd_core[busid].remote_wakeup_enabled && g_usbd_core[busid].is_suspend) if (remote_wakeup_support_flag && g_usbd_core[busid].remote_wakeup_enabled && g_usbd_core[busid].is_suspend) ``` 直接判断自己有没有配置就可以了,自己有没有配置,自己还能不知道么? 由于CherryUSB把描述符都写在一个数组里面,先发**设备描述符18个**,所以配置描述符的偏移值是`18`,bmAttributes是配置描述符的第七个参数,所以**bmAttributes**描述符的偏移值就是`25`,检查`D5`这个位有没有置1就可以了。 ```c# bmAttributes : 0xE0 D0~D4:保留值,一般置为0。 D5:设置为1表示支持远程唤醒功能,否则不支持。 D6:设置为1表示自供电,否则不支持。 D7:设置为1表示总线供电,否则不支持。 ``` # 百思不得其解的现象,但是功能是正常的。 ## Host触发休眠之后,疯狂IN  ## Resume触发时,K态只需要持续至少1ms,但是,K态持续了1400ms以上  ## 问题复盘: 正常唤醒:  睡眠(s3)/休眠(s4)后拔出设备: 按键触发[2]->唤醒信号清除[3]:测量的是1.95ms就清掉了标志位,按键没有用中断,直接是带2ms间隔的延迟轮训,所以大概就是1ms~2ms左右系统接管唤醒信号的触发  复测时优化了关键点的信号触发后,就整个唤醒流程就很明了,但是,原先的现象倒是没有复现。  1. 主机准备休眠时,Host向Device发送`USB_REQUEST_SET_FEATURE`,CherryUSB的HID目前就只有一个Feature: 1. 利用这个信号设置远程唤醒使能标志位:`g_usbd_core[busid].remote_wakeup_enabled = true;` 2. USB FS是大概1ms一帧,所以定时会向FS设备广播大概1ms间隔的SOF帧,如果丢失一次SOF帧,则会触发1次ESOF中断,如果连续3次丢失,则是触发休眠中断也就是会进入`if (wIstr & USB_ISTR_SUSP)()`这个中断分支,进入中断当然是需要设置休眠的标志位的: 1. 设置休眠标志位:`usbd_event_suspend_handler(busid); //设置休眠 is_suspend 标志位` 3. 唤醒按键点击,判断标志位是否达成,达成则触发唤醒信号: 达成触发标志位: 1. `g_usbd_core[busid].remote_wakeup_support`[Device]:CherryUSB默认是需要枚举时判读标志位的,但是休眠后插拔会丢失,设备自己支持不支持它自己就可以判断,所以我这么写: `bool remote_wakeup_support_flag = (g_usbd_core[0].descriptors[25] & USB_CONFIG_REMOTE_WAKEUP) ? true : false;` 2. `g_usbd_core[busid].remote_wakeup_enabled`[Host]:Host休眠(s3)/睡眠(s4)时设置; 3. `g_usbd_core[busid].is_suspend`[Host]:USB IP核挂起(suspend)时设置; 唤醒信号触发: 1. 设置唤醒信号:`USB->CNTR |= (uint16_t)USB_CNTR_RESUME;` 4. USB核唤醒后的1ms左右,没有收到SOF帧,必然会产生ESOF中断,然后通过中断清除唤醒信号: 1. 清除唤醒信号:`USB->CNTR &= (uint16_t)(~USB_CNTR_RESUME);` 5. 如果休眠时拔出设备,那么就需要按开机键唤醒,系统启动后会拉低DP信号触发复位(至少10ms),复位后设备正常使用,不需要重新枚举。 6. USB核休眠唤醒后的2.4s秒,Host向Device发送`USB_REQUEST_CLEAR_FEATURE(0x01)`清除标志位: 1. 利用这个信号设置远程唤醒使能标志位:`g_usbd_core[busid].remote_wakeup_enabled = false;` > 测试平台:惠普工作站 EliteBook 8560w > > 测试仪器:PX Logic 32 逻辑分析仪
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
Ghost_Girls
这家伙很懒,什么也没写!
文章
3
回答
4
被采纳
0
关注TA
发私信
相关文章
1
Cherry USB可以在cortex-A7 开发板上使用吗
2
gd32使用cherryusb 枚举报错
3
使用CherryUSB软件包如何处理与组件USB的配置和BSP中的USB设置
4
CherryUSB适配
5
使用Cherryusb host加载u盘后,shell就不能输入了
6
CherryUSB使用msc类,如何挂载到fal分区
7
rtthread cherry usb rndis调试遇到的一些问题记录其一 error: conflicting types for 'rt_kprintf'
8
CherryUSB和RTThread自带的USB如何合理使用
9
cherryusb适配EC800M-CN,如何实现拨号上网?
10
PICO 的 Cherry USB 支持
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
DMA
USB
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
rt-smart
FAL
I2C_IIC
UART
ESP8266
cubemx
WIZnet_W5500
ota在线升级
PWM
BSP
flash
freemodbus
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
keil_MDK
ulog
SFUD
msh
C++_cpp
MicroPython
本月问答贡献
RTT_逍遥
10
个答案
3
次被采纳
xiaorui
3
个答案
2
次被采纳
winfeng
2
个答案
2
次被采纳
三世执戟
8
个答案
1
次被采纳
KunYi
8
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
5
次点赞
lizimu
2
篇文章
8
次点赞
swet123
1
篇文章
4
次点赞
Days
1
篇文章
4
次点赞
YZRD
1
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部