|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
* w' e* N+ [- p- S" m+ c: P h8 [- Z: J( P X% `- k! x
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。+ r. K3 C. C% G% }8 c2 c. ]
, \# Z7 {7 f. g要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:3 G6 Q" P& E2 G: K
* ~9 W" [+ G! W1 y6 A
# W. P. J- P& t' c* [$ E+ iconst char hid_keymap_qwerty[14][8]={/ j) o' F3 @: S8 F
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
5 ^, b4 ~! g6 C, E* |5 A0 D* D {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},) @& W2 K J" u! Q; g/ z1 e! |
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},2 O! f2 u$ B p" c% y$ P% B+ ^- E1 ]
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},; Y- D% d/ B. K1 M _; u
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
9 Y5 o% E" ~7 u) w# x% `" R {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},- S" L3 B/ B0 k# H
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},$ N) f$ J! R; d: F+ Z0 ?
{HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},' j! A4 O( w7 i- K
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
" r3 t0 D. _. ^: V9 L% |* } {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
8 K! C' A- G9 y& T" v: k7 c {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
3 b$ ^: p- I9 X! }; s; F5 r- j {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},! l& x% q, C2 B" v4 X% u$ @
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},4 v9 B* q1 y7 c
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}5 T, Y/ Z1 K1 l. Q# J, H/ `* {
};3 P1 y4 R* c# V! Z" j% n
0 M. A- ^' r7 ~6 L# W
' J% \; y' z( C* `const char hid_keymap_dvorak[14][8]={7 v8 P2 g( A5 Z2 ?, }9 f) |) f9 Y
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
s" m2 j ?% t# j4 k) l! c8 A {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
" Y3 ?% i2 n6 T1 ? {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},! q) i8 s/ ^# v5 L% T2 l
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},/ y4 k6 A' B) Z/ n
{HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},' q! {$ d% c6 h" |4 {* a) L- p1 \
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
2 E& g4 ^& Y7 m: {- W5 G {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},. {/ M( E7 r. {6 p. z5 Y, S! }
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
2 D1 T1 T6 x: s {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
, g5 A/ g. x9 A6 l {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},+ |, y8 Z6 i% }
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
, Z3 M% V# Y* ^4 c {( U! ]! j0 l {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},7 a0 I' X# l0 K7 R3 a; ~; H
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
! ^5 ^- n: \- K1 e6 z1 p+ M$ q {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}% h8 G6 g. k; [
};9 O& t; e4 k+ K- ?2 J7 p" }/ \
! C# s+ R9 p1 K+ \' [, q1 J$ q
4 u3 K: T; ^$ O3 p. V上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。! Q. \. T6 T: g$ i- D! g
4 V4 F3 e% p0 j) s: @+ G
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:6 h2 y7 Q* |2 S
6 x- V, K, h' m* X7 @' jvoid update_key_matrix(char row, char col, char onoff)
$ U0 j( J# T! y# \8 p{
4 V1 K/ D4 b% I+ Y3 V+ e static uint16_t hid_report[4]={0,0,0,0};
5 {; ] g. ^& R static char (*hid_keymap)[8]=hid_keymap_dvorak;
, A2 |; I3 k _, [7 U" k8 h& i8 Q2 b8 P' b) r/ \& U$ u
0 j1 O4 K9 l4 A
unsigned char key=hid_keymap[row][col];) l: G/ l6 O1 l
unsigned char *report =(unsigned char *)hid_report;
6 s5 q+ k$ ?+ n- P0 K7 }' u# _ char i;
5 J5 ]2 w; T- }# N) g3 ? `* V2 B8 c
. k% i1 \. y' d8 z' L4 y0 h if(key==HK_MODE)
: v% G- P/ V a7 a7 x M! H {+ W: r( }4 z3 V9 H* o& {
if(!onoff)% z( T, w$ ~& e' p( i
{
: v" C4 i3 F* Y5 o if(hid_keymap==hid_keymap_dvorak)8 M7 {# e$ z0 M
{) b5 L; A8 d; Y
hid_keymap=hid_keymap_qwerty;
9 B* b( Z/ v/ t" E4 e GPIOB->BSRR = (1<<2);
$ {% L8 c% |% }* u+ M } m+ j0 ~1 O/ H3 f! l+ ?
else8 l2 v, D+ W' j/ w: T$ E) k) K: X
{) f' P: x1 p" g1 }
hid_keymap=hid_keymap_dvorak;/ O3 n9 b: t' k: m9 a) S' ^, {
GPIOB->BRR = (1<<2);
9 ?% T' i$ Z+ \' A7 {7 N }
% _ U) V' r( N; _7 |5 P }
1 S! \% v! f5 z7 C8 G4 X. d return;
0 A6 c+ f$ O* {3 U }0 Q+ B. Z/ W, z+ Q
" W1 b* d! a( E, `( r7 q! R
1 M A* |1 \" B; ?4 Q if(key>=0x80) // Alt, Ctrl, Shift) p% X5 p' B6 \; F/ Y+ b1 ]; m
{
7 q5 P/ d: I8 t6 [: Q6 |( i% [ uint8_t bitset = 1<<(key&7);1 @7 W5 `2 U& ~1 ? Z V+ z! N
if(onoff) // non-zero is key up, T9 _1 _/ z, [5 z) N
report[0] &= (~bitset);' i+ F$ ^% e' b( `! |% V+ U
else
$ m5 i8 J& I) ?! H0 [ e report[0] |= bitset;# P6 p" d; t5 c R1 k
}0 }" k& v \8 A
else
$ A6 n2 S$ j8 j z {3 _9 _0 {! a2 I! C5 N5 H c4 Q# O
if(onoff) // non-zero is key up H4 Z6 g Q- a1 D. c$ u
{
. W% b$ K+ O- V+ H# U" _! N% z for(i=2;i<8;i++)
4 K. t1 w3 k4 a {
' r: C1 N. X* g) w* ~ o if(report==key)# R1 w' i; ^8 i: `
{
5 \& T# ?+ Q( q' [( F+ u$ J; c; Z4 v report=0;
/ D; k9 k+ v/ N% D8 r) r& Q5 L% c break;
5 N, J& W9 s' w4 N4 ] }, \% r4 S: m& k r- d
}
6 l4 s& u" o) X7 |: _! D+ o }' W3 Y) H; N9 c4 \" `( _ G
else" y- o+ _& e2 [
{7 U' |1 y9 n$ E7 X
for(i=2;i<8;i++)* B+ f8 T1 Z( J$ Z4 ^ s5 n L
{% B+ k+ k @9 Z
if(report==key)2 j0 x4 k' J( n) X- |/ p1 j
break;
; E' v a. z2 v6 g if(report==0)- q/ @# P. v8 Q
{
- J2 F$ Q# }* f' s report=key;
2 a4 L( H# p; W# ^+ x" |) R break;- f& w, k* e; d4 T3 ]) z$ Z
}: n+ ]) W3 c( q+ V8 e
}
" p' |, r4 V. D3 E% S1 x }
; u0 A: Q( C- p }3 a5 S6 M i. m2 y
for(i=0;i<4;i++)3 L m- s( H8 p& V
USB_PMA[192+i]=hid_report;# a4 d @/ |* S9 j% t& _4 i# Y# I' U! [; w% i
USB_PMA[5]=8; //COUNT1_TX% r+ i1 K; e# b8 q
if(ep1_wait==0)
. U) @: A$ J) o% u: K9 h; U1 R6 {; J {* L6 i& m6 B: e _) h+ W# c3 N
USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
( i+ `8 e- @# S- m ep1_wait=1;' s y g& \+ T, K
}9 Z0 J0 S# x5 \) U4 _7 S
}% L3 v8 p+ W& Y( _; i- r4 \" O
g% X+ n/ G- M: L
' t: J$ E" R( t
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。; c, b& Z2 k$ Q
keyboard.zip
(8.7 KB, 下载次数: 6064)
* _2 v3 N# E9 N
: E: N) ~3 p9 K3 \2 E* j' ]( `7 K
& }9 |. h8 Q% m2 a, i! B/ a9 H1 N2 h5 e* _; l# Q8 j( h
, b+ s! z! F2 S) b9 A" @ |
|