|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
, ?% @5 e- q7 o" \- \& _
H7 E8 H4 P# k% D! ^8 k. h扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。3 ?% A; {5 t; x/ ^! q( q: A
7 M7 R3 z9 r) `1 C1 d. a2 j+ ]
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:% A, E8 ^0 i" G" d7 a5 N! |
/ H3 \. C' F$ u* P, F% _; R
. ~. ?$ q7 X& X1 xconst char hid_keymap_qwerty[14][8]={
, K4 I% M" L9 L4 Y& m' t {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
4 e3 W$ y* ?& ? {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},4 i) _# _( M( L" ~% D3 [/ i
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
! q' \1 _9 o: X; ^ {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},+ j2 r, @; ?; b- x# p4 s: ^
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},/ z" ?, k/ `& Z3 x' R* T0 P& {# O' u2 ~
{HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
* G4 V' E5 W- q {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},9 q$ W9 O* F4 o7 w
{HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
5 x+ k# B. T- ? ^3 Y+ N {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
- x3 X( p2 h' H {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
4 V0 Y% C7 Z& j- G! _ {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},+ e1 } W k+ t) I; B" h
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
- R5 S# ^! P9 Z3 c {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
( X% H+ g3 V" b+ v1 ] {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}: Y2 @5 C! b$ L A- k* i
};
% h. C/ F5 _9 C' l8 q% p
4 p0 G( H) f1 m* Q9 I. A. @. X, T# {: t" t$ O: ]
const char hid_keymap_dvorak[14][8]={$ s: w- L' c4 ~9 o
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
1 s6 _, o! F4 U& e$ K {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},/ b" C, ?3 C% V7 u8 E1 R8 E
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
2 R% m' `+ D+ u1 c& t, [# t% ]8 h {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},+ E; `$ ^/ d% \5 p, T
{HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
. p o* k1 @" f5 @( v {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
; {' ?5 e) ~" O- j e) e( D6 q {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
" H, _2 h1 D/ \2 ] {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
: E" O# q* U3 j% [$ M. s; }8 j) F {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},% W: f1 T1 `! _3 n
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
7 C9 s( |2 X- q: P" _. x& [# { {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
) E6 Z$ ?# Z' F8 O7 W" |5 n# O {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
) H8 {( E$ I) B$ Y/ Z {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},! I" s6 x2 E2 g8 I; V- {
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
+ `& E4 J7 V2 E; ~& S};
+ l4 `( a0 A, P
+ H$ V+ |4 r* p9 q. h% B) ]: F5 p/ }" T; ?' {6 ~, X6 I
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。0 R4 `) o8 h! n
' b5 E* `8 m- m" `1 a( p7 @
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
8 I7 p; R* j# s
/ ?2 u" @9 e( y( x- J* F4 {void update_key_matrix(char row, char col, char onoff)
4 B9 Y# h0 u/ {( U{
7 p& n( d$ A& o, u( O8 y$ Z static uint16_t hid_report[4]={0,0,0,0};/ D8 Z, e) x" X. \% f c+ o1 v
static char (*hid_keymap)[8]=hid_keymap_dvorak;# {* A- @0 c. g+ r9 j& k3 S
7 Q9 I% W- ]) e2 ]: {2 ?2 w/ l9 ?/ J: k# V4 |
unsigned char key=hid_keymap[row][col];% ?5 T' k+ F5 n0 j3 F
unsigned char *report =(unsigned char *)hid_report;, u3 o S$ h; I; Q
char i;, _4 F0 N4 v a( H" a! C4 W' ]
4 d: r5 C R' v. k9 H$ v# |8 t6 f2 D; W; _2 j
if(key==HK_MODE)/ w0 b- E Z3 S5 {# Q. u
{. v# y7 X/ c" G
if(!onoff)
( v* l" s3 Z5 j' K: A% g1 ~* [! Z {
5 Q' D/ S, p9 k0 O# u if(hid_keymap==hid_keymap_dvorak)
. b0 |6 d1 L& I {
9 W j! y3 L$ k+ h$ E9 Q8 E6 \ hid_keymap=hid_keymap_qwerty;6 f: G& \1 u, q% R B. u- l
GPIOB->BSRR = (1<<2);
5 o# i( E3 A2 Q( v }( u. k9 K+ d8 A
else
& W4 E+ n4 _, }7 L; r! M+ c$ f; ^/ P+ x# W {
2 \! Z" p. P% G% k hid_keymap=hid_keymap_dvorak;
. S; v' ]4 s* e; K' B3 D- ?' a! \ GPIOB->BRR = (1<<2);2 s2 e$ s! b% K" K# y
}
* m# Z) g8 B+ U8 p8 \9 @* S5 J. c }: F$ @0 F4 `& T4 t
return;! t0 }# S5 L; Z! a1 @' B
}
! `- u5 \, x* i3 a' [: U( h; O+ [1 ]# T. N% u/ s K
| ^. k3 m) V if(key>=0x80) // Alt, Ctrl, Shift
* u1 Q# _ p2 f" R9 d {
" {0 N: ]# p1 p) n( p uint8_t bitset = 1<<(key&7);
- u+ {; z2 v3 J" p; S' B if(onoff) // non-zero is key up. B6 X R$ w" z8 _
report[0] &= (~bitset);
; c5 j9 B( b. ^7 h0 J0 p else
- v3 x4 H( X* a$ \ report[0] |= bitset;/ k( u6 }* L2 ]5 A) H
}
/ u8 A7 E$ d" @4 F' y5 B else
7 ]$ G* E5 X* Z4 Y. f- [ {8 @8 `5 w$ R/ G
if(onoff) // non-zero is key up
/ g% Q- h: o, o5 u2 \) y; s* o {- {& h" w! ]) S5 Z1 Y- w
for(i=2;i<8;i++)) L* [. e b9 `3 S3 H' z# B8 B# |
{0 D6 N: J; `0 N4 i% j
if(report==key)6 T' O& T( m X2 X* `
{+ R* \6 |& |/ N; {
report=0;
0 H# k% _6 U; m1 o/ N break;4 ?9 U3 u/ K8 C. _
}. r0 b v8 V' W. T. g
}
B( K: ~4 B7 F }
# z2 q, d8 `5 G5 v3 P else
6 y Z. C3 x( e, Y {
2 M- i2 B# p- Z) C. e( X$ B1 p for(i=2;i<8;i++)
, a5 Y2 w) e0 A9 O' t' }/ J {+ t; D8 N0 \% N" k0 K' a
if(report==key): s# h$ X/ N3 K# [
break;8 p8 h# u( `/ S, C2 V* q/ K
if(report==0)
6 H, V5 A1 n* [; y, Q5 a {" e1 Z$ r) q* ]7 j; j* W
report=key;1 X+ |6 p$ j& Z4 R5 v4 ]- G
break;2 I4 @8 [* {3 h+ Q/ i. X
}' P1 O D; R) p, w. i1 ~: D
}
+ H& O; S4 p4 Q2 F# }" O }
" v/ n- c( c% {/ B }
f0 g4 |6 { d( A5 `$ w! e for(i=0;i<4;i++)
( U z& Q7 ^/ c5 y1 y USB_PMA[192+i]=hid_report;
) p% k! H- b) T- n, T6 L& z' d USB_PMA[5]=8; //COUNT1_TX3 m. ^5 k/ P9 ?
if(ep1_wait==0)* w1 m) }) B1 Q P/ v) ~. Z
{
) V' Z( y2 T' |$ b USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
+ w: k2 `) n) A7 \) q. ^) z ep1_wait=1;4 t) u) _1 ^% e4 O
}3 I- k/ i0 @6 ~* e% h
}+ U" L) h: \" U) ]
8 p. z* S. |. S2 A
7 M6 w" H# C2 a- _7 o; u完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
6 s" v- g3 K! b
keyboard.zip
(8.7 KB, 下载次数: 6330)
3 j/ d6 V! M l% E3 A" b
b* N* u. x9 G5 x# R4 [3 C+ X, C- N0 u
' `, N9 r/ {9 w# v
# B: K! U6 Q6 n, k& \9 A |
|