在有些情况下,比如:功耗测试。我们需要让系统一直处于 S0 状态,最好的方法莫过于摇晃鼠标。这次的作品就是一款基于 Dell 廉价鼠标的扩展方案,它每隔10秒摇晃一次鼠标,让你的系统不会休眠。
整体方案设计思路非常简单:鼠标的USB进入CH334 USB HUB 芯片之后,转出来2路USB信号,一路给PAW3515芯片,这是一个鼠标芯片;另外一路给CH552。我们通过编程,让Ch552将自身模拟为鼠标和键盘设备。Ch552键盘设备用于接收主机发过来的键盘LED控制信号;Ch552鼠标设备则是用于模拟鼠标的动作。
电路图如下,左上角是PAW3515鼠标的最小系统,右上角是Ch334 USB Hub芯片的最小系统,下方则是Ch552的最小系统。
PCB 设计如下:
这个的尺寸和戴尔MS116-T 的有线光电鼠标内部的PCB完全相同,用我们的这个PCB替换掉原版即可。焊接之后如下:
编写代码如下:
#ifndef USER_USB_RAM
#error "This example needs to be compiled with a USER USB setting"
#endif
#include "src/userUsbHidKeyboardMouse/USBHIDKeyboardMouse.h"
// 10秒触发一次
#define INTERVAL 10000UL
// 每次动作间隔 20ms
#define ACTION 50
// 触发计时
unsigned long int Elsp = 0;
// 记录当前是否已经触发过
boolean StageAssert[4] = {false, false, false, false};
uint8_t LastLed;
unsigned long LEDAssertElsp = 0;
// 触发状态标志
boolean StartWaken = false;
void setup() {
USBInit();
Serial0_begin(115200);
delay(3000);
Serial0_println("start");
LastLed = LedStatus;
}
// 判断是否满足条件
boolean MoveCondition(byte stage) {
// 条件1. 大于 INTERVAL 给出的时间
// 条件2. 小于 INTERVAL + (stage + 1)*ACTION 给出的时间
// 条件3. 之前没有触发过
if ((millis() - Elsp > INTERVAL + stage * ACTION) &&
(millis() - Elsp < INTERVAL + (stage + 1)*ACTION) &&
(StageAssert[stage] == false)) {
// 标记已经触发过
StageAssert[stage] = true;
return true;
}
return false;
}
void loop() {
// 如果发生了 LED 切换
if (LastLed != LedStatus) {
Serial0_println("B");
// 如果 1秒内发生了切换
if (millis() - LEDAssertElsp < 1000) {
// 触发状态反转
StartWaken = !StartWaken;
Serial0_print(StartWaken);
Serial0_println("C");
if (StartWaken == false) {
// 重置
for (byte i = 0; i < 4; i++) {
StageAssert[i] = false;
}
} else {
Elsp = millis();
}
}
// 记录切换时间
LEDAssertElsp = millis();
LastLed = LedStatus;
Serial0_println("E");
}
if (StartWaken != false) {
if (MoveCondition(0) == true) {
// 向右移动
Mouse_move(100, 0);
} else if (MoveCondition(1) == true) {
// 向下移动
Mouse_move(0, 100);
} else if (MoveCondition(2) == true) {
// 向左移动
Mouse_move(-100, 0);
} else if (MoveCondition(3) == true) {
// 向上移动
Mouse_move(0, -100);
// 重置
for (byte i = 0; i < 4; i++) {
StageAssert[i] = false;
}
Elsp = millis();
}
}
}
对应的功能有一个开关,使用连续按下键盘上的 Caps/NumLock/Scroll 两次即可触发。这里使用到了USB键盘的一个有趣的特性:操作系统会在全部的键盘中进行同步。比如,系统中有3个USB键盘,当你在其中一个键盘按下 Caps 按键之后,操作系统会通知其余两个键盘要求更改 Caps LED。使用USB抓包软件可以看到,下图就是 Windows主机端用于通知 Ch552 键盘要求更改LED的命令,我按下2次,Byte0 是Report ID, Byte1 是键盘LED的状态。
对应的代码在 \Ch552MSWaken\src\userUsbHidKeyboardMouse\USBHIDKeyboardMouse.c ,收到来自 EndPoint 1 的 Out 数据后,会更改 LedStatus 数值,以便主程序进行处理。
void USB_EP1_OUT() {
//Serial0_println("A");
LedStatus=Ep1Buffer[1]; //LABZ_Debug
if (U_TOG_OK) // Discard unsynchronized packets
{
}
}
经过改造,你得到的是一个表面上看起来和正经鼠标一摸一样的鼠标,同时它也有着和正经鼠标一摸一样的功能,但是当你触发之后,它会每隔10秒晃动一次。
电路图和PCB 下载
完整代码下载
工作的测试视频可以在这里看到