|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 5 `" r- d6 e i6 \( C( x4 Q. w
3 a" t' R+ B3 v- {. N扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
* ?+ Q2 D+ |8 Y% ~- P* Z& Z9 I1 o, ~# o9 }: D {
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
* O6 g$ e' {" q* n0 l& j) a
9 W: m0 V p9 q3 O1 M0 H+ o' _8 a' t! p- J" H8 Y! n5 x7 L* V2 k
const char hid_keymap_qwerty[14][8]={# S# |$ C% K) ^1 B" q) ~
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
- J6 ?7 s4 i h% E {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},$ F: O/ n9 r: F7 v3 d, o
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},8 l4 B5 U' x1 i: v4 D9 d
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
) y1 F* t! |8 T) N0 U {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},) n' u e0 g) }2 _- n! W& s' ?' f8 r
{HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},' {3 ?6 @( Z+ K' W" u4 {# w. x4 j2 g
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
$ E$ b1 R) K$ d+ E5 G' N {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
0 v( e. m G! ?: J3 [ {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},4 T6 g. b- I( Y3 I* E8 w; R( P
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},* e( X% a$ G& a$ `* x' N# G
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},# K$ f* m+ X9 n: d
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},+ Y6 I' K6 F* q- H( u2 @. q- e( A
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},: L: F* ~1 g0 P( \
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}! d- ?' O. [# p: u& d6 c' Y5 ^) D3 ]
};
5 g/ o# |, k) w5 H( s
5 |# r6 Z& [7 ^& V* l' M$ r! O' f6 g- i1 |! m. {0 }: \ y
const char hid_keymap_dvorak[14][8]={2 z3 r2 U2 Q2 S; H
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},* p9 `' g6 ?4 f0 B4 T
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
9 e: M/ p# ]. X9 K4 i {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
( x: g/ e; x% W2 D {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
! t% Q3 ]! B* j' y2 O0 v/ ^" ] {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
( \- \2 K& l+ F) W. C6 |5 I {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},& ^$ @7 Y7 N3 t* h
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},; s" N% j: {: m
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},3 n' W5 Q( C0 D
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},5 h/ o) a* v6 I, c
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
E0 d, R- N1 R0 H% y {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
( x2 X$ P; I$ c' d+ F/ W {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
" s2 `# n7 U* S+ U2 O( P$ g3 Q {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
6 M1 @4 R5 f2 H3 i1 U {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}+ d; C: {4 N) y1 w
};
- Q) S; ^: q+ |3 |- ~" o3 p+ K0 F# Z9 M7 L
6 Y! X7 T6 `- {5 i! Z上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
$ g* L6 b& v A* d9 |0 i. f0 J, S+ Q/ l8 l$ h; L' W
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
, ~& w0 C# s: \' L I9 C$ {" Q8 B( Z. ^ P) i% t0 A7 e9 _: ^
void update_key_matrix(char row, char col, char onoff). N% y, k# j3 [# D& W2 }4 c3 F+ n
{4 h- r* @7 a+ E) T+ G1 U
static uint16_t hid_report[4]={0,0,0,0};
: w* O. W' |5 A$ I static char (*hid_keymap)[8]=hid_keymap_dvorak;
/ y# G# m" Q3 h( s5 w# i8 V* y8 R7 f5 [
" C2 e" \1 X* {1 _8 _ unsigned char key=hid_keymap[row][col];" r. n" [ _: |1 h$ {9 L4 W
unsigned char *report =(unsigned char *)hid_report;
3 B) e) ?( t+ ]& n% b0 x* T9 b: D char i;
6 M* { }1 D' t3 P
# j& @8 q& h" P. W. \3 o* b O3 [0 Y7 j
if(key==HK_MODE)5 Q( U" D, {/ ~( q2 t% D
{# G0 w) u% {4 q x
if(!onoff)* d5 ?, S V8 o g" p7 g
{ S3 L( i/ i9 t; \7 v5 Z- M }1 Z
if(hid_keymap==hid_keymap_dvorak)5 y: [, R/ d, b, z6 `
{
4 k& h2 s2 f- n5 ` hid_keymap=hid_keymap_qwerty;
6 z3 u6 U7 @0 i! U GPIOB->BSRR = (1<<2);3 Z' p1 T- u, G( e
}
9 A. P7 N3 _! l4 ?6 o/ z else% C1 Z# _, J0 G6 r9 ]
{( _. B ^* l# e, B4 w& M7 }: W
hid_keymap=hid_keymap_dvorak;0 O _: t' C4 q( E
GPIOB->BRR = (1<<2);7 ~# c7 n- j. g- b( Q. _5 z3 V
}6 Y* _ R# r1 T+ \) X" D4 F. B
}) T# ~2 X9 u9 w, J2 B6 {
return;; c% a: ~4 X+ E* L- Q9 [: A
}! v) T! n) x$ k* X- K$ F B7 Y6 F
2 i9 c; J! Z! M2 J! Y. j$ v+ r9 Q4 \
0 j" q+ O8 K# [0 N* x& i/ ~ if(key>=0x80) // Alt, Ctrl, Shift
/ Z0 k, l D7 s {: t) ?* P/ M6 [9 q- b
uint8_t bitset = 1<<(key&7);1 @+ b; z- P [$ r- S1 M& F/ W: V
if(onoff) // non-zero is key up
2 d X' Z6 I7 P3 d j report[0] &= (~bitset);. j' p/ l3 f. V
else; v# Y! }& F) a2 y- p
report[0] |= bitset;4 W" f; s2 }# W3 J
}( b7 {# D0 U5 i/ l) y: d( P7 ?2 c
else: q, N: M0 H C# w8 N1 t! T% v
{2 D3 a9 K# q. ~/ B
if(onoff) // non-zero is key up
0 C# u& Z3 K6 ^% m {
7 K, v; J( _2 K! J& }8 W4 N for(i=2;i<8;i++)
& q! F, c% g ]! M% l) s8 N% S3 w {' h4 v! w L' P2 e
if(report==key): K) a" Y! Z) C3 ]# i
{0 g% Z, ?% E; g1 c) c5 H( E1 \
report=0;- j& Y% ]5 z t* C y' m" @
break;
" V" {( x' \% K, C2 B- b+ m$ v7 V }
& F! w$ h: f4 {4 X! p c7 S* E }5 c! r# m3 K; e% _
}( S4 ^9 J4 v! `7 x3 t
else
. ?7 |& |: c, \$ F$ u" R6 g" l {
# {. X2 J& c: G) p4 W2 D for(i=2;i<8;i++)
" [: W$ I1 Z, B8 `+ i {
) ~7 c F0 i2 u1 Y( ? if(report==key)- S) L4 W+ t8 M0 x9 v" F
break;
# J+ d; F$ }, x: D% X if(report==0)- N' A( H+ n% f& o8 _) }3 V
{2 w; b* e/ V5 A- [% Y ^
report=key;
% G( D& v. H, M! b break;) g. e9 U, [( V% Q9 N6 }- t0 u
}. U1 l" p4 J6 j! j
}
' n4 @- o3 e# ~4 P" \! ?1 e6 R }
2 z4 P" b: C! m }
2 }$ U, P* q1 f' E" K+ M7 i for(i=0;i<4;i++)6 ^$ X0 Y; x) ^& p& K
USB_PMA[192+i]=hid_report;
F9 b; ^$ ^. {: a8 R USB_PMA[5]=8; //COUNT1_TX
- {5 k$ }1 M% k H" k, J6 G if(ep1_wait==0)* f6 W& ~& O! @, [/ B. p$ u8 o* ?
{
" h9 c+ i' B: N1 O: g USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;, s8 n6 w6 R, p
ep1_wait=1;
5 O: u3 J' Z3 M8 J; N }, c( b+ u, P. h _0 b& Q; V6 A6 J
}: Q4 L0 i* }% O" {; z
+ Z( n$ e" n. |- J* Y) k9 L# G8 I' ?" x- k
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。/ G, _4 n/ s: W8 ?; r
keyboard.zip
(8.7 KB, 下载次数: 6653)
) h, c+ P Y$ z8 g1 l: \: P0 q$ ?9 t. ^* `
4 v& f. F- v, P7 J+ E" A4 X; l
: v2 }. C$ L E4 I( K
* O0 |) \$ ?* g8 S. a |
|