|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 - r: y3 j! H3 _( h9 @
! t7 A$ W3 W$ a* ?+ y扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。& ]. i# ^/ g0 w/ d7 l$ O1 ~! ?7 R
1 @& v, G6 W$ [( S' W/ K) v0 Q" J要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:. L+ x# ^. F1 ^* D2 }' x! @: T
0 m+ W# C5 b$ R) L8 I" X6 a8 D/ }1 y" p; {5 Q# o0 @9 j$ e h
const char hid_keymap_qwerty[14][8]={+ ?4 ?7 [2 g9 d% X
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
, D9 Y* g# D; F. ^0 L) W6 e {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
" ^% r: m- G1 i& q {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6}," Z; S8 {1 i* ?5 |1 c9 C
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
; y! h, M4 v/ y {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},% I7 ^8 I4 {$ u! L( i7 z
{HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3}, n0 o2 F. E% U) W9 j3 @9 N" h
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
) P( M! K! ^7 @! X+ b6 o- ]! a( | {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
8 b$ _& p7 p& G$ L- q. | {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
' O# o7 A( }" Y9 O* @9 ` S {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},# k5 i- B1 Q6 u! K9 E* I: ^! c
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
/ E! c5 c6 b; v+ e {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},- H& H# J2 e" J" D' J$ s) D+ y
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
$ {& L- t5 U0 S+ a# W& R {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
3 G( p+ `1 p$ S$ h4 [3 U" C};
0 x: V& a% p2 n% f# t3 Q* l
v( [5 c/ h/ c& X( C* D$ K1 N Q- }
% a5 g5 J. `6 ]2 ]! ]' gconst char hid_keymap_dvorak[14][8]={
9 E. E6 u( ], `8 a8 \3 E' w- x {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},6 k+ W! u5 `* ]" f
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},3 ^ `& p; Z K% y+ X
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
0 t y/ |3 g1 d. i3 ^ {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},1 u% l v: D$ _; ^/ d
{HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
) m2 A3 T% V( j) s% }) I {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},; t- u0 R' V1 W$ q5 o# p6 y
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},, r/ P+ e: G6 E3 D9 ^+ F
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
. i! d; z9 ^+ o' I( ?5 t& ?7 l {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},/ R" a- Q! U6 |4 c
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
7 z& C' Y' |8 a/ L: z* l {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
, k3 o _' T6 l, P {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},+ X9 d+ A; |1 d9 m8 o5 @
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
) b- |% D' X/ ~+ D {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}5 v+ K, s q) C7 f8 M: G
};
$ X, c9 \% i* o) s1 u1 G- N' }$ l, J' i6 Z; ^! F* t
2 t9 Z) c/ |+ Y0 x% `; o. U
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。- Q0 [) K% l, a8 I$ i7 R# |: R
8 c3 M b' b/ [" DHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
- h: i9 f* u( b3 a. V% }0 }$ F& P7 _2 N
void update_key_matrix(char row, char col, char onoff)% V% j( k2 F# I& { k+ B- F# V5 P1 b# l
{
1 Z( t! L2 L9 k5 a0 C, v static uint16_t hid_report[4]={0,0,0,0};
7 A4 y* ?, H6 \$ a static char (*hid_keymap)[8]=hid_keymap_dvorak;; k4 g! Z- u5 _) E
& L; e7 `: ~7 g6 V. T5 L8 Y
0 m7 n4 n8 u' B7 r+ a& I+ r* E unsigned char key=hid_keymap[row][col];
. {0 u2 Z( q p7 H+ u unsigned char *report =(unsigned char *)hid_report;# ?4 W7 o3 f) n% G" @
char i;
: g9 D* g. f* g
0 x( Q8 t$ Q B+ S* M. f" Y
) i$ v8 r i. X* i" e. K if(key==HK_MODE)) X8 ~5 i" X8 k6 M i
{
# j# v) J* p0 D! T3 Z" p" ~ if(!onoff)
+ q. P5 q2 L1 K' z- A! [0 t7 ^/ d {
# N# M; J, [) A0 L, m4 P$ Y; x: u9 J, A if(hid_keymap==hid_keymap_dvorak)) z. F! f# P; E% r# U2 U& G! n
{
; t( W; z' U4 t/ ?/ @/ E hid_keymap=hid_keymap_qwerty;
: V- u- H# y) [ GPIOB->BSRR = (1<<2);
; t$ t1 f7 V0 r! X }: b! |7 e9 \- {& q+ I8 n7 p% ?5 \, o
else
$ V3 [4 \4 X8 n {
7 w. J+ o: e# k0 J! b! @ hid_keymap=hid_keymap_dvorak;; U }: v6 o1 D8 \0 @* R9 |1 I
GPIOB->BRR = (1<<2);
* W$ ?8 m# z% u5 j. B" C }9 j, a: G+ w; e9 `
}! N( x" j0 A& R, Z) l8 {4 x
return;& I p3 m D# ]! ~. U( A$ _
}
3 i9 L" x' l0 E, ]) C
, Z2 X$ K9 u3 @! o( Q/ S3 b1 p6 m. d5 s
if(key>=0x80) // Alt, Ctrl, Shift& b% J. W; d+ T5 U! H7 |
{9 b- {$ t8 Q/ M; c9 a' Y
uint8_t bitset = 1<<(key&7);' l% v$ N1 q2 J4 \8 D
if(onoff) // non-zero is key up: o1 N- Z! ?2 u& h* ^0 y, Z
report[0] &= (~bitset);
* V/ B7 e% F3 c$ } else) [) }- ^/ n- }
report[0] |= bitset;$ }6 x& w! y7 L" I! o$ y6 B8 ]
}! O% L; x' f8 ^. }1 z# }* d7 U
else' Y- u0 |6 J3 K* Z) G5 @
{; c# l& j( X% R C$ Y
if(onoff) // non-zero is key up
! | u" R( i& ?1 l {
+ z7 P8 L2 Z' ~7 `% k& e, x for(i=2;i<8;i++)
7 y/ r# k. ?9 U {: r, g; L! L$ w) t1 O# O3 w5 m
if(report==key)
0 {& e2 Y/ B% a/ t( Z {: U5 q' t2 P4 U
report=0;
9 y( W8 v& r; c& ]; z break;; n3 S, ^% G# I$ Y8 ]
}$ k4 {" ?# V% R
}& S5 @: B+ P# c' U
}
- k/ v Y" R* k9 T else+ Z: b6 Y9 P& P7 {5 F
{2 R! Q% V4 e* C$ Q' R- F
for(i=2;i<8;i++)! Y" ]: d5 Z' ]
{
# i! I( V. ?4 l/ n if(report==key)
0 H& Y2 T+ i% ~$ i3 O3 `7 ^0 m* P break;
# b, J0 q) o& }. }5 b if(report==0)1 C. W0 L. ]% [# e7 w8 w9 x
{# `- s, d) m4 w* n8 W; K% n" i3 a
report=key;- b/ z& C1 @, d$ z% \3 |* B- q
break;1 x+ s0 ]0 ]0 b T9 H$ Y5 M3 o
}/ L" C4 U# f6 Z: \# N; \
}+ o8 i( |/ X! \
}3 f) y$ a3 l; K0 W' e
}6 G8 i& _# w6 L3 V9 y
for(i=0;i<4;i++)
& E5 M5 M3 p$ t4 y; l* ] USB_PMA[192+i]=hid_report;4 s# T6 W% P; {3 D
USB_PMA[5]=8; //COUNT1_TX8 d3 F7 k# `% `
if(ep1_wait==0)( e9 C, @) ^9 m0 v/ q* L' `+ ]
{& W! l7 m- Y* H' I0 _ ?* I
USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;" g9 D8 P# B7 h" H
ep1_wait=1;( t$ i! s I- i
}
1 j! |. t% Q! r' S& P}
3 F: B! C3 e# h* r, q( ?
- j3 R+ n F/ e; A* G c# U& t# M: G
7 _3 x( B2 i3 E# s完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。0 ]2 Z$ N. K: v& d
keyboard.zip
(8.7 KB, 下载次数: 6418)
' G; ^+ ?# @: I9 y# R2 A0 A, ?7 F, }0 I, {' _
! W; M6 ~( e) V9 n3 X/ Z. ?; i: p- k4 ~
8 |. k* q1 n6 B4 y/ q) u
|
|