|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
p: K) @0 @! y! e
p) M$ l1 C/ ^4 Y+ D扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
9 E% B) Y; ?! K, X
; L) v o) _4 L, q" f5 l. e5 ?要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
) t" s: h+ T5 q4 B
- X" L7 X/ m1 F8 ?7 `* e* e6 M3 u# t3 E0 C
const char hid_keymap_qwerty[14][8]={6 q' C+ u7 u) [2 Z
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
' R# D* g& z# L% | {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},8 J3 m+ j* ~4 @. D' { v O
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
0 r% L- v; C: V8 b! c {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},3 V% |# F5 v8 _$ X! f) f6 Q' h
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},+ s: K5 \ D! I( s9 U6 ~% t
{HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
9 e, ]6 |8 b5 ?# V {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
1 r9 q) h- e2 T. q- Z4 H7 D: Z5 z {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},, u7 Y& P- r+ y
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},8 ^5 S! ?6 I- m3 ~ y {# P/ j7 H
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},; z$ e1 P" Q7 I8 l, J
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
# N; h; o8 X5 J0 E {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},& M2 C+ ?8 S! `- d2 _4 Z
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
9 T) D) G+ G8 K% ~ {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
" _4 f2 `. h* X z};0 R. W8 l) Z0 y7 V5 [; h8 o" w! N3 ]
/ A; y- y1 g6 D2 E J
; T$ ~# b* {* ` y6 j, Pconst char hid_keymap_dvorak[14][8]={
- \! s: R) N7 ~5 ` {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
! ~, B ?/ S. x9 {/ B H {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},/ f0 H' r/ g7 _
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
; ~. f3 O9 S: U {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
! j( u$ e; B% O' b& c4 a {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
G# S# ^: n- Q; g {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
/ q1 ?- r t v* t; I l! M {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
$ G' f6 u5 G+ A& _' y9 D {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
9 E2 l6 n" J- p2 ~. C {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},- n3 j. J0 B5 X% D( g8 N
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
: M$ p7 x4 J. o9 Y3 h& ]! J {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
. S6 \- J d( y9 Q( g+ U {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},( N1 d: r* |: ?+ `5 o4 d* t
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
3 g0 o% r0 k! x3 A' \$ x {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}) P7 @8 ]0 h7 ?
};
9 y1 r/ J4 d' W" _# ^: K- N4 _) @+ i# |
- Z$ e5 l0 h f& r: f5 C上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。' j' Q, Z0 c7 L1 m9 g: O) r5 w
; p! j+ N5 g# H( c2 C" X# {& aHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:9 B2 G2 a. R6 D
3 [% h' j8 T5 k; T
void update_key_matrix(char row, char col, char onoff)
6 T" x5 t+ V8 f; ~6 a{/ g* P+ i) l8 x8 I4 o* Z7 ? H- P
static uint16_t hid_report[4]={0,0,0,0};# S* k0 C% a5 N( l% q
static char (*hid_keymap)[8]=hid_keymap_dvorak;9 Y% Q- R$ R2 U6 v% K
: S4 y4 r0 \: h
" Z* D# I# i) X, |0 b
unsigned char key=hid_keymap[row][col];
( j+ ?8 ~. g/ _+ l4 Y: Y: V unsigned char *report =(unsigned char *)hid_report;
- B+ V; y8 t/ L6 o6 I# J# W# Y char i;
( l) g& ]& f1 r& I9 d8 ^5 T
- A, j& Q7 d' N2 S5 W2 F
8 D2 S0 Y2 @" p+ P5 S2 v+ k; _* i if(key==HK_MODE)) W1 I7 W9 E" ~$ N: a; [/ _
{
; ~1 J2 h+ K, K3 m, v4 K, H if(!onoff); k. C0 ]# {, D* N) l8 H
{( V" C! B* C9 {' D& u- n4 ]0 B
if(hid_keymap==hid_keymap_dvorak)" `& m2 e3 [! t, |7 d' c. `' E
{% l& F3 E' I# K, a( j3 a( W5 s( l
hid_keymap=hid_keymap_qwerty;4 V2 q: b: h/ k
GPIOB->BSRR = (1<<2);- C: w0 `! O9 e4 C% \
}
+ a. m# c! V" U# d0 b else3 i$ d$ _/ \1 a- T* G: w: J7 u
{
* ~4 `1 e) v# G9 h hid_keymap=hid_keymap_dvorak;
% D4 X( Q" i7 M) ?7 n" |: \ GPIOB->BRR = (1<<2);" P8 _" X- f- f, I- `% t3 u S
}
1 m; H5 i! } R* e- ~: i6 e }: H; u+ W0 c( v( w$ i& U# _) m9 c5 M$ _
return;* m0 s, z, x' @
}+ Y0 F! { o# Y9 z& `+ ~
% u$ O( Z2 ^/ E% U4 G( K9 R4 q, ]
/ q$ R9 n/ z( H% F if(key>=0x80) // Alt, Ctrl, Shift
' o* j1 ^* V' {5 K, B {
+ Y# u! M8 y% {% t# }8 j uint8_t bitset = 1<<(key&7);
9 L3 ^- \$ A8 x- J if(onoff) // non-zero is key up& K$ T6 n5 S1 A
report[0] &= (~bitset);% j# f9 c; ?/ J: X4 |
else6 g; }* e4 G$ y! {7 \1 P
report[0] |= bitset;. M" c+ c9 T/ [) l7 A
}
& g" g8 s$ ] X4 u else
( {% `. P2 w6 n' V0 ^8 Z% U; h {
0 x" O7 j3 z5 b* K/ x# M if(onoff) // non-zero is key up# T# p- v$ T6 s. [: ]
{; Q9 x0 p0 t ?8 j. c6 \
for(i=2;i<8;i++)
1 w8 J' [7 n' v, h' @; x6 ]6 P: N {. j F2 @! T* y# H% ~! {$ J
if(report==key)
0 U% d4 ]: p5 ^: A {" ]5 R( t- Q3 y9 N/ l$ ~ T
report=0;
# i6 b2 V& s% j1 b3 q' u* f1 N' v/ _ break;
$ L( W7 c) A. D }
3 ]8 E0 W+ z3 y$ g% u& { }) A0 j7 i: F2 J7 G. i3 P
}
# R" S" V6 b2 o, Z" h7 t else
* `3 n2 l- h: w5 N) e' c$ G {
7 J8 B- g) E1 |( l! s# ` for(i=2;i<8;i++)3 Z9 D5 D$ o& z* q% Z
{" O$ c: f) p2 [) a5 m5 y1 a
if(report==key)
5 ^7 J+ G$ w2 L( o. k5 M4 R6 k break;! h) E9 q1 b8 J2 a8 t1 A2 O$ t
if(report==0)
# n$ K' D$ A9 r+ E# T* n% G* v {8 \" d! |: U6 m6 E
report=key;! }$ ^. `" {) Q4 {$ V. e6 Q! O
break;- ^" x: w8 G. K, w7 o
}; I# O& z$ g, z v1 t( u* |9 ?
}
, y7 @: n& w2 b5 S }
& V' m* n" ^' u( }9 x7 J# ? }. B7 @; ^7 }+ E: @
for(i=0;i<4;i++)
) \- v4 y. Z9 z7 d; L# u( B USB_PMA[192+i]=hid_report;1 M' H/ q+ _; Z) @7 F
USB_PMA[5]=8; //COUNT1_TX' N) J& E/ Q h9 W# Y( p6 e2 h
if(ep1_wait==0)* M, ~5 m/ u$ g8 N0 r
{
9 u* D: g1 a9 ~. P USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
K: a+ \, k* m4 ` ep1_wait=1;; I/ a8 t2 u- O( s4 D
}
: G5 n8 _. \1 [! c# p}& n# [; m' C7 M7 y9 L+ P A
4 j; T5 Q+ u/ u8 c+ E- \( u' N% ^5 `! _
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。) |( t$ k# F8 b( h, g: ?& {1 s# d
keyboard.zip
(8.7 KB, 下载次数: 6442)
- C1 d; b( f0 r: o$ I' J3 A; s$ F6 }) P( c3 A
9 X, E7 t( Z+ k+ N* o
2 ]. q+ X0 j9 m3 v
/ ?( C! n4 R* t/ ^& _5 k- Z" N |
|