|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 ( [% V1 d1 e* i6 w1 @- r- g
, {+ \9 R/ L( o1 M1 a) v% _
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
6 q1 z9 l2 \6 X* @" B d$ M/ N' C5 o$ j; }: N; [2 p
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:7 Q% m* |! a# m) y, X( H% p5 X
1 F$ T0 n/ l' Q5 `) i W" c' c+ f/ v0 w0 t
const char hid_keymap_qwerty[14][8]={
% l/ j# p6 w- n0 `6 p6 K9 [ {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
6 X# X9 l& E. y. Y8 T, M {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},9 L" k( R, i4 N) f& U
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},( T8 O$ S) `. l% b4 N" [
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
$ J2 t2 k" [( X4 t- ?! X, K {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},6 u' I+ @/ g( p2 i7 g. v3 g/ H5 |* i
{HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},/ |+ ~1 E7 U# D4 P0 n
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
: C$ p: v+ ^4 |8 V0 o) e {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
: ^* r( F( T* g6 h4 A' D& K$ |6 ~, } {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},$ J# n3 F# e/ c
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},* a; z" ?' c# [) F: T
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},% |* `' @4 t( H. l+ x% p6 i- w) V$ H
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},' m& Q4 k! p0 H% r' E- _+ S, Z
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},6 O' F7 Q8 p3 _( T0 \9 x$ ^7 b
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
5 e7 k" \1 p0 U/ D};2 i" D6 U+ Q% S& o( x3 g2 [$ ?% \
, Z1 o' j! t( ^3 Q! M
# A) v, D( p! x+ i
const char hid_keymap_dvorak[14][8]={/ h. t& |. p/ m$ B
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},# u) i7 c: J) f- B+ A8 _+ [2 q5 l
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
5 v, P4 S% \) z: C6 N {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},2 `5 v6 l# \2 @4 @; r" ?- k; I
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
' M; h" V2 K, A& J: w# G" `# ]) P {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
# v: b3 N* g* Z5 s5 H! W) _ {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
2 I% |) D% g, n {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
, k3 O% [4 i8 B" ]; | {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
0 _8 E; [1 u3 s0 G1 F; T( O {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
2 q/ e$ X& X" J4 B: n d! W0 l {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},( i+ m0 b3 u3 |( ^: b- ?* t2 L
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},* z! O! s4 x4 \, o/ \% D& x, d
{HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},$ {6 P$ v0 \7 H+ P- p
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},, ]1 {8 c# V5 W$ J
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}% f) s3 r9 w$ P; f3 Z. H! X& L
};% K- ^: Z. q, x3 k" Z" D
, g# x) @* z0 s" x c' [
4 e! N& {9 u1 J; q: n. ~6 I$ l
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
# K( n# N+ r# i
7 o0 j$ M3 K# s+ K( [- qHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
( [4 O C5 Y% N" P5 |" y' [
9 H* P8 T, _4 g3 N; \+ L, }void update_key_matrix(char row, char col, char onoff)
7 |/ M+ e, M9 s3 z5 d2 s7 {{* _% i* B W- m
static uint16_t hid_report[4]={0,0,0,0}; T/ F5 E# F7 M M7 d) D/ k
static char (*hid_keymap)[8]=hid_keymap_dvorak;( W" k7 P u( _/ ~) r
# L6 ~# O) o. k* ~" Q7 H% j" k
8 U" i- T5 d+ j unsigned char key=hid_keymap[row][col];. {" R5 q- b5 M
unsigned char *report =(unsigned char *)hid_report;# q3 l) `" E& D9 G, V5 Y2 p: {
char i;" c9 y, m' [3 n0 [1 r' e
3 s$ B) l" `# |1 m2 Z: O% V$ }9 N
: x+ M) F# Z* Z+ x
if(key==HK_MODE)
; o2 S1 _6 x* z1 c- o {
# D/ L7 ]4 K0 t" |$ e- g if(!onoff), P( v( y0 F; ^
{/ Z8 Z' m- F3 ?" W
if(hid_keymap==hid_keymap_dvorak)" p) `; ^( Z6 g: l V
{
8 J, H: G9 j' F. j4 u hid_keymap=hid_keymap_qwerty;5 P4 a" f' D+ \4 D
GPIOB->BSRR = (1<<2);
- @% f0 G2 V0 k1 n: } } g: Q; Q) y) ~, ^$ B( m
else
: _ C8 M( I7 \' q4 o {: j5 D! L; I3 }% r9 E; ?
hid_keymap=hid_keymap_dvorak;
( k3 ^3 ]) h) ^" n! D9 [3 ~7 [ GPIOB->BRR = (1<<2);
8 c" e" E0 [/ p9 y$ L }
* s3 H. ^, ~! q- g! d) B }
4 j$ \3 J) K) F- D' S! q return;
% K' D B$ t( ?8 v* s! G }% r7 {! A! H# \
e$ X) M5 A/ Q4 { ~) w3 ~5 ~& k
$ z. Z3 ]" H( H" h; |
if(key>=0x80) // Alt, Ctrl, Shift
7 a2 V7 V' v9 t `* A& q {
: @' ? V6 k7 ]3 {0 s2 M3 C uint8_t bitset = 1<<(key&7);2 G4 V5 b5 k4 z# x! ?
if(onoff) // non-zero is key up( l0 }( t" _* _6 p
report[0] &= (~bitset);
8 B* O, b" s, i& [ else
: Z0 g* q Z0 w3 h9 i1 \ B9 \ report[0] |= bitset;$ t9 H U8 P6 `6 G
}
. q) r% c7 y1 r1 J3 i% i else
% v, z( h3 d. j {
& Z. R4 @3 l( c" l+ T$ A9 T7 M# ]/ k if(onoff) // non-zero is key up, _/ ?4 u l4 t, q5 }# T2 K
{' `* ^( m# J7 C: N9 K' f* D' z
for(i=2;i<8;i++)$ M7 l5 [1 z$ B1 j, X" }
{
' w% [7 g5 A' e k- a if(report==key)+ M. V1 c( h2 u4 o5 T$ J: F
{
* L9 ?( S& ?4 V+ s3 u2 `( ] report=0;! e% y f7 w4 @
break;% F) \: j w5 z. O$ |$ E( n
}
4 i4 C$ E' h9 m5 w9 M8 f. d }' G- l7 M0 k0 H% c8 X; B! E1 n! z6 T
}
9 _( |; I) |) Z& E- E else: U+ n1 B, S: B) ]
{! V7 d1 l3 y' i- M
for(i=2;i<8;i++)
. X* b9 ]9 w q p1 q5 a {
( O: p% e y9 O6 x" ~: p if(report==key)
) v b4 v0 {5 H' n2 _# m2 {$ u break;7 v' G9 l P. r5 k" z* p1 f
if(report==0)
2 L J% q6 c& p6 r4 E1 b3 J {
' Q6 j+ ~9 E3 C! r: R report=key;8 q5 ~% J- \4 D) N! c2 w0 A
break;; t; L+ V2 K1 O- [; u* B: X3 m5 q
}: h* L! m' p( X. b
}
" a0 A7 p% ~; K0 q/ D6 C }4 n2 @, U- F- |
}
& Z2 V; w3 W# M5 C$ M# Y8 Q for(i=0;i<4;i++)
* O, @4 U8 I" F+ \ USB_PMA[192+i]=hid_report;
}# ?0 d- `, i6 Q, e( C USB_PMA[5]=8; //COUNT1_TX3 c/ f% e& b4 q$ Y6 a! x0 j7 U
if(ep1_wait==0): g3 v0 A/ p! O: I
{: z: c9 c4 j Q J# l4 D- @( Y
USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;' n7 P9 n, g* o Q
ep1_wait=1;
. U2 u. b: I6 y. C* W' d }8 c0 J/ s8 L; j
}
$ Q2 O, {* E' R+ @+ H0 I' d* L; M2 t6 _% l# `, u u! a* S, M( ]' G
" S) {- l6 g* `7 T- ~7 H3 P
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
0 G. D- Y* n/ t0 h/ c$ M c
keyboard.zip
(8.7 KB, 下载次数: 6036)
: [' Y9 M8 j* Z; A; {5 ?8 i' z+ {6 h4 S' H
! F0 y8 N+ T' `6 N+ G' I( H3 s8 D2 J0 A' U6 f
3 Y6 j: {* v" o* K' C# J9 W5 I |
|