|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
( F6 f" k- F7 |6 ]+ Q# }7 l1 n" K( l
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。) t( H! ^, s; n; U7 B" D8 ~/ O
2 W! F4 g- K- R, c要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:( {% d4 w: r4 e0 g/ T, M1 I1 n3 o
- O- J4 X- {' p" r) g
K# c3 ^8 g# B/ Y) {* y) W
const char hid_keymap_qwerty[14][8]={
" B0 B: G+ \# Z; T2 X! ~ {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
, A. s# o ^1 v1 v" |/ w* @( W- h {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},9 a' j1 ?' Z3 t/ ~' E9 t1 v6 G
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},4 F5 q$ u! O0 k' N( C
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},8 { D- \; ?9 n" k4 Y* X
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
2 s6 i$ ?" g' A; w: a {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
' t' p$ ]7 p, G) Z {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},$ g$ b- `5 X* M# g4 }/ F$ P+ R# M
{HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
+ p6 T2 C, r3 s+ P A {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
8 w8 ]) z" K E6 q$ d. Z. K* ^ {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
$ Z, s `# S) C% I6 P" P {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
* r3 h, W, b+ }7 r0 `& t {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},' ^5 [6 U1 F, J2 n6 \
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},6 q4 F4 b/ R1 A' v6 d) ^' z
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}6 g5 z4 |5 `3 [
};
" x3 }0 t3 e: j2 q' R
m0 J3 I; |& a/ Y7 q/ S8 Y3 X) [7 ?; V# j
const char hid_keymap_dvorak[14][8]={* a; Z- X2 ^" {4 d) v
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
, x7 R) a0 w1 D5 {! o- q, u {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},& a: B6 M6 [8 J8 G
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
& B5 f" o4 c7 E1 c# k0 a {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
- A$ t' h7 @. s0 G7 Y5 F {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},6 N: I' C4 h7 i3 M. Y
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
1 L% a! |, V, G$ s {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
& t7 \! Y0 j: Q6 D {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},/ D& |( d& g( V |$ b
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot}, k& I- i9 \3 E! Y8 u2 P, r! \
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
% s6 A7 o9 |7 z) w {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},- t! D) t2 }6 l5 Z7 J
{HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},# D8 g N( x/ N- l
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},' Y8 ~& ~' T0 O; J$ n3 j3 m
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}( D2 k4 }9 V+ ^4 P: p
};0 S% g( }- d. R8 t% @) ]6 h* D
5 l% n- ^4 u; b1 M' h, d1 I: E Z2 S' K7 J5 F5 ~, }9 ]' L
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
7 W V3 ?8 Z% P
5 S: C8 U0 V" X5 q* k2 sHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
. z$ [1 ]5 X" i0 A3 D1 K4 k$ W: a. P* i% V! x2 v6 d
void update_key_matrix(char row, char col, char onoff)
4 S6 |! _& i3 K. {{
. d! Q) i" V' @' \* I7 i static uint16_t hid_report[4]={0,0,0,0};' o4 x1 ~" j- t8 r5 {+ T
static char (*hid_keymap)[8]=hid_keymap_dvorak;' z6 E! g2 m' i: x4 {! Q8 W [- V
1 w% G: O& h+ Q: U) e0 [6 L, V7 l7 ]# a, |3 L2 l' |$ H9 Z2 Y
unsigned char key=hid_keymap[row][col];
9 B7 P9 v! X& k0 w6 J7 @# b3 T unsigned char *report =(unsigned char *)hid_report;8 I" v' j! U. u2 e/ k! D
char i;9 q2 N' t5 J5 y/ \9 ^
+ J) | U$ V" o& l2 \
$ S3 H! a0 [8 ]4 ? Y$ j1 e
if(key==HK_MODE)! }7 I' T5 u! F$ I2 k
{" h+ r3 s" X$ C: i- V# _
if(!onoff)6 J+ D5 p- o0 G0 V4 M6 J- @5 Z) Y% ]* w
{! n- u; _3 W1 j' k
if(hid_keymap==hid_keymap_dvorak)
) Z% y" ]0 |0 y; _. I5 j# y {* y# `3 t$ h& F
hid_keymap=hid_keymap_qwerty;7 | H ^3 j7 i( }# }+ w2 m2 E
GPIOB->BSRR = (1<<2);
9 A4 y. P! ?& s6 L( Y3 D3 U }$ o/ B% s @' N9 T( f7 O% Y
else! {$ H h6 K+ D. d! i5 I6 @6 n$ }
{
* R; ^1 q4 e' h& o hid_keymap=hid_keymap_dvorak;( Q- Q; T5 N! T3 i
GPIOB->BRR = (1<<2);
|8 _5 [/ r" Y0 L }( {' f5 S: y9 A, P, d
}
0 U8 s" t) `1 s return;% F! k6 Q! d5 u
}
7 |$ j# l( d0 R f1 @6 u9 Y
! G8 L& @ J" S% k$ l" O0 }
& J$ t4 f2 \. x% `7 C& C if(key>=0x80) // Alt, Ctrl, Shift
" l4 u. y% d# Q& \" i {& W' k+ c" ^0 a% k5 C( H2 C* n
uint8_t bitset = 1<<(key&7);
2 E( J5 \4 R2 W0 ]3 H5 a: O if(onoff) // non-zero is key up1 u, y8 p5 a; `- m0 y2 v
report[0] &= (~bitset);/ v2 d; _) Z/ f' U% M. V% Y
else' o# Q. A9 g" I: x3 @
report[0] |= bitset;; v/ ^$ f( ]: P6 F# [
}2 Y, y1 r9 i/ X
else6 v6 t6 h1 a5 D: _; D
{7 o l: F- ] \$ u. O
if(onoff) // non-zero is key up
* z' [4 N3 ~5 i5 I! @% S {
; L; m: h- u+ @2 w6 j9 Q for(i=2;i<8;i++)& U4 b" i- c, y0 V( M+ q
{* o! j, w3 n, M, z: v
if(report==key)
4 O/ i4 M; n- @ {) Z% t8 U$ Q) @. ^& G$ f
report=0;# H- O& i$ U+ u/ Z) v
break;8 U( T7 F; m1 Y* R; z7 `# H# v. ^
}4 B: u* h) b& X& s4 ]0 |$ g( Y% M
}
% A0 a+ `5 C* M0 u f, d7 Y }
; N4 P: `" _+ o. M- t) M6 ~. r else$ ]5 T0 f) v/ m7 {0 Q
{
, ?* o4 Z) B# S6 R& ?$ z for(i=2;i<8;i++)
; c( a0 v" t2 P* i& G7 s {
% K6 c& x4 F& g2 A2 s: P% g if(report==key)
+ j( p/ g. w& V break;( i2 T: G. @+ q2 U
if(report==0)
: ]* Y' q% t" J7 J- V {; d9 x! ]9 v7 |
report=key;
) w. g S& y, ]& S6 u break;2 \9 h, ]; C1 T z
}
/ x! Y( D/ \% }4 a J: ] }) X0 S, p# L9 `4 n4 }
}1 s( ^4 s8 D6 M9 n$ R6 X. `& _
}
3 `. N& t5 N* o! W for(i=0;i<4;i++)
, ]2 ~4 ]6 X2 g* C" }$ q- w7 f i" F8 B- u USB_PMA[192+i]=hid_report;! L# i0 F4 A; H2 Q2 S# S6 F
USB_PMA[5]=8; //COUNT1_TX( {" \' g- W6 R y% L* R
if(ep1_wait==0)
' o( s$ [& C( @9 d4 { O$ ^- m9 A, L {
[! e; h P6 e2 w USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;: |& `. A: G! {$ A
ep1_wait=1;* ^0 l4 D0 @, H# O" S, B/ X9 M0 ~; z
}7 z# H# d7 I! u: N3 D
}
/ U9 J& w" z( `
- z. B* _" t+ n3 `7 E; j8 `' y& T& L; i' H7 B
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
5 z9 w2 ]! m$ I" b) @# U- [' I
keyboard.zip
(8.7 KB, 下载次数: 6694)
( G9 I+ Y4 f6 v9 Y8 ^! V( i
* ^8 X" U! B- V
: l; R+ X3 |( h$ ^ g6 }) i: n7 _6 F# h
1 ]8 D" f# n% s& K
- P' Z0 F1 j9 _+ U8 F m! b |
|