|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 . w- }. Y7 q, L( T
+ K' }1 \( R6 B" C扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。$ f* {" T8 ~# w( u& c. c2 R0 h
( \0 ]4 z# M9 `$ n, u( p7 |8 [
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
8 r* Z& {( M# r5 M9 A" U) C4 W7 N4 A2 P! T3 t$ l2 \7 ]' x% r5 n L' y( G
' R/ D8 d* z+ E: K" B; xconst char hid_keymap_qwerty[14][8]={
- V' S+ ^1 A0 b) J' @& @ {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
& z* ^! c7 ^2 E {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
0 q' P* V$ K6 {; ` {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},/ s6 Q$ `1 V2 _7 W1 [1 C2 D' ]* H W
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},6 O$ l8 Y) X$ N9 @9 O1 ]- Y5 O
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
4 `: d0 y( K R0 o9 _ {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
1 N0 T# e3 ]- q9 j: ^# G! c1 C- ] {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},; j y& X' K l: \* {
{HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
& P6 p, n( |0 H' X0 O {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
* |1 M! P6 X5 [: M/ {7 z {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
8 F# i2 H( p* O) e$ y {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
6 d6 O; z4 a a% y/ A- h9 s g% g5 \ {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
* t4 ^. V( a4 c {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
+ G4 L4 G, k$ ~0 T/ U {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}. M& I" N$ N# `$ b6 C
};
! }* R' w* o- r' ?& S1 _
: w! N; R; M3 b' S" @# ?: p% F# b% p; \% Q z3 g; u
const char hid_keymap_dvorak[14][8]={$ l1 ]$ s3 d$ y, i& L
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},0 l9 J1 R# [ n
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
+ ^" W% w) Z, Y2 @ {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
. v& f" }( }% Z* w& I3 P {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},. x8 M& o' E: q$ \
{HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4}," V& l. e/ s ?' M7 \# ]1 x2 ]
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
4 z* F; }3 w$ G. J {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
* M W( L; [ h) k9 J# S {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},* H& ?0 ?+ n2 V3 E
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},: x5 z ~( e' y$ e3 I7 D# @# ?
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},6 R' K/ D* q! I
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},1 ~ N! j' m. J
{HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
6 C9 P# h4 z7 B* U3 w2 h5 u! \ {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},* | N% \: ]+ F' {0 _
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}- p1 b3 {. E; ]( K% n2 R0 Q
};
5 b: U/ [1 O+ u$ N( B
# f& q3 a7 a4 V3 J& A
% h0 a! K! z! H7 a$ ?( s上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。0 M1 H! Q% J3 X" D) z* `! K# i$ K7 c
9 _. L! e/ g8 B- m
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:. f1 P% [- V, Q5 @' c) U
7 q o4 Z) k# }/ B+ Lvoid update_key_matrix(char row, char col, char onoff)5 _. R# @- r; O' @
{
: o" H4 r* i* F4 {5 X static uint16_t hid_report[4]={0,0,0,0};
! T' p+ Y8 ]& I2 Z) n$ t static char (*hid_keymap)[8]=hid_keymap_dvorak;5 [7 Q e. l' u7 x2 }3 n5 R
9 o5 m) v) N' u. u9 r7 V
) x8 q; }0 i+ ^ unsigned char key=hid_keymap[row][col];
* h$ i% V# \' e( P% h1 y6 t6 Y unsigned char *report =(unsigned char *)hid_report;' l) X. p! P( w) j5 G$ d+ E- a
char i;
2 l& ~; ^; |( c; q x7 Q1 |6 f2 | H5 q0 {* {# _
1 m$ H( ?% R$ K5 \- `3 F if(key==HK_MODE)) k- L* X8 Y* v @ ~ K% K: }
{+ E; |& R3 I7 _0 E0 Z( C* s
if(!onoff)
5 b2 C5 D/ F+ R# w! e1 n: x {$ R3 p4 X* F- L1 k8 x8 S: M, n
if(hid_keymap==hid_keymap_dvorak)- N# N8 ?) U9 D* I- c6 e# f* n
{9 O7 l8 a$ R' I; W" h7 X! j [
hid_keymap=hid_keymap_qwerty;
) i7 n& Y/ o, d9 n, i, h6 B L! f/ q GPIOB->BSRR = (1<<2);" u$ i2 H% F9 s: _- g* l$ P0 ?
}8 _4 T+ o* j' |# ]# i
else
3 o8 g! ~* \+ F! P {% i3 M& ?5 I% r8 n7 v
hid_keymap=hid_keymap_dvorak;7 x. m! K4 o7 w6 p" @. G1 o
GPIOB->BRR = (1<<2);
. A4 Z5 s& R1 M2 J3 i }
9 Q; n9 u6 l4 n }7 f% @. t% U6 R
return;
9 L# K- J) p- F r0 h }3 O3 i8 b, U4 R p/ J7 x
5 z" y" p! `+ h4 [: o
; t5 W @/ |/ R3 X: b/ ?5 J if(key>=0x80) // Alt, Ctrl, Shift6 V; v. f) V6 \" W! e
{
9 v" O5 l3 I* v. p. z! v+ E uint8_t bitset = 1<<(key&7);. m% X- L* b* B# o- }
if(onoff) // non-zero is key up. m; ?- T3 C# `( z
report[0] &= (~bitset);2 H, T6 }% Q8 [, ]. x
else& s$ _" a- S2 \( T, U7 u
report[0] |= bitset;
4 X6 k) J- O" T7 D$ z }- `* _- [+ D/ g1 v$ O
else
) N: A1 n" t, o8 `) q+ ^0 c {
) ~7 X. [! t0 z if(onoff) // non-zero is key up
. s- Q4 C9 \) v' v2 |( d {0 H7 J1 F) b* |2 Q
for(i=2;i<8;i++)2 w9 o: D& f$ }+ {$ A
{
8 Y# ?* C4 u* ?. S4 X9 Y if(report==key)
; k) \: ^& X9 ^( Y7 j! Y {
; T5 q. u4 S4 A7 T! M/ t report=0;
9 ^+ b: U7 O, O6 b! A break;3 a2 X1 N2 L; \% M* y- L
}$ g K( \( G7 O+ c3 B* I8 b; x
}
) q, v( C( o/ {" G+ _. F }0 c3 D$ Y( A! q; j; y' @8 z
else& H) M2 _6 l. c0 l& y" k% \5 S S/ w$ H
{
3 Q. q6 R2 {% {# v& y for(i=2;i<8;i++)
A/ g+ Z; k+ S& s {
& \$ `1 \) ~, Y0 K; o if(report==key)
. A) `+ i9 o2 J6 g9 j break;/ H) K* @$ O* j. M6 t: c
if(report==0)
/ H/ X9 O) V/ V& n( a& a {6 b# J/ m9 B) B' W- c- k
report=key;$ N( l7 V2 y c: ^4 F. w6 _* V
break;
1 |) O7 @! P- ? }
! a+ x* ?, F- S }
, F. G' n( l1 }* _3 M1 j! k }
- s! m* h& G! P. D7 d/ l }
+ F; u! h+ c9 L9 y for(i=0;i<4;i++)
2 ?9 ?( h- c2 M7 y0 C- t USB_PMA[192+i]=hid_report;
' b8 Z, U6 K6 p; T; ^4 ] USB_PMA[5]=8; //COUNT1_TX5 Q( W0 G5 X5 _* I
if(ep1_wait==0)4 p0 Y% G- G: ~
{
G* A1 X4 l- `: }. e USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
7 k4 W: T+ a( z3 @/ W% Y ep1_wait=1;
2 f( N) Q1 ]+ M0 Z }' Y3 ~6 j! d9 a
}$ L0 @2 L: b2 h! t3 `: L
8 j6 U8 {0 M+ i# N
1 W1 D/ _4 p% x- X/ k
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。% [2 z& O _2 @1 m/ W# Y3 f
keyboard.zip
(8.7 KB, 下载次数: 6124)
0 v7 X2 q2 `1 `
+ q1 n1 n: w# X; C0 |( P
: |% y& W0 l! ?- n+ I
6 ^+ l+ Q/ A3 l/ {4 J
! o( i. \0 g2 T+ R/ I
|
|