|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 0 n5 _; R# O* ]7 N3 G( R7 ~) Z
8 ?8 D5 N# N1 X) N; w# q3 ?# p! R0 ^扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。+ B) A4 }& @1 L4 R" N% |5 ^
; x, a% }) ]- L( ?
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:4 E Q0 v4 L) X' q9 j
. e4 d4 h. x7 H( E; I7 J
% l, l- g& D0 b( V. Fconst char hid_keymap_qwerty[14][8]={: d8 x; j5 u q+ ^2 H- V
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
) V% g9 F- U9 U; V( N {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
[3 d- l r1 O* N h1 X8 Z, x {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},/ r. E, m& H# e `
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
, C+ T9 ?- e9 `$ e3 z {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
/ V6 V o: m Y' { {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
1 X8 K8 p7 B1 Y, J1 | {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},0 Q8 q$ `# G" \! n7 y- L* i
{HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
' a! M, U( P7 _5 }) C+ i0 `* b {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
: V0 n1 `! g Q6 j {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},( p4 W) j/ ` f- a- q3 b
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},2 @( N: a! L: G" p) m7 q
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
$ d) _$ v/ W j4 V4 r8 Q {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
9 A/ E' Y9 X. u2 a2 K; I {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}+ K. v& {$ H! }7 t
};+ U- }; `9 @% O2 ?) T
, M4 r, N; U! q' H4 {$ {# B
8 K' s0 w, f- n7 Jconst char hid_keymap_dvorak[14][8]={
) M* u9 a0 }: @0 c4 w4 f8 _ {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
4 V2 g8 R* m4 b1 D( F; O6 Z {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
8 U; G# g2 [( P {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},. t5 L7 F0 C. r- }4 J* K
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
3 a$ x, _$ m2 u4 i2 ^) L% S {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},3 x1 ?& G( L) R+ {/ \+ v
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},7 Z0 y) H# p/ r4 z. V" v. H- \
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},8 Q- a# L# x+ {5 F% @" {1 G
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},. ~) t& n& W: n7 ]3 A
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
6 b n1 W. Y, ?! @0 N% i$ G* g {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},( `4 o$ n" M' s9 P- D4 Z
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},) W1 n6 D/ w8 t- I4 i
{HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},. ]6 @; g: v' u! ^: R
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
) s) R+ k9 D" [ {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
; f3 K# W2 t+ Z- c7 I};
2 o5 ^* f9 Y1 `8 [9 }! w
0 H$ ?/ a+ L& ^; Q: N$ b6 L" }; Y Z8 }
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。 d3 S6 {4 M ]! c0 N
9 o) H8 W1 n, l, l7 |8 cHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:* H' ~: d# M0 _* T( N
+ h s* J) O# T) \+ W9 I+ Q8 v, s: b
void update_key_matrix(char row, char col, char onoff)( r, d+ d- F1 @
{, E$ Y; T+ B: t
static uint16_t hid_report[4]={0,0,0,0};% [1 C1 d' w# M
static char (*hid_keymap)[8]=hid_keymap_dvorak;
( t. ^) i/ E) h T" E/ m
; \. y9 y1 J4 T9 p( f
2 {. ^& X( {% C unsigned char key=hid_keymap[row][col];
4 R& E; P0 Q4 J; |7 `& P unsigned char *report =(unsigned char *)hid_report;/ E! ]( N: a, P8 b( Z6 O; X& [8 L5 T
char i;; r# _6 A/ `% }9 J& }/ X0 g
+ j" p! ^, W# q* h3 x) j3 A- c
- J+ K: M1 H3 A. B- B" A' F if(key==HK_MODE)$ l2 y% D. Y* U3 h, ?# [' T
{
/ K2 F3 x) k5 R `: @, }, N# N* z( m' o if(!onoff), F3 i/ q) _+ Y6 `5 w
{
) ^7 a( n) y% U& T if(hid_keymap==hid_keymap_dvorak)* ]1 F/ r, j6 }# V
{6 p: Q. `: u/ @1 x
hid_keymap=hid_keymap_qwerty;0 `& N2 i1 h* Z& o8 h
GPIOB->BSRR = (1<<2);' u3 f/ H0 M4 k* ]$ l
}% g j% [' C& T; t0 j$ s% S
else
# A! c! h! v [ {/ N- l9 p; j+ D2 ?5 h2 c1 H, k) K
hid_keymap=hid_keymap_dvorak;
8 n5 s' C3 ?# m/ F+ m. C/ ?- P( J GPIOB->BRR = (1<<2);5 t+ i, a4 v& m6 N+ H3 J6 R' w* m
}
6 o" l1 v0 O2 t* w: u* O6 ^ }, X9 K; m, a& O( ~/ |0 r3 d) ^9 n
return;. Y$ g5 Y1 E1 t, ]* \; t
}; {/ |" _2 Y8 c# N# j% [
4 `# k N/ J7 c1 J6 ]$ l: q" b, g5 ^
' b! D$ t+ s' @) ^# u- d2 e
if(key>=0x80) // Alt, Ctrl, Shift! w! z, ]+ k, V7 X9 N( e; i% z. I
{8 m2 e$ e& b, ]; K( o* A W
uint8_t bitset = 1<<(key&7);. e8 {% a, y: [2 ]/ K$ ]
if(onoff) // non-zero is key up
- b+ m1 A# k0 b. h: n report[0] &= (~bitset);
3 j: |3 q! j" m3 |- m$ ]. o- c else
9 k$ U. p6 P$ ` report[0] |= bitset;& i, w7 X! ^; \+ h& q# Y
}, U# A* W. }$ e- A/ a
else v9 {& j2 `2 u9 z9 m+ E( m# l
{
* s0 h1 g: x3 q2 e if(onoff) // non-zero is key up) i: q3 _8 k; s# S- p
{
0 f7 z r' z6 Q' }: |: H for(i=2;i<8;i++)
+ ]$ H& P5 B- O% m% R$ a& _- t( \# } {% X( ~5 q' S" o4 f6 @
if(report==key)
) b* P) V9 ~. o! S% G2 s- R {, }6 D0 D1 n6 P' c: i6 Q
report=0;; u* r9 ~% E" ~& ?9 p& @, S
break;0 r& N* a/ U6 q: P' ^ j: F7 u4 {
}
* W, Q" e3 K# t3 z+ W: u" H/ Z }
. ^8 {' B1 q, ]1 c3 f }
! I% @6 k% m. M1 v else6 w& H/ z+ Q G2 i) K: j
{
: S, _) v3 z* E4 e, q7 k for(i=2;i<8;i++)
- X! Q& C; @0 @8 U- B& n; M v6 Y) J {2 ~% w' H0 |4 c5 L7 {
if(report==key)
: _3 u" C: a+ I/ O o5 p& @) q break; q% z* ?+ K' m% U3 w$ z
if(report==0)$ D. Z# T1 v* z* j8 t- z$ E0 o. L
{" m9 N$ O1 L1 @ Q0 C4 d0 Z
report=key;
. K# A3 [( f2 _2 ^1 V/ R% X) w2 C9 P break;
- Y, z1 p3 k% h; T- u }
& b& S3 R; {2 |0 ` }
( e9 d9 L+ A; Q }( A x( v$ s8 F
}
* E1 W4 m8 y7 c) v) ]% r6 k0 ] for(i=0;i<4;i++). t" R4 l+ n8 r: b0 P
USB_PMA[192+i]=hid_report;# c! _) P2 w- R/ o! Q- v# C
USB_PMA[5]=8; //COUNT1_TX4 o$ M9 o0 [9 O8 S/ ?" ?
if(ep1_wait==0)4 m# L2 ?8 F8 j8 T% m) T3 ^1 D
{
, s1 z6 C! O& O. @( `8 l$ b' x USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;7 Y: z3 M4 }. U0 P7 v
ep1_wait=1;
, l8 _1 g$ p' s# w }
" ?4 A6 w* l* p$ G! P" u}( d; U1 a, d9 _2 ^, s
4 C, r0 x5 v& }3 X# R( E |% [& X" G
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
' U( a/ {+ D8 R6 y
keyboard.zip
(8.7 KB, 下载次数: 6377)
# |! z! a' D. g0 d3 d4 R) \' M5 W) W& p
8 f* i( l, ]- Q7 s' i
`( F* [0 i4 s& v9 C
' W& K! p; a5 W3 y8 l$ a4 F |
|