|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
- [$ u% B0 ~5 G/ B
5 `* b* g# H- r扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。. b+ P' b% S4 W
: \( I' P# P6 a6 z7 A" v
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
# n F! X& l* z% z6 @2 W4 }! z9 u6 z' J8 s. f$ ^ j$ \7 ^0 a- j
# L p/ x) w( [2 }% i4 l1 tconst char hid_keymap_qwerty[14][8]={
; P G0 X3 h8 M. F8 M! \ {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
3 x+ [+ Y0 u! d( t# | {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},# T4 h8 X+ f$ e; L$ d# r
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
! G& E( n# Q3 _+ C- d9 s7 V- R/ q {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
+ U a. a. Y2 G& h' F {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
6 \3 a5 G6 x! M2 d" D {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},6 `3 G. F; x2 m% o4 X' |
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
6 Z# g5 G3 a2 W' I& g {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},3 K( N- E+ l9 E( ]! U; t& Z' B
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},' {$ C7 r0 B! m1 N
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},0 N" Y! z7 Z4 b/ f
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
/ W( Z; l( g9 q# g3 g/ { R {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
/ F# t9 V [: g: i' o/ q: @( z {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},; T) w+ y- [$ C- S
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}6 z# x. B9 W" Y5 x% D
};* e0 \* g: v3 C, ?; B- c& G
: R- V. ?+ k3 r
9 j J0 x6 m1 c6 `
const char hid_keymap_dvorak[14][8]={1 V ]% J0 A5 [! Y9 _: ]6 F5 A
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},- [5 ~5 V5 \( W7 X; v( _
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
: L5 [5 L5 e7 w3 l7 q& n Y {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
' T3 Q0 N' z# c* i7 S% d7 e {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},5 y3 ^; q$ W5 V3 h1 `7 N
{HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
5 _8 X5 ~ {* _! _3 X7 v; W; ` {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},8 C' @( _- H! {2 Y" k! N9 D
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
* [6 f3 n% n3 p {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
a ?+ ~3 F C l0 x {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
* [9 n" m$ j% I7 l- U& Q8 v" Y {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},# x" r6 i ^ C" b0 v0 T5 t; P0 l
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},. e4 i# s k( c2 ~
{HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},, v% ~( L& X4 l' N! g% e1 `
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
5 r0 z# `; J3 W d% @4 t9 |) J {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
4 p! u* J8 F& c" ]2 G7 d# R};
2 K, Z( x9 M$ T: k7 E1 X9 g
5 R. w, W, e, m8 q( c+ H% N. L4 K3 G1 M _! X$ B/ Q, A
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。$ ?2 N! o+ B/ r; l
$ d1 m* b/ O) I' O# CHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
) y$ y( z: T7 b! g. K4 ?/ z6 {6 X; P" F" T! L5 |
void update_key_matrix(char row, char col, char onoff)
5 x7 r3 ]& L0 ]( j" |: ]& T9 ^{
: M1 d) w# i' ]: T5 n# {' E! m0 K static uint16_t hid_report[4]={0,0,0,0};
8 y/ B1 |3 K7 S# Z+ z* i) ? static char (*hid_keymap)[8]=hid_keymap_dvorak;5 b4 M3 b2 `' F6 f; K, u8 s* c$ y
7 _. _) F( o1 w
8 G8 h% m' @% o# P" ^
unsigned char key=hid_keymap[row][col];( k5 X0 \/ [( x4 i' M; R
unsigned char *report =(unsigned char *)hid_report;$ R+ @5 f# ?$ a$ \5 _. h0 g7 V
char i;* `, _) \5 n7 ]6 H
- I1 {! q% N- T' ?/ N
6 _8 {6 }) Q* C& Z* E5 _ if(key==HK_MODE)
5 [' D5 ?$ f R ^$ ]% k2 u- T {
* ]; i# N/ z3 k" v( d if(!onoff)/ V, z0 R' r# _ @' N+ a; `
{: i* Y0 b) n" P- E. v' a
if(hid_keymap==hid_keymap_dvorak)( r/ p. s0 t) M: Y& n i$ k3 T
{! _9 `2 D! W6 i2 Z8 h$ {0 L' d
hid_keymap=hid_keymap_qwerty; z4 w% A9 p3 S3 |+ W; n* c
GPIOB->BSRR = (1<<2);; u+ E: g* R& Z3 f
}0 y1 ~* l4 G Z6 E5 v
else7 x9 [, c: J) v
{
4 D: H) _# N4 B: R hid_keymap=hid_keymap_dvorak;8 `$ j, ^1 G3 k# z( R ^. G
GPIOB->BRR = (1<<2);
9 W, A+ D5 z+ t }
8 G+ S d0 g8 ?" W8 s G* _ }* ]) \1 J+ G, d4 q% B6 y8 m: C
return;
3 B+ A# U, Y7 N) Y }
1 y9 V& O$ Z# Y" q( e4 A$ H" X* s" O. @! ]- W+ x, B. i
% R& K) T Q" Y4 s3 u% y if(key>=0x80) // Alt, Ctrl, Shift
3 Z$ U; x7 j& W% q+ Z5 c4 |' S {! o' L( I" T# Y7 o! r$ K9 F+ U1 t
uint8_t bitset = 1<<(key&7);
" e# Z; U$ R" `# T if(onoff) // non-zero is key up
% c2 x+ e. O- v* Y- @* E* Z. w report[0] &= (~bitset);6 T, i6 a: o$ \3 d, q; `
else' N/ ^1 ^7 \) M8 W
report[0] |= bitset;7 o4 P( g* Z$ a
}9 C/ ]6 D4 H( a' q2 e! u2 v$ R0 w& W
else
5 k: ]) e; F# Z A) c( H; ^# v3 Z2 l {& P: g( i z0 P- |2 e/ W$ i
if(onoff) // non-zero is key up
& t* X) q5 D4 _7 c- j( S6 a {
# z ?' b1 @" W0 a for(i=2;i<8;i++)3 C- J$ ~4 x5 C; A* ^
{& m) m# H F6 j( y! \, t
if(report==key)* I8 ]- C# @# }' E j Q
{$ S9 {" T1 k. Z6 `: Y6 v/ M
report=0;
- n0 ?# c& u+ W7 I! n) R break;! E1 ?9 z. J# q5 u
}
" e! O( c/ m7 ]% y, U }, O; m2 h1 z* ~
}. N9 [5 Z1 h) o; X t
else
l- p; k7 v1 f {
( `& g' [# O* Y$ \8 E for(i=2;i<8;i++): Y W) @: Q {: n" s3 j! g* `
{
8 K% O9 R0 K( D/ m! ?9 a! z if(report==key) X, A$ l8 V, h& m2 P6 v5 [- p5 Y
break;) [% ^- D6 T4 b! {* O3 ? D" i: l
if(report==0)
& W! T7 Y5 x2 R3 E {; E9 M/ m1 w# c. L/ v
report=key;! K9 s( r: }4 A6 h" Z/ H
break;
4 X0 K% y" c+ Z& t3 N3 V$ R }4 S7 c# G+ g& y' ^) O$ o2 K! j5 {
}
( U- `0 `. D; K2 e9 d$ l7 l }
" f; \' G! N( k2 r }. M6 f2 Q$ l( M: k: H- g
for(i=0;i<4;i++)
$ n6 A5 e7 s9 }) k9 {: C3 p, c# G% W USB_PMA[192+i]=hid_report;
* J& K2 m7 e. _( m; L. Z+ b USB_PMA[5]=8; //COUNT1_TX
6 r1 j" w+ l' ~' ?" _ if(ep1_wait==0)
/ f, Z5 X0 g/ ^- _( q {! Z+ y& t; X, {- B( x" `/ J8 w
USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;0 d, u! B. P$ a, J8 g* j, }
ep1_wait=1;: H8 O& k& X$ C4 x- r
}0 m' i' v x6 l0 T/ ~
}
2 T& @" q: A5 F. s+ P! C: m
6 N" Y% \8 F4 j; L6 Z4 r& u4 H6 a0 z& u% t3 T
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。; }# ]* k# X# G% S8 O
keyboard.zip
(8.7 KB, 下载次数: 6697)
4 G6 d6 v1 K, I9 k2 i( @- \ i
$ T# P! V' T) w
: X0 H# c, \5 P3 L
. I1 I) O( T. n( c; S% m! i
' j" T/ |4 u- a' H( d |
|