zoologist 发表于 2015-8-6 12:15:32

Arduino 控制USB设备(5)解析USB键盘的例子-

本帖最后由 zoologist 于 2015-8-6 13:37 编辑

下面是一个获得 USB 键盘数据的例子【参考1】。原理上说,是将键盘切换为 Boot Protocol 这样就避免了需要具体解析HID的麻烦。

/* MAX3421E USB Host controller LCD/keyboard demonstration */
//#include <Spi.h>
#include "Max3421e.h"
#include "Usb.h"

/* keyboard data taken from configuration descriptor */
#define KBD_ADDR      1
#define KBD_EP          1
#define KBD_IF          0
#define EP_MAXPKTSIZE   8
#define EP_POLL         0x0a
/**/
//******************************************************************************
//macros to identify special charaters(other than Digits and Alphabets)
//******************************************************************************
#define BANG      (0x1E)
#define AT          (0x1F)
#define POUND       (0x20)
#define DOLLAR      (0x21)
#define PERCENT   (0x22)
#define CAP         (0x23)
#define AND         (0x24)
#define STAR      (0x25)
#define OPENBKT   (0x26)
#define CLOSEBKT    (0x27)

#define RETURN      (0x28)
#define ESCAPE      (0x29)
#define BACKSPACE   (0x2A)
#define TAB         (0x2B)
#define SPACE       (0x2C)
#define HYPHEN      (0x2D)
#define EQUAL       (0x2E)
#define SQBKTOPEN   (0x2F)
#define SQBKTCLOSE(0x30)
#define BACKSLASH   (0x31)
#define SEMICOLON   (0x33)
#define INVCOMMA    (0x34)
#define TILDE       (0x35)
#define COMMA       (0x36)
#define PERIOD      (0x37)
#define FRONTSLASH(0x38)
#define DELETE      (0x4c)
/**/
/* Modifier masks. One for both modifiers */
#define SHIFT       0x22
#define CTRL      0x11
#define ALT         0x44
#define GUI         0x88
/**/
/* "Sticky keys */
#define CAPSLOCK    (0x39)
#define NUMLOCK   (0x53)
#define SCROLLLOCK(0x47)
/* Sticky keys output report bitmasks */
#define bmNUMLOCK       0x01
#define bmCAPSLOCK      0x02
#define bmSCROLLLOCK    0x04
/**/
EP_RECORD ep_record[ 2 ];//endpoint record structure for the keyboard

char buf[ 8 ] = { 0 };      //keyboard buffer
char old_buf[ 8 ] = { 0 };//last poll
/* Sticky key state */
bool numLock = false;
bool capsLock = false;
bool scrollLock = false;
bool line = false;

void setup();
void loop();

MAX3421E Max;
USB Usb;

void setup() {
Serial.begin( 9600 );
Serial.println("Start");
Max.powerOn();
delay( 200 );
}

void loop() {
    Max.Task();
    Usb.Task();
    if( Usb.getUsbTaskState() == USB_STATE_CONFIGURING ) {
//wait for addressing state
      kbd_init();
      Usb.setUsbTaskState( USB_STATE_RUNNING );
    }
    if( Usb.getUsbTaskState() == USB_STATE_RUNNING ) {
//poll the keyboard
      kbd_poll();
    }
}
/* Initialize keyboard */
void kbd_init( void )
{
byte rcode = 0;//return code
/**/
    /* Initialize data structures */
    ep_record[ 0 ] = *( Usb.getDevTableEntry( 0,0 ));
                           //copy endpoint 0 parameters
    ep_record[ 1 ].MaxPktSize = EP_MAXPKTSIZE;
    ep_record[ 1 ].Interval= EP_POLL;
    ep_record[ 1 ].sndToggle = bmSNDTOG0;
    ep_record[ 1 ].rcvToggle = bmRCVTOG0;
    Usb.setDevTableEntry( 1, ep_record );            
                           //plug kbd.endpoint parameters to devtable
    /* Configure device */
    rcode = Usb.setConf( KBD_ADDR, 0, 1 );                  
    if( rcode ) {
      Serial.print("Error attempting to configure keyboard. Return code :");
      Serial.println( rcode, HEX );
      while(1);//stop
    }
    /* Set boot protocol */
    rcode = Usb.setProto( KBD_ADDR, 0, 0, 0 );
    if( rcode ) {
      Serial.print("Error attempting to configure boot protocol. Return code :");
      Serial.println( rcode, HEX );
      while( 1 );//stop
    }
    delay(2000);
    Serial.println("Keyboard initialized");
}

/* Poll keyboard and print result */
/* buffer starts at position 2, 0 is modifier key state and 1 is irrelevant */
void kbd_poll( void )
{
char i;
static char leds = 0;
byte rcode = 0;   //return code
    /* poll keyboard */
    rcode = Usb.inTransfer( KBD_ADDR, KBD_EP, 8, buf );
    if( rcode != 0 ) {
      return;
    }//if ( rcode..
    for( i = 2; i < 8; i++ ) {
   if( buf[ i ] == 0 ) {//end of non-empty space
      break;
   }
      if( buf_compare( buf[ i ] ) == false ) {   //if new key
      switch( buf[ i ] ) {
          case CAPSLOCK:
            capsLock =! capsLock;
            leds = ( capsLock ) ? leds |= bmCAPSLOCK : leds &= ~bmCAPSLOCK;   
                      // set or clear bit 1 of LED report byte
            break;
          case NUMLOCK:
            numLock =! numLock;
            leds = ( numLock ) ? leds |= bmNUMLOCK : leds &= ~bmNUMLOCK;      
                                  // set or clear bit 0 of LED report byte
            break;
          case SCROLLLOCK:
            scrollLock =! scrollLock;
            leds = ( scrollLock ) ? leds |= bmSCROLLLOCK : leds &= ~bmSCROLLLOCK;
                                  // set or clear bit 2 of LED report byte
            break;
          case DELETE:
            line = false;
            break;
          case RETURN:
            line =! line;
            break;
          default:
            //Serial.print(HIDtoA( buf[ i ], buf[ 0 ] ));
            break;
      }//switch( buf[ i ...
      Serial.print(buf[ i ],HEX);
      Serial.print(' ');      
      Serial.println(buf[ 0 ],HEX);      
      rcode = Usb.setReport( KBD_ADDR, 0, 1, KBD_IF, 0x02, 0, &leds );
      if( rcode ) {
          Serial.print("Set report error: ");
          Serial.println( rcode, HEX );
      }//if( rcode ...
   }//if( buf_compare( buf[ i ] ) == false ...
    }//for( i = 2...
    for( i = 2; i < 8; i++ ) {                  //copy new buffer to old
      old_buf[ i ] = buf[ i ];
    }
}
/* compare byte against bytes in old buffer */
bool buf_compare( byte data )
{
char i;
for( i = 2; i < 8; i++ ) {
   if( old_buf[ i ] == data ) {
   return( true );
   }
}
return( false );
}

/* HID to ASCII converter. Takes HID keyboard scancode, returns ASCII code */
byte HIDtoA( byte HIDbyte, byte mod )
{
/* upper row of the keyboard, numbers and special symbols */
if( HIDbyte >= 0x1e && HIDbyte <= 0x27 ) {
    if(( mod & SHIFT ) || numLock ) {    //shift key pressed
      switch( HIDbyte ) {
      case BANG:return( 0x21 );
      case AT:    return( 0x40 );
      case POUND: return( 0x23 );
      case DOLLAR: return( 0x24 );
      case PERCENT: return( 0x25 );
      case CAP: return( 0x5e );
      case AND: return( 0x26 );
      case STAR: return( 0x2a );
      case OPENBKT: return( 0x28 );
      case CLOSEBKT: return( 0x29 );
      }//switch( HIDbyte...
    }
    else {                  //numbers
      if( HIDbyte == 0x27 ) {//zero
      return( 0x30 );
      }
      else {
      return( HIDbyte + 0x13 );
      }
    }//numbers
}//if( HIDbyte >= 0x1e && HIDbyte <= 0x27
/**/
/* number pad. Arrows are not supported */
if(( HIDbyte >= 0x59 && HIDbyte <= 0x61 ) &&
                   ( numLock == true )) {// numbers 1-9
    return( HIDbyte - 0x28 );
}
if(( HIDbyte == 0x62 ) && ( numLock == true )) {//zero
    return( 0x30 );
}
/* Letters a-z */
if( HIDbyte >= 0x04 && HIDbyte <= 0x1d ) {
    if((( capsLock == true ) && ( mod & SHIFT ) == 0 )
          || (( capsLock == false ) && ( mod & SHIFT ))) {
                     //upper case
      return( HIDbyte + 0x3d );
    }
    else {//lower case
      return( HIDbyte + 0x5d );
    }
}//if( HIDbyte >= 0x04 && HIDbyte <= 0x1d...
/* Other special symbols */
if( HIDbyte >= 0x2c && HIDbyte <= 0x38 ) {
    switch( HIDbyte ) {
      case SPACE: return( 0x20 );
      case HYPHEN:
      if(( mod & SHIFT ) == false ) {
         return( 0x2d );
      }
      else {
          return( 0x5f );
      }
      case EQUAL:
       if(( mod & SHIFT ) == false ) {
      return( 0x3d );
       }
       else {
      return( 0x2b );
       }
       case SQBKTOPEN:
         if(( mod & SHIFT ) == false ) {
          return( 0x5b );
         }
         else {
          return( 0x7b );
         }
       case SQBKTCLOSE:
         if(( mod & SHIFT ) == false ) {
          return( 0x5d );
         }
         else {
          return( 0x7d );
         }
       case BACKSLASH:
         if(( mod & SHIFT ) == false ) {
         return( 0x5c );
         }
         else {
         return( 0x7c );
         }
       case SEMICOLON:
         if(( mod & SHIFT ) == false ) {
         return( 0x3b );
         }
         else {
         return( 0x3a );
         }
      case INVCOMMA:
      if(( mod & SHIFT ) == false ) {
          return( 0x27 );
      }
      else {
          return( 0x22 );
      }
      case TILDE:
      if(( mod & SHIFT ) == false ) {
          return( 0x60 );
      }
      else {
          return( 0x7e );
      }
      case COMMA:   
      if(( mod & SHIFT ) == false ) {
          return( 0x2c );
      }
      else {
          return( 0x3c );
      }
      case PERIOD:
      if(( mod & SHIFT ) == false ) {
          return( 0x2e );
      }
      else {
          return( 0x3e );
      }
      case FRONTSLASH:
      if(( mod & SHIFT ) == false ) {
          return( 0x2f );
      }
      else {
          return( 0x3f );
      }
      default:
      break;
    }//switch( HIDbyte..
}//if( HIDbye >= 0x2d && HIDbyte <= 0x38..
return( 0 );
}

实验依然使用上一次的USB小键盘。上面的按键分布如下:



关于键值的介绍可以在【参考2】找到

NumLock OFF的情况下,各输出键值:


*输出三次62,相当于输出3个0

NumLock ON的情况下,各输出键值:


*输出三次62外加一个53

运行结果



本文完整代码下载 LCDkbd


参考:
1. https://github.com/felis/USB_Host_Shield/tree/master/examples/descriptor_parser USB_Host_Shield/examples/LCDkbd/
2. http://www.quadibloc.com/comp/scan.htm

本文首发 http://www.lab-z.com/usbkb/

nick_zm 发表于 2015-8-7 00:52:01

我看过有用32u4做键盘记录器,可以记录输入,并转发给电脑,很强的黑客工具

zoologist 发表于 2015-8-7 09:38:56

nick_zm 发表于 2015-8-7 00:52 static/image/common/back.gif
我看过有用32u4做键盘记录器,可以记录输入,并转发给电脑,很强的黑客工具

记录只是记录 ps2 的数据吧?

http://www.lab-z.com/ps2a/

不搭配硬件,直接解析不可能,性能不够

maxims 发表于 2015-8-8 08:41:50

请问:
1、这个只能针对usb键盘吗?其他的,比方usb鼠标?usb手柄(游戏手柄之类)?
2、键盘切换为 Boot Protocol,关于这点,能否详细一点?我百度了,出来的好像都和你说的没多大关系。

谢谢

zoologist 发表于 2015-8-8 09:12:10

maxims 发表于 2015-8-8 08:41 static/image/common/back.gif
请问:
1、这个只能针对usb键盘吗?其他的,比方usb鼠标?usb手柄(游戏手柄之类)?
2、键盘切换为 Boot ...

1.其他的HID设备也可以,但是需要额外的程序解析
2.国内基本上没有介绍Boot Protocol 的资料,这种情况很常见......这个 Protocol 最初的设计目的是给BIOS之类使用的。基本上所有的键盘鼠标都会支持。

maxims 发表于 2015-8-8 22:09:45

zoologist 发表于 2015-8-8 09:12 static/image/common/back.gif
1.其他的HID设备也可以,但是需要额外的程序解析
2.国内基本上没有介绍Boot Protocol 的资料,这种情况很 ...

好的,我再继续找找资料看看

nick_zm 发表于 2015-8-9 02:08:21

zoologist 发表于 2015-8-7 09:38 static/image/common/back.gif
记录只是记录 ps2 的数据吧?

http://www.lab-z.com/ps2a/


https://github.com/tmk/tmk_keyboard/tree/master/converter/usb_usb

happy1991 发表于 2016-6-19 08:41:12

不好意思,我是新手,想请教
如果我需要直接读取hid讯号
(我有个rfid读卡器,目前是直接透过hid-usb接到电脑上读取卡号)
想改成用arduino,请问有没有建议做法,或参考资料,
非常感谢。

zoologist 发表于 2016-6-19 22:22:04

happy1991 发表于 2016-6-19 08:41 static/image/common/back.gif
不好意思,我是新手,想请教
如果我需要直接读取hid讯号
(我有个rfid读卡器,目前是直接透过hid-usb接到电 ...

不知道你的读卡器是不是 hid 键盘设备,如果是的话,直接按照键盘解析,如果不是的话,

那恐怕必须慢慢研究如何解析了。

ps:hid协议的话,windows可以直接解析,但是 arduino 还做不到

lyly 发表于 2018-8-23 14:08:11

nick_zm 发表于 2015-8-7 00:52
我看过有用32u4做键盘记录器,可以记录输入,并转发给电脑,很强的黑客工具

可以请教下吗 大侠

ming3834436 发表于 2018-9-11 21:14:05

请教一下如何把键值输出一样(输入A,输出也是A)
页: [1]
查看完整版本: Arduino 控制USB设备(5)解析USB键盘的例子-