|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
# p8 D4 Q% e% |2 f' h4 ~8 {; n+ U' |
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
6 W: y1 s) ^7 t* E3 ^+ r; ?1 i
5 k$ K' |# }3 _ Q7 c* D. F# @要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:. t7 i- ^. e2 q# P% \: x. k
4 b: |! C: Q6 W f
7 V* N0 E3 Q/ b/ ~! Sconst char hid_keymap_qwerty[14][8]={- e, N% x8 Q3 L
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
7 l/ r" n* p& k0 p8 _2 V6 a( x {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},/ Y+ Y( F& T, I5 S2 b. V! _
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},/ f+ \& [8 b" [2 z* i# T( N
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
/ p T) S K) F, k) H+ I: o3 O {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
+ h0 {: @2 ^' L3 @( Z {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
3 v8 Q& E, p( ?. V& n {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},+ A0 R) d# i* v7 Z' ]
{HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
- V- Q, J& e3 X [+ `2 n {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},' H. U6 {3 ?+ X6 `
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},) b' I- H( b @9 k6 _9 d/ m5 _* {# L
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},8 [5 [( h w8 ]$ F6 E
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
. X" ~% F1 j% p! v! ^+ x" N; Z3 N {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
' f& u: f7 \. m- w3 C" f! r {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE} S6 z" X7 p* U$ B' H1 M
};
6 ]2 C ?1 @/ |$ f/ q# N) l) T! I( T4 q$ r) k
* |/ R ~$ F V
const char hid_keymap_dvorak[14][8]={1 Q* ^. p5 p3 N6 {1 f& }/ C5 O3 f
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},0 e1 I+ f7 o3 d) ]2 |
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},) n4 t3 G9 @& q- T- y
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},6 y& L! X9 @! ^* e# i& u
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
' H/ F$ g- N& ]4 p' e5 [4 Z- \3 K {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},- E! M: {) N2 X* P; [8 t
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
, r9 g- A) `; R/ o2 U {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},% ^, G. G5 l5 X' e3 o; X
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
- w0 f& d |& K V! t! ? {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
' ^/ O9 z, R* h' r& |! k% H {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
3 l/ H: F9 S& K9 B) F {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},/ ^) x; ?5 T$ D3 w4 s6 o1 {1 P1 r
{HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8}," i3 n$ q2 U: ?# M$ {$ g
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
0 R8 r/ P9 n0 [+ Y5 ]- U# h {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
0 Y/ o; N: [$ E2 ]6 U};
5 F) z& Y; {- ~3 U( T) R% ~# D
2 A3 V% C! l: r" R/ f1 @5 v
1 S/ G6 s( L1 Z! D上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。- g( k& q; h# B0 f8 Z* k
+ [$ _- V$ P0 f5 SHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
4 M4 u$ E( t3 y7 @8 x/ d) e2 ?% M0 v. v/ y
void update_key_matrix(char row, char col, char onoff)
4 x' B& E" P; D& `( V0 k{8 E0 v1 H* b' n' [* D) k3 p
static uint16_t hid_report[4]={0,0,0,0};/ K" ~* F& \6 j4 w$ Y
static char (*hid_keymap)[8]=hid_keymap_dvorak;
8 S7 k. C7 m2 C2 f% ?: e2 U! }( Q+ K0 S; f6 \+ e( j; m
/ h `9 N n$ l% L! `1 H1 g
unsigned char key=hid_keymap[row][col];7 l. I0 }" O- L( R
unsigned char *report =(unsigned char *)hid_report;0 g8 P5 T5 _9 F. p7 t5 E3 j
char i;
; p" D$ \& m$ p/ B* v2 h
* d* k+ U9 V% }8 A* f+ ?( T& Y6 K6 Q" ~4 ]/ }
if(key==HK_MODE)2 K5 o6 ~) Q5 ]" i; `; y* a1 u
{% u( R( G0 s1 |3 O {7 A% [0 C
if(!onoff)
" ~% A4 m$ A4 G. C: g+ B$ [# C {$ d5 L) ` k# k' i& u1 T& _
if(hid_keymap==hid_keymap_dvorak)
& Y. z- r' X5 F4 d4 H9 M. u6 ` {
$ ^( I8 T+ C0 j6 _6 z2 s hid_keymap=hid_keymap_qwerty;4 S" a( ?4 R$ O o6 [( s1 j
GPIOB->BSRR = (1<<2);# I( ]7 @) x& {! f* R
}/ f$ o1 ^( F7 Q; u. }2 x1 O
else8 p; }- S* c: w. j; {
{
* L- `" t1 s, p: z( ~ hid_keymap=hid_keymap_dvorak;: Q, F) ], @: J& [; R$ S6 r; m
GPIOB->BRR = (1<<2);0 c( t/ A! d( t- H" y) X" a
}. o- a% L, d6 v% P! u2 D# }' G
}- s" B+ u" i5 g' Y: t
return;) z- G) z+ J: y9 o4 G: g+ d# s5 ?
}
$ z+ o* H% r) k) I, R- c% E! G" z n" v
|# x* w+ ?5 ?) M! ]
if(key>=0x80) // Alt, Ctrl, Shift8 {; L3 e' b* @# K- B5 J3 y6 O
{
( n/ Y# L6 E2 n uint8_t bitset = 1<<(key&7);
2 u: @& R2 @8 O& P7 b$ U& @ if(onoff) // non-zero is key up% n% A2 x) d( m. v( s% R [* |. h
report[0] &= (~bitset);
" f/ ?5 T: K# P1 H; {0 w: a* [ else- a" K1 s6 D1 K8 |3 k( N
report[0] |= bitset;
* L0 p1 N5 r- e$ A) p7 K }( x! H/ Y# S( ?+ @# C, e
else# ?5 J% [1 W1 a# [: I1 a" w
{+ X+ d4 v* T! n+ W3 u) y
if(onoff) // non-zero is key up
1 G, ?; ]/ L y) t% J {
+ m4 w- A/ D4 B' a2 n for(i=2;i<8;i++)
% M* \' _! g6 s& Z) N; _, } {
: B" h$ P+ ?- r5 ]* k, B; L- C9 q if(report==key)) \' D Q) H! k# K& T, m6 c
{
3 \ v% l2 e8 J report=0; A( `# x. q: D0 H5 i1 c
break;# m- Y- T' P0 V3 A
}# T+ z, Z; r/ a, R8 U# Z
}7 v" Q' e& r" F R2 n. F& [ Y
}1 ]1 g+ i' D* ~- R2 a. v9 s
else9 ]; [8 q$ {, S$ @
{
- h: @% p7 b& J- x9 \1 @# p3 w" u( q3 z for(i=2;i<8;i++)) h. Y, A. ^$ T: ^6 D6 k# F
{$ Y1 \4 I, l7 I, ?- u8 m6 B0 L
if(report==key)( o9 ]& V6 t4 N( z; n8 D0 b
break;
# M& S C& i+ } if(report==0)' \ X' v( Q2 w( ~
{2 ~& u- X" y" A4 Q
report=key;
+ e: P7 ?- ]" { break;
% u: ?- {9 E. J$ o3 @% ] }
/ q' t2 y7 `) K2 f# R+ g }, _+ v. \# k* V3 A6 X7 K
}
. D1 g$ u* H5 j }
8 j2 P4 n# H1 w5 W$ P' y for(i=0;i<4;i++); V) k' C) B* m! K
USB_PMA[192+i]=hid_report;
7 U% H* o/ R% ^; X9 P" V USB_PMA[5]=8; //COUNT1_TX
9 w' z1 O; L* p9 X if(ep1_wait==0)/ P8 J. E: E' _& C
{
0 I1 e- a8 {( \- t+ r( B USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
: m# Z; Y( ]. C1 f' c ep1_wait=1;4 o- V8 [. B0 R# i
}$ \7 M5 c# z; h5 `
}
" r. h7 {, s2 `" e' I8 Y3 V# ] F: @) b
* Y$ G5 {% f, D1 r& d T
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
V% w' w) ]2 D6 A* i; [ T
keyboard.zip
(8.7 KB, 下载次数: 6064)
1 e% D: H8 s' F/ d( }4 x
7 I$ d+ w' E" v6 _: c1 l7 t
+ D& p( n" [# Z$ g! v5 M2 P# b i4 q" z. B ^3 B' t
, w& Y9 o) O& ~3 C. D! b
|
|