|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
8 n! v! @# A# S! Z/ F5 L- ?
* Q! A( p* Y, U' R \扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
, j) v0 E( ~8 j% M% @
. k) y; m7 G m+ T要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
) m& c8 m/ ~3 A. z3 i( l0 I, ?3 N; @+ M+ L
# I! m) p+ C1 U0 C0 N5 z
const char hid_keymap_qwerty[14][8]={* \9 B3 u/ n8 W% K6 q& `$ _) q
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
Y- l" B8 h$ V' c1 { {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},$ h) R/ D( j+ t& m# X; P6 k* Y
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
; |% n2 c3 H T6 | {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
" v7 l/ R d8 w {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
7 E% P% H t3 V5 b! n8 u0 V {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},0 ?& O+ Q& H1 a3 Q
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},: k& o) a; `. y3 E8 o+ R# J$ {5 R
{HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},/ Z3 H1 ]- _/ w
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
/ h. d0 A6 e& |% @, q! Z {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
0 C6 J/ U/ n, J1 A0 n {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
9 X% \- e, \" t( B {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},2 Y/ A( S3 }, L8 a/ B6 t
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},2 @0 r- P; |% V' I( s+ T$ s1 z
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
3 J; }' ^, n2 ?- a; w3 J5 s};
/ a5 A* i/ I6 Y3 C ]- P6 q
& b5 H3 u2 Q( w
4 D" L+ }$ e4 I! ~+ Dconst char hid_keymap_dvorak[14][8]={9 Z1 [( t/ k2 b v4 L
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
: p( l; j C3 H& s0 Y+ {! g4 t' O {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},$ y) r$ ^1 |8 V' ^" p9 ~+ s
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
& Y) u% ^# z! `, r- k& h% s. ?0 I {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},* I4 T5 o# b1 c- N. c
{HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},2 j# f! t7 r( r% n1 ]
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
& A9 ]$ `5 k" r% j9 Y% x3 V {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},& N/ f2 V$ H5 X' I2 s9 M
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
$ X, V* ]' ~& ^! W {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
8 R1 e$ S. H0 F& k! I4 ~) h2 e- L {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
: C+ Y* n+ s# P2 N6 l {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},% N; K2 d) ]8 j, x. T, h1 Y7 F
{HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8}, ]2 F0 \/ B+ I" X2 y& p. O+ b
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},0 ^( K% F" L* S% P; @
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}. E: Y7 W) d: d2 p- A. n4 x
};5 u; N) p; ~. u" X# E% P, J9 s% s/ j0 \9 `
% [( j' `9 [& W, c& B# D
5 }4 C: S+ o# e1 H- V, ?- N9 b
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
8 {$ F- U0 Y% r* }# y1 w& x- a% j2 q0 l5 a. K
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:' e( C0 t7 ~2 n0 Q( h8 S( G3 n
. G) W4 j0 A0 N) |( n
void update_key_matrix(char row, char col, char onoff)" G3 w6 `" k s5 p
{
8 B# b! n2 q9 v3 f1 | static uint16_t hid_report[4]={0,0,0,0};
% Q Z0 m; L% n& ~ |% G static char (*hid_keymap)[8]=hid_keymap_dvorak;
4 B3 E& @/ x& u, @6 p
& M' T' Q5 f- s. v' t1 L% Y3 o; I: z
unsigned char key=hid_keymap[row][col];
$ f0 b9 O- O* Q' P2 O unsigned char *report =(unsigned char *)hid_report;
+ W5 l; J& C- o. J6 N char i;
8 F9 G7 ^2 i2 \2 r! A. J# \1 Y8 x4 L L! a5 h
/ D1 d- ?& k$ \# H1 T7 r if(key==HK_MODE) k- P" M# C3 e/ Y4 o0 \8 a6 D
{$ T; N) t* n( ^3 h
if(!onoff)
: B6 [/ ?0 ]; z9 Z {1 v/ U7 ^) I/ n6 F! a% ^
if(hid_keymap==hid_keymap_dvorak)' N) A8 E2 n* p8 H
{
" W' }. Z3 w( v# x. B hid_keymap=hid_keymap_qwerty;
/ ?& H- t4 u# g0 G, u D' Q" ^5 D GPIOB->BSRR = (1<<2);
* Z! v/ T5 J* G }
9 D8 r* }! ^( o! w else6 k$ K* n* H2 @% i
{
5 |, m4 p$ U" r; P- z' U0 C1 L4 q hid_keymap=hid_keymap_dvorak;0 X. m0 W/ o$ r
GPIOB->BRR = (1<<2);
$ E! T, F- s; Q }
' p, U8 ~* m5 T6 m- U }% Q$ u* d5 `" `6 ^. p0 k
return;) p( h* y, w# Q- L
}4 c6 E) ?- p7 L0 R7 G- Q6 e
7 G# e* Q. S N% g4 }
. c9 F5 b+ d( x$ u. D3 n) |9 H: W
if(key>=0x80) // Alt, Ctrl, Shift0 C$ k. C. u7 Y9 X7 t! B. N% L# m$ w5 `
{9 H: e0 u! u+ f2 g) s
uint8_t bitset = 1<<(key&7);
% R3 @* l3 J# { if(onoff) // non-zero is key up5 h, y% e0 n$ ]. h" h5 g
report[0] &= (~bitset);
9 S! G" Q/ v$ S else
5 V% U% @; P/ a' d( d" k report[0] |= bitset;
' c# |% M) f1 z) C4 z! b }
! K2 W) y3 a* [6 i else
# i3 ^2 a$ g! ~, B! w5 L7 c! F$ p {5 V* U5 J9 h" h, M
if(onoff) // non-zero is key up0 d# L7 r' K3 K' U, J+ _+ O# ~
{" a* t& a8 T9 u
for(i=2;i<8;i++)% e7 C8 S- [. N1 L( ]5 c
{6 P/ ?# v1 A C6 d' v
if(report==key)
* M `8 h. E1 a( R5 N {
u2 ]: v" B' v% n report=0;+ d" p/ S: o. F) l# [: q
break;! R2 T; ]3 N5 s7 R8 N7 L
}
4 z; |7 k! p" ?7 _7 i } j1 S7 T. b8 z. s/ |4 s, \- J$ Y
}
& X) K2 X! p, c: v4 ` else
4 j1 g. M! U9 _, d/ v, G/ l, _ {! |7 L8 Q7 a" t$ i. f" h u* f2 w
for(i=2;i<8;i++)
# g! m! w5 p5 ^& W% N7 b4 a {
' P: r9 r& {* I; {$ G; t" I if(report==key)
$ c6 p5 w1 n: @ break;3 H# R8 t, f& }) ^& E. p, {
if(report==0)% x) e, j5 R( ]
{
: Y* u' K0 Y" i6 y4 [+ o report=key;4 n9 R& z" E& a9 `$ H# ~
break;
9 f K: e- c; q M }5 y- {: q j7 g/ q
}
& B" P2 A. m6 p! D }
' }6 c% e/ n( X* I }
5 X0 q8 |% x7 M for(i=0;i<4;i++)
% u! p( x9 y) _7 T c* p+ o/ d7 C USB_PMA[192+i]=hid_report;6 c5 Q; k" j0 b$ p
USB_PMA[5]=8; //COUNT1_TX# i7 ]! E! f% _0 c0 G8 }. z
if(ep1_wait==0)* b+ r) ~4 w4 U7 s% o
{
n4 r% I# k3 R: F) r5 p USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
- H1 w: }: t$ a1 q" \% O/ u ep1_wait=1;* U F: N! L h5 E
}% |5 ]) e) F- W' ~/ z! T( D
}
0 L0 @' v4 U: m3 W1 C J) y9 |6 i3 I: |7 {
% d1 @1 ?+ x. `8 \) e
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。! K* d5 X$ f5 M; v
keyboard.zip
(8.7 KB, 下载次数: 6124)
3 R# r+ h0 K6 ^. T
" p6 w! {, m2 C O
; U& e" u- r1 L: _& o
. X9 C, _2 X3 w& n
, A5 f; f' s% J |
|