|  | 
 
 楼主|
发表于 2016-9-21 21:01:28
|
显示全部楼层 
| 本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 ( E3 J8 X, l# g4 D + e. {* k0 d9 s2 N6 `
 扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
 * I" l# r4 Q, T9 F9 y9 m. U2 U3 k1 k: X. U
 要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
 1 G/ s+ l8 C. @" m' o3 g# |3 P+ G) E# Z% w! c+ Z
 ' Y5 a- x1 S- W6 l
 const char hid_keymap_qwerty[14][8]={
 $ Y1 B0 Q# ]8 ~5 m* G    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
 2 M  w# k9 N6 j+ d5 }) v3 g    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},. m* c% `. _# w2 m0 k
 {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
 1 S9 p  q5 ^  u* ~* ^    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
 2 P. e0 h1 A# r. q% c    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
 ! U4 h8 R1 V' g: Q+ d    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},5 N; {: {' P4 A6 {3 s' S# G. n; w$ K
 {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
 - V+ }/ K# O) r# a5 P6 K* G% L$ U    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},4 |" A- k' ^) J' k4 H
 {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
 ; V8 k0 q* g$ ]" g# Y0 ]    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
 B9 L8 _$ _9 j2 a    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},( L, z$ h" P8 j
 {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},4 a# Y9 d3 F. Q! c. `% Q* y; B
 {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
 d8 o2 S1 P. m7 B0 r    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
 5 p; }0 a/ t* [, P};$ U1 v4 _0 H$ N
 
 - @+ l2 r3 l7 p) N' E: T
 3 w, ?( K5 `$ W4 pconst char hid_keymap_dvorak[14][8]={
 8 j( p- Q# S% l2 C) F( H    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
 - r, g$ o( D$ M) o( j. v% S    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
 & P" X) J1 M. y    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},0 p$ x6 a' r. A# H
 {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},/ F! P5 O& q' J9 r0 k, }! P8 j
 {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},6 C( ~- I% O# S% L
 {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
 # `9 c* y/ h* H6 z# I    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
 8 [! I* r* ]. v3 i    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},4 m9 R2 }$ j3 U6 C
 {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},, {0 o9 p1 v* c5 g( A9 N
 {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},4 Q, F6 l4 ?# Z' q9 \
 {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},4 A2 c/ }2 r9 ^4 d+ r
 {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},/ I# h' u' q" A3 P; M+ t
 {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
 5 K% U; b4 E# L* f    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}# i6 H0 w& J- _
 };. H6 ~# R+ I" t, Y; y! Y
 * ~& U" T1 k% h3 r* V
 : O! o, t2 Y6 R4 ~+ b
 上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。! h0 F/ h/ I, ]7 i  j
 2 _! M3 U) w: o; U7 e
 HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
 9 q1 Y4 ]0 H8 I- B" H0 z, s7 p: m
 9 s9 ?# O% J3 B" g: ?6 lvoid update_key_matrix(char row, char col, char onoff)
 4 E" Q. R3 h" t  }7 U{
 4 E5 |% ~# w" p% \    static uint16_t hid_report[4]={0,0,0,0};
 , o7 z  F0 B$ a- B* ~4 U, W5 b: H/ `    static char (*hid_keymap)[8]=hid_keymap_dvorak;" X, p. m/ Q; g7 F
 
 3 A7 Y1 ?% \. X) s( S3 C& f  w; T1 z/ t3 y
 unsigned char key=hid_keymap[row][col];7 y1 c/ F" ]( u2 P. ~# A8 |
 unsigned char *report =(unsigned char *)hid_report;
 ; x9 j9 T; x  v4 Z9 t& x. D1 e. {    char i;4 ^/ u1 Q+ ?/ U
 - @5 p/ G4 x) P4 `  P) _
 
 1 X9 f: A0 ]  L5 W" a" q    if(key==HK_MODE)! t2 y1 a( e1 ~+ R) F9 }* t
 {
 h/ ]0 n3 {9 Y) B2 k        if(!onoff)
 : _$ |+ |2 j; l* X& j. v( d3 J        {" a- ~3 N" |! i. e( W  ]) p
 if(hid_keymap==hid_keymap_dvorak)# q$ l7 I& k: r$ y) ~: W1 v: B
 {, f7 }: O1 b2 \+ e  }
 hid_keymap=hid_keymap_qwerty;2 o: {4 w+ a$ ?2 f- N5 i
 GPIOB->BSRR = (1<<2);& s$ p; `6 d; r9 N$ Y
 }0 j: L- V8 r9 ^- N6 v8 w; F4 r& y
 else* k+ C# C" E% g# H, P
 {
 + A# m" E/ |/ |% {                hid_keymap=hid_keymap_dvorak;
 3 {1 R7 r6 [0 {, E9 S$ }1 a# O                GPIOB->BRR = (1<<2);5 }6 l, S, J+ w( C
 }
 ! y' N, I! G7 c$ s4 h        }$ T8 e6 o- M/ u  |9 M8 q" a
 return;; O* O& F( R+ m0 _3 z
 }
 & D4 O" ^8 n; h  H$ H1 S4 ?
 : W; p3 K: U& [- _" Z- Z/ o- ~; O
 if(key>=0x80)   // Alt, Ctrl, Shift4 g4 V6 B( d- ], I* T$ C
 {; _# t2 o: H2 `7 {* |- X# T" M2 [& G
 uint8_t bitset = 1<<(key&7);+ v! x% ]* b, w+ p: o  p" @- `
 if(onoff)   // non-zero is key up
 g& d! h. T1 B, O! i            report[0] &= (~bitset);
 / S) Q& k" G6 E0 q1 r, s- `5 u        else  B+ V  z7 R, z' B
 report[0] |= bitset;9 F3 u5 [) b/ G' d8 G, F
 }/ h4 D7 q- n1 U6 y
 else
 3 G; C. d" g/ \9 c- k    {
 . U: U. a* l, n        if(onoff)   // non-zero is key up6 ]$ G9 g! j1 S  q4 ?; B
 {
 ( ~, ]6 s' o2 W+ }2 ?4 s8 z- g4 ]1 @            for(i=2;i<8;i++)! {* z5 c. ^! F6 v$ K
 {
 2 {- R. _2 r% N                if(report==key)6 S4 m* J6 [  i( u- S- C# ]* `: b
 {
 ) K% N; m, m( n, T5 Q9 w                    report=0;& U% A+ _, {9 w8 F# e
 break;7 o+ i; G5 H& _. `3 ~9 l7 Z
 }
 + ~' e2 g$ h* Z" A5 K5 e+ x            }' z4 v  h8 Z8 T
 }
 1 B( z7 a! V& e9 Y        else/ b4 l! b, E2 J+ n
 {9 s/ i# D3 C0 M7 w) {) v
 for(i=2;i<8;i++)* r0 x) V6 x( f  |- y
 {* w8 K# e8 ]3 H
 if(report==key): J; M! O. K- @! _
 break;+ w4 m) R( t; H8 d1 x) L  e4 X
 if(report==0)* B$ ^$ g, \8 x7 G+ i
 {
 ' U* s3 N$ p0 u6 }                    report=key;
 * P( R8 B3 `, ?/ F& w9 @3 [# f                    break;% F5 ]# E% m4 \% z
 }
 : u6 x" t* s3 V$ K0 ]* `            }
 & Z, F- Z, R+ c6 }        }! u8 n, l1 X6 B2 L$ J3 D
 }
 & B) _4 w! y( ^: Y7 [$ T; a    for(i=0;i<4;i++)1 _" u# Y3 H- v( f. n: N+ z
 USB_PMA[192+i]=hid_report;1 ?5 m/ h1 C* v2 U
 USB_PMA[5]=8;   //COUNT1_TX
 % [0 y6 n* |) t( V& _& U    if(ep1_wait==0); f2 [% L5 U0 a
 {
 8 m4 o5 Z" D9 i+ W4 t        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;) \) B, {) F2 ?& B- M
 ep1_wait=1;
 % D; ~" u% I# K" R    }
 ( Y4 N2 f" N* w: D}
 9 v$ f( a; F: T6 ?. S% z: b$ v2 _1 X3 @
 ( X- h3 O) C# w
 完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
 , l0 `% m# y' Y% x7 b
  keyboard.zip
(8.7 KB, 下载次数: 6333) # {8 j# o9 ^5 ^4 M0 I6 W( b) [8 B
 
 7 ?  r" L+ z/ h$ S8 r3 ~
 ' _2 q0 J% @$ ^$ B7 }, ]: P
 . N* W. r7 Y% {; V
 | 
 |