|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 & d$ C8 e/ w4 ^6 o
: l$ ?6 O; p l" j' x+ y% q扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
2 r& T* b; k" R" O% c0 U* U# a$ H% e& H, O8 a8 f3 l4 f
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:6 W/ i6 l$ ]3 U3 i; M
- B" _# }. ^6 B1 O, n7 I0 u: ~# F+ E6 B% j
const char hid_keymap_qwerty[14][8]={& S! I9 \$ r' r7 s
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},' W# o& v6 [, k' P+ p
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
4 P+ n$ p& s5 ]/ G4 d {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
% e v6 J5 t: I" u' N" F# z {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
8 h& B4 S* b! C" u: M {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},. ^. L1 n* n( L/ C/ L
{HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},- i; b) O8 x$ b! ^8 S8 _* ^
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
, \; I1 {! R( t$ ^2 A {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},& i& N+ j' N3 n3 J
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
5 B( O- i; S: s {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
% y1 z: v0 A" ?* }# j1 n. C) }7 P {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},/ t% b" U! ?8 |" I
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8}," k+ I9 ]+ [- S# |) s1 [: P8 G3 E
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
) c5 x* d$ V; R) K {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}$ n) r; Y; m% @8 J
};
^2 T; [6 q7 s
( M/ ?( E4 h) h' S+ T( h! X4 F X0 N, T
const char hid_keymap_dvorak[14][8]={ v v* c* i& v0 P9 ~/ k4 S1 f
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},; q& A% l) l4 A B
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},0 w$ D# ?, [0 z, O+ a. Z
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},- \. q0 O! K ^( M$ y* w( z
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
& J- l$ A0 t5 N0 @7 ^' X0 z {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},5 p% s) C' H: J8 X9 p" p6 s
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
" @0 n6 N7 @8 y {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},2 R$ ~. f9 P/ M$ V" l5 {
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},2 {& H' [( g% K1 o/ c' F
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
* e5 S& j" y) j) c7 N {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
% D4 |% y/ ] f; H1 w {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},- `& z' s6 ^, K8 N0 C
{HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},2 b3 ?7 n2 x0 \# @5 b% w: e
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},, i! O, b9 D1 j( X1 T7 X4 J2 F1 q5 L+ p
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
. ?5 F' W( T u$ b6 E};
2 c6 s9 U6 T; C; I8 A
% |; i. O) L( B- {# L) }. Q/ w" \6 J4 ?# H/ U
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
3 \! K% Z+ O# w7 I0 w2 V" W" v6 s! v; ]
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:9 p# A* O* O9 P' Q- Y& \" V1 B4 K7 M
- E3 q( a) t+ I' x: [void update_key_matrix(char row, char col, char onoff)4 Q# k R2 A8 E4 w+ h! B( @
{6 M# ~0 d3 m6 x
static uint16_t hid_report[4]={0,0,0,0};
3 r) @+ V2 J, i! h" d static char (*hid_keymap)[8]=hid_keymap_dvorak;
3 \" x/ x6 V: {* S/ x! B5 j5 W% N; c5 m& R' s E
: Z$ a! q3 C; L( s: G! y
unsigned char key=hid_keymap[row][col];* Z1 e8 Z+ A2 }! {
unsigned char *report =(unsigned char *)hid_report;
8 Y3 L+ M4 a& e: k: N4 {0 I char i;
3 n- w2 e, G# V: h! L' D
8 k# W3 N' b' F. Z; @2 R( A3 d
( t f. i z4 U# ~2 _2 N% W5 ~# ~ k if(key==HK_MODE)
* T6 v( e+ Z5 J {5 Z5 i# n" |. p0 W. Y# ? s
if(!onoff)/ e$ m! v( `1 O) j# r
{
" @1 r! ]4 Q5 Q7 m' [ if(hid_keymap==hid_keymap_dvorak)
2 c9 f' f! \$ B. y {
" h! S; o/ ^' X+ p! r& c2 l hid_keymap=hid_keymap_qwerty;8 n# m, U, H1 O0 K# h" Y, V4 S
GPIOB->BSRR = (1<<2);
7 v: B- l% b* a }
7 e4 L j( w6 I f/ w else
) @" A$ i* A. J$ t$ s R% K {/ e4 l2 i- l2 M& G
hid_keymap=hid_keymap_dvorak;4 K8 X$ k# V) G# x8 R
GPIOB->BRR = (1<<2);
; @; K2 o( L0 D. h3 M }
6 ~# K1 n/ P) Z6 g( G }, L6 ^* l5 u2 V- `) `& Z
return;
9 B1 t6 a5 q) L' I% j4 k& B }
/ H/ K: m. c F) F7 k O5 K
# O/ A! n6 }* s% E
% F; A4 o$ h- [& I5 a- L4 P5 o0 g if(key>=0x80) // Alt, Ctrl, Shift2 M. C( Q/ E0 m. S
{. \7 ]+ [1 r4 x# P$ v
uint8_t bitset = 1<<(key&7);
0 G" x: M7 @. H; Q( |- o7 L; {) ~ if(onoff) // non-zero is key up
7 ?# G% w- _) \/ ^8 ^ report[0] &= (~bitset);. j# S6 y j4 N! X- v7 u R( q/ `
else
7 `% e+ ~1 z [* q r/ @6 @ report[0] |= bitset;9 r- _4 z a1 r9 v! p
}
R0 z" m% G9 ^# S/ B) L* ? else' V! N* J/ H6 |! Y3 b' |3 r9 i
{' O, l% m$ M. E" D
if(onoff) // non-zero is key up
5 s5 O( W: u# c" {, } {
2 _2 Y+ \ q: e2 O- u0 m: M for(i=2;i<8;i++); f' Z6 d' ^; H! u0 p; W) u
{& s% O" J5 U& S; y/ r1 G
if(report==key)
/ T" P1 l& P, X/ Z. s5 G/ T6 { {
- T: C6 O' X) y) q% h6 N$ ? report=0;
+ r5 l0 Y) r' J; K: \ break;1 i& `$ U4 n; i8 Y* V
}1 }3 w+ a: B- G9 T5 m8 a
}
2 D& T- p4 X( v1 P }" b ]: `4 g+ y: C
else
6 `, ~2 M3 [7 t {) u. \& e. o$ F: N- M* F$ I% b1 m
for(i=2;i<8;i++)
1 P* N& U0 V: p" [8 H {
- K) @% e- ]7 f* k2 ~ if(report==key)$ l; G& R! Z3 b) ^: z7 A
break;" \% E( U: [" {) X/ t |0 W* p' v
if(report==0): S8 Q# P* k# M/ w6 o
{
8 J. g- N$ L# [ report=key;
! f- L- a3 k( t' e: {# ] break;
+ X5 _; t) _( U% c) k }
' B1 d$ R r+ u/ p" r) N! i }) D1 Q T6 q2 j- H& k) d
} G; B: @2 k4 f8 B( E
}
. k8 t8 T( u" ^3 M7 c for(i=0;i<4;i++)
8 P; e1 i# P$ W V USB_PMA[192+i]=hid_report;+ _7 ]# E6 Z( f4 K1 i. w
USB_PMA[5]=8; //COUNT1_TX
9 I5 L! [& L0 n& U5 d if(ep1_wait==0). G8 f) \5 Z& Q( ?
{7 j& G/ U+ K6 h# K7 }1 ?
USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;7 j1 o3 n) Q7 I' B) Q% j1 @ M
ep1_wait=1;
o3 w# n5 L$ w7 h2 k: K$ D8 M; E }/ @; ]/ L3 L, i4 w- P v
}
1 _( @5 ~( N, Z* ]2 e* W2 Y: b
8 v: I6 L% @0 I1 ?5 \$ o+ j' v# Q$ I( A5 G, P; F% x
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。( i b6 ]5 L- F9 j8 X& ^
keyboard.zip
(8.7 KB, 下载次数: 6582)
) d' T M; x, P, f! {+ I A" y* D; Q
& l; B2 }' ?2 x: z
: h/ z) T( ^% \/ u8 U( r! z9 G5 `- @
|
|