|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
; w1 t+ B* T- `- ^7 t" @/ f
$ N3 r5 \/ G5 x9 z# B扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。- Y+ r% q9 ~$ U, T
) T# i v r. a% c- o, ]
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:/ I( u( {2 j8 S; [3 a+ {. I+ Y
& L- a/ n' H0 b4 b1 z; {
1 G* m4 @8 |8 q% S1 z, g4 Iconst char hid_keymap_qwerty[14][8]={
/ u0 m1 O$ D5 m( k {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},! `! U# D) L) r/ O) [# x+ p
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
3 N% z1 k! L+ N0 A+ ?. w {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},$ W) t: i9 c4 ~2 b0 j: ^, ~+ J
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
" H' ~: p% @4 ` {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4}, a, z! d; Q& F% e( _0 I1 L
{HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
6 N2 _$ i* u2 ]8 x8 m. H {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2}, g/ y$ j% v$ R( H
{HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},! U2 K* W) y2 z, G3 ~$ A
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},0 \' O( Q* T$ \( R, r
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
" K4 o! f: x b1 S1 h$ b3 ^ {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
1 a: z. X% f. ] {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
7 @& O( \1 o: W {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
/ B+ }/ n% n9 D+ h. }( G+ l {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}6 z# p+ ]# A, c. B/ h; l9 x! A
};, b W5 @6 C/ a
- O9 m k% n7 c9 i" [/ p. v
6 L3 \8 |; k7 m: j" D" y1 yconst char hid_keymap_dvorak[14][8]={6 T1 f% w7 H6 W2 t4 K& q4 S
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},# p5 N8 z( O9 ^# T( I0 M8 R
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
0 S+ g& ] ], M. r1 v {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},! O/ _8 a4 s# l& e
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
! h3 `& F* o( R0 `# c {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},( O' P7 V- \: F; n+ q" w
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
& I: x1 A1 i* Z/ P+ V {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
+ P7 J* k) o9 b5 S* ^ {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
1 v' A# \9 t: v: m {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},* n5 }" R) n4 W
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},: c2 {+ A/ X, F# `
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
4 _/ H5 C( z# W9 u0 `0 m {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8}," Y" s1 K i. r6 ?3 c
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
) I9 m+ |. b! l' i% \ v' q {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}( B" L; s3 ]8 D+ F
};9 _3 O4 H8 ?( ~/ m) i% V: m9 x, V$ N
5 P* ~7 Q9 d0 I, \: }5 y9 s9 B
, i( q& `4 I( Q! H上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
! E' ~- x8 X( E: U5 b- h( `3 e/ w) i) s( _9 X5 L
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
3 ?$ t" ~2 c! {: Y, A# O
' _; h' l0 ~$ K1 K6 C9 B8 E8 ^void update_key_matrix(char row, char col, char onoff)
, P. D/ x8 k/ h6 g1 N: `{
' S" V& q" v6 O3 Q) z/ k# Q6 U static uint16_t hid_report[4]={0,0,0,0};9 Q3 i9 T4 d' M# G% |3 @
static char (*hid_keymap)[8]=hid_keymap_dvorak;
. s# p0 n# E g. S4 ?" [# V3 c9 A7 u# r5 u5 }
- N1 w* X9 K* _7 N unsigned char key=hid_keymap[row][col];
$ G* g0 p' I" m unsigned char *report =(unsigned char *)hid_report;( r0 C! j( t3 J' Y2 I
char i;
! ?) c8 }, x5 ]( x K% U+ g: O2 ]0 `9 B
& ~9 `) v& B3 l; n' [6 R' K2 h
if(key==HK_MODE)
; S6 x& Q# Y: T) @ {
! y8 L7 x; l: D8 X3 G9 ?8 \9 {$ } if(!onoff)
' |: _( P! S# R9 u9 q1 c {
' H2 Q- x3 _/ p; ?: g* Y2 D if(hid_keymap==hid_keymap_dvorak)7 Y9 y" P5 l3 [: @/ F
{5 _. v2 X+ O C4 q2 P; f. i
hid_keymap=hid_keymap_qwerty;
J h$ Y; a% K& U GPIOB->BSRR = (1<<2);
3 j. h" L* B: u2 b, f$ ?6 F }* {% A; M) V/ @3 ^! C4 S+ K
else
: H7 {! ^" m/ p8 \ {
5 W" p( ~1 e3 n4 g. z1 U; \- P4 C hid_keymap=hid_keymap_dvorak;
A) h0 Y, U8 @ GPIOB->BRR = (1<<2);
. s* B A9 ^+ `5 z' C. w; B }
' Y3 U. l6 ]5 j9 s1 H( A }
4 z4 J0 F. e, h: T: F8 l, S9 d return;5 |* u4 g( j" j. O! p7 x5 N+ c3 A
}# n2 w# V G1 N3 B
c- M* ?- r9 G) l3 w1 P. J* k, s, B
# o! S7 s, B2 h3 a9 q if(key>=0x80) // Alt, Ctrl, Shift0 ?+ z: T& f' _
{
% U3 _6 F5 v9 i uint8_t bitset = 1<<(key&7);
! V4 l1 j4 y+ c8 |+ j if(onoff) // non-zero is key up
) v2 ~; f% T, ~0 m: S7 t report[0] &= (~bitset);
; r# B6 K6 b: @/ z+ ~6 r else
) v* v. V: Q+ C+ v/ ~) `! [ report[0] |= bitset;* t2 ^( Y7 B& E' h! J7 l& ]0 y
}! m0 I- |$ ]$ Q7 \7 U
else0 n6 P( }2 h! a1 d
{
$ ?2 k! b. J1 H* e$ | if(onoff) // non-zero is key up
6 `, L' Z, f% ?1 t$ \8 P {: T% b3 V$ u: A' ^- U8 c( c& t
for(i=2;i<8;i++)9 p! y; k5 V- v9 L5 r+ O W
{
0 R$ k, ?; o3 r% ~/ ^0 r" ? if(report==key)
4 V4 U) W4 z$ W( B6 W6 Q {8 `7 C3 b+ U; [, r+ B' f
report=0;& \8 P! }3 L% X5 ?0 N% D
break;' ?/ J9 U. m E$ j
}
6 x8 Y& c- [2 l: W; l2 j }
, `" ^% X+ M0 x0 P6 g, m }2 J# f; I9 M9 \) }( Z. c# \
else, A0 [# c* {( S# b* P: H6 B
{
( \* p4 B* t6 D4 m for(i=2;i<8;i++)
& {8 ]- V8 D& ~2 F% x {* V5 t3 c( o" e7 ^/ _( Z _
if(report==key)
8 P3 C# E3 m5 B3 W: R break;5 _! ~7 q- `( F% `
if(report==0)- t: @) B; a9 B& I+ m
{
0 {) z/ B, a2 \/ s report=key; S# Z7 U1 S0 V+ ?" z3 P7 C
break;
- R/ R& R/ _; {9 w }- G7 B& w( I. _, y
}
! l: L( W" F% r& H) j }6 U" C4 \% t2 I$ j5 y& z/ Y+ @
}
+ I% e t' k* l! q" e& C, z for(i=0;i<4;i++)
8 f3 ?. x& L( {. T USB_PMA[192+i]=hid_report;; ]8 q' J( i( `1 D: u+ D* v
USB_PMA[5]=8; //COUNT1_TX9 b+ p& q9 x6 e( x# |: B
if(ep1_wait==0)
& R& v) X( E; n( U, ^6 _. E {. R I& S+ ~8 ]. ^
USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
: W1 s& y: R: e& H, q ep1_wait=1;
' J8 u0 s$ w& c7 O4 w0 w }$ J% ~3 {4 ] `* L$ L/ |
}
1 D2 W( A/ R- F& D Y/ k2 ?6 p* J* Z( M
7 S3 R& p& d( O7 t
9 b. c1 X( d# z* L* B完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
! ?/ v2 P' w- Q/ L6 F
keyboard.zip
(8.7 KB, 下载次数: 6209)
, s5 j9 P9 d1 ~5 G: V; p7 H
$ a1 Y8 N# ~( T% G. u z4 t& R
8 [4 V' m; m# {( Y
1 |) B' }* C3 C8 n, B! V) o
" c( w' [ C0 v ^ |
|