|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 ! }1 d# J; N$ g
3 Q7 m+ }# y3 P* U) T" F h扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。9 f$ j S. c) P: Q$ I) h2 w
+ \( r% @( f+ o2 Y
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
# j# r! w" b/ p$ L. V" X& J0 p2 g# s& T# K$ {/ |
+ t% V" P i4 _0 H- M$ m; P
const char hid_keymap_qwerty[14][8]={
/ Q$ P* Q( y6 M5 E3 r$ c" e {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},7 @9 x1 v$ u; @6 ?* i
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
6 I# p' i8 H% G1 Q {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},# A# G: J9 B1 v8 l
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
) j" ?" p' R5 b {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},, b; P. i) V- N+ e, j1 n% d6 i
{HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
& {; V% O- `5 v$ `# n: A {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
- K/ P5 O. [: M+ X! N& n {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
* p7 S. i. X1 R8 s {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
# Q# _# j" N! D, m {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
' g3 ?% A8 b* t% j. f0 u {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
U! [( s7 {2 T0 U5 s5 i {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
' E1 F: R; O, C6 n {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},+ j) t/ v4 K) ]5 C8 `0 \: ^
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}7 Y3 |3 N+ \. Z. B3 P7 z( {. V
};+ ?/ N- G) F; v; u
) q) u; x+ B( d! e4 | P& P8 d* k: }4 W1 Q! o2 G) u
const char hid_keymap_dvorak[14][8]={: p& v. t- V7 q2 V
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
$ y3 N( J7 p# T0 X {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},% }+ r5 z' l7 [
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
6 H/ }# ]& j ?5 ?, v8 ~; y8 |% F {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},, `+ K; ?; J" \) G6 o0 O, P. A7 ]
{HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
. S' A* u+ e* c! t% G6 ~& g5 b {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
, R! }5 s M X' L w {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
. E6 H, w1 z+ q7 D) ? {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
0 }: S; X$ ]4 o3 z9 R; Z' g8 f {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
" f" W; D: V% J {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},3 C7 b3 K `) D! ?
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},% u/ S+ {3 v& |; x0 W0 e0 v2 \
{HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},6 y$ T0 }: S3 c
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
! m4 u. V% O- B% I: @ {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}3 Q5 c. F2 H' J
};& h7 H$ e9 a) S. b7 m5 j5 l
. _6 q: d* C' b2 z! {( J- E
! H# U- i t; w2 r" u+ N, r9 ~上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
% s5 f9 X* ~/ j6 E3 i! H' O' w2 G8 d* Z7 h1 n9 v& W5 `
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
8 E D; M/ m! O, M
/ k5 y; H! A! kvoid update_key_matrix(char row, char col, char onoff)
" F$ ~6 v# G$ L. h. y2 m; ]5 _{
, F9 U4 a0 [" R static uint16_t hid_report[4]={0,0,0,0};- B. v6 a* [ E4 g8 Z
static char (*hid_keymap)[8]=hid_keymap_dvorak;
( n- G7 D' b" r1 }5 T" M( M" G
6 `$ {8 w. Z: H7 h! R
% x! _) r; G: t- r: ~) w unsigned char key=hid_keymap[row][col];
; @- V' k1 L% ^! M unsigned char *report =(unsigned char *)hid_report; u: C7 v% O: {8 t
char i;0 `- x; M3 f5 r9 i; h% \1 @, k; a
& X; o& c# n, m" K- {: b! G3 [
# a) I+ `/ d, {( h4 W5 O if(key==HK_MODE)
" k2 y' U* x) Z/ \ {; m2 [6 c& z) r* b& q9 ?
if(!onoff)
4 G: \) V3 T" U" q* F' V* I {, t' V, R" t& t4 ~1 G
if(hid_keymap==hid_keymap_dvorak)& r3 x/ m/ L U8 a- J% l' L1 e# C
{
4 }: E; n/ A' l1 E hid_keymap=hid_keymap_qwerty;; x _+ Y* y' e& j
GPIOB->BSRR = (1<<2);
8 f9 O4 K7 k0 b U5 B }4 s$ j0 r: s1 k2 c6 ~
else
- f9 j2 b4 j0 c3 Y9 k {* ]9 _2 ^9 Y" b4 f0 a9 }) w& |
hid_keymap=hid_keymap_dvorak;
- k. f) |7 R' G3 Z0 n GPIOB->BRR = (1<<2);
( d. O- W8 ]: S9 P/ ]3 M: Z }
3 y2 M/ T: N* i }
. G" M! ]8 d( w n$ @. e" U7 f# ? return;$ I9 b8 a0 Y% H, u. a; L* s- T$ U
}
1 o1 }& k, t1 w7 p8 {2 v
: B6 r$ u2 S1 W* H8 K2 B6 ]6 |, @ ^$ {* X* i2 c7 H5 \+ i. I
if(key>=0x80) // Alt, Ctrl, Shift; [9 M6 w& L: w+ U( n" |! v
{
% X r8 c5 n0 B( @# {+ N uint8_t bitset = 1<<(key&7);
# k* {( V! l/ H if(onoff) // non-zero is key up1 W9 m3 ~ v2 j+ E+ W `5 y
report[0] &= (~bitset);
) O0 E3 I- c! q: k' e" \ else* E h @7 L! y8 i% p
report[0] |= bitset;
" Z, U @. G5 k: H* ] }$ k5 T, L! p! {) G6 L3 F
else
# H. R3 Y7 x$ O4 u* y& g {5 C9 w2 k7 J& f2 e0 s* q
if(onoff) // non-zero is key up! D# v/ ~8 V& M
{3 B/ o1 j' q5 P6 ?/ c$ L! Z
for(i=2;i<8;i++)
- \$ g) d z9 c {
4 Y6 }1 x$ Q6 G) v0 p) y& f if(report==key)
, [5 K# |0 B' i# p! ^& K {# ~+ S v' [% o7 t3 V
report=0;) |+ O+ t& V* m, ^: ~/ o3 G1 C
break;0 n' m0 H1 U! t8 {4 T' Q5 A8 O
}
" b' g. \) X5 u9 y% G1 F }. i }4 q. X/ L& |
}1 r, x _" X b+ p. B$ i
else
1 P8 i0 D1 n& m3 W {7 J/ M" V {
! V- ?: G7 l5 m for(i=2;i<8;i++)
: g+ g3 k. Q" C7 \& H; H {
8 e2 n5 F* v. w3 J7 x2 S if(report==key)$ a' ^8 `0 e4 L
break;
: D+ f1 E% L' j2 Y if(report==0)
3 A) Q. D1 b* |$ k$ O" } { {
/ U* B1 S" B( f# M/ Q report=key;
% e6 F; `# ?6 b8 e6 F9 `* B break;
L3 O. i% o! C( c9 O- i }
+ h0 V7 \/ k) ?, o$ t }# t, B0 v: K2 r& Z4 U
}3 i8 X( i# M0 A$ m4 }! p
}" U8 O2 D& d/ ^
for(i=0;i<4;i++)
) p" D0 V# _9 @3 F* p USB_PMA[192+i]=hid_report; t8 V, q7 ]8 S0 U
USB_PMA[5]=8; //COUNT1_TX! t1 n7 [1 m/ D
if(ep1_wait==0)9 d' T" G. P+ N7 \
{
5 B, h F( B) R6 k1 m2 t USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1; x0 h4 V+ i+ x+ J1 @* @$ p
ep1_wait=1;4 P( t+ R7 {. p; l* X7 N
}! T p% S! R/ I0 t4 ?% _$ w
}
8 I% ~" V2 t9 Z( q" J8 f- D
n* R- }4 E4 n$ Y9 U9 I3 C2 e3 _, u: ~: L. c( m( e
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。& E8 x- {0 O" O
keyboard.zip
(8.7 KB, 下载次数: 6580)
, s0 g$ b: w/ _4 V: t
: u- S0 B$ K; R- o8 M
% r8 W/ I( g+ F* X2 Y F8 B& b) Q& L2 D
! {3 ^5 ^6 X$ [2 [
|
|