|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
+ {) W; w. g+ E! t, x" M$ `3 q1 c
, X8 g$ a& C" b4 S$ {扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。; _6 E4 E5 ^* }# d
) C' l& Z; ]6 L' h2 Z% `+ B! q% c" h要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
^' h+ }* y: w9 S4 ~6 C
8 w2 v0 C% x: e3 ~7 r6 e5 f1 S8 g" X
const char hid_keymap_qwerty[14][8]={2 ^1 r7 }/ _8 Q+ s; b( u
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},4 _. {% @; b Y
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},6 t: y+ }; M1 A1 y) b8 @
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
4 S* F p# |7 w% ?6 r7 ` {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
# X. q2 V4 w, N! ]% z {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
$ Y# B4 n- P- ]& J6 w$ u {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},6 }8 J3 l% e- _9 Q1 j' e' z
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},& V; B2 {2 y4 h% N q
{HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
# H8 ]- e4 ?& s {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
; @& w+ x* W8 W( _0 I$ j3 A {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},* x: P) c% t/ j1 m0 p
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},$ l0 N, P, T5 @3 y$ U* L
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},3 B, h7 h8 W) f: C
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},; l0 B: k& @2 r$ W: D# |& o
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
2 D/ D- e5 ?9 q( l* ~7 C. h5 m" i};
" \* P$ Z8 I! u0 ]8 }3 `0 l- K$ Y
; R3 u( G: ]3 i, G8 k$ v- C) z2 {1 y0 \; p# A
const char hid_keymap_dvorak[14][8]={* X+ R1 p% u6 t& q& z$ e
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
1 O; `9 E# R' k6 q: i {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
: F4 X4 @* ?- W) |" M. c- m+ V* Q {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},2 ^& T) q( L4 S( K) ?: x
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
* Y7 n" Q. E7 G% h {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},1 v v, A% c! M Y' v; e" }
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
; x" v8 p( N- G7 r `# f# z5 r {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},9 B( w: v6 W. {/ Z+ E
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},; q( v0 d( N4 n& S- ]3 D3 c( D+ b
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},/ T4 K" @* o: R* |5 ~2 A
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
8 G- x8 o0 m& l# ~. w {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},1 Z- |( _- q w9 a9 ^& ?
{HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},) h# u' X t" g5 y
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},8 j$ h& q3 A8 Q1 H8 G8 n+ G0 S
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}) X' _0 ~. q- e6 `" a
};; s5 h4 O! T% _" i& u- n: |
4 \3 k' L' v& r( S( s. h9 ]
/ q" g$ J; K9 X( M; H$ Q" B- o上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
5 e) z6 e8 d; `/ s- C% _9 @) h! s; A* n3 K1 o: |$ X
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
- O4 k% Y: M: g( Y) ^
5 |. u8 K3 v5 P4 ~0 C9 U( Tvoid update_key_matrix(char row, char col, char onoff)
; q" n1 y' ^5 C a{
+ N5 X. C- W3 l" R, M! H) b static uint16_t hid_report[4]={0,0,0,0};
& d$ F/ k/ \1 `8 _ static char (*hid_keymap)[8]=hid_keymap_dvorak;3 H' c& N( \$ F+ {, J
. }6 ~" B2 d& ^3 H
4 L3 H2 D- N% v0 g$ f9 d# {2 u6 o unsigned char key=hid_keymap[row][col];# Z. G- E! e. P; s
unsigned char *report =(unsigned char *)hid_report;1 d* [: f% p, Y% a$ g
char i;
4 ^( |$ T! x6 r6 P e4 f, Z% B4 H& r0 t8 k' J% G7 w
M$ Y5 h( v- v. H0 ~' h+ w- H& T if(key==HK_MODE), ]4 X' d4 g7 I" ]9 e# g2 d$ b! ~
{
4 H8 D# T3 W' y. F0 z4 } if(!onoff)* v- m% b0 d9 C. P3 ^
{
) F+ F3 [ ^" F* v5 Q" ~* S if(hid_keymap==hid_keymap_dvorak)/ i1 @9 E# m' B( B6 H
{
5 p7 b5 `0 [5 c+ q, `0 X hid_keymap=hid_keymap_qwerty;* h0 ~6 A5 M& K6 f
GPIOB->BSRR = (1<<2);
3 b% J& S* m- d: S& k' K/ p1 \ }
( K- A( c6 O- `8 o0 [1 [ else# r' J' O9 P9 `, a! a4 y
{- d9 H0 W+ K7 R8 f q
hid_keymap=hid_keymap_dvorak;8 g9 y! D: U! z, W, u' T% X9 R! d- _+ Q
GPIOB->BRR = (1<<2);
+ Y- i$ n1 j) o }% H) `2 E9 R( i0 k$ J% u
}! A j( j- o, ]/ M- h: i" O q
return;; }1 V# P+ m7 `0 I. Y
}
( u5 }8 d4 h" ^/ {, R0 _$ ]
6 W0 U' v$ L ]; h* I1 g8 C4 _
3 c3 ?; F! G9 M1 I( ~: y, r4 m if(key>=0x80) // Alt, Ctrl, Shift
# W1 d/ z, j1 f/ s6 n {
! m. D! E1 }# ` uint8_t bitset = 1<<(key&7);
: [' ~8 s o! @6 M* X if(onoff) // non-zero is key up
9 d& ^: x+ A/ F4 a" Y report[0] &= (~bitset);4 f5 v2 m* f/ B. Y2 r
else
* |& s$ u9 a i9 Y. ]4 n report[0] |= bitset;6 @8 H! G/ k5 |4 D8 W' K8 g) b; h
}9 s/ a; X$ ]( j# `, Y v
else* l; ]' f& Z, J7 C s
{
$ K+ v/ z) s6 r+ ] if(onoff) // non-zero is key up6 S) b5 x/ f$ ]% a: i
{6 @* {( o. ] H
for(i=2;i<8;i++)0 k# [0 o# @# d" n4 E5 F8 y @
{
* h5 A& ?6 h6 ^1 w9 D* X if(report==key)- H1 j& p/ Z, c# J/ l: y
{
! R/ N: R: M% P3 x report=0;; v1 B( P: q9 v* l5 c3 G0 b1 i
break;
a0 |8 n+ E, \: C' h9 S v2 o5 Z$ b }
6 w: x7 N* V( g" Q$ P }
5 l. B, a% j: r' G }
* {! h+ n% o h8 n# V/ a% u, E else+ P7 T Q+ T J$ w9 _. }5 d
{) y6 @( B: T7 ^0 b6 d' R
for(i=2;i<8;i++), L, S( @. i( A. P: I3 I
{
+ _2 S4 _* `7 a8 o( P" m; L: y) } if(report==key)
; w3 c4 x5 e9 D3 T break;
5 Z0 J6 R. c! ] if(report==0)+ b/ P7 q" ~6 |3 A# j
{
x/ G" T* \2 Y5 s5 w1 p4 G report=key;
: h! d; v8 K8 r! A break;. `+ ^7 [' N, k% d9 e! q
}
+ i% B7 d2 ^! n% F; I }5 S0 H0 ?+ ~2 c1 p7 Q
}1 V. p) H4 ?6 }3 L, I/ l
}
g. m, Y% a2 c! S for(i=0;i<4;i++)
! u" }5 e( f; j/ N7 D USB_PMA[192+i]=hid_report;. b8 f8 q/ Z* Q+ M; v
USB_PMA[5]=8; //COUNT1_TX
: Y5 w0 _6 g, A, H9 ~0 O F$ N if(ep1_wait==0)
* O7 w1 j! `1 D: G$ g {' M! g H: ^$ b; `
USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
, S/ ^9 u# ]( T h' U ep1_wait=1;3 O% k( w7 g! W6 q
}
, X$ z0 E6 j- r' Y) A0 L E. ~}% Q9 x/ m1 c: v( i3 g& B+ @
! P f7 G- M, i3 x! r4 t3 j [4 d4 [8 ~1 w8 W$ K% S% s
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
% }' x& C R4 ?' |6 Y9 p
keyboard.zip
(8.7 KB, 下载次数: 6302)
2 [: g& U3 s0 c) x$ X
6 ]4 y1 H$ J* X/ ]& N& B1 e/ E, U2 I& K6 k2 r ?% s+ h0 C
f1 I6 q0 j: v+ g4 O, ~- i: u
; N1 k1 \. C( h. u |
|