|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 ' b# v5 V9 X) h7 p2 [; _1 @
7 I' M2 X" x+ D$ p# M& K' t
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。# }! ~- Q3 j1 k
% o' x: y6 q7 F _: [4 w
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
! V, f( C: y4 G2 O) Z
4 o3 Y: {$ {/ G- Q; ?1 f a+ i2 l6 _
const char hid_keymap_qwerty[14][8]={
0 m; u7 Y9 \! H K1 V% E {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},# f! P/ L% N" C, c1 p! e
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
; g0 ~# ?/ Y& \8 g {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},7 X" ?+ @0 R! ^+ s& {) H0 U# G
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},( e7 g( k0 } `# ?& P0 A- |
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
* _1 A! F) f& {5 P( J5 C) i" H" A4 h {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3}, b" G3 ^3 F! o, q5 j
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},5 I- b, I# r0 h, P7 n8 N
{HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},8 j4 g5 o9 o A5 g/ {& \) q
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},* U6 G9 J( k3 _9 \5 w O0 K3 R- J
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
: m! ^& X; u8 b. t1 }- g; \ x0 U {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
# b4 ]! P' i6 z% r9 } {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},+ V4 N4 E( E$ T2 e" v
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},2 U, s% H1 [% V! S d7 k# Q# K& i4 F4 r3 _
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
4 y' W6 V# o* V1 X4 B) ?};% E- P0 @3 E+ N( r2 e+ v( d
9 Q/ o9 L% ]8 f5 X/ N" \, J, J
7 {: F* T; P) {1 M% Rconst char hid_keymap_dvorak[14][8]={6 f( B& A: `: N ^
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},/ r+ o8 u0 j, H' \# ? L; Y4 S. A
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
) `, l, I4 T5 y4 u B) l {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},% l1 \4 a0 [" [5 u
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
. B$ c( w: M2 p4 r" W: I( o {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},; F9 a: r, w7 \
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},+ W: A, m( y7 D5 V
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},, ~: B( S1 a. G- O# z! g9 j
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
7 E0 n9 p$ S/ i5 V1 I: H {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},8 ^" e) V( d8 ?' g4 Z0 y) J
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},* p8 K E# t' g7 F3 y
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
. x1 ~4 o3 {4 X+ l- ] {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},+ U* v: i% S3 Q$ P
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
3 S9 \5 b! }1 O1 B2 ` {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}7 x9 l ]6 L& D" R: A7 n4 D
};0 j# P3 A) u+ R% G8 n! S
s1 | I* k. t; R8 ~6 R9 x$ \
) x o- M) W! i: D# y上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
; o; G3 A- y, Z1 F3 R7 H9 H2 L" J( i7 T6 z
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
, W5 ^' m% Y8 J s( P9 R/ T: D9 B0 B% W2 ^2 P) y0 _
void update_key_matrix(char row, char col, char onoff)$ `' W$ g3 v$ R9 S9 }" {0 l% n
{4 ~$ {( N0 E$ @* z7 d" J* F5 b
static uint16_t hid_report[4]={0,0,0,0};
! O }- O1 q7 r A) g" |. b; o static char (*hid_keymap)[8]=hid_keymap_dvorak;
v, W) C7 q% d+ }+ \2 ?
/ D/ o y9 J- R) V
# C9 w4 E5 ~3 z2 x# r unsigned char key=hid_keymap[row][col];
$ t8 X. p1 a: ^5 d' r+ ]: e, X unsigned char *report =(unsigned char *)hid_report;( D2 L" H1 W. o3 F8 O* q
char i;, h' j6 V" [) \/ F
5 G& \; x% |( X1 N8 t) F/ a3 l2 f$ v; K
if(key==HK_MODE)
/ |- O3 M/ a# `- n7 C1 g* f {
) q" a [( s5 T9 I( ~ if(!onoff)
4 u6 M# _: y- O4 l: N3 Q! b {
" U" a! ?8 A! h) H5 J$ n if(hid_keymap==hid_keymap_dvorak)& B3 W" C5 [: e3 P- ^# q4 n
{/ Y) z0 ^* f' C( |
hid_keymap=hid_keymap_qwerty;( v5 T; l( H4 z; N2 x
GPIOB->BSRR = (1<<2);
7 [5 k+ ^$ H) K) M0 G% ] }
3 i& c, H& `0 t3 K' D else! `4 G0 V* u0 H2 g8 t! K
{
$ P, L B/ q, X8 U+ h i hid_keymap=hid_keymap_dvorak;
+ R0 r/ ? N! k GPIOB->BRR = (1<<2);8 o9 T; D3 B, {' I+ ^
}. W% i* I V+ Q
}
4 ^( V+ V% j& v1 `( f( E return;
6 M4 i* }; O! ~1 i; M2 p1 m }7 b1 N7 l0 j) U5 j: j0 D2 [
9 X3 r9 {. y( p! F' q
. J- J5 ]' i% g6 e
if(key>=0x80) // Alt, Ctrl, Shift
$ L" [4 Q1 u6 d, c! y% u {3 b X* u0 E* m/ {
uint8_t bitset = 1<<(key&7);
% r }0 T+ A0 W- A" | if(onoff) // non-zero is key up* R) N/ Q3 U6 z' b5 z
report[0] &= (~bitset);
" x3 o5 |4 J, ]) S+ I else: a5 ^+ Z' u+ o; ^1 F
report[0] |= bitset;
) i. I! ~$ ~5 T( g }
5 |2 n+ P6 |8 A( X& {% A; f else
, Q: Q; F: N+ i {! Q# H! J: ]: J$ S
if(onoff) // non-zero is key up
/ f& U& n) r) u8 z! n" @ {- v# t* v4 u5 f* B- p% Z
for(i=2;i<8;i++); o6 \, q& p; ^8 X% l( Z& i
{
# r" c9 m2 c# K0 U if(report==key)
) ^7 R& M* b* g' \+ O1 I# B {
7 Z) a, G1 G4 H4 `7 K" B report=0;
3 f; e- M! x$ ]+ f6 ? break;
5 U5 X _. [8 Q& \; u }
% ~ z o" t( C x# Z( c% e }
+ a7 E( P, L. X }
) T p( R# k3 {5 q, P( `$ H R& u else
8 u$ S% c. E7 T! j0 ] q) u. H3 T7 s' y {5 M# C+ P1 Q6 q/ y8 D4 G# I8 y
for(i=2;i<8;i++)) [: |% q; Z; ~, D9 R2 o- O$ K) T$ ^5 x
{3 o0 Q- `+ q! T: |
if(report==key)6 F, P7 q' q' |6 X
break;
4 K6 \( O7 a0 D: g0 }+ }' B8 e if(report==0)
& n R) Y% ]( p7 B' n {
% o; v! f4 W0 \: c h/ T$ C9 _5 W report=key;3 ]6 a0 {" h) S: n: s1 d _
break;
0 Q7 g3 S ~7 N& Q }
- X3 Z! Q% c( W+ j) c }# l4 e3 C- u$ H6 K# w4 o
}
4 L7 ^: R2 `; ^' r3 l1 V }
: S/ Z `9 v0 l) D for(i=0;i<4;i++)0 |/ ^6 d M. v
USB_PMA[192+i]=hid_report;, `$ I; A9 z: n. x" y& |
USB_PMA[5]=8; //COUNT1_TX$ Q0 Q q) x h
if(ep1_wait==0)
1 t6 k- N v1 A$ o6 S& \6 N/ T {
8 T% g: U/ b7 `3 E- W USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
& b9 X! [' W { ep1_wait=1;
. W" K2 i$ v5 O3 u% [0 P& f }- F! Z$ ?& f: X( \' ~
}
+ [9 z! U) A. I U4 ~ M0 E8 }, z6 p. Y: y r3 P* a( y' \8 A( s
# z; M% N7 W7 K8 G完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
% j- D; ^( o8 x8 j7 E3 G' A
keyboard.zip
(8.7 KB, 下载次数: 6209)
" l6 B2 y) x; E; X; j) e
* b+ }2 n. Z- d# u! F( Q# W/ ] `* m% i/ P7 p2 b
E$ S7 ~6 s$ Q- g" p
6 r9 {3 d% t- y
|
|