|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 5 D0 ~0 O. \. r4 {
, Y& r$ M8 e1 K
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
# D) B( Y6 K7 O1 E& h8 B& G' R" Y# m) ?8 w3 ?. u9 U0 w
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
1 \. X# p( A* E7 s3 Z. N$ L$ ]8 q$ E# e% R
- N5 P5 [9 L, Econst char hid_keymap_qwerty[14][8]={; B, c" T- R2 B( X$ a7 L8 X# f7 G
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},: `6 x: {) a# G/ @. c& q, Q# I
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
# b! G. t# q4 z) j6 _8 A {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},2 ]2 _; C! S! N
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
. Y& E+ O8 E% h1 O2 a; W5 C2 C {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
* A' g" n: u/ ^7 K$ q* w {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
/ }) ^3 V- c( h! T5 _1 r {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
) y5 Z9 o5 E4 ^" O. _ {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
3 E8 l. h7 p6 K( ^, Z! b0 P# E" z* \ {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
& [+ O# P3 t; C: ^2 N; l0 ` {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
) P) V9 a% n- p" Y {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},8 w$ k I# ~5 P" j4 `
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
: g8 E* o4 a. o {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},. H! b1 u3 l7 L7 k
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}1 J3 J9 m% p, Z: E) Z. T. @9 @+ X
};8 Z1 V# O; e0 r* L( B$ h) |
2 J/ l& z: l3 J x
8 f6 I6 D9 z% ?( Iconst char hid_keymap_dvorak[14][8]={" I1 V& R j4 Z
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
& h! f. z, m+ H! q2 f8 @) X {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
3 F" g) F1 k% ^ {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
3 f/ B9 u+ [. M5 f# r. ]+ ]/ g {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
8 N( J. O8 w+ P6 j4 C& T {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
; x3 G0 P' N9 P( g7 Z {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
1 c9 a7 Z2 v$ `; m4 M1 i4 W {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
5 h4 ^. l4 A8 f3 H; }# r/ F8 d7 o {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},7 ?3 ^ W, e( j9 s! R
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},, a7 J* L; t+ W+ ^& n |
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
* a' o0 m$ i1 G5 ?, m; m, L# ~ {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
0 O7 a" |' G+ _7 F* |0 C {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
0 R0 F; o) h9 D/ O1 `6 c6 L! u {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},+ S. u- F$ O& e# L, e, }
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}) u- t" z$ c( t/ ]' H% D0 j( j
};: A3 I) e# ?. E2 ~2 ^ g% K
3 [5 |4 i' @% p5 ]7 d8 s- a( X0 o
- {) ?% M/ `2 N( V3 J1 L; `上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。% c' z5 C" n, D' P) O( z
1 u/ g3 x7 N9 y$ Y* a' \
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:! _2 q* i7 O" R- w8 T0 e! Q
- J$ l# H1 C4 M" F; `$ ^: hvoid update_key_matrix(char row, char col, char onoff)
7 [: f: y; [$ O# E7 I" h+ q6 ?{4 T* A* l. k1 B' R! f5 Q
static uint16_t hid_report[4]={0,0,0,0};6 w5 D5 U3 h; _, P V$ |% U6 r
static char (*hid_keymap)[8]=hid_keymap_dvorak;
7 ?1 K3 e, U" x) R6 M& m* J# W$ T* Y$ C% Y' h$ Z8 K$ T$ S# e
9 h, T. b* t. U' g5 _& x; c unsigned char key=hid_keymap[row][col];
, B" s: g6 x& x0 t unsigned char *report =(unsigned char *)hid_report;: P; ~& \- p$ s# a4 N6 _ }0 @0 y5 y
char i;, Z' a+ m# B5 q3 C& b
4 j- a4 H$ a+ k u1 C
?5 R" f" p* H9 M! B if(key==HK_MODE)4 l6 ^) c O4 y5 v S, u
{' b) j2 d! O" C P! V4 ~9 C+ d6 @8 P
if(!onoff)
9 w0 d I2 p$ e {
1 V7 b9 S, Z$ y5 W" g- q4 Z if(hid_keymap==hid_keymap_dvorak)# L! t+ M* J+ M0 D8 [( x% j
{
9 K8 B# R) D2 W8 f hid_keymap=hid_keymap_qwerty;5 |9 a3 j# l- A/ F( R( W, W/ ^
GPIOB->BSRR = (1<<2);8 ~& ^0 o1 o$ r Y4 h
}
% C4 D# x- |0 D0 N; e else# w2 x3 ^+ `5 h0 g
{
* p) v9 Q1 n, N. [7 i t hid_keymap=hid_keymap_dvorak;, r, P7 l% D; e, }
GPIOB->BRR = (1<<2);& U- M' L7 B" {+ R3 I2 G
}
5 [; u+ I1 }) _1 n3 V }
2 s5 _, Q! s+ N0 h+ A return;
! O/ T( D; C+ }1 T7 D }
5 t; ]5 a+ G# _
$ n+ {9 P* e" K. R( M2 L! U9 N0 D/ w$ l* y6 `8 A/ W6 A* J
if(key>=0x80) // Alt, Ctrl, Shift
. U8 K8 x* f& u/ |* Q9 m7 R {2 z: \6 \3 ^ n
uint8_t bitset = 1<<(key&7);
6 {6 k" X5 G/ I8 ] if(onoff) // non-zero is key up/ R4 d8 q3 z5 @" }( \0 ]
report[0] &= (~bitset);' g# L/ V, } W! n5 z
else
& w7 \5 P. @; J) W8 a report[0] |= bitset;
5 b# u1 e1 K4 E" j: ~ { }, I# y E: E" K4 M
else
, v4 N, P3 w6 K {. N: _( b+ i/ p+ R
if(onoff) // non-zero is key up, k' ^- V Y) Z% e$ {1 t
{
- }, \8 Z4 d: M1 C: q for(i=2;i<8;i++)) s \* ^* f/ a+ L4 L3 W" S" p
{2 u/ n% B# [6 ?: `9 U4 ]
if(report==key)
( B" y$ [9 S S {
( @) s$ f, `4 Y: R) I report=0;
6 c# l4 J+ P9 W' I0 [+ s7 m break;
2 a! w4 g$ @ q U2 M }
; p+ f& G) m3 J4 H+ j* Y, I }
+ n9 }1 Z# o& i& Z; ^ }
/ k6 ?: x' g& h" r/ w/ O else
t' x) g' Z5 o {
- R3 Q8 }2 a6 [ n8 M for(i=2;i<8;i++)+ b/ R1 m! ]3 R0 O9 o- i
{
3 {& E& E; l# k if(report==key)
" {. t0 Q" O/ b break;
4 L) W& E' J( ~3 u& s$ ` if(report==0)" C# u* K Q9 k( A9 F6 }7 G# Q
{
- X5 M7 O5 | m report=key;2 B) E6 I( D* f# f' s
break;# @4 W( b" ?! N$ u! d9 v3 Z1 W
}
4 o/ H6 g5 C+ K+ R' n8 J7 b }+ l2 c/ U3 r! Z( o' M6 M5 h& {
}7 B- R1 x; G7 {1 s1 a5 I' A
}
# e2 T, ]0 n7 ] for(i=0;i<4;i++), {' P1 e; a( M+ S
USB_PMA[192+i]=hid_report;! t4 S/ ~- P. j+ k) D, B
USB_PMA[5]=8; //COUNT1_TX% }) u" B) K2 x$ F' ]; S5 c. }
if(ep1_wait==0)
9 _3 v* _/ b$ ], [ {% o! A5 v/ W; ^8 ]! y1 Q. J: ]
USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;! ^$ e. G9 W$ Y$ W- B
ep1_wait=1;8 {( a s, n# J0 L5 ~! M
}% a* |1 M/ N% U! q# C& e# P
}/ K2 Q( ?2 c' w! M* L
4 N7 R. o( d! q* `# H+ Z
# v1 k: j4 x% j' x完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
) k5 ^- i1 X+ k# u& E' R2 g% P, i
keyboard.zip
(8.7 KB, 下载次数: 6124)
" _3 s, b; `, R
4 J) H1 u: o0 I, x
& }! ~- k j- _
7 Q& N' V* I# Z! L
/ C% L- J& ~5 |& P5 N, u& [* V3 B |
|