|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 7 O/ {& j! G+ F/ Y a
% |+ N% w5 Y9 g& y, l$ A
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。. b3 K5 S, `: e. N+ ~
- ^, c3 }% o, F' T, n) I
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:$ g( ?, m) o; N9 m0 D7 _
& @8 ]: [. j2 |! [; u% b1 _, L$ p( t. _$ N- x2 z3 a: q5 {3 J
const char hid_keymap_qwerty[14][8]={
6 A# U& L& R K0 n( s& j! B {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},4 }: H$ b5 t: \
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
9 \! }( L8 T+ ^1 j( p }) k {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
0 J; j/ J0 Q/ K; {! Q+ Y {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
9 _0 `& m! S! }! n7 e {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
0 M0 f9 r! r. F* j3 b9 P2 r {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},: c* _6 Z5 h, r. [0 N& A
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
. `# }) T" p) u' U: d {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
8 Q& K: ]( M% ]( j: `5 F6 M+ ] {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
- M5 W% i& @" q2 f8 B {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE}, g) ~& _3 a4 Q' N$ l4 ~" y4 p
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},: G K3 b: h4 y! ?/ o' H
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
4 {2 v- h) W2 |/ T {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},# ^2 D% {4 p( l7 g
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
# \6 f/ Y( ?4 z W};1 b7 G4 C% h/ W4 Y m& u& }- d
1 a1 m: b$ n3 {, j6 C- d
: F' |. P/ i7 s0 A( r
const char hid_keymap_dvorak[14][8]={9 R0 I H' }, w' p3 _ r! S& {, v
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},1 L. a9 D% L. p
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},3 C5 I2 A# T9 f+ ~7 S
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},, Z7 u8 z. Z* K) Y3 `9 ^
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
- v3 n" q" R! d {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},/ r1 J6 }! @$ J. _6 Q0 v3 T
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
2 {( F9 p6 {5 |2 K6 @ {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
. h* w6 V0 ]. D& y% w, x4 l {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1}," N' Z; y* ~- J1 c' j3 D: V
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},4 w( ^# t# l6 y
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},9 Q0 m4 }1 Z" I2 a; i
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
' n9 c6 b+ s6 @4 \' l. e {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
- h4 d6 {. \( D/ z" F3 | {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},' j: k3 q- e1 @
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}0 @/ Z* T. G- Q3 R3 D
};
5 X8 c8 B0 p! V6 |
}( b* c7 g8 |6 O8 _6 ?
6 a9 H' c# f$ H( b$ K上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
, V5 S3 c4 G1 V# W3 s
$ @4 q, |. h9 ]6 @- VHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:$ v8 B2 z. U# G% L' k. m( N8 x
+ }( n; f v2 a/ Dvoid update_key_matrix(char row, char col, char onoff)& i- T/ C7 A4 Y9 e
{7 r# r3 U* d/ {& Z' L+ t9 m% l
static uint16_t hid_report[4]={0,0,0,0};
& S( W! P" D; ?4 f: z static char (*hid_keymap)[8]=hid_keymap_dvorak;
5 E4 ~6 u' G, T7 O2 T3 B; H
# h, R$ D& j# R8 x" x7 O( M( l. _8 L) b4 i9 h- y
unsigned char key=hid_keymap[row][col];! f- ?% a, K, Q
unsigned char *report =(unsigned char *)hid_report;( I( X- q" r* h% w% J( c
char i;' X- w M) a% ^* W# }" `
' u% p: H: z6 Y( X
: f* ]# u+ Z/ B/ k; B% ~. ] if(key==HK_MODE); a* s" \3 [4 C
{
) S4 O& V2 p ]: E if(!onoff)
. p% ]8 ~4 @ o2 O5 n. F {
, n4 b7 B9 X# d( d$ C if(hid_keymap==hid_keymap_dvorak)
7 ~2 ?$ N$ ^+ t* ~ {9 m. ~5 X) H2 y6 Z: _/ h" g2 f
hid_keymap=hid_keymap_qwerty;. L* _5 Q3 F, O9 }
GPIOB->BSRR = (1<<2);
2 y+ w! l% Z' f( v) \5 g5 A }% O& E, Y: O4 t+ E. Q$ H: n& y# N
else
% R& i- p8 ]0 S {0 ?; M. q7 q' l
hid_keymap=hid_keymap_dvorak;' T$ |/ z) T3 |
GPIOB->BRR = (1<<2);
) a$ S- h" [3 h7 @ }
& H8 C" K2 b: l. M" a1 n( Y }' N7 j2 W9 w2 R
return;
2 b0 |6 t. k+ G) x4 H( L ~ }
# y2 |, ~ H7 E+ E1 h; R. c( B( A- H$ ?
0 y" w; ]& ?9 }5 L9 Q7 P2 K
if(key>=0x80) // Alt, Ctrl, Shift
- n( {% C: t% | {
% g* @* |8 C" m' N+ h uint8_t bitset = 1<<(key&7);- ~1 }; q- b: S' ^1 N% q
if(onoff) // non-zero is key up
p; z, S7 R2 y* P3 n! Q1 y: C; H8 e, A report[0] &= (~bitset);
! Y# }, n" Z% N: B6 G) X$ g else# v9 y, e4 [: ~) v2 e1 x
report[0] |= bitset;
& W( m; d0 ?* c2 Q }" L$ k8 d- S4 n. i$ i2 r
else
1 n6 t8 b j. q1 ^$ a4 H8 T {! Y A; H! N8 K1 x
if(onoff) // non-zero is key up7 g/ e4 b6 X/ O) i! W, n& h
{
- Z' C* @5 w: e& J3 F6 @ for(i=2;i<8;i++)
' S9 }' X( A2 R Q {
7 Q9 w% B, }3 L& _, b, H ] if(report==key). L! y+ G2 X- F" J/ D; G: G6 j- R/ A
{ T* E3 O: m2 z( W" L! H( x& F$ b
report=0;
2 ^4 i" N+ r3 a& A' i0 K( ]6 g9 a break;; O5 E1 R5 {. y5 F
}
, k. U- y8 D* _' K: k/ l }
$ L( e( k, z' d9 H+ O1 Y$ Y: S }& ~' |! D2 h% Y& o5 Q" q" D& l4 v/ Z
else
, Y* x8 |- m' p* o {
, Y2 B5 `- s( h" M% F; S3 ?$ { for(i=2;i<8;i++)1 P- i I5 v w
{8 \( X$ M" }4 ^, l7 R
if(report==key)
8 Z& h0 D7 f: e break;
# A. O/ u, J& E( S$ f& o if(report==0)3 }) I8 V) _- c8 c- f
{- w, m6 s% z. v. S' V; Y# J
report=key;; h% p1 O+ G; ~3 h
break;
) ~$ v/ `5 Z2 U; h( B }
+ b2 ~- _0 C# _ }& T! [0 ?' `% ~. M8 W7 m
}: [( V: o) R4 J/ W! y5 A
}
0 r2 F% a/ w7 e& T+ A4 C+ Z& p for(i=0;i<4;i++)
: H# E3 ?& J4 I2 A- k; ~( d9 q/ e USB_PMA[192+i]=hid_report;
5 K" c. U+ U+ B% x3 ^! } USB_PMA[5]=8; //COUNT1_TX& N# d$ F1 _0 R7 i& s9 S1 s" g
if(ep1_wait==0) z3 ]( A# u0 U; U* n: R0 t8 z
{& x0 O5 v, h4 }. t. L8 J& z' B
USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
) J' z, V% B4 _ ep1_wait=1;
$ y8 F- y& ]6 M& B }
! {, j' k3 {' r) ?) k4 @" i}7 R9 e$ h% Z5 p% H& @5 H4 d. l
1 U3 ^. t& C- \; E! m' o' w" u7 c7 `; w- @% D9 U; ^; P5 Y: J% I
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。' d9 d- t, E* _0 G) c1 B0 j5 Q
keyboard.zip
(8.7 KB, 下载次数: 6442)
q9 x6 L; w9 A
9 A- E& a* t4 B8 G
- F* R& B& G4 r" j; [% k/ h
5 E4 J6 B6 ]7 g' A$ [+ C; f
7 J9 `6 I% F4 ^8 n( r& Y! d" K |
|