|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
) j7 S$ z _9 J3 e2 b, w
, I+ Z1 F6 F* M, e扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。 J% |5 U) K/ [- W
: U% {: t) m2 Z8 t
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
" \' |; j6 V" P4 C- B1 n2 e* d# G) S; B% b0 M, o+ Q: u
+ v7 q H" G* h1 S6 c. O0 K$ [const char hid_keymap_qwerty[14][8]={
- G' a) h$ ]5 A: ` L% g* Z# S {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
. W" f: ~7 e1 S/ T3 _. {; s {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},6 B( s* M4 i. \9 ?7 p
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
& Z# }# o" s" [! } {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},9 `" W0 m( J! F2 y/ [
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},7 H7 Y: Q+ B0 D3 Y( Y( h+ f, j6 K
{HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
6 u/ b6 @) l# R/ ` {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},. D3 L. n. I7 Q( [
{HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},. Y8 x! H9 J0 |* n j; a
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},7 ]8 O) x+ x! t% A9 t9 k. q' c) r" [
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},' j+ y$ L3 E; ]* p
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},% w0 L( L8 ]: S5 i' Q
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},: V9 H1 i6 g1 m# `; _0 A
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},3 _+ A. u% Q0 T: p4 v, F% c
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}* i, u# s2 q0 `/ Z8 m
};
: d2 Z( Q3 i2 B, Q' [1 W1 T/ P. ^ k/ e. ^
- g9 E& a9 }1 _7 g4 ^) Iconst char hid_keymap_dvorak[14][8]={/ @. G9 ]4 F% w) b4 W% ?
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},: ^- ~- m7 R8 t5 `' j5 ], E) q- F3 F
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
* V, k9 D% B) {1 a$ _; V {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},/ _1 o0 U$ [1 G6 E0 D" \+ T G
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
+ i( {) Q. ~; M4 H! c& N# ^3 w {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
( P" h/ S I+ i- f+ S4 G9 t {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3}," u7 {& x! E& x0 t& a. E# |
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
4 [; v- S1 G T2 A+ W5 Y3 ^% l {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},! ?& W3 `" `- n3 t# M% z/ s5 L
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
- S0 q9 Y' ?0 |- l% P {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},# E3 o3 x0 A* z2 H1 l5 S
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
8 [# D0 s, K) O4 a. @3 S' c {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},8 O: _! X0 M% d. S+ D
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},0 I8 w6 F. y9 C3 u% c
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}! G. x/ r! g8 O7 v2 `9 T+ i5 V
};$ e# B& P) J( d
3 ?; A2 `, b! w# I4 d4 w
3 w0 G/ ]1 {. m& k
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。1 A. \# n+ P0 R4 }3 g% j5 O
9 F7 b! F( }. |. r7 |, m* s
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
! J$ S. _7 z/ s+ @; f! z
0 F- \& t) n" ^8 D3 jvoid update_key_matrix(char row, char col, char onoff)
5 f7 X. g( `( j7 f0 q5 G{! H7 _9 S+ l% y3 V3 K
static uint16_t hid_report[4]={0,0,0,0};
( M9 w, s, f: @/ M ?. F static char (*hid_keymap)[8]=hid_keymap_dvorak; z/ H9 P. L- ?7 w! K9 ?
1 @) k! f: W2 R. u+ u4 n* h* X! ?
3 ]( h5 L: y' T* o9 y& n) s
unsigned char key=hid_keymap[row][col];4 H/ U& ^" N9 A4 q( T4 O. T1 J- U
unsigned char *report =(unsigned char *)hid_report;
) O0 ?' d0 C) u char i;
1 h$ K. P# e' @9 A0 K: j: w9 ~: w& d$ R0 x# x% P
& d% M8 s; [1 a- q! W' f5 Z, K# \0 W
if(key==HK_MODE)8 c q3 K. D& ~/ }
{& @* ]' a" x0 Y+ ?, F/ Q L) [
if(!onoff)# h' V* I: J; L4 a2 o! t9 a
{5 @+ Q1 L/ I5 {5 ^" J
if(hid_keymap==hid_keymap_dvorak)
' k% Z) y- K! J- ? {: k; o( l9 e, N7 E% O; E! T* ~' }
hid_keymap=hid_keymap_qwerty;
6 {. S! i8 Y+ O GPIOB->BSRR = (1<<2);
1 I5 N P7 L3 K4 v }
- y3 \; V& n6 y0 V' d1 w else! [7 B2 D3 e1 `
{
# \( Z/ C/ m' R! j( } hid_keymap=hid_keymap_dvorak;
$ M! X3 P* f/ f! z GPIOB->BRR = (1<<2);6 e0 u- |" ]1 D c2 X9 D8 N) }
}5 N: f& M2 j A$ R! p
}- L2 S! m$ A4 z, R. Y! \
return;( |9 H8 g- p2 U4 \2 U
}' h( h1 [5 t* X0 F3 \
1 v* D% M3 ^) C3 @5 E: y
9 N( E8 n5 g9 q) p5 v' {) ^6 i
if(key>=0x80) // Alt, Ctrl, Shift% _% m! A' B, n D H5 v( H' s
{
$ r# h' {! E _- h6 F9 \% q uint8_t bitset = 1<<(key&7);
8 l: t' V" N1 L) { if(onoff) // non-zero is key up
( [- k5 n) x4 g" m' _ report[0] &= (~bitset);1 d: f. H/ F4 J5 {$ c
else
$ b3 D- y8 F1 M% b5 d# x8 H+ L report[0] |= bitset;
) U: c; W2 M( U6 [+ w2 N+ W }
1 \" F- @% l; k7 m0 _3 h else
+ M) j, ^) _$ h3 G* ? {
" A' Q* S- s* ?5 a if(onoff) // non-zero is key up
9 m" \* ^/ o' W. R) N8 p" c {
* t1 y0 S' p8 {7 [( V for(i=2;i<8;i++)
, Y; h& p# _# } {& w' Y" b8 Q" H$ v
if(report==key)* }! e" L2 H0 u4 A; r# C
{" Q- t" k( O% h- i9 M; S
report=0;) H! Q% A5 W, Y2 S0 Y
break;
$ h5 d' {! A2 \! k }: Q) P) ]) v) g& C$ h' U
}1 {7 e D A' `2 x" V$ \3 E9 Y! Y
}
/ Y2 `0 V6 M1 M! Z |. J& H else
2 _0 \2 s3 \0 U3 \) o" E, j {/ j1 L9 A: n/ Z# o1 H& B2 a
for(i=2;i<8;i++)
) I5 d6 X4 R8 X; q7 S {
% T6 G6 h: A% S/ l2 t( s if(report==key); G) ]9 ]& m& h
break;1 b8 d# y9 F* k; t! l% d
if(report==0)
' E$ X, q! h. t7 Z {
" x5 C8 R; L/ I' Q. o% ~ a. k. j report=key;
, p# {( z I( z break;$ y% u8 o. H$ x( a! J
}
% d1 H' w" s! w+ D! n6 M }- P" f9 g3 a' v8 w O5 m, G( g
}5 d4 Y8 _( @* p" c" m; e
}
, h* B2 q: K( K: u3 X. ^8 H2 Z for(i=0;i<4;i++)
+ J. d; ^4 T, \ USB_PMA[192+i]=hid_report;* \5 M8 I( B$ i$ E5 L g
USB_PMA[5]=8; //COUNT1_TX
- M; e6 f& i/ T6 F% j+ r if(ep1_wait==0)! @" j/ a [& G* h) I# t
{
$ G5 g7 y. f% L2 u9 x USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
6 t% C% `( E+ T* Z9 S& K ep1_wait=1;# H- L0 @; ?/ n) f) s
}
0 p1 B9 W r, g# ?5 j) b% N}
, j1 A) \% _8 S$ W* m$ }5 {6 f3 F3 C# _# q9 k! i# s$ g$ | M
6 ]+ n$ e% G' Z8 X6 [7 y3 {完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。7 A! {% b( a! @& c
keyboard.zip
(8.7 KB, 下载次数: 6701)
' X* r0 R/ J: D/ O1 N& h+ R* V; M1 H" r2 s. K A3 ?; a- h8 z8 v& h
8 {: j# q% B) J* ?5 X0 ]
! U( D4 B( X9 V+ _
0 t1 ^3 _" j; F# y% `+ w; I |
|