|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 M. z7 M& j& @% U; w
! a3 q9 U: o; ?/ @# N5 b0 C A' x% I$ Q扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
: ^& G( }6 u5 U( C' R9 s. P9 D2 L% k& C, ]6 `0 K
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:: u' }5 D! r9 z" h: w# ]4 f
$ ?; J0 [3 h4 |5 \* s* h4 c7 w# P- O
+ E# ^' C, |4 X' r( Y$ Lconst char hid_keymap_qwerty[14][8]={
% S2 Z; K% }. u# o3 a8 i {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
z1 Y" ?0 v. x4 f: a7 ?% t+ m {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
2 ~" E& h4 q o; s {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},/ d$ R, Z& i) z
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},2 N; _0 y M5 r3 ?' P1 I& o) H
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
" P" v. ~3 V3 e3 V( h {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
/ f9 s& b0 M% k6 C9 n( p/ _ {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
v( U q8 X+ l# X' [" P {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},: Y( Y' x* a# \6 c% `# Z
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
2 N) _7 p% H+ E: W {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
4 s' u1 G! m1 A1 B0 D, X {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},1 D( V) y: u4 E% X7 U$ J
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},5 V: I f4 r T: V" G) O- h' f
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},5 p9 i. C6 X7 ?- v5 q" d& i
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}5 X& _+ \7 a/ H( z6 s* K9 h" Y6 [
};5 M( }" X; o: [+ ?; E* H# P
2 m, T' v9 L' t( X7 P6 L, p- J/ T+ \' f$ C0 O
const char hid_keymap_dvorak[14][8]={* f$ H, Z8 A" V+ e8 W$ l
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},! o& j* W7 @4 X2 E7 m- `7 H
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},2 m+ n" J4 w( Q& S D7 v( w a; m
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
0 z( V0 ~6 a* F% [3 V {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
$ Y! C+ b: n) c& u& f! N: S& \, C' L {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
) `+ W6 \1 j8 R0 R% J {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
) {/ }- {% E1 m' S) V1 N0 V {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
! K8 k# S8 u4 q9 t% H {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
6 m+ j& _5 P9 j {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},2 W! m0 `) P# i* H$ D+ S2 h
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},) ?) }- Y- @) L- Z' Q8 g$ X
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},# z6 P" c6 ~: C9 d* Z% r
{HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},0 K9 N* L8 M f% e, d8 S
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
2 v5 z' n5 F+ Z' v: p9 b( k# L {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
$ g5 A2 Q7 M: Y, p6 {4 H: A7 m};
, k! M7 s4 k2 S$ _$ r+ p* m. j
# r* z, `" }' `- v
/ Z5 {, L( U( A/ Q3 P6 }上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
. }' E# p: H% A# Q, i' D
" C x2 ?4 n @$ ^1 Y6 KHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
9 y o9 `! [2 a8 [
+ v& k0 a5 O( V. e4 I' v3 @# Kvoid update_key_matrix(char row, char col, char onoff)! Z) G. G! V( v# T) `/ Y% [
{
* V% l' y: {3 m. I+ p static uint16_t hid_report[4]={0,0,0,0};
- I) M' I( c& x! ? static char (*hid_keymap)[8]=hid_keymap_dvorak;
5 F( ^7 h# L. j1 y
2 n: ^" P. o- p2 w/ ^/ d7 w. h1 m) C% T* ~' L3 {3 ?) U
unsigned char key=hid_keymap[row][col];9 A A5 E0 G9 j. v
unsigned char *report =(unsigned char *)hid_report;- H3 j1 ~2 F B' i0 e8 N! D
char i;
" d1 s' r0 k1 q# o- {* T* F# E0 n
7 {9 o U% S) l2 S3 a8 {+ R% e) Y% K5 u5 a5 W$ j1 [( H
if(key==HK_MODE)6 x9 Y# ^# P Y1 x k! H/ P4 E2 j
{) O# O4 o7 }, y2 O- e
if(!onoff)3 M; \0 {. B- M
{0 c+ {$ N3 h4 c) W
if(hid_keymap==hid_keymap_dvorak)6 ?$ k# Z+ v! f" {) J' J
{
: p3 \4 p& P, S2 r0 R hid_keymap=hid_keymap_qwerty;
C% |/ i, r: _9 h4 ` GPIOB->BSRR = (1<<2);
h* f* ? q+ L+ K- {* ^3 v% F }
8 e5 P0 K0 w' A3 i& e0 d% X else/ S% C( o* C2 u8 J3 k
{
: ]& E( v" @8 D1 B9 i: L hid_keymap=hid_keymap_dvorak;
9 T4 |- G5 w/ v GPIOB->BRR = (1<<2);. Y! |0 \1 d6 T4 e" ~' l6 \
}
) a0 A. Q$ x2 ] }0 w& c8 | `3 L
return;
/ G! p9 ^7 m$ w- {1 v3 W! g# R6 t5 c }% i4 i0 B( H1 i; d) D! G6 x) O5 r
' P3 h8 ?' \9 a# B, j9 O+ b
7 d* t3 t7 H4 H+ K if(key>=0x80) // Alt, Ctrl, Shift
! k% w6 R7 ~8 L" W w {$ E4 T0 i% K/ m
uint8_t bitset = 1<<(key&7);
$ N! n; a9 b+ a if(onoff) // non-zero is key up
8 p5 N& w! ?% B. E" @9 x6 [ report[0] &= (~bitset);
, o/ h6 f, L8 x) I3 D else
! y \" o1 N6 E9 @! B- L: |" q7 a report[0] |= bitset;
4 L( o8 Q1 K) H a: \ }* K- W1 z9 @6 _# m' t
else
' _7 D( e9 s# t( e" g {
H0 h: o# r. o @6 w if(onoff) // non-zero is key up9 ]. K) q2 Z) q( H$ l+ a9 `6 X
{
. r n' z5 Y8 _" F for(i=2;i<8;i++)
2 X2 J: e) o2 ]; ^ {5 Q, ~+ v! y P
if(report==key)3 J( p+ V) n) @* n/ o. N
{' L& k, A2 N+ i0 E# |% p( w
report=0;
$ d7 K$ H* D+ v, X) r break;/ g( ^( C! Z" C5 E5 M/ U
}7 Q# }3 i' n5 H! t+ Z( g
}
, D% u5 j- z/ d) c }( k+ j1 z, k- N, ?6 C6 B! i" W
else
% u- M: m, K1 y {: p$ t! Y1 V. ?
for(i=2;i<8;i++)8 B0 M) j5 |+ |' w" A
{
0 q& V0 \4 ^ f' |' y) O2 j' X if(report==key)
8 N: {, W( Z& X. ? break;
2 K2 _9 p, Q; F. F: [/ b/ A K4 S if(report==0)) G' [- u, c# F0 [- t, E6 @/ W
{
- v5 W. N: a5 }" _ report=key;% \3 b# B3 J; H6 O1 V% @
break;; \5 T1 e0 p! c0 ?2 A" N: n9 k
}
6 C: C3 \+ q. y' l }
/ ?3 P) w$ L* w1 Z$ T+ ~ }
1 o* l- U. z# h a }, w4 U2 T- v' D) @- h; {0 `
for(i=0;i<4;i++)
& p7 V# D; q5 U' `9 s: g- g4 f USB_PMA[192+i]=hid_report;
7 N, P% E* M. @" k8 Y" @ USB_PMA[5]=8; //COUNT1_TX5 ] K0 {8 I# Z l
if(ep1_wait==0)
8 q5 m: k& b5 E" ^) X3 R$ m {
: y4 Y; D$ w7 M3 @4 E4 s USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
; e, R2 s: |$ a" S* f ep1_wait=1;
6 g) q k) ~* o' @& C( w# E }+ ^/ K. M0 b* [; Z- [
}
$ m5 e5 e# p% Z
( W0 z. `+ e9 i( _# i
G8 g! t. ?4 O& F7 a) K完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。+ u; j6 L n8 @ i, Y
keyboard.zip
(8.7 KB, 下载次数: 6377)
' c* C m" l$ f, f! }' W) A' ^. n7 O5 M' s, D) I
4 s: ? g+ l5 {+ B! c* C! _0 |4 X
, W* L! c* V- m: k
7 N5 O0 [! i4 ?6 } ?. b5 G! h. |# ~
|
|