|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 ! s; ?% K; k5 d' R" k
+ T5 Z4 {% \! X9 M# g$ E4 z扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
7 n$ x0 c8 ~5 n7 V; X- f5 E: g- f, r& r( S4 \8 ^- d; f, ], l. ^
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:% J; r9 L5 W6 n' g v8 I4 B
6 s. p- c- r( e; h
: v* P# K g Qconst char hid_keymap_qwerty[14][8]={
& V! i5 F6 Q0 k9 K {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
8 p5 b1 _8 k ] J" r {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},8 K. |% L5 c, _( U3 P: }
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
0 }% F1 e# p5 A% Y2 q {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},3 I' }# X/ T: ]) @6 q- e
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
4 {- ^, y/ U- ^! @ {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
( U3 t9 @+ F0 s$ }+ Y7 h0 k. V {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
9 L9 A% L" U6 q7 x/ g4 {* [4 u {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},: ^: h/ P( U, ` P2 j2 n( J
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},- y3 D* T3 c+ \% t
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
* |8 G& ?* X7 |% V1 V! }+ y- l {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},4 S6 f3 O. y2 o+ g: T
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
/ }2 H: \0 K" o( a {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},: h9 `' A2 _1 Q2 D3 \& u
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}0 f" k- \% I! j; v
};
( F" v( N" j9 f; F& P. e
, ?! g, L* [7 y% c* `" ~" N2 c$ x7 L
const char hid_keymap_dvorak[14][8]={$ H# o; w# t2 ]
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},: }1 C! _0 H( N, D
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
1 k$ f8 m! `2 {0 s& Q1 r {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
- L: \0 T/ P! S4 T4 ]# Y, L {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},: }$ P+ U$ n3 y. A/ q! a" e9 ^
{HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},: T% o/ L% |, x& v% h0 D1 O R
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
- d% F4 q& c% d. ]6 X; w5 s1 J {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
7 L+ I0 s A, a# \$ l4 L, b4 _" j, j {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},/ G; {% R# ?, D$ G/ J+ V
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
( f, [( D6 h7 k6 ]# b {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},: m5 D; G" N5 q) M& s! B
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
. q1 u* G: C! _1 j6 [& p {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
& F) H" I+ R1 R) g" f& E8 T' M' ~# i {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},- ~. r& H; [6 }. t
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}; b+ m+ c) J1 d8 N. Y5 {* C
};- F9 X6 d9 Q+ a' {, Z5 N
; \( `, q6 o( X) o$ S- I, f8 A/ o1 ]8 S- I8 i
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。4 h6 W9 a0 K. ?5 I0 C
% `# s5 v4 d( G* y8 R
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:7 |( X/ o7 |$ L' [3 v! e
7 W( h% ^! f" E# Hvoid update_key_matrix(char row, char col, char onoff), Q) z& ~4 l$ P4 a% \! I; F
{
# J2 f! c* _8 x0 b% \" A5 N static uint16_t hid_report[4]={0,0,0,0};/ d- Q6 G& ~+ L4 I- U% c% Z
static char (*hid_keymap)[8]=hid_keymap_dvorak;# _9 o0 u4 X, x4 j* s6 h* o- w. z0 }
2 d; X1 {$ u1 I$ t5 K) U% L- l% B5 H/ }3 E9 R5 g8 r: B
unsigned char key=hid_keymap[row][col];9 U6 ? H3 f; y4 Y; k
unsigned char *report =(unsigned char *)hid_report;& i. P" O6 F/ V# k6 }9 I
char i;
$ }/ Q' q* g+ H0 Q0 }; X1 n% ]3 t, j j* @% F
' j* K3 E- A% v, [: B: U
if(key==HK_MODE) ~, l( ` P7 ~5 |+ o
{
1 P/ }5 O2 }+ j$ F if(!onoff)& j9 q! w/ r/ h% \4 X$ h
{
+ E, ]9 t$ h; R$ W! S if(hid_keymap==hid_keymap_dvorak)
* y/ e0 H1 l& j { O. m2 Z0 {$ Z% P- l
hid_keymap=hid_keymap_qwerty;
. M! Y2 \6 m, w* M d% Y' { GPIOB->BSRR = (1<<2);$ a6 {# g( x& @
}
& Y) E# P9 j. m6 \' L else# L4 @9 q3 o" A% f! q
{
* ^& O: a6 T( _: l7 G% B hid_keymap=hid_keymap_dvorak;2 D* H* _" w" }: e2 u
GPIOB->BRR = (1<<2);/ i6 T$ e- }5 C; W( d+ D
}
6 E+ r4 v" g* W( ?5 I- M }
3 V3 Z ^) ~! N; D; x! X return;0 n/ Q5 |8 ]. T+ F8 m
}
1 H4 T6 j4 Y- j/ }- l" }/ h5 q4 ]" l( F2 t( C* s O/ H6 w9 R# b
2 q( _+ h: U& X+ }1 q. q/ K( m if(key>=0x80) // Alt, Ctrl, Shift( ^% [8 i& \" h, @7 a0 K
{
3 R1 b4 [ e- j% o$ H: h( B uint8_t bitset = 1<<(key&7);
# L! J& O$ D; R& u$ y r/ ]6 E, Z if(onoff) // non-zero is key up( [1 f% z; ?, l( a4 E9 y/ z6 f; f
report[0] &= (~bitset);
* P: e. s4 _% u9 ~( S b else6 \- t$ ]# ~9 Y6 E' G( y) C% d
report[0] |= bitset;2 ]( ^9 G- P+ w: _8 w9 Q
}9 I* c# ~( }7 ], S
else
4 f5 c7 H/ S1 F% Y4 f {
7 a) C& }6 X8 D% x if(onoff) // non-zero is key up# L; E) p4 M! K
{- e9 m4 o3 k, t) {' K4 l4 X
for(i=2;i<8;i++)
& D( _0 g6 S7 p% C8 c* | {1 Y/ d5 g# U5 q. F
if(report==key)
) C4 m! k9 `1 [, w {
+ c# J% c( A7 V/ |; C4 Y report=0;6 S8 J0 F, A; V/ x/ v& z2 H. A
break;% ~0 x5 c g' x1 H. m; i5 B
}; R8 R* x5 X* h; z( ^
}
0 }5 o3 x% a' u/ { }9 C; S; [. o# S: D) U
else$ @" y- J0 Z% W4 K# L& I3 H. C* ^
{
- E K1 W, t, b9 a2 ~2 b2 l for(i=2;i<8;i++)1 T$ q( C% A4 u4 m% p
{
5 d6 U8 n% }" E if(report==key)- K$ ^8 r$ g- o2 B6 ]) [
break;8 v8 C) I$ _. F
if(report==0)
" C. i5 V3 v: u' [1 }8 e. `1 V2 s {! r& p7 l& D- g+ o5 C$ X6 U9 P
report=key;
$ `- O& `/ |& ] C$ b break;3 C7 g) G. }1 D6 @$ _5 c$ |
}
7 A+ S9 K% ^- S4 g3 U/ T }
5 e! w1 W# A( O( n }
' A! u M0 E- o* p0 D; s }% L4 J6 L/ p1 g! R
for(i=0;i<4;i++)
! i; o. N4 W2 j ^* ]) l6 K USB_PMA[192+i]=hid_report;
, ], H5 p" L2 G1 j1 ?: L USB_PMA[5]=8; //COUNT1_TX# T, _5 R/ a- _# G3 \- \3 j. P. y
if(ep1_wait==0)
4 @# f; O( b* l {
4 T+ ^5 ]$ K- T5 w USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;0 M) H6 L+ g" ~. F. ~! G
ep1_wait=1;% O* e. }. j6 R& W, N+ ]! s
}& I1 d! {0 U/ I7 {9 ?7 U" N. a
}
) \" }+ w8 B* n6 E. b" h, E [8 ~
0 R, v) j4 |8 \
( D- ^. x3 |0 J* S( M2 t完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。, P% u$ t# P6 |8 Y
keyboard.zip
(8.7 KB, 下载次数: 6306)
h( y) G- h/ J- Z0 |
9 d) ]3 ^. N8 B1 U+ q& D5 R) r
2 L1 Z1 G- ^, E+ q1 Y: {
7 l' ?4 L! r+ N$ x+ G+ x+ z! [
8 Q$ F: M5 y6 u+ y( K2 h
|
|