|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
9 U% L1 t7 e' Z5 |: h4 p
' d0 c* N: s3 S# }. ^扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
% k# l$ Z i, w5 T" ]
1 p9 F& A; N. }6 z3 [2 H要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:# m- E) I* L! {
/ S1 Z% P! o; Z% t
5 Q: g! w4 @$ @9 rconst char hid_keymap_qwerty[14][8]={1 r3 v. b9 x! d
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
C' P9 T% x5 M& C5 q1 b {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},8 Z* H- Q+ J* w" u8 y ^
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
5 r5 w9 r9 y& Y- c {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},# o c5 C$ [0 M4 b6 Z( N
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},% r2 ~: z- e9 F# ]( Z# B5 T
{HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},+ I7 ?% E8 Y4 h) O
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},1 Z! ]; u# e, D# L
{HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},( I2 j3 y0 K* C# h6 U
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
6 A w( b& Q( E1 } {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
7 g9 s" R" v; i. c1 @# ~7 a {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
3 V, }! |- d. t! n0 V! J {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
2 I8 q9 H# J$ B) F, O4 g8 ?' W) I8 V {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},& u7 o$ V! e5 n2 d5 q4 I
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}8 }9 o& U. u* N w: z
};
+ a$ k7 P" W% y! r) {7 i* z
. s* W( L1 N5 o8 D8 o6 y8 n
% ~: c) {6 @( [const char hid_keymap_dvorak[14][8]={. X: g) S. V) B4 g! b$ k \3 ]
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
) ^) r4 p; Z, N. V {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
* G) O& X1 I/ J; t {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
1 {* l C9 W2 I {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
5 Z. n3 v$ V q! P- T% b( B Y {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},5 D) z7 A2 z; S# v
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},4 D: `/ Z: K! u) x6 K7 }& U7 n
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},/ I2 P1 E; x/ `8 T: X' u
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
$ K: O ^ t* m: p {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},1 [7 w2 o5 j$ u8 r2 \
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},9 `+ {1 i9 Y1 l6 t8 e( k( v
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
: f. z$ Q8 c5 ?( o0 ?5 w0 `3 V# r {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8}," `6 l/ M" p8 ]7 j) _
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
8 h& A3 ]5 ?/ w* u. w2 d {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
. v( j9 C! o# Y, b6 a0 h) v" v& x};
# {7 _% F5 j7 X% i4 T/ @5 \! X# C% i3 g( x3 k
2 c d1 T% h) f$ n; \
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。' c2 K. \7 m/ T
1 ?# z+ o) N0 [ m+ e6 T( A5 pHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
, ~; N! I: i1 O, d U/ s! g, K% v/ O3 f- Q8 u7 H; c1 k3 Q2 u
void update_key_matrix(char row, char col, char onoff)) [6 Y' Y. H* F3 N$ @# q
{ f; }9 X+ |) ~. l& U3 _# u6 u
static uint16_t hid_report[4]={0,0,0,0};9 z* Y7 {/ k4 ^
static char (*hid_keymap)[8]=hid_keymap_dvorak;) Y0 ^5 \2 A4 w( n. B4 H C
7 g l' n o$ ?/ B& x. v) b: u1 D1 Y6 `. z m" R! b1 x) p: g" [3 W* h
unsigned char key=hid_keymap[row][col];
2 F8 i2 f# v0 w: a# y! x unsigned char *report =(unsigned char *)hid_report;
- d; p5 a! p) }) q+ p4 t; | char i; A8 @: k& c2 H' @. r/ `
6 O& @, Q- @* b) ]3 f4 M4 F6 F/ N# ?3 }: D+ R! b7 {0 y
if(key==HK_MODE)$ ?7 d0 R; p. M$ }) Z! V3 {
{6 i) E- Q; t% d" S S' T4 B) r
if(!onoff)0 G1 R( _$ Q7 f& L Z% ~
{1 [, N: C) C/ S: q9 Y8 r5 t4 s
if(hid_keymap==hid_keymap_dvorak)* N7 h8 W7 y0 K- r) i+ h2 o
{' M, ^7 z) m b8 v
hid_keymap=hid_keymap_qwerty;
0 I5 Z* G, o* L; \( i9 ~( c$ n0 k GPIOB->BSRR = (1<<2);" b0 r0 A1 f/ J$ h+ J# @, {
}
' f: h ? e/ ?# ~8 G else x) p/ M7 B$ f
{/ t7 X, Y) O+ T1 m
hid_keymap=hid_keymap_dvorak;
4 C/ B8 [1 I- c, f6 e GPIOB->BRR = (1<<2);6 \- j6 T+ C( a" f" F5 f
}9 W/ H7 p! h) ]4 y* @5 a
}
8 s7 [ R; p6 Q) k return;$ y6 z" m/ i! q
}
* Q+ e4 c5 k6 w
5 B7 M! g; ?: j5 k; l/ e& ~4 r1 o2 `. M9 p
if(key>=0x80) // Alt, Ctrl, Shift* r$ H* p* N$ i; D
{
3 q( C8 |& D% d" Z" q uint8_t bitset = 1<<(key&7);
8 a# _6 N% x. ]/ h I if(onoff) // non-zero is key up
{9 R& T: ?6 O: I' D; O/ o* K report[0] &= (~bitset);
6 Y; q: R& q/ m- [* G' \3 B. d# I else5 k% A' m! Z" ~- s/ G" Y
report[0] |= bitset;
+ P& {! O( d* n" Y$ ^1 u: B9 H" f }
% n& K7 b" a# K) i5 {$ K0 Y7 o) E* |2 x else6 N( k: A' ?: S; Y7 X3 K; J
{) d2 S; f! [* ?: Q
if(onoff) // non-zero is key up' `* Z) w( j8 i A1 @7 Z5 t& S
{6 a+ D8 F" k4 p$ i8 Y( C
for(i=2;i<8;i++)
+ o$ {- ~7 B. i' y' g1 f {
" n; a2 ?4 w) K5 Y/ ^: c) ` if(report==key)
- p* F5 x$ o! l/ ?8 K( x1 P/ w {3 y5 B( W: p5 T6 w+ f
report=0;6 F6 x% {0 y- s# ?6 l$ l
break;
% u" H( I3 m2 B' M' B& ~ }
: Q- [, J, q2 n, o4 d }# Y: X% |8 n8 L6 A+ v. W6 Z$ b
}* y: t9 s/ k! r [' |2 s
else2 o2 i! A' h% E+ K4 }. f. D0 u3 s: z
{7 [# z0 K% S6 L- j
for(i=2;i<8;i++)
/ q$ s8 d, ~( n! b/ F {7 t/ r3 q7 d: ~" {" T
if(report==key)
# y. d9 k- {3 H7 G) f" c8 x7 I break;0 ~9 w3 D1 E- ~' O
if(report==0)
2 {, I8 E* \1 T& ?0 l% ^4 O {* e: |3 L6 S. M* n8 k* Q
report=key;* @- c$ I1 A0 N. Z2 c
break;
& g0 U9 K# N- ]4 O }
4 o" @. E: }* O4 h }
$ \+ c* r5 }9 J# [- ?: v }1 }/ N7 B- j0 K* j- ~
}' _, B0 _, ^3 Q4 g' g
for(i=0;i<4;i++)9 s( ~# s5 p5 J0 v; ~- T7 t
USB_PMA[192+i]=hid_report;0 D* V2 R/ h* x \* I$ X
USB_PMA[5]=8; //COUNT1_TX( W0 k8 ^. d- Y1 m6 g4 u" P1 r
if(ep1_wait==0)
* Z: o; C m' h% T {
1 y5 H; d [) s7 ?) G USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
& H, L4 F) ?( [$ i8 D ep1_wait=1;
: U h7 ?/ x4 h( q) j$ d+ o$ L }
4 w K+ J4 ]; G K( g}
& B4 B: t+ Z% O% \% k6 k; o, |% o
8 o' N. E4 R: f r; Q$ _
& J1 ~$ Q: G4 o1 w完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
4 {' w- X+ X1 \7 {' e) T
keyboard.zip
(8.7 KB, 下载次数: 6207)
, E3 w4 h5 U4 q1 `, `5 G7 `, ^, J% L+ a' P+ s
$ ]& ~% e0 z2 ]$ L: K# Q. D x+ f; ?9 Q+ _' B$ a3 k; `2 v! r; B
! _& n& {/ h! f; ^* F" H6 U+ W7 g
|
|