清醒鼠标

在有些情况下,比如:功耗测试。我们需要让系统一直处于 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 下载

完整代码下载

工作的测试视频可以在这里看到

https://www.bilibili.com/video/BV1zz421m7Xc