|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
7 T% N9 B/ B% M C% i% D; ?5 N5 J( W: ^" e8 W
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
$ x' M f0 F7 v7 ^7 N! L- S
% @4 @% o2 {% b: A要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:6 s9 N$ ~! r" s( J" S- G, A" j/ h
/ v% Y0 t- c6 I& y# |& f
% y a" v/ h0 b3 d; H! w; gconst char hid_keymap_qwerty[14][8]={
8 k9 l4 `# s1 n8 j4 T {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
2 q X" J: u% X# X/ L {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
0 p: }8 X8 J+ @6 c {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6}, d+ k) g5 t3 b+ {: [, H# Q
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},* {; Y) l) `/ q% D8 u
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},2 u% ~+ Q9 p+ z2 ^$ e7 G
{HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},+ b @3 j9 L$ l4 k8 A/ t
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
* i. F! j7 {$ W+ c7 L5 u9 ` {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},0 A% F9 |% k7 k1 h( [5 ]
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
$ ^* l8 ^% {" y1 z2 E {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
$ A: A0 s# b t% W {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
, v% S8 k3 p( W* P$ Q6 D5 P {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
3 c/ f- P7 w( G* |) m/ z {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
+ t7 [" m0 I8 O4 ^ {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE} K- }, [- E' `7 @
};
7 z+ L, J, T' S' z, Y5 B
6 C% ^ N! W/ e. A1 Z1 O& J0 @! c8 A
const char hid_keymap_dvorak[14][8]={
( J7 a, i, x! ~3 Y, S( J {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},! v% r$ V+ y6 }. S9 S2 O8 [
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},* r- ]9 b9 z/ ^' _" E9 a
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},. y( h+ G& d, P5 z8 p
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},( R* ?" g* n% e$ C% [# D# _, f
{HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
1 w: I; g: _% @3 t% E6 V {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},9 m$ P$ J. m2 }+ s2 D/ w7 X) H
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},9 ~/ d* v/ g3 I
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},6 A0 M' {, ~9 J$ q' W2 R0 M, X' o
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},1 ]- H p% v9 b
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
0 C% a. K( @7 v. ? {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},+ A4 K7 z+ ~, q- x1 }" T
{HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
- O; K: i* S3 t; n/ R+ |" l+ ] {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
$ z7 ]6 Q3 x* U1 b) O I1 E {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
; v& x# F; Z1 j. M) f};2 c* f, N p; Y0 M* |( Y g4 o6 }
* b: D, }( H$ Y2 n
" {/ Y2 N+ v: m" `0 G0 q. J
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
* ?" O, p' H( y/ b1 V, W5 w
' \! J0 n, @* r9 A3 vHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:# U% G, V, w: k
2 S) v) [: I% \+ B1 G- {; M7 uvoid update_key_matrix(char row, char col, char onoff); Q& [6 y/ ` i- R5 D5 M6 F4 R
{; Q- v9 Y6 w/ z$ h3 j
static uint16_t hid_report[4]={0,0,0,0};# X& K" ]( _* b J; Q& o
static char (*hid_keymap)[8]=hid_keymap_dvorak;4 O# S( s6 x8 b# Y
1 [, p* x- m8 J; e2 _ y
! S, {" y* X+ ]/ ~1 b1 k$ l9 G$ u; ^ unsigned char key=hid_keymap[row][col];
6 H" g V& s- R7 z: K: } unsigned char *report =(unsigned char *)hid_report;5 ?( o% X( a8 b9 M% _% f4 z5 D+ y
char i;* p! l) W {. ]% P7 o _9 C& r
4 a$ r' g$ J7 i
f( V1 f" U4 N) U4 P$ J2 p if(key==HK_MODE)) S3 ?# Y0 c a' b& W1 E, a
{
: g2 S1 {4 a' Q- R, B8 Q3 R if(!onoff)
( I4 v4 E, D2 @0 G1 f$ q4 n2 Q {
( u; C6 @) X- Y; t% D/ t if(hid_keymap==hid_keymap_dvorak)% j7 A8 a7 @+ W
{4 B9 o8 S" }+ P' u! m! \
hid_keymap=hid_keymap_qwerty;
/ j" |' c5 y8 }7 \ GPIOB->BSRR = (1<<2);) D k9 ?. K4 V' |3 M
} N0 I! m: f* b
else5 Y9 S" y5 u% R5 b
{
9 F* c3 X( h# ` m hid_keymap=hid_keymap_dvorak;3 K5 ?: {% t$ X) o9 S" a$ P
GPIOB->BRR = (1<<2);
# T! K9 J" @: p6 B: J }3 B! H" @; v7 u& Q3 H+ T4 v, ^6 {5 E7 v
}
3 h+ q( O. J! n" B& t7 G- ?, ]+ K return;
; M3 g' w- w2 {- X! W% M }
% L) ^0 P4 l7 E8 ^8 ~" P% m1 D0 z$ m- c+ {7 P# D
2 ?% ]5 c1 R) t) V( I# K( G5 C if(key>=0x80) // Alt, Ctrl, Shift0 A) D: V8 C+ V( X/ X
{6 A3 q0 `5 I8 i! G$ C* X
uint8_t bitset = 1<<(key&7);
, p5 x3 K7 ~0 c% S/ _7 Y9 F if(onoff) // non-zero is key up( U6 P. l. {1 y2 H
report[0] &= (~bitset);
2 A0 E4 h$ R1 Q e8 E else0 X9 c$ m7 g6 e, Q" g! j
report[0] |= bitset;: t M" d- j. S, q& n! s Q$ s
}
. o! d! `5 A& C* a else4 _+ x! W' Q8 Z# P5 S
{- W$ F8 Z( u/ l$ `9 p
if(onoff) // non-zero is key up
) H( M) l' x; w3 c {+ E) L$ A0 q1 Y% l! l0 f0 I: d5 `
for(i=2;i<8;i++)8 J0 @/ o" V# v& ?& v
{
& j9 U7 e# K: u* ^- c: u* @ if(report==key)4 l; ?2 |+ h; z. {' s9 s
{
- y+ ~' E' J# ` A report=0;
# Z q/ A) K4 Z _7 ` break;6 m; z" G6 R- b. E
}# R. [2 H$ l: b' m6 o
}
8 }9 u! Q# U) Q/ L+ G) l- ~ }0 U# l6 t/ {2 t) W. b' K6 ]
else* g0 P/ l8 X6 \% D6 T
{& [5 W; @+ d3 `- F* T
for(i=2;i<8;i++)
5 l/ Y: K; l6 o7 M4 H {
0 ?' \0 I+ P. s' ?6 ]6 o* X2 e if(report==key)
. Y# s2 V9 {0 S& _. ?% o3 F break;
# x4 K/ Q: W4 |3 X* a3 h if(report==0)9 W! m0 v+ F3 w6 k" Q8 i$ I m4 h
{
, X6 w1 W8 t! I0 H report=key;
2 F' P, Y' m8 z break;7 P& f3 P0 C* |# v/ |* ?
}7 z q% { f: v
}: }7 Z$ j8 Q" @1 K* Y4 @
}
9 D1 J$ P9 d: D" s" W) x9 w) ]' s }
, I; D5 s+ G* K! j% q2 L for(i=0;i<4;i++)
U! v. M/ v1 N" z7 M4 D8 h: e USB_PMA[192+i]=hid_report;
6 [" c1 [5 `# v( h4 l9 j6 } USB_PMA[5]=8; //COUNT1_TX& ]/ J0 K9 W6 k$ D7 s
if(ep1_wait==0)
$ @* o, a6 B m; O" u+ U {
0 n3 |4 f6 l! `" A3 \! x USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
, R( @. i, n3 B$ j3 m* Z% b ep1_wait=1;% z8 ^+ p& z" g. [! O E
}" ]( v! }' J- [) c! G$ ~- ^5 Y/ X
}
8 h/ n! |8 _. s, M+ j
' T! I/ r9 I2 r; k+ e; V: f# k6 Q% m$ e3 P
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
8 G6 s9 i8 z5 z4 E# U$ j9 @: ` s
keyboard.zip
(8.7 KB, 下载次数: 6124)
# C( l0 C r2 r- Y2 S+ D+ I2 D
3 c4 j. i4 P* i2 p3 n% q8 x; `" `- b* N( v+ E( Q
' U& `" w. J2 {; R+ w0 g, D
4 ]1 k- z# X# T f |
|