|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 ( ^& W w h9 z7 @# ?3 `/ _7 v/ x
# {) H$ S5 a, @扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
+ _5 r2 ~, t; c0 e" h8 M5 m* O/ Z: R& k# R" {4 r* s$ }$ {
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:8 P1 h% |& H+ A% ?2 c
- @* y$ \$ X8 O! X# v! k# q+ t
+ h$ ~2 y9 Q0 I7 [
const char hid_keymap_qwerty[14][8]={+ [" n; b% C# Z( m
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
" W- m" {% v& v* x6 z5 `! B# G {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
9 F" V; w+ k, s) A. ] {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
& a. Q; Z( K5 L- _' a {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},6 B% a8 M( K1 K& u8 T ]$ `
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},% |% W) X: `" `, D
{HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
4 ~$ y1 P1 }) ` {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
' ~3 @8 _6 H J! B3 o P8 V {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
' {1 o0 n" f$ T+ g* e {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},6 ]+ V V* l+ T6 H- j+ ~8 U( E% B
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
3 ^1 c5 r' k6 z {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},, K1 n. R: ~& o7 W3 U
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8}, P( _* _* s; u5 x9 {; h2 g h
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},- o) C5 o. E _: r) \
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
& {; e0 w- j4 Y: o4 {; r}; U, w/ A* F; p: T) W
+ |2 k# d- _+ r
% R7 N+ L" f3 b7 x, Rconst char hid_keymap_dvorak[14][8]={
; `+ M2 @9 r; ]3 c3 `; Q {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
- {/ q5 Y& }8 G {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
# N( e7 N- {' C! ?- L$ J, D: Q L. P {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},2 v- r% Y9 b3 V+ I3 `" q6 ]
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
# Z& d3 l) F5 A! b% ~ {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
. U; w+ @0 u2 r0 `) [ {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
6 m5 e: I# i( G G4 X1 n" v+ e, n {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
, V- @; W3 z3 l5 [) X {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},% C5 @# M# d" k* |, c0 Q
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
# ^0 u& s8 ~% c- y# H, X! J {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
3 \& Y5 y1 V r! ^7 j9 R7 P {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
: C N3 ~) A0 l' t( O; s. { {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
4 c+ O/ w% n0 C; P {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},! w# f! U: O( z! @" I0 p
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}9 w+ ` s6 u3 o# M4 v# h
};
6 ^0 m D7 G1 D8 Y' q
) @ F# q+ {" g) M$ G1 l y( N' l
1 U E. j, ]9 P3 C E. U上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
$ r4 |0 M1 ]+ @2 y2 B; F* g5 n+ u! ?
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
5 ~8 `. |2 y% a0 X" f# X3 P# l* N: O8 M$ g2 ^* X& @
void update_key_matrix(char row, char col, char onoff)
4 W2 e' O5 K' f: H% H9 T{
8 ?& X5 D) \' u static uint16_t hid_report[4]={0,0,0,0};
% x' j4 r: \- J2 P7 k4 c) Y static char (*hid_keymap)[8]=hid_keymap_dvorak;
1 O3 d7 R+ _1 Z/ D( D9 W+ m
7 `# T- ~3 S) L9 F# R& |+ z* a! F, x2 A8 M" b( r. |' t& Q
unsigned char key=hid_keymap[row][col];
! T) E- K0 G5 b unsigned char *report =(unsigned char *)hid_report;
, j, ]$ M; j! T char i;
$ ?/ @* x5 Q5 ]! N. h7 g- X; R5 k' K" Q$ b3 f% W' }
9 @" L) i6 j# _- Z4 t
if(key==HK_MODE)
7 {: L' ]* W5 M. n" ^. K3 h {
3 f, d, @$ u z7 m if(!onoff)- `2 a8 m9 [! i, h1 b% q; j7 c3 b7 K# ^
{% ?) `* ^ Q6 T4 q* Z
if(hid_keymap==hid_keymap_dvorak)
! p$ ^; v; F$ d& e3 f1 K& a1 N {# J5 l, w" W q: C3 ]- x' v- e
hid_keymap=hid_keymap_qwerty;% c: H; ^6 i$ i0 F4 B5 I
GPIOB->BSRR = (1<<2);4 C! I9 z6 ?* c. _5 s6 M
}
% V" r* |8 F* ^/ n/ ], g else4 Y1 t) J3 s- ?2 _! ^
{
1 Q' d7 f9 v k( g: H hid_keymap=hid_keymap_dvorak;' `+ U* {" u- R6 V
GPIOB->BRR = (1<<2);
( {% h1 s1 {9 ?8 ]* X# C }
4 j- ~( o; \/ |, V$ l' B% X }, ~& R' J0 D6 Z) S
return;
; C6 L0 C9 L( l" w+ j) p# }# F }
4 I( I f2 T( Q! b, Z9 s. Z1 Y
T# S. b1 n- b5 ?; i# F$ U5 ?6 u2 G& G3 \: B
if(key>=0x80) // Alt, Ctrl, Shift
& x1 Q2 R, f0 I( o* h1 H" O" ] {
$ l3 z& h' n6 T7 O uint8_t bitset = 1<<(key&7);2 h2 C. M) V# W, K! y6 l7 v
if(onoff) // non-zero is key up' w% [' i) z/ p. y5 e9 v" e) D1 q
report[0] &= (~bitset);
) T( q& u* m* j2 } else2 M6 A7 U6 c0 O# C
report[0] |= bitset;
/ t& \. ?$ a8 ~' t6 H/ e2 K }
% Y G& H& Z- e6 k$ o. o2 Q else
) s$ z3 L7 I ~, O, m2 } {
" U9 u) n6 j1 ~' q if(onoff) // non-zero is key up
+ U0 S( n$ G4 g. I6 t6 d {, \+ ^2 t# [; F% {7 c+ |% C
for(i=2;i<8;i++)5 m S- L) h/ K. @5 s! ]
{+ C% J T- P9 h" F
if(report==key)
A/ M; o: t w; O' r1 K9 G3 d9 Z {5 U; L# Y6 q2 k6 v& I# G5 H
report=0;9 T; f7 q$ E0 N1 D
break;: O3 l/ ^3 r$ s8 A% W' h" n* Q, c
}
0 ]& W! w6 M0 n! E2 \! ^2 e }9 ?0 |' e: Z( @& S/ w7 E6 Y0 E
}9 d) a: Q6 N. s4 X
else* ]# s; R2 x. N) _1 M7 \
{
v u0 `6 |) a4 k4 V7 L for(i=2;i<8;i++)
) _/ m) e% r- b7 t {
& J+ Z9 n' v2 B# H# |4 C if(report==key)
, u- E$ ^& P% q- }+ Q2 J8 L break;2 @1 j! S* ]8 K9 C6 G( r
if(report==0)
' M) _4 h% @5 G! R/ H* ]+ D {' T& v/ L. E8 y1 ~; ]* h
report=key;
: x3 z- o5 l; i+ M& o break;
; h/ C# W2 F; }$ g }' ~" V/ w4 r8 s# u; P
}$ z" t+ F1 X) q/ V0 ^" C$ m
}; }3 }: |+ q- W) M, `7 N* `
}
0 P" S/ p$ j: G- |6 }& O% j' V/ O' m for(i=0;i<4;i++)
! `2 q/ {5 c2 \ M# o- x USB_PMA[192+i]=hid_report;# T0 ^$ J& C+ D/ ^# p- x: b% M
USB_PMA[5]=8; //COUNT1_TX8 J2 m/ L0 g- P9 p3 e- Y/ T- T
if(ep1_wait==0)
7 Z v, b' }4 J' y; A& X/ H2 b. |+ c {
/ X( y; K8 [9 r USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
7 t# U5 [6 a0 p! x7 Q4 j ep1_wait=1;
& H0 f& ]4 ~# ] }2 m7 i! _* }5 L* y
}! B$ x6 t: z* C4 v" l: s
2 T' B( G6 S8 N8 D0 `
, `) ~6 ]- I& J- V; y8 t
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。9 s0 J" g$ w* t6 \# Y) r
keyboard.zip
(8.7 KB, 下载次数: 6375)
! }- C0 g" @, n4 w4 F) i# [
8 n* x3 p2 ~5 M1 d; m3 C# R
. e9 E# U: C. ^' d$ ]) I0 c% d+ |6 O' M3 U
: R; K) D' T( c! n |
|