|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 % J) k, y8 n6 g
# I1 ]; |# [1 }
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。; i" V7 e" @8 i% c8 l6 e" x
$ ?9 X4 w8 W& ?- ]' g, R9 q
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
$ X% Q# ~( d8 h4 M. G }; O* a9 b+ }
! t/ P4 M; j* Rconst char hid_keymap_qwerty[14][8]={
# B# E$ R$ i; ]7 S3 R& E4 [ {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},, _# Z5 D2 r7 A2 L. q
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
! o1 ?- V5 |" l; f3 l {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
b( S9 z# O# ^# f \$ v6 u" B2 _ {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},+ Y& d& E# z# ?! o
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},$ }* A$ `1 _" B# B" U+ A
{HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},; B* f9 g0 H# m! l6 r
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
2 N9 H0 O5 S6 s1 L1 w {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
( D( J$ b; `, A/ y6 U* \ {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
- H: u5 D5 D @/ r, E {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},) l. J3 V- |$ S* L* B: q% B
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
' e* y+ S6 M1 f {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},; H6 a* O& h0 z0 g. {7 ~
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
/ Y& g( J b: M0 Z; x; ^; _ {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
( L9 c, x* ~ E& N- O};
$ b4 ^: f& u T4 m: e, ^7 k
. }6 H }" K: ~% t$ J( ^* Y6 x
" F3 u& B# O' D$ h8 W2 Aconst char hid_keymap_dvorak[14][8]={9 T" }& H9 Q! p
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
) m, H0 H* Z6 E4 }& L- l$ i {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
% r* a( H: S( S* x4 R {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},. R* u5 E9 A* c' J+ Z: L
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
9 ^! y& v5 n5 {, x8 y. s {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
; G% B, T7 u- _. ?4 M5 Q {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
# R9 }4 X- _! Z5 b3 _ {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
; k3 Q' A1 }2 G. N% y {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
& b3 g& _8 O ]. f- J, P5 b# Q {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},/ W* P5 p# A+ o
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
9 a7 i2 M& n0 {6 V+ I E {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
+ P7 K5 S, |* K$ g, G {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},3 V1 m$ S+ K; r, W( N
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},, a6 q! D* F4 n
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
5 ]- V4 o9 e$ u3 H, _/ ?3 n};6 s5 G |, Z. `# a6 N! S
& V! u; X/ X+ \ G) O6 o$ }! I/ s: [; {2 J5 D7 o- e0 u0 `4 M8 Y
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
% k& G3 r G3 g2 n( U) v7 Y9 O& K6 G& c) L
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:1 r9 N& n: y' Z
6 `( N) V) R& z( Y) tvoid update_key_matrix(char row, char col, char onoff)
* M2 v) l4 y. o4 M3 u. R9 J{
- Y2 K' b/ Y/ L& v. i" U8 R, u static uint16_t hid_report[4]={0,0,0,0};0 `% y4 z. S+ Z) C
static char (*hid_keymap)[8]=hid_keymap_dvorak;) }( q. o4 e. T5 M% U
8 |+ N" u, a. }0 l; O4 O0 u- {% E% I$ k+ ?% [; v
unsigned char key=hid_keymap[row][col];
Z# W: C/ I/ v) T* X8 M; S9 }& Y unsigned char *report =(unsigned char *)hid_report;
2 F8 b7 ?% r8 z) u) f char i;8 u- \4 S' B0 t% I9 W0 P
6 ^) L x! ]! c {0 n: j
" e& s0 b. F! K
if(key==HK_MODE)
0 ^: ?% [, b) L! c5 S/ N9 ? {2 @2 {: U' {8 V7 f
if(!onoff)# J* V w! { Y! B6 y5 F( u! |1 }
{
p% `! r5 r- c if(hid_keymap==hid_keymap_dvorak)" P' y: b4 g4 j+ _3 P; ^
{
& o* G# n# N9 w& v) ^2 G hid_keymap=hid_keymap_qwerty;) ?8 ]: P& C/ P4 R- _1 f
GPIOB->BSRR = (1<<2);
$ r, L; |* \# ^ }) y L1 U* ^7 _) L( f' M; e' l+ d
else# ~4 }1 r% g. L8 U2 T
{+ I$ V! _/ j; ]
hid_keymap=hid_keymap_dvorak;
?7 t1 |; o8 ^7 e& T3 g7 y% R GPIOB->BRR = (1<<2);5 V# O: O6 L5 A' ]. T
}
$ Q. c/ X' o0 y4 o; _* r5 O }
% ]1 g# d5 p4 i% R4 d0 q/ L4 M return;
9 F' s6 ~ |) } }
# {. K+ n5 ^6 l* ]: [5 b1 H) O& M- W7 o3 C2 H0 H
0 Y. n4 E3 V, Z n2 w0 [3 M
if(key>=0x80) // Alt, Ctrl, Shift& b6 Z+ A* \6 R" L% }
{- r( @; c/ J7 ?' m4 G _" w* |
uint8_t bitset = 1<<(key&7);) I# v! z- d- t% J; b8 s3 N
if(onoff) // non-zero is key up
, M0 V! {/ X4 K0 n0 K# i0 D# o report[0] &= (~bitset);1 @ t' O6 X. a
else) |7 h' r- ~1 v& ]
report[0] |= bitset;0 ^6 }$ ^$ Q& t! L1 W
}$ `3 {' i! X" p0 `4 y/ V7 m
else' g+ P& O! V% K8 q5 y+ T
{5 O4 v6 U# ]" U
if(onoff) // non-zero is key up6 Q+ c4 v) i" E, |2 f
{* \/ ^0 A6 f( M5 {
for(i=2;i<8;i++)
1 u5 y0 G& p- x9 O, D {' S+ U) \( E0 |+ b2 [1 v5 \" l; A
if(report==key): Z, x8 ]1 l* Z+ H' b
{7 C' w7 H% Q: I0 ^$ _
report=0;/ ]9 s* q0 W0 H! f4 C
break;% _' A- G3 \) r# ]* W9 }: |' D
}- _! H# p8 i9 h7 Y# t
}
$ i) \$ ^. `3 `0 j }
* G7 C% s2 W* e0 i else: @+ Q) H/ c' }" d- ^" U, ~$ M
{9 q- j* m, r- l5 F5 d: F
for(i=2;i<8;i++)4 J3 C- f3 o: H# {. G. n
{5 }+ s' T" F- G' W
if(report==key) Q( _" ?* X% F5 K9 |: v* V
break;+ q. n# g9 P; N* v
if(report==0)
) d2 m* p ?5 Q* V2 p. I {
8 `# D" T& [2 D2 Y T report=key;6 `- {& a. b) a& F
break;8 u' q- l: M& a3 m
}
2 d( F9 n* i! l9 j7 m, ~ }
X6 y6 p, z6 j7 _* R; R }$ A6 b! m: }9 j+ Y' z# P+ j6 c, ~
}
! S ]$ w4 W6 h( x8 _ for(i=0;i<4;i++)
; Y2 F' q, Q8 |# A" L4 X6 ` USB_PMA[192+i]=hid_report;& y$ E& J5 {' G& \" b& n6 @& m6 Y, O9 z
USB_PMA[5]=8; //COUNT1_TX; S5 p$ Z; s/ R. f$ d8 O
if(ep1_wait==0)- S m8 Q$ O5 t# l' l( ~/ u
{( I8 B. w8 P! Y6 l1 O* c7 \
USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;0 m4 @+ x5 E9 d: _
ep1_wait=1;) e1 F4 K+ v6 E( f0 M
}
9 U0 h/ |3 T, Q5 I4 O" Q3 U5 V}
8 A/ G8 o4 [/ F2 g5 Q( X
' e& c$ |3 s+ j4 j" O' S# `) U/ s( I: q, P
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。, `; t; J* R% W4 i
keyboard.zip
(8.7 KB, 下载次数: 6188)
8 V0 Q4 ?; s6 g( U( {: Y
, E% ^# Q7 Z7 K9 y& s) V. H
( o! b( F9 H* q! Z6 t: @' j; O
% L, I. m6 _6 `# \ _: B7 }, d( `
6 Z; D$ H% J* W0 f O1 r G |
|