|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 " _+ b% J3 D2 h! {+ f
! t" K. q' L8 J$ k5 e扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。+ U0 ~ g# ^6 L' T0 r9 o
$ o0 m9 T2 X9 B* E
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:/ H5 P8 I! B+ [) `1 D- _% Y
5 U- [) t9 U$ F+ b
( Y$ n4 X5 H( Q, S' y3 k: iconst char hid_keymap_qwerty[14][8]={
/ C5 A8 f( ^; Q3 k$ U5 l {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
9 ^4 E5 C: b# V b0 O8 B3 ` {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},1 s5 s7 b( t2 C
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6}, ] M" r d3 H2 }, }
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},% e( j% ]" O: z! D/ T1 F0 n
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
2 o+ P: \0 H" }' P2 _ {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
) ?& I# G/ y2 ^# Q$ C' |; N9 h {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},. G% R) G9 |' z0 w8 u4 ^0 Q
{HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
9 J* m5 V" d) F {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},: }' L* ^; |2 @ G3 d& z( T' x
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
9 U, R* C* T3 Q6 @1 D7 v {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},' g6 c j9 x; a5 i& L5 L5 w
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
) D8 C, B' B. y- ^. k {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
; L: c. [. v( Q8 L. ^ {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
, s4 R* ~7 Y$ D' _5 y};
# m' u/ }3 s( v. r+ H6 `7 ~2 ?; k0 n- s+ Y# c
( N7 `0 b( n1 A7 M' l, \- d
const char hid_keymap_dvorak[14][8]={
5 K! [# m9 F9 f; w3 Y$ ` {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
' V' [# J ?. ^9 q/ K* A {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
# X* P+ h& x, F' U9 b {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},& ~2 ?& Y3 c4 d9 F1 }
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
, l8 X g8 \0 t7 _; e/ t7 g" |/ y% l0 c: ^ {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
# n" G2 D6 A% t7 j2 t" V {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
. c( c# f) r! o% k {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},) d T( R: m: x% K; A( u( \
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},# [# r3 B; U2 }6 |. B6 Y: L' c% F3 a
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
( o) m& V! t1 i& t4 A4 X* Z: U2 l$ D {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},0 d6 T/ y; P ~& \0 i
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
% \& Q ]9 `# W, e% a z, S0 A1 X {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
! f0 H# ?( m+ ?5 F. p0 F3 I {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},* k9 ?% M! ?( q7 Q% H. X# c q0 f
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
# m- C/ i5 N$ S' x};
: U2 Q/ `4 ]( B1 t9 e/ n2 [1 N
9 x. d9 j' {1 X8 X4 i) ~* I8 _- y上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。) r, Z1 A9 a4 @" F" _6 u4 A
5 w% B, x; T7 D9 T& _" a/ i/ ~) r
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:8 o" e/ }4 h' B" i u) o7 j( K0 M
1 b/ ^2 _5 _; Z6 w$ H8 q& M& t& avoid update_key_matrix(char row, char col, char onoff)
8 K0 S! v* J% R/ ]1 {{
& C2 ~! V9 R7 U } static uint16_t hid_report[4]={0,0,0,0};
: @- x0 g1 o8 ^# E7 B2 A+ W static char (*hid_keymap)[8]=hid_keymap_dvorak;; ^+ o; j7 T1 v
; ~' w/ l4 r) u1 K$ h: F
D7 V! p: c. b8 T! B
unsigned char key=hid_keymap[row][col];
! {( B5 O1 M6 i/ S! j8 k unsigned char *report =(unsigned char *)hid_report;
5 ]' s+ U# D7 @7 @3 G char i;
9 q7 j: l8 l, O1 J2 k0 V8 O( R1 x0 B
. R- f& M$ K2 _" R if(key==HK_MODE)
8 O# X2 c0 y' V' z3 i {
! X+ G3 ]& @$ `9 r if(!onoff)' y, O+ }& U# l% Z$ E8 r, G+ H
{
$ L* s( E* u1 Z* ?$ @ if(hid_keymap==hid_keymap_dvorak)
8 C( s! r+ }5 R* j, {2 c {5 {1 |2 c' f% H7 f7 d
hid_keymap=hid_keymap_qwerty;
@: f. u7 B1 ^ GPIOB->BSRR = (1<<2);/ x( E4 I: Y7 r( Z- L
}2 K' Z& }% G. }/ r! D! ~( F9 o9 s
else
* Q) z3 s: a' c9 s. D3 q* k {. q }1 X" {2 s/ t5 q8 @6 e
hid_keymap=hid_keymap_dvorak;
# q0 i+ H" s9 I* a2 h, Z/ A GPIOB->BRR = (1<<2);
% A2 x- Z9 }0 X& V) [# a }5 e H) w y, q6 Y2 b! x' h
} u! l* ]; V& D$ B( b! `3 N
return;
5 k, s, s$ x9 R! m0 a5 q }& m, p$ l ]2 L! A; g" ^
5 a) w: ]1 @" a, P" G9 ]9 t) }0 {" S% m* g) w# a9 S4 Y
if(key>=0x80) // Alt, Ctrl, Shift* A+ Y# Q6 J! v7 {5 V, X" w( P
{& \' d( {7 m( _( j
uint8_t bitset = 1<<(key&7);
S$ e1 T* H& r, `6 [ if(onoff) // non-zero is key up6 p; b# ]- B) ?, ~: Q4 n5 N
report[0] &= (~bitset); E* j7 V( t/ e: o* e1 r0 l
else
9 G& c r9 p' t8 A report[0] |= bitset;
/ x) B8 `( ?( w8 H }
* i1 o1 T c2 x( J2 D/ r else' [7 h" I' d! \: }, [. Z
{
/ O, s1 m* ]" ~; L! a+ B# w# y if(onoff) // non-zero is key up
" b- b& L4 l/ x {0 M7 ^! W2 `2 }' Q5 T
for(i=2;i<8;i++)3 i% R; i: R0 |& v5 |8 v0 Y
{
( U# o! d* S. L" K1 R( G# J$ S if(report==key)
* l: k' X' m* \8 `- w; l {5 y6 c g$ R' E6 J
report=0;, `+ |3 J/ a( E% Y: U5 W! y( P$ e
break;) _( R* @0 F5 `: B$ Y4 O
}+ d: }: r- g3 O8 h7 k5 ?! U
}
% \% ~ ^! V# {$ a$ @- Y2 O9 c7 g }1 K3 p2 q$ J' r5 c; N
else' ], f+ [' s+ ^$ C" x* H* o1 A1 \
{; q2 T. L) z) t C
for(i=2;i<8;i++)
9 B. T9 y- Q% z7 F# ^ {) H D- B _' G$ n, I0 H
if(report==key)2 }- m3 u; |- x0 \
break;
8 n; L S' Q% I6 X7 L' O9 W. F if(report==0)7 P5 b3 l2 Z/ |' p+ a1 X. A
{4 `0 [; T9 P: T) d" [! ^9 L! D ?
report=key;/ ?4 [, B9 ?" v& t# w* p
break;
7 Z! s; C- f+ d }
5 N/ E$ b8 Z; o7 X }; }. X+ T& K: d+ x' a
}
. a5 z1 T1 |! w0 ^ }
5 ^% h& {6 A* @" }& \ for(i=0;i<4;i++)
7 d& m& {2 A7 F. T, | USB_PMA[192+i]=hid_report;2 _' z2 |1 Q0 _0 g! d$ [
USB_PMA[5]=8; //COUNT1_TX+ `0 @- W& J7 `/ A# ?
if(ep1_wait==0)1 i0 M8 d) _& \; P( x
{
! _5 L- o+ Q% E) e' _% l USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;' V% V% ]' R' h' N) I/ B" \
ep1_wait=1;+ q. @4 u. b8 ?: _+ A
}: b1 \& y$ ~8 ?1 ]0 h8 A
}
1 J3 [. h" v1 C0 {; g f C; x
9 Y& l5 N& S$ G* V7 _4 ^ ~0 Q6 k4 Y9 Q+ g2 S
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
7 f$ i3 }8 Z! }5 v2 n
keyboard.zip
(8.7 KB, 下载次数: 6450)
# v% J: y' p$ R+ @& S0 l
8 d# u* K. ]7 X2 S0 W5 [6 }0 h8 K4 O8 t! a0 Q9 p1 ~7 ?- t& m. w4 `. r2 [
* {5 |: x( M4 V
_- |. f9 v: ~1 \; y( H/ { |
|