下面是一个获得 USB 键盘数据的例子【参考1】。原理上说,是将键盘切换为 Boot Protocol 这样就避免了需要具体解析HID的麻烦。
| 001 | /* MAX3421E USB Host controller LCD/keyboard demonstration */ | 
| 002 | //#include <Spi.h> | 
| 003 | #include "Max3421e.h" | 
| 004 | #include "Usb.h" | 
| 005 | 
| 006 | /* keyboard data taken from configuration descriptor */ | 
| 007 | #define KBD_ADDR        1 | 
| 008 | #define KBD_EP          1 | 
| 009 | #define KBD_IF          0 | 
| 010 | #define EP_MAXPKTSIZE   8 | 
| 011 | #define EP_POLL         0x0a | 
| 012 | /**/ | 
| 013 | //****************************************************************************** | 
| 014 | //  macros to identify special charaters(other than Digits and Alphabets) | 
| 015 | //****************************************************************************** | 
| 016 | #define BANG        (0x1E) | 
| 017 | #define AT          (0x1F) | 
| 018 | #define POUND       (0x20) | 
| 019 | #define DOLLAR      (0x21) | 
| 020 | #define PERCENT     (0x22) | 
| 021 | #define CAP         (0x23) | 
| 022 | #define AND         (0x24) | 
| 023 | #define STAR        (0x25) | 
| 024 | #define OPENBKT     (0x26) | 
| 025 | #define CLOSEBKT    (0x27) | 
| 026 | 
| 027 | #define RETURN      (0x28) | 
| 028 | #define ESCAPE      (0x29) | 
| 029 | #define BACKSPACE   (0x2A) | 
| 030 | #define TAB         (0x2B) | 
| 031 | #define SPACE       (0x2C) | 
| 032 | #define HYPHEN      (0x2D) | 
| 033 | #define EQUAL       (0x2E) | 
| 034 | #define SQBKTOPEN   (0x2F) | 
| 035 | #define SQBKTCLOSE  (0x30) | 
| 036 | #define BACKSLASH   (0x31) | 
| 037 | #define SEMICOLON   (0x33) | 
| 038 | #define INVCOMMA    (0x34) | 
| 039 | #define TILDE       (0x35) | 
| 040 | #define COMMA       (0x36) | 
| 041 | #define PERIOD      (0x37) | 
| 042 | #define FRONTSLASH  (0x38) | 
| 043 | #define DELETE      (0x4c) | 
| 044 | /**/ | 
| 045 | /* Modifier masks. One for both modifiers */ | 
| 046 | #define SHIFT       0x22 | 
| 047 | #define CTRL        0x11 | 
| 048 | #define ALT         0x44 | 
| 049 | #define GUI         0x88 | 
| 050 | /**/ | 
| 051 | /* "Sticky keys */ | 
| 052 | #define CAPSLOCK    (0x39) | 
| 053 | #define NUMLOCK     (0x53) | 
| 054 | #define SCROLLLOCK  (0x47) | 
| 055 | /* Sticky keys output report bitmasks */ | 
| 056 | #define bmNUMLOCK       0x01 | 
| 057 | #define bmCAPSLOCK      0x02 | 
| 058 | #define bmSCROLLLOCK    0x04 | 
| 059 | /**/ | 
| 060 | EP_RECORD ep_record[ 2 ];  //endpoint record structure for the keyboard | 
| 061 | 
| 062 | charbuf[ 8 ] = { 0 };      //keyboard buffer | 
| 063 | charold_buf[ 8 ] = { 0 };  //last poll | 
| 064 | /* Sticky key state */ | 
| 065 | boolnumLock = false; | 
| 066 | boolcapsLock = false; | 
| 067 | boolscrollLock = false; | 
| 068 | boolline = false; | 
| 069 | 
| 070 | voidsetup(); | 
| 071 | voidloop(); | 
| 072 | 
| 073 | MAX3421E Max; | 
| 074 | USB Usb; | 
| 075 | 
| 076 | voidsetup() { | 
| 077 |   Serial.begin( 9600 ); | 
| 078 |   Serial.println("Start"); | 
| 079 |   Max.powerOn(); | 
| 080 |   delay( 200 ); | 
| 081 | } | 
| 082 | 
| 083 | voidloop() { | 
| 084 |     Max.Task(); | 
| 085 |     Usb.Task(); | 
| 086 |     if( Usb.getUsbTaskState() == USB_STATE_CONFIGURING ) { | 
| 087 |  //wait for addressing state | 
| 088 |         kbd_init(); | 
| 089 |         Usb.setUsbTaskState( USB_STATE_RUNNING ); | 
| 090 |     } | 
| 091 |     if( Usb.getUsbTaskState() == USB_STATE_RUNNING ) {  | 
| 092 | //poll the keyboard  | 
| 093 |         kbd_poll(); | 
| 094 |     } | 
| 095 | } | 
| 096 | /* Initialize keyboard */ | 
| 097 | voidkbd_init( void) | 
| 098 | { | 
| 099 |  byte rcode = 0;  //return code | 
| 100 | /**/ | 
| 101 |     /* Initialize data structures */ | 
| 102 |     ep_record[ 0 ] = *( Usb.getDevTableEntry( 0,0 ));  | 
| 103 |                            //copy endpoint 0 parameters | 
| 104 |     ep_record[ 1 ].MaxPktSize = EP_MAXPKTSIZE; | 
| 105 |     ep_record[ 1 ].Interval  = EP_POLL; | 
| 106 |     ep_record[ 1 ].sndToggle = bmSNDTOG0; | 
| 107 |     ep_record[ 1 ].rcvToggle = bmRCVTOG0; | 
| 108 |     Usb.setDevTableEntry( 1, ep_record );              | 
| 109 |                            //plug kbd.endpoint parameters to devtable | 
| 110 |     /* Configure device */ | 
| 111 |     rcode = Usb.setConf( KBD_ADDR, 0, 1 );                    | 
| 112 |     if( rcode ) { | 
| 113 |         Serial.print("Error attempting to configure keyboard. Return code :"); | 
| 114 |         Serial.println( rcode, HEX ); | 
| 115 |         while(1);  //stop | 
| 116 |     } | 
| 117 |     /* Set boot protocol */ | 
| 118 |     rcode = Usb.setProto( KBD_ADDR, 0, 0, 0 ); | 
| 119 |     if( rcode ) { | 
| 120 |         Serial.print("Error attempting to configure boot protocol. Return code :"); | 
| 121 |         Serial.println( rcode, HEX ); | 
| 122 |         while( 1 );  //stop | 
| 123 |     } | 
| 124 |     delay(2000); | 
| 125 |     Serial.println("Keyboard initialized"); | 
| 126 | } | 
| 127 | 
| 128 | /* Poll keyboard and print result */ | 
| 129 | /* buffer starts at position 2, 0 is modifier key state and 1 is irrelevant */ | 
| 130 | voidkbd_poll( void) | 
| 131 | { | 
| 132 |  chari; | 
| 133 |  staticcharleds = 0; | 
| 134 |  byte rcode = 0;     //return code | 
| 135 |     /* poll keyboard */ | 
| 136 |     rcode = Usb.inTransfer( KBD_ADDR, KBD_EP, 8, buf ); | 
| 137 |     if( rcode != 0 ) { | 
| 138 |         return; | 
| 139 |     }//if ( rcode.. | 
| 140 |     for( i = 2; i < 8; i++ ) { | 
| 141 |      if( buf[ i ] == 0 ) {  //end of non-empty space | 
| 142 |         break; | 
| 143 |      } | 
| 144 |       if( buf_compare( buf[ i ] ) == false) {   //if new key | 
| 145 |         switch( buf[ i ] ) { | 
| 146 |           caseCAPSLOCK: | 
| 147 |             capsLock =! capsLock; | 
| 148 |             leds = ( capsLock ) ? leds |= bmCAPSLOCK : leds &= ~bmCAPSLOCK;    | 
| 149 |                       // set or clear bit 1 of LED report byte | 
| 150 |             break; | 
| 151 |           caseNUMLOCK: | 
| 152 |             numLock =! numLock; | 
| 153 |             leds = ( numLock ) ? leds |= bmNUMLOCK : leds &= ~bmNUMLOCK;       | 
| 154 |                                   // set or clear bit 0 of LED report byte | 
| 155 |             break; | 
| 156 |           caseSCROLLLOCK: | 
| 157 |             scrollLock =! scrollLock; | 
| 158 |             leds = ( scrollLock ) ? leds |= bmSCROLLLOCK : leds &= ~bmSCROLLLOCK; | 
| 159 |                                   // set or clear bit 2 of LED report byte | 
| 160 |             break; | 
| 161 |           caseDELETE: | 
| 162 |             line = false; | 
| 163 |             break; | 
| 164 |           caseRETURN: | 
| 165 |             line =! line; | 
| 166 |             break;  | 
| 167 |           default: | 
| 168 |             //Serial.print(HIDtoA( buf[ i ], buf[ 0 ] )); | 
| 169 |             break; | 
| 170 |         }//switch( buf[ i ... | 
| 171 |         Serial.print(buf[ i ],HEX); | 
| 172 |         Serial.print(‘ ‘);        | 
| 173 |         Serial.println(buf[ 0 ],HEX);        | 
| 174 |         rcode = Usb.setReport( KBD_ADDR, 0, 1, KBD_IF, 0x02, 0, &leds ); | 
| 175 |         if( rcode ) { | 
| 176 |           Serial.print("Set report error: "); | 
| 177 |           Serial.println( rcode, HEX ); | 
| 178 |         }//if( rcode ... | 
| 179 |      }//if( buf_compare( buf[ i ] ) == false ... | 
| 180 |     }//for( i = 2... | 
| 181 |     for( i = 2; i < 8; i++ ) {                    //copy new buffer to old | 
| 182 |       old_buf[ i ] = buf[ i ]; | 
| 183 |     } | 
| 184 | } | 
| 185 | /* compare byte against bytes in old buffer */ | 
| 186 | boolbuf_compare( byte data ) | 
| 187 | { | 
| 188 |  chari; | 
| 189 |  for( i = 2; i < 8; i++ ) { | 
| 190 |    if( old_buf[ i ] == data ) { | 
| 191 |      return( true); | 
| 192 |    } | 
| 193 |  } | 
| 194 |  return( false); | 
| 195 | } | 
| 196 | 
| 197 | /* HID to ASCII converter. Takes HID keyboard scancode, returns ASCII code */ | 
| 198 | byte HIDtoA( byte HIDbyte, byte mod ) | 
| 199 | { | 
| 200 |   /* upper row of the keyboard, numbers and special symbols */ | 
| 201 |   if( HIDbyte >= 0x1e && HIDbyte <= 0x27 ) { | 
| 202 |     if(( mod & SHIFT ) || numLock ) {    //shift key pressed | 
| 203 |       switch( HIDbyte ) { | 
| 204 |         caseBANG:  return( 0x21 ); | 
| 205 |         caseAT:    return( 0x40 ); | 
| 206 |         casePOUND: return( 0x23 ); | 
| 207 |         caseDOLLAR: return( 0x24 ); | 
| 208 |         casePERCENT: return( 0x25 ); | 
| 209 |         caseCAP: return( 0x5e ); | 
| 210 |         caseAND: return( 0x26 ); | 
| 211 |         caseSTAR: return( 0x2a ); | 
| 212 |         caseOPENBKT: return( 0x28 ); | 
| 213 |         caseCLOSEBKT: return( 0x29 ); | 
| 214 |       }//switch( HIDbyte... | 
| 215 |     } | 
| 216 |     else{                  //numbers | 
| 217 |       if( HIDbyte == 0x27 ) {  //zero | 
| 218 |         return( 0x30 ); | 
| 219 |       } | 
| 220 |       else{ | 
| 221 |         return( HIDbyte + 0x13 ); | 
| 222 |       } | 
| 223 |     }//numbers | 
| 224 |   }//if( HIDbyte >= 0x1e && HIDbyte <= 0x27 | 
| 225 |   /**/ | 
| 226 |   /* number pad. Arrows are not supported */ | 
| 227 |   if(( HIDbyte >= 0x59 && HIDbyte <= 0x61 ) && | 
| 228 |                    ( numLock == true)) {  // numbers 1-9 | 
| 229 |     return( HIDbyte - 0x28 ); | 
| 230 |   } | 
| 231 |   if(( HIDbyte == 0x62 ) && ( numLock == true)) {  //zero | 
| 232 |     return( 0x30 ); | 
| 233 |   } | 
| 234 |   /* Letters a-z */ | 
| 235 |   if( HIDbyte >= 0x04 && HIDbyte <= 0x1d ) { | 
| 236 |     if((( capsLock == true) && ( mod & SHIFT ) == 0 ) | 
| 237 |           || (( capsLock == false) && ( mod & SHIFT ))) {  | 
| 238 |                      //upper case | 
| 239 |       return( HIDbyte + 0x3d ); | 
| 240 |     } | 
| 241 |     else{  //lower case | 
| 242 |       return( HIDbyte + 0x5d ); | 
| 243 |     } | 
| 244 |   }//if( HIDbyte >= 0x04 && HIDbyte <= 0x1d... | 
| 245 |   /* Other special symbols */ | 
| 246 |   if( HIDbyte >= 0x2c && HIDbyte <= 0x38 ) { | 
| 247 |     switch( HIDbyte ) { | 
| 248 |       caseSPACE: return( 0x20 ); | 
| 249 |       caseHYPHEN: | 
| 250 |         if(( mod & SHIFT ) == false) { | 
| 251 |          return( 0x2d ); | 
| 252 |         } | 
| 253 |         else{ | 
| 254 |           return( 0x5f ); | 
| 255 |         } | 
| 256 |       caseEQUAL: | 
| 257 |        if(( mod & SHIFT ) == false) { | 
| 258 |         return( 0x3d ); | 
| 259 |        } | 
| 260 |        else{ | 
| 261 |         return( 0x2b ); | 
| 262 |        } | 
| 263 |        caseSQBKTOPEN: | 
| 264 |          if(( mod & SHIFT ) == false) { | 
| 265 |           return( 0x5b ); | 
| 266 |          } | 
| 267 |          else{ | 
| 268 |           return( 0x7b ); | 
| 269 |          } | 
| 270 |        caseSQBKTCLOSE: | 
| 271 |          if(( mod & SHIFT ) == false) { | 
| 272 |           return( 0x5d ); | 
| 273 |          } | 
| 274 |          else{ | 
| 275 |           return( 0x7d ); | 
| 276 |          } | 
| 277 |        caseBACKSLASH: | 
| 278 |          if(( mod & SHIFT ) == false) { | 
| 279 |            return( 0x5c ); | 
| 280 |          } | 
| 281 |          else{ | 
| 282 |            return( 0x7c ); | 
| 283 |          } | 
| 284 |        caseSEMICOLON: | 
| 285 |          if(( mod & SHIFT ) == false) { | 
| 286 |            return( 0x3b ); | 
| 287 |          } | 
| 288 |          else{ | 
| 289 |            return( 0x3a ); | 
| 290 |          } | 
| 291 |       caseINVCOMMA: | 
| 292 |         if(( mod & SHIFT ) == false) { | 
| 293 |           return( 0x27 ); | 
| 294 |         } | 
| 295 |         else{ | 
| 296 |           return( 0x22 ); | 
| 297 |         } | 
| 298 |       caseTILDE:  | 
| 299 |         if(( mod & SHIFT ) == false) { | 
| 300 |           return( 0x60 ); | 
| 301 |         } | 
| 302 |         else{ | 
| 303 |           return( 0x7e ); | 
| 304 |         } | 
| 305 |       caseCOMMA:   | 
| 306 |         if(( mod & SHIFT ) == false) { | 
| 307 |           return( 0x2c ); | 
| 308 |         } | 
| 309 |         else{ | 
| 310 |           return( 0x3c ); | 
| 311 |         } | 
| 312 |       casePERIOD: | 
| 313 |         if(( mod & SHIFT ) == false) { | 
| 314 |           return( 0x2e ); | 
| 315 |         } | 
| 316 |         else{ | 
| 317 |           return( 0x3e ); | 
| 318 |         } | 
| 319 |       caseFRONTSLASH: | 
| 320 |         if(( mod & SHIFT ) == false) { | 
| 321 |           return( 0x2f ); | 
| 322 |         } | 
| 323 |         else{ | 
| 324 |           return( 0x3f ); | 
| 325 |         } | 
| 326 |       default: | 
| 327 |         break; | 
| 328 |     }//switch( HIDbyte.. | 
| 329 |   }//if( HIDbye >= 0x2d && HIDbyte <= 0x38.. | 
| 330 |   return( 0 ); | 
| 331 | } | 
实验依然使用上一次的USB小键盘。上面的按键分布如下:
关于键值的介绍可以在【参考1】找到
NumLock OFF的情况下,各输出键值:
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
【转】Arduino 控制USB设备(5)解析USB键盘的例子
原文:http://www.cnblogs.com/airobot/p/5303772.html