【转】Arduino 控制USB设备(5)解析USB键盘的例子
下面是一个获得 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 |
char buf[ 8 ] = { 0 }; //keyboard buffer |
063 |
char old_buf[ 8 ] = { 0 }; //last poll |
064 |
/* Sticky key state */ |
065 |
bool numLock = false ; |
066 |
bool capsLock = false ; |
067 |
bool scrollLock = false ; |
068 |
bool line = false ; |
069 |
070 |
void setup(); |
071 |
void loop(); |
072 |
073 |
MAX3421E Max; |
074 |
USB Usb; |
075 |
076 |
void setup() { |
077 |
Serial.begin( 9600 ); |
078 |
Serial.println( "Start" ); |
079 |
Max.powerOn(); |
080 |
delay( 200 ); |
081 |
} |
082 |
083 |
void loop() { |
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 |
void kbd_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 |
void kbd_poll( void ) |
131 |
{ |
132 |
char i; |
133 |
static char leds = 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 |
case CAPSLOCK: |
147 |
capsLock =! capsLock; |
148 |
leds = ( capsLock ) ? leds |= bmCAPSLOCK : leds &= ~bmCAPSLOCK; |
149 |
// set or clear bit 1 of LED report byte |
150 |
break ; |
151 |
case NUMLOCK: |
152 |
numLock =! numLock; |
153 |
leds = ( numLock ) ? leds |= bmNUMLOCK : leds &= ~bmNUMLOCK; |
154 |
// set or clear bit 0 of LED report byte |
155 |
break ; |
156 |
case SCROLLLOCK: |
157 |
scrollLock =! scrollLock; |
158 |
leds = ( scrollLock ) ? leds |= bmSCROLLLOCK : leds &= ~bmSCROLLLOCK; |
159 |
// set or clear bit 2 of LED report byte |
160 |
break ; |
161 |
case DELETE: |
162 |
line = false ; |
163 |
break ; |
164 |
case RETURN: |
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 |
bool buf_compare( byte data ) |
187 |
{ |
188 |
char i; |
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 |
case BANG: return ( 0x21 ); |
205 |
case AT: return ( 0x40 ); |
206 |
case POUND: return ( 0x23 ); |
207 |
case DOLLAR: return ( 0x24 ); |
208 |
case PERCENT: return ( 0x25 ); |
209 |
case CAP: return ( 0x5e ); |
210 |
case AND: return ( 0x26 ); |
211 |
case STAR: return ( 0x2a ); |
212 |
case OPENBKT: return ( 0x28 ); |
213 |
case CLOSEBKT: 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 |
case SPACE: return ( 0x20 ); |
249 |
case HYPHEN: |
250 |
if (( mod & SHIFT ) == false ) { |
251 |
return ( 0x2d ); |
252 |
} |
253 |
else { |
254 |
return ( 0x5f ); |
255 |
} |
256 |
case EQUAL: |
257 |
if (( mod & SHIFT ) == false ) { |
258 |
return ( 0x3d ); |
259 |
} |
260 |
else { |
261 |
return ( 0x2b ); |
262 |
} |
263 |
case SQBKTOPEN: |
264 |
if (( mod & SHIFT ) == false ) { |
265 |
return ( 0x5b ); |
266 |
} |
267 |
else { |
268 |
return ( 0x7b ); |
269 |
} |
270 |
case SQBKTCLOSE: |
271 |
if (( mod & SHIFT ) == false ) { |
272 |
return ( 0x5d ); |
273 |
} |
274 |
else { |
275 |
return ( 0x7d ); |
276 |
} |
277 |
case BACKSLASH: |
278 |
if (( mod & SHIFT ) == false ) { |
279 |
return ( 0x5c ); |
280 |
} |
281 |
else { |
282 |
return ( 0x7c ); |
283 |
} |
284 |
case SEMICOLON: |
285 |
if (( mod & SHIFT ) == false ) { |
286 |
return ( 0x3b ); |
287 |
} |
288 |
else { |
289 |
return ( 0x3a ); |
290 |
} |
291 |
case INVCOMMA: |
292 |
if (( mod & SHIFT ) == false ) { |
293 |
return ( 0x27 ); |
294 |
} |
295 |
else { |
296 |
return ( 0x22 ); |
297 |
} |
298 |
case TILDE: |
299 |
if (( mod & SHIFT ) == false ) { |
300 |
return ( 0x60 ); |
301 |
} |
302 |
else { |
303 |
return ( 0x7e ); |
304 |
} |
305 |
case COMMA: |
306 |
if (( mod & SHIFT ) == false ) { |
307 |
return ( 0x2c ); |
308 |
} |
309 |
else { |
310 |
return ( 0x3c ); |
311 |
} |
312 |
case PERIOD: |
313 |
if (( mod & SHIFT ) == false ) { |
314 |
return ( 0x2e ); |
315 |
} |
316 |
else { |
317 |
return ( 0x3e ); |
318 |
} |
319 |
case FRONTSLASH: |
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