让 Leonorade的键盘有“输出”能力-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 4168|回复: 0

让 Leonorade的键盘有“输出”能力

[复制链接]
发表于 2017-1-14 20:12 | 显示全部楼层 |阅读模式
本帖最后由 Zoologist 于 2017-2-22 22:09 编辑

Arduino Leonarado 和其他型号相比,最大的特点是可以方便的将自身模拟为USB键盘和鼠标。从USB总线的角度来说,数据通讯本身是双向的。从整体角度来说键盘只是输入设备,并没有输出的能力,但是如果仔细观察会发现键盘上有三个指示灯,分别是:NumLockScrollLockCapsLock。这三个指示灯作用如下:
1.     Num Lock 是副键盘中数字键盘的开关。在这个键对应的键盘指示灯关闭的情况下,小键盘的按键用来移动光标(上下左右、行首、行尾等等),在这个键对应的键盘指示打开的情况下,即锁定数字键,小键盘的按键用来输入数字;
2.     Caps Lock 是大小写锁定键,在这个键对应的键盘指示灯关闭的情况下,键盘输入的字母都是小写,否则键盘输入都为大写;
3.     Scroll lock (滚动锁定键)最初是用来设计为DOS下自动滚动屏幕的,但是进入Windows出现图形化的界面之后,这个按键就没有用途了,只是为了兼容等等考虑在通用键盘设计上仍然有所保留。
如果你的电脑有两个以上的键盘,还能观察到一个有趣的现象:在一个键盘上按下上述三个键中的某一个,那么其他的键盘状态也会跟着发生变化。原理上来说,是Windows会将收到的切换信息再广播出去,保证所有的键盘状态都是同步的。我用USB逻辑分析仪抓包,当在其他键盘上按下NumLock时,系统会向每一个USB键盘发出广播。比如:下面系统中有3个键盘,当键盘1按下 NumLock ,系统还会通知全部三个键盘“NumLock状态改变”的消息。
Capture.PNG

我们无需关心Windows中消息的格式,对于USB来说,USBHost (WindowsPC),会送出一个 SET Package来通知 USB  Device(USB 键盘)。下图是我用USB逻辑分析仪抓包的结果:
2.png

进一步展看查看协议,是发送了 07 USB 键盘
3.png

上面的原理可以帮助我们在Arduino Leonarado上实现。简单起见,我们的目标是在ArduinoLeonarado 上装上三个LED,让这三个LED和我的 USB键盘上三个LED实现同步。原生的ArduinoIDE 并没有设计这个功能,因此,需要对源代码进行修改:
第一个需要修改的地方是 \arduino\libraries\Keyboard\src\Keyboard.cpp 中的 USBKeyboardHID 描述符(Descriptor)。增加了下面斜体字表示的部分:
[mw_shl_code=c,true]  0x95, 0x08,                    //   REPORT_COUNT (8)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)
//ZT_DEBUG
    0x95, 0x05,                    //   REPORT_COUNT (5)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x05, 0x08,                    //   USAGE_PAGE (LEDs)
    0x19, 0x01,                    //   USAGE_MINIMUM (1)
    0x29, 0x05,                    //   USAGE_MAXIMUM (5)
    0x91, 0x02,                    //   OUTPUT (Data,Var,Abs) // LED report
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x03,                    //   REPORT_SIZE (3)
    0x91, 0x01,                    //   OUTPUT (Constant) // padding

//ZT_DEBUG
  0x95, 0x06,                    //   REPORT_COUNT (6)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x65,                    //   LOGICAL_MAXIMUM (101)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)[/mw_shl_code]
当然,具体作用请参考USB HID协议对照解读。简单的解释:修改之前的描述符只有INPUT部分,就是键盘告诉系统“我只能发出数据”;增加部分的作用是键盘告诉系统能够接收HOST过来的状态信息(USB协议中,INPUT指的是设备对主机的方向,OUTPUT指的是主机对设备)。我们的修改是加入“我还能接收”的能力。如果不声明这样的能力,系统不会将数据发送过来,具体实验中,一些USB接口的小键盘上面的LED不会跟随主机状态变化。
下面需要做的就是实际处理数据了。1.6.X 系列的代码和之前的差别很大,对于键盘这部分是分开在 HID Keyboard 的类中。

Keyboard 只有输出的代码,通过调用 HID 类来进行发送的处理。因此,我们需要在HID 类中开一个接口。
重新定义如下:

处理部分, \arduino\hardware\arduino\avr\libraries\HID\src\HID.cpp 中的 boolHID_::setup(USBSetup& setup)函数中,当我们收到 Set_Report 就是系统发过来的关于LED的设置,我们取下来即可。

对于 HID 开一个接口,直接返回即可

uint8_t HID_::getLedStatus(void)
{
               returnled;
}

我们给用户的接口是 Keyboard 类,我们要在上面田间一个读取的接口,

在这只文件中声明 \arduino\libraries\Keyboard\src\Keyboard.h

\arduino\libraries\Keyboard\src\Keyboard.cpp 文件中实现

编写一个例子,使用 Arduino 同步显示当前键盘的LED状态:

D5 最左侧灯,D6中间灯,D7 最右侧灯。
[mw_shl_code=c,true]
#include <Keyboard.h>

uint8_t old=0xFF,n=0xFF;
     
void setup() {
  pinMode(A0, INPUT_PULLUP);
  pinMode(5,OUTPUT);
  pinMode(6,OUTPUT);
  pinMode(7,OUTPUT);

  Keyboard.begin();

  Serial.begin(9600);
  while (digitalRead(A0) == HIGH) {
    // do nothing until pin 2 goes low
    delay(500);
  }
}
  
void loop() {

  n=Keyboard.getLedStatus();
  if (n!=old) {
     Serial.println(n);
     old=n;
     if (0!=(n&1) +) {
          digitalWrite(5,HIGH);
      }
     else {
          digitalWrite(5,LOW);
      }
     if (0!=(n&2)) {
          digitalWrite(6,HIGH);
      }
     else {
          digitalWrite(6,LOW);
      }
     if (0!=(n&4)) {
          digitalWrite(7,HIGH);
      }
     else {
          digitalWrite(7,LOW);
      }
  }
  delay(100);
}[/mw_shl_code]
IMG_20170110_214906.jpg


工作的视频
https://imgcache.qq.com/tencentvideo_v1/playerv3/TPout.swf?max_age=86400&v=20161117&vid=r0365fwdzml&auto=0
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-11-28 14:31 , Processed in 0.079852 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表