|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 ' E; H. C# [, v5 k. n! {; A9 M
/ {2 Y( @5 U8 I Q扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
5 w( w: l! ^# \
' d% S9 I$ n3 G6 r$ }1 c要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:5 {+ z1 U. P6 k5 h% `: F t1 d, _
; H0 T. d4 A1 b$ S0 v7 r' W; f; x+ }' Y8 P6 G* L. ^
const char hid_keymap_qwerty[14][8]={
( k- V" [1 \ V: J% e1 g {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},1 ?4 U6 i) Q% I0 o1 e$ g) f5 z/ k
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
8 y8 P. R* _+ I9 g) x: A {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
7 f9 a, G$ p+ f2 E+ s" }6 t {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},# Z! N# v/ D0 ^# ~
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},) h) U6 H" ~7 e$ d% D
{HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
7 f* ?/ d8 g. j# C: g. }% p" T {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
* S& j" p) L B, p+ o" T {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},8 p7 y' ~; _9 u& p
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},! G$ H! N' t [- K1 v" D
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
; E& k& ?0 E I6 c {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
& k2 v$ r7 {, _2 @0 B- b( z% q$ q1 f0 d {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
' v+ [: _8 }/ @" z; E2 W9 k6 w ? {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
* A' m: ~* @5 g6 ?/ P: D& a {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
& A' d" J- \: ~% j/ W, r};
% N, F5 |% d1 f* L, T+ Z0 A- D" n' Y2 b- l" l- h# W
. n' i8 m) k Q9 o/ O5 {const char hid_keymap_dvorak[14][8]={7 j' r5 T' ~4 ^6 k+ f6 g. e0 `
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
; E# j/ d, A, a/ P: ^- v2 K& U {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},/ A% Y8 D. R; {. c6 A: O. D
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
, k# A% E# ?- Q/ r9 e( m1 n {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},8 a) v2 n! d6 ]+ D& w
{HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},+ l7 S Q& R, g6 ^$ a% m
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
4 M6 h# U* q7 y- O5 q1 H {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
! n9 X, {8 [4 O4 q+ k& p {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},( u1 T* }; |2 H P+ r* ~/ J+ ]
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},% }# p8 M$ d" ?* z
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
0 X, a8 ]+ l+ \# E+ _ d9 Y {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},* J; B' |, S, r/ g _" A8 V
{HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
! M3 \' d# X0 w {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},) T* P, @/ G/ w7 R- e) R
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
4 E! G4 J( v+ R+ j* W: y};
0 G: X* ~3 K5 C8 i% b; e
& O: ^; ` z4 v8 {7 i$ E
" ?9 N3 j, Q5 Y: f+ n& z% ?9 ]上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
* J0 o% p" [% Y$ b: p8 u
+ `2 R+ W! i! X" GHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
# M- B0 Y& H. s; x. [7 x: @ K3 J! _$ ^- ?4 f
void update_key_matrix(char row, char col, char onoff)2 _. A C9 r7 R% g7 {) h
{5 J4 W( V+ F7 H' r: y
static uint16_t hid_report[4]={0,0,0,0};
( H& u1 k( X6 k% g n static char (*hid_keymap)[8]=hid_keymap_dvorak;
3 V* n1 C( Y5 r3 W- y7 D' A; O |
% ^) v1 y/ d; d; N( f7 R! q+ h
unsigned char key=hid_keymap[row][col];( o1 i* J+ S6 |( ]9 W7 Z) z0 v
unsigned char *report =(unsigned char *)hid_report;1 P1 j1 A1 K* z) Q
char i;. l: \4 D- r- a2 ]3 g8 S
6 t; G9 B4 ~' v+ Q* }2 f' n0 {
. A, A# Q# j& P! Q( G
if(key==HK_MODE)4 r7 j; k4 E' F4 {, S, a9 V
{
- ?6 b l9 Y9 V- e4 N- b if(!onoff)5 o$ g S& @* R+ x
{
( @" J, h; R: [4 w5 f if(hid_keymap==hid_keymap_dvorak)
$ I2 o+ N6 i8 R9 u {
' y" @- ]2 b6 Q1 @ hid_keymap=hid_keymap_qwerty;) c: P1 ^' \" t8 U- t
GPIOB->BSRR = (1<<2);
! A% U5 F8 g# V! s9 }* {1 y }6 J1 T# H9 S" N3 X
else
: O$ Y+ @" I+ d# w+ J" R0 I/ d8 p {2 e$ D6 B6 x! q, V# k$ P
hid_keymap=hid_keymap_dvorak;, L% Y: t2 J0 Q9 i* h! b8 w
GPIOB->BRR = (1<<2);
1 ?: Q3 J, V' T" w5 y# E1 l }
* `0 } S1 u3 m2 o2 y; i }8 ?" m2 s: P! q+ j9 V
return;+ L/ N$ s; V+ r
}
8 \0 h- _# [. K: Z% k8 y1 P
5 w$ p8 b. ^5 D: Q- d, \7 B3 R) k2 t. ?* Z: z- U& B# a
if(key>=0x80) // Alt, Ctrl, Shift2 J2 m; |( @5 ]: D
{
, h% W9 l9 h# A' ?# u( ]7 c. R uint8_t bitset = 1<<(key&7);
) B- c: {3 `* Y7 ]7 J7 d& H# c+ e if(onoff) // non-zero is key up
w: Y" O8 C" }% _ report[0] &= (~bitset);
. z7 }# y" {" J; M else- r$ i$ t) K7 E# `- W0 u
report[0] |= bitset;
; X* R3 z: M5 I& |$ \) a* [ }
t% }1 I7 W; p else
3 x% w- T8 F* a* A- X9 q" s {
2 f- _3 c8 Q% |7 F# P) `& ]7 H if(onoff) // non-zero is key up
/ _: c8 r: Z0 ^/ |9 L7 K# k0 | {
" g$ h. M/ m7 s' `# y for(i=2;i<8;i++)( Z. [; o; d" U, A$ [
{0 o7 k9 q3 C8 f! _8 N. \" @1 n
if(report==key)- B9 L/ V5 Y# H2 w2 K
{" T0 N4 [' A' I7 g9 b7 I/ n
report=0;
4 a, S! u4 R/ R6 D, ^ break;0 M2 b1 d, j+ f0 Y
}
( z* ~1 }2 E* P$ N1 }8 x& s }
/ f# Q7 a3 r# \* X: H, J9 i$ ` }9 l u% a) D) N# T' u9 I
else
5 j* Y7 M' j2 p4 Q7 T {
c0 g, X1 O3 |% @% D+ j+ B0 j- [ for(i=2;i<8;i++)8 F9 V+ T& u" \) h* R- h
{6 h* |- ~' T( G; [% @
if(report==key)
2 x) j0 Q$ r1 j9 ? break;$ b( ~4 w# U, R$ i$ ]" m, k
if(report==0)9 ]/ N1 z1 n- f. N
{
% ?' R7 N7 C- U7 U' B( u2 v report=key;/ _4 D* w3 W2 Y; o7 b4 C
break;, e7 P- K9 b! h" ?! P, l; j' e( f4 A
}
& p8 a" R/ i( f+ b }
5 R! }! }2 c! ?/ R, Z! d3 V3 _ }( v% F8 H0 N( r! ?3 ~
}
' ]3 F+ |4 J$ k. Q% X# G# U for(i=0;i<4;i++)9 Z" x/ u* f6 _7 j
USB_PMA[192+i]=hid_report;
# H0 V8 @( H5 Y7 \, e% p USB_PMA[5]=8; //COUNT1_TX
; s" U- f. X7 }: c if(ep1_wait==0)- \/ h; c6 z% x5 g8 U W- N9 r
{
6 W X. w1 J$ E0 W ?( o D4 b4 H USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
, F) r2 x- ] U4 U+ K( |- P ep1_wait=1;
# h: t( j& f6 z5 T" U2 g' L }( I `# r! X7 w5 @) z" m/ N C
}) W# j3 {& t( g/ q0 K$ A) }* n
' |4 B1 D0 Y& @( G
# f" W# o) S8 ]% [8 Z, n! Q* N
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
4 i: ]! ?, K G
keyboard.zip
(8.7 KB, 下载次数: 6653)
* [$ @0 V) J. V1 g( U( z
- K+ L8 l9 R5 C9 |' m
) S+ O" [1 I+ T1 A: @
; P* W+ V9 ]( k8 b& b
. z/ d+ I" i! I# P# C; F& K! v |
|