|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 / ^' q4 S! j5 B7 w" w, G) V
/ K" ~2 e- E* u$ P7 {扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。1 F# r9 s' D* \2 g7 k; c
7 o; w; X& T' z0 [+ B1 M
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:7 t/ d4 _1 V7 \
9 r7 L7 f M) ?+ [6 v7 ^ U; g9 y% f' _. b s/ e: k2 V9 l
const char hid_keymap_qwerty[14][8]={$ Q2 m& B4 {3 u0 F
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},$ \' y* m% ~1 I5 l/ U
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
5 [* x7 L0 X8 K$ g* G/ B+ j {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},% ]# D: U, D" {2 q
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
* m! R0 t9 P5 `1 n+ \5 l0 z N( s {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
, S. @3 q" R4 [) w7 D {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
3 B) [/ j) \# {+ ?4 n {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},; C8 T, y7 L% u$ ?2 S# H
{HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1}, ?" ]$ M6 F, Z4 e
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot}, t- r6 |9 {7 Z
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
; W5 W- c4 w1 ^0 n) u {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
: n. |# V [3 \) a" g1 \% l {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},% w# B. B5 K3 Z4 E! i% ^$ m# X$ v
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
2 B% q' s: L7 C. ?# w" O5 s% a {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}) @* b; Z! ~: [7 {
};
2 `* O& K6 y% w7 a
9 L4 @' a3 Y" G: b6 g* y" H0 Z
/ q0 q' x& S2 s0 E! Hconst char hid_keymap_dvorak[14][8]={
$ j1 `& a) ?3 q( e& p( c {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc}," s9 m* P( H9 b4 F% W
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},* s' j+ E2 q9 }7 l
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},: p- ^. d1 J1 {' K* d/ p$ h
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
# O) H0 r) f- t2 h% p {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},- o& H" Z( N. T4 P* G
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},' z F& X j# X1 \; @! R6 j
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
0 t% s% o# x* ^* l {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},% y3 H+ W+ e n1 p! p4 j; j
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
9 R7 v6 Y7 {4 ?$ C) L6 K: r1 q. K {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
8 V1 C& I/ w0 K, v8 i5 O {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE}," m& x1 Z+ W$ O' S8 Y
{HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
% w& F' C4 p ?" H" ~ {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},: a, \( `% _3 q. L" W
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}% L) B. D0 j$ o& T2 o( z5 ^5 Y
};6 I2 K" n2 A7 ^' s
w- w/ C/ s [2 s0 J/ w
2 c7 X, d. f) z% w0 B$ t上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
0 T" l7 w4 E, ]. v. ]. f8 p8 t$ T# ?/ b& e& W5 ^0 D
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:& p( o' q& w; Q8 x/ T5 l) f1 f. R
* \7 Z: ]+ t; }/ V7 @void update_key_matrix(char row, char col, char onoff)
; l6 y+ ~5 \1 u( a- Z l3 W{6 T% X( Q' i3 u8 y- p/ J
static uint16_t hid_report[4]={0,0,0,0};- K5 \0 q) l, W$ N3 H
static char (*hid_keymap)[8]=hid_keymap_dvorak;
2 t8 q( w9 P/ W. H9 p, q8 f. |5 D4 o5 l! [$ _. `8 p. M% r! e5 U) O
+ _; Q9 _0 w6 }3 m0 s
unsigned char key=hid_keymap[row][col];+ h: `$ j' l" Q5 P( W0 y7 \' q
unsigned char *report =(unsigned char *)hid_report;' f" D9 ~; r/ T i3 v
char i;+ v9 R: z; {+ D4 `3 w" w( l, g
6 b& g# x5 P! X0 b0 y+ d* ?4 X* P: I' r0 L8 V s/ F3 A! O4 `$ E+ c
if(key==HK_MODE)- \+ P& m2 p% Y& j
{; e* t z8 _+ f
if(!onoff)
* T- n6 Y3 _$ h" P! L {# W; D# A# X. q. A8 c0 [* F
if(hid_keymap==hid_keymap_dvorak)
& C' F3 h( G! x! v) G2 W+ K+ D {
- T8 W9 s0 O( F! e) t hid_keymap=hid_keymap_qwerty;
" Q5 I1 f5 z1 i! t: W8 d; k GPIOB->BSRR = (1<<2);, F8 |% \0 G! m# P# E* N7 g* r3 e5 Y
}1 T# R: p* A4 r
else/ H3 X) l6 Z0 ]8 _+ c
{
8 a% M& P2 @1 _" J9 L hid_keymap=hid_keymap_dvorak;
4 i2 d1 I/ G" z1 w% z GPIOB->BRR = (1<<2);
+ v+ o: @/ A/ `# A9 a }
) W) O; c9 E# C) X7 q1 r, H }
7 _. I* |8 ~/ I9 K4 e P" q return;6 b7 I* I, w+ k; U* w
}, t. V& \9 i' U
: [& f$ |- N+ ?' R& P4 q7 \3 A( v) B& H2 d5 C
if(key>=0x80) // Alt, Ctrl, Shift3 V* Q2 i- j/ @7 `0 i" w
{
; n% M: U" U" H- M7 ]2 V+ v uint8_t bitset = 1<<(key&7);: S, B3 p- S, Q5 h# V
if(onoff) // non-zero is key up
) M) r: s4 {0 u) @4 U report[0] &= (~bitset);
# T `4 P" P; \$ g( Y else
; p9 a- B: t9 ?# ~) ~ report[0] |= bitset;9 Y9 C: Q9 u4 S7 y7 k
}
, s7 [ F2 e8 q* f$ |) n' H& ]9 Y else. X( F( }5 y4 N& }, f! m) Y, O
{
% M0 E6 P( T# a if(onoff) // non-zero is key up* I A0 _7 v3 k- [, A7 \. \
{
* h- q+ H( q! @& P for(i=2;i<8;i++)
3 y, h1 ?1 t" j/ U5 S {( S! ]0 G. r# F, A3 D
if(report==key)
! d5 W5 b, W+ y/ ^+ H+ H& [4 ? {
$ }, E- T: j5 R1 `1 d. R* L6 Y# u report=0;
* h+ E: w: i' t ~: U }0 B break;
2 t$ {6 T$ V1 z3 f/ _ }
# E, L' v; |2 r. Y' w }* F# l. X _( z$ D# {9 c1 C, e
}6 d6 j7 S, ~$ `) S y
else
0 K( Z/ {9 B: b3 v# D2 [ {& q5 n) J2 ?! V2 H- J- _* i
for(i=2;i<8;i++)) X) w+ b4 k+ Z* Y
{
' H/ e% j' ~0 E R% f2 Q0 X4 K2 t if(report==key)
! ^' c" z* N1 E break;
; q# }& ]$ e# `. g& @) w' f if(report==0)$ @8 `# L0 Y: `" Q7 w, O9 w
{
, e" U L9 K4 }* m! ~+ a1 k7 T4 J report=key;
# l" n* D- P8 f- j$ M0 z break;
& o I2 S8 L7 M }# l0 E% w: K9 [; l! s4 K5 p
}
( o4 `! p% w. }8 w! t }: z5 b$ A9 e) U3 ?3 R. U$ h
}4 y# H4 h' X4 ?' k8 Y; l$ S
for(i=0;i<4;i++)
& Y! a# m: v+ ~3 U USB_PMA[192+i]=hid_report;
' p8 ]% e; U7 ^, n6 M7 ? USB_PMA[5]=8; //COUNT1_TX9 }% D5 j W5 L8 m
if(ep1_wait==0)
- [) } Q- w1 W1 o {; H' m+ U' ^+ L! }7 m" j! ~
USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;6 e# X+ B8 ^( p) U& \
ep1_wait=1;
2 b+ w, }" p' }# C0 K2 Z }7 e; N1 g" p, S2 R) S# m* q
}
. L/ _6 F I3 R! p4 G& ^% k( H4 c7 |
" X7 b$ B+ @' N% G4 \
0 X" d1 b8 L* ^( E$ w% S完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。2 h3 ~6 [3 }; ]% T
keyboard.zip
(8.7 KB, 下载次数: 6347)
# b0 F {" t' K5 c8 G9 w4 ^" d0 Y
( E3 A0 ?7 z; M6 p- x
7 O2 M+ ^+ R- l Y! d, V4 {# |4 _- S6 E9 v! [' v3 a, r
* ]3 B1 e u I |
|