|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 7 k$ G" y% F) h4 \3 K0 }
8 ]0 J; W' _! {0 d) }扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
( l. @9 j V; _! \" [1 D' R: A8 l* W# o4 V. P5 M) }
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:; L7 z# a; R h- Q
( o) I$ l% ^1 j& \- n1 @' F/ F8 s5 Q! ~6 z) Q) }
const char hid_keymap_qwerty[14][8]={
( {# o: @9 C; o" B {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
, w( L$ C% J0 E( T {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
. v! k/ H+ ]8 G, H+ {! L/ G. Z {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},2 q* l- a. f3 e
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},# T% S5 {' ~ F. y- c# r
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
3 y( d- h8 c) r4 b0 X* ` {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},4 H# S0 |8 T: \' h+ x# U4 P
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
- R0 \1 s% K. k: ]) X0 |, B3 a {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
( s1 c9 W9 ~( } p$ U {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
, H7 u/ C1 X# C, x8 m {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},% Y0 j* W' v5 l' o/ X( C+ E
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
5 o/ K' K1 ~* X {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
' _: o; x. Y) m8 W5 E( R0 w% U4 @ {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
8 i6 U* J: l" t1 o {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}& L1 f0 A0 p$ R; ^
};! F: w2 C0 H |9 O& A; O5 `
# x$ J) j$ |9 X3 S9 X
. a% C% ]5 \/ _: Q/ h. V6 P$ ]# |
const char hid_keymap_dvorak[14][8]={
9 j1 u) p6 O4 v% [$ Y {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc}, J: Z: ]8 J. V* o
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
" P: e* r t- S, | {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
2 p- W- D: J9 M {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
" W1 S& k: g. i: ? {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4}, {7 U) X" q2 A1 @
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
* T' `" `$ }" N3 D- w4 p. H: T {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},1 }' F. c4 P% h9 F; Z) p
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
5 J& V+ I& v* Y {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
, h& ^. a- X% f {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
$ ]& F8 R0 Q) }' U# U {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},3 K" t; _- }7 L6 n
{HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},+ u. o, t' x4 V; M1 M& r5 I
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},$ O; A' n; r1 @ F9 O) q
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
6 b9 {% @& X+ r* h};
6 M; {+ j+ o+ `% S$ b9 Y) t( F0 @
1 W' X) t- i- d+ L/ [) @1 ? L
( N) l# E# P# e& v3 n- K- B: H上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。# t0 L1 r, N+ x, J/ @ x7 b4 U# [
% G, Q+ ^# ~+ S( J2 n! b
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
& m8 e2 c. ^7 I
|9 D& c7 U( A1 zvoid update_key_matrix(char row, char col, char onoff)
% f* R* @" J+ o0 F8 u{
z+ {% Q( p# P9 O static uint16_t hid_report[4]={0,0,0,0};
) U: X# {) L. O/ h6 R( B static char (*hid_keymap)[8]=hid_keymap_dvorak;
; F. t$ `, S. {. t" S6 N/ X1 f% }; k6 J) K9 y. Y0 r) |
+ F; o/ V1 r, N( Z# T unsigned char key=hid_keymap[row][col];5 c3 W1 s+ b% Q+ D
unsigned char *report =(unsigned char *)hid_report;1 d6 n$ t: }5 H) L
char i;+ k( v) i+ W' a6 C6 {; `5 U* T
* x; I" c. {0 A* [' L( L+ v2 } R; K: q1 {3 ^8 }' f6 i) }5 f* I
if(key==HK_MODE)
5 W; F0 U. R, W# T {
( S* q0 o+ \ }- z! R) L. r if(!onoff)* o$ e" P5 }+ W8 S: L1 |
{+ g- N" }3 ]+ @5 |. m7 w) e
if(hid_keymap==hid_keymap_dvorak)
: ^7 L; I+ w( u0 x1 f9 v, o6 P M {0 c, U$ l4 N2 t% M1 T9 o8 ^
hid_keymap=hid_keymap_qwerty;
1 Q6 p, r3 X" _ GPIOB->BSRR = (1<<2);
& u1 B8 M, e, \/ N) z# O5 ?6 e2 s }
1 j- g7 p6 R( o! K( P" f7 N+ r else
) }% J$ l; B* N k. I. W, \ {
. \' A$ ?5 p% }" S hid_keymap=hid_keymap_dvorak;
: F/ r& Z) b h( ^% T GPIOB->BRR = (1<<2);
! N! G: ~3 E- y& }7 B, Y& e }
" B3 v# u3 A6 y9 p }
% \1 A1 i7 [4 p5 Z5 C return;
6 w+ D) y% L5 A& x. D }
( ?0 Z& b5 U2 L+ z M! |) _
, |1 I& P+ k/ [/ U; |; @$ s+ o* ]
4 V+ p X5 H E8 k if(key>=0x80) // Alt, Ctrl, Shift
7 m2 v6 u& {1 j. _, P. _, R# V {
* v9 l3 k8 N) W; v7 |7 s uint8_t bitset = 1<<(key&7);$ U( R& W# P' a/ E% j6 |- M4 J
if(onoff) // non-zero is key up
$ c3 W9 |7 J4 ~! y' Z: l report[0] &= (~bitset);
# P1 X, L$ x1 ~! }3 z9 N else
6 K$ X( k) Y6 ~* ^" V% ~: D1 X8 z report[0] |= bitset;4 C M2 e5 U6 d8 v" p) A; Z5 S, f
}. K8 c" o" z: Y1 Y2 _! X" H$ b5 N
else
9 G% M J' g! e Y% _) m0 p0 t/ n {
6 ] A `" X5 |2 {6 ?$ N, n( g5 R if(onoff) // non-zero is key up% c; |5 x: m ~" n- R* [
{
& p0 N& K6 H: r2 r for(i=2;i<8;i++)- N/ d2 r, j* g/ O" k
{/ ?9 F2 ]- S5 s$ d* a. V5 G4 T
if(report==key)
$ o1 w7 g8 g$ n( J+ {+ j" D {# M! I4 k, O* L; N
report=0;
: r, f! @& Y7 {+ w break;
) ~ J2 T( Q9 R }8 k7 \2 [# a+ i4 v/ o& z
}
/ E" m% ]+ v- O: O }
/ W% Q! @2 S8 Q8 s else
% n6 c5 f* ?1 f2 a+ M8 G0 }' E {2 k, H* y! I" @9 q
for(i=2;i<8;i++)
. `, R) N2 L8 _( E. M {
, ~& t/ Z8 u# Q# X& _! K$ F if(report==key)/ X; v E6 z. L K& o' y7 _
break;. J7 d4 A; j) U; {( U
if(report==0)/ e9 a8 }8 y5 G. _
{. }) [. v7 x6 S9 K, w7 H3 j
report=key;
/ ~9 |2 x% Q9 ~0 } break;
) O6 [9 F' G4 S }
# N, v; l) z+ ~7 E9 R7 D }
# m; C& L( {5 N }
# }- W% K! K; B9 b4 m9 X }; m, g% @, T! n+ w0 ?% O7 G$ ]
for(i=0;i<4;i++)
- g. D, i& p% C* V. M% ] P) F2 t) K! { USB_PMA[192+i]=hid_report;
8 L0 a+ c9 o% a* ?; Q/ Q2 b USB_PMA[5]=8; //COUNT1_TX
( W1 r9 m! k, V/ e" k if(ep1_wait==0)
7 V6 f3 x% ]( R$ G/ t1 G {
: Q& Z+ z1 h& @ USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;+ @, I; Q& G: C; Q! R; x
ep1_wait=1;$ e3 j$ {5 ~0 y4 l6 r6 y4 t# M
}
' @& `' u2 b* w( W+ |; ]}4 j+ o+ m3 [8 @) h, l2 L
* l3 ]* `$ x/ A0 V! F# d x, N7 C& y6 g Y/ Q% K
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
/ V( N+ h/ S- J0 ^9 B
keyboard.zip
(8.7 KB, 下载次数: 6378)
0 \" K0 T3 q1 X- J) S" T4 @& x
/ I3 g5 M+ |* G2 g% M# g1 ^/ @3 c+ J
, A; C! D& o: p( [2 I0 C
2 j2 ]: L* W# |6 j. o. { |
|