|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 ) A4 E, _4 H; Z- b. ~9 h: E0 S
3 N& {. z; l) O2 h' q/ o& _
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。) J5 n( ?3 g4 _( k. o0 a) z. B
5 M* y5 Z9 [/ g+ B+ _- Q. e
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:& E, ?0 q9 m- R' G" h c, n
: ~5 P; d* I$ w0 _" N' M( j( q' w5 ]
) D D2 \, h! {# ^const char hid_keymap_qwerty[14][8]={
8 Z9 _/ n4 W5 L* i5 e7 o; ^! ]/ H5 d {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
* l5 `, h# q7 } {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
7 Z) M' ?' V0 b/ i0 ~! Y. o {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},) v+ I- G" L) |- c) k& p5 c
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
1 ^+ _: g5 U; |9 G {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
8 r7 X/ G+ c8 h9 V {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
. Z) r- F! I0 h/ i {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
# }# J1 ^! A5 l- M {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
/ j# C4 K$ I) L {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
+ \6 J* i0 N+ U" _' {: ^6 Q K {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},1 m ^9 ?3 ?7 j" q! U$ |
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},5 J6 D% |3 v" ]( |% x/ x
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},( z W5 L4 x t+ b/ e: @7 J
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
2 h; r- _) j/ S9 U! _9 O0 B {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
4 Q# Z- p7 w2 S6 I, `1 w4 _};5 e$ U: v, k& X# D( S
# Q& h5 l! \5 r" Q5 U- t6 Y6 X* M- Y
const char hid_keymap_dvorak[14][8]={6 X2 _4 t( o, v" u; a$ _* T
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
+ O4 a- V* F/ e# [- o {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
1 x! f$ Y! X; }9 F0 e' H {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},3 V0 u7 m* | [5 c' E1 e. @
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},& Q& \( S& D, _ t P! [
{HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
* p& n0 N2 f: Y5 I. C/ s5 t {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
3 Y a5 R6 B0 k {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},# N* x }0 u4 D+ r- d7 V
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
9 i* x: f6 f2 L8 U2 w5 r3 x8 I {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},% c9 c& Z+ y# n0 ^/ ]" U6 D
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
3 o) J& ^1 l7 b! x7 x {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
O3 r2 \* A# K5 F- H6 ^+ `$ e- R {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
6 | R t# X- b; @+ D {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},9 ]5 e9 l* p7 t- ~. ^5 `& P
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}$ a" {1 N6 M0 ~
};! H4 D+ W4 r7 T# v: d
4 Q! N1 \5 p) r6 l8 Y9 @1 d! c) L! J. H
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。$ r$ h2 \3 G' s0 N4 c1 b
0 u( A; w: R6 G4 W# Z
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
+ e' R& S4 T4 ], z* e$ [" {1 T$ b- c+ T m
void update_key_matrix(char row, char col, char onoff)
% u4 g+ h) Q# U' f! m/ s" P{
6 `, l. q, Q* r static uint16_t hid_report[4]={0,0,0,0};' `/ o3 V2 r F. u
static char (*hid_keymap)[8]=hid_keymap_dvorak;/ N* B/ B+ k/ X! C6 J8 e! t
8 |+ c' d, L9 J1 r
" j% y" `. Y; K
unsigned char key=hid_keymap[row][col];
0 H" h+ k6 F4 A& x3 j: _- T! z1 T unsigned char *report =(unsigned char *)hid_report;
, Q R0 _7 y+ Z' Z char i;; n; V+ H8 o$ S5 |, U; k
) f7 E$ j3 l4 W' k9 \
1 w6 N+ ?( E/ I ^- S if(key==HK_MODE)
3 {( p% i' q5 c& T6 R1 f {
5 Z J6 T! ?$ @/ v if(!onoff)
- S& T4 T- u) ]) a" \ {
- N4 [0 f$ @' P7 ^7 T if(hid_keymap==hid_keymap_dvorak)
) u- W/ f$ E( _4 L' C. b {' }4 Y) c( b2 f2 s- Y4 W
hid_keymap=hid_keymap_qwerty;% |5 S/ R& }& `
GPIOB->BSRR = (1<<2);; g' j3 J0 a( C3 K
} I$ H8 p. s3 G" D
else4 T4 e4 b* a9 T* s$ ~: Z/ I9 n
{
2 p) I( n, y! \* e/ i' s7 v- A x hid_keymap=hid_keymap_dvorak;& E. C% r+ N7 M3 F' l! G: v8 r
GPIOB->BRR = (1<<2);
5 p8 A7 A0 ^+ F" F' h9 y5 Z R }/ M& ?, O$ B; A* S- ]: U1 C$ R
}9 z2 I- L' S2 Y9 F `: E4 \5 u
return;
7 B" D: y# ^- \$ D0 Q/ f5 b }
+ L. m% x* Z8 f( S& ^( X1 l/ p
6 L( b3 }4 e* f. Q
if(key>=0x80) // Alt, Ctrl, Shift! d; u3 B5 D: T
{) k O7 ^: s E
uint8_t bitset = 1<<(key&7);
7 v* B; F7 M b4 N& G if(onoff) // non-zero is key up5 H3 M* s/ d/ L( a
report[0] &= (~bitset);( `! T* x/ X8 b. `
else
7 T: i7 T5 T2 ~3 j- _" [' m& N2 {# G' D report[0] |= bitset;$ L! Y+ U' X! E9 q
}
- X6 v1 k. l7 R! I. R1 V else/ }4 i6 D) x9 M. K
{1 Z c& c" |/ K! L, O
if(onoff) // non-zero is key up
- S# S7 e% ]" a: w) f b; k- W* G {, R/ ?# e0 }$ _2 e r5 N
for(i=2;i<8;i++)8 `. S, T5 q9 e! q' I5 Q' y& B" h
{' f# L6 O' P% s" _/ V) `9 S8 T
if(report==key)
* g9 {0 q& E3 r# B) k; o& y {* m2 _6 b4 ? z0 @' r
report=0;
6 F. l3 B' o2 T3 Y break;; Q: {' k r' I# [- o7 \. A% u
}
+ J8 E3 d% o7 x* n" C, B% I }
, F' e* f _$ c6 O; ^- a: e1 C, A }3 L1 t! M9 I* e6 A$ E4 `
else
9 b9 d" I( ?2 i4 M0 Z: K {
$ C1 d9 Z* {' ^. x8 r for(i=2;i<8;i++)' n f4 V6 \( q1 y ^# A3 o- H
{+ k: S: ^6 ` W
if(report==key)) ]; a7 ~' ?/ Z* B% u" _$ v! h; x
break;4 T# r0 y# m. v8 C1 }! e
if(report==0)7 t B2 w6 o9 d7 f- b% i' e
{- ^1 W* Z5 C8 \( k# S0 m) i" T
report=key;% m+ \9 |# w `. }
break;0 I/ Y9 A7 \+ H: K% T, V K! k
}
( K% ~" C) F3 ^. ]5 N }* d6 U9 A: Z4 ?% l& `) j
}! l- J& [- Y r6 B0 D( y- E
}
6 w/ @8 ?& {9 p' p for(i=0;i<4;i++), M: x! I8 R, F3 D% E! m
USB_PMA[192+i]=hid_report;
% k& e& X0 K2 Z3 M/ q: m9 w; s. V USB_PMA[5]=8; //COUNT1_TX
2 r5 B% f7 @. @ C0 l, d if(ep1_wait==0)' S+ P2 d, M* [& N! {$ m. i b
{7 b- p, T- C% h( B
USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
4 l$ W# ~7 W2 V( T0 o3 j6 { ep1_wait=1;
9 z5 X5 n+ ?. p8 n6 r }4 }, D- C( @" }& g! [* l7 }3 k% k
} M7 B7 U) {0 ~ m
" k2 e% S# n q' G/ Q1 E' t) z9 q1 Q% C$ I- W6 @
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
2 e+ f! \- l# d% A' x3 ~- d
keyboard.zip
(8.7 KB, 下载次数: 6398)
9 ?2 s8 n. y! n+ A0 [
_, k& Z/ x9 |4 {; S
x% C: D' x! ]$ D* W8 N& l2 z8 P( e( Z4 Q
3 v7 c' ^& r7 m( V5 G. S ^ |
|