|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 + c, Q' E2 D% R, l. j- g6 T2 o
3 e+ f- H9 }4 z/ z: L1 q
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
' K, B/ l5 g- d8 C; [/ A5 r! U
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
. d" p6 i( T- l; g: u! O& k% W" W: U* Q( u: Z
' {, u& q) r, j' a5 [const char hid_keymap_qwerty[14][8]={
) d; g( q' h2 h, R" ]* h' M {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},; @: O4 ]0 ^* x. a
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},/ _4 F! A5 W# a% J B
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},! w. N+ _9 V- j: S
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
* x& u( t2 D6 [% @: s7 K0 P# b {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
+ d7 v5 X& a% s3 E2 x' M c- h {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
$ ]- _3 f$ q$ I$ R5 r* j {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},$ q, {6 R* y9 f" d7 @
{HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
# E5 ]! y1 ], I3 E* w1 w {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
3 P9 y6 @* O1 B0 E4 Z {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
y( v" ]! s) v0 ~ {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE}, D9 ^' \. M4 Z2 F
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},$ L- T# e; j2 P) K2 O# P: ]8 V
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
0 R: @( X) ]6 ^ h. { {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}6 B s2 V, b# t" E) s% G) R
};- |, p4 V1 j' d2 b$ d
* y9 x$ h7 f* e! V. E; P0 \" a% o9 T4 [
const char hid_keymap_dvorak[14][8]={
6 m. b; x" [! J {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
& I$ {, z6 c+ e7 U& ]5 g" | {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
/ K7 d& O% Z$ l1 h3 S% p {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},1 _- e5 O2 k8 F0 y: ]: V
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
( p* N4 a) N; P6 p9 g/ n$ T3 y {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},: F8 P4 t# S c( C9 _+ Y# x' y
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},2 A+ V8 S4 a1 E+ l
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
5 b2 O; b' P& g" O {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},+ L: ^9 Y& v) b% c% b9 E {
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot}, ]3 G' f7 D; ?) `! e6 l8 v& \6 N
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},$ v- p$ Z( t; u
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},* T: b# y7 w: u1 X4 i% g& x
{HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
; k b# M% M4 Y' O2 j/ ] {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},, G2 q) x& C1 g" _
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
! |. ]9 q& `: X- q9 _};
G% o' d# R4 N& _4 r7 o+ ^7 [5 z! }1 O, {' b6 B/ C9 V
V+ G3 Y) ]) Z- r/ L上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
/ K6 i( E4 C3 M7 K# a8 V% |( ]
$ v3 ?6 y+ V6 u L) N3 v0 hHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:8 A! o! c- ]- ^# F. q3 g, t
# C: [2 m3 M& b1 c9 _void update_key_matrix(char row, char col, char onoff)
' [$ n5 Z( n ?* Q; G N{
4 }7 G+ q/ Q6 q J+ h+ w6 Q( m static uint16_t hid_report[4]={0,0,0,0};
+ i( [. a+ f0 A. b" V; M( S* C! \6 p- k static char (*hid_keymap)[8]=hid_keymap_dvorak;
- Z! q" Z G8 {* F4 Z3 u; C5 s' p* ?# X. g( M
/ w' m" ]) l' \
unsigned char key=hid_keymap[row][col];. \2 ^" S+ ]$ V# }+ J
unsigned char *report =(unsigned char *)hid_report;
" D. w, ?; y: m% [9 P3 K char i;
2 i% n7 M, Z5 c5 U
$ ^$ T! s: P! c8 u( x+ `
4 S- V. H+ d- e! B# b if(key==HK_MODE)
8 F# `$ U0 a4 B" U/ I v1 ~# s {! w" f% Q5 O, _3 g
if(!onoff)" W: K' f @0 R$ D: u
{
4 s) D" H/ j& B1 G$ c if(hid_keymap==hid_keymap_dvorak)1 I" r2 m* Q* K: n& V" p( k) n
{# ~; l* |- G j4 }' [7 d: U$ w
hid_keymap=hid_keymap_qwerty; g7 u$ q. |0 o, D4 h
GPIOB->BSRR = (1<<2);
; t' O" L' @! `% b }+ s* N5 B& W Q# f5 }
else/ C% F2 C2 `! |
{+ Y _# g7 r8 L" K" e1 v
hid_keymap=hid_keymap_dvorak;0 g4 S G6 s6 C/ L- v# g
GPIOB->BRR = (1<<2);
) C% a5 b8 O( `* N: [! N% h( K }# s& _* W9 e0 c, K1 s- [# i
}
8 O9 a8 q2 `8 M8 {/ O3 d return;
- s1 ]& Y4 M1 X# p }! q5 ]0 U' i% H3 A$ o% a6 G
& J9 I: A' v/ F
4 t4 p( O1 v* I, A8 x+ I3 M if(key>=0x80) // Alt, Ctrl, Shift
1 s! u# P/ |# b9 A$ k {, @# }3 U9 i3 z4 i3 [7 k0 i# P
uint8_t bitset = 1<<(key&7);
! O W, D( N5 U u if(onoff) // non-zero is key up
7 Q4 T( v' o3 \1 W5 _% Q report[0] &= (~bitset);
' t" O7 P. U4 o4 y else: R, U& e9 U6 b0 O8 T
report[0] |= bitset;# J/ a" m8 Q8 Q2 ?0 T- w
}
* F2 [5 O- d3 W) k4 l else
4 W' G- v W0 a: l- f {. i4 X7 Q+ ]& S; N
if(onoff) // non-zero is key up& U! ^3 K: S4 s
{" e+ `0 A6 ~/ X( U" O( F- X: }
for(i=2;i<8;i++) ^; A* s5 _% L' N
{
; z* A& q. F& ?9 k- _+ W+ d& X! X if(report==key)' Y& b/ E0 U n9 B9 h* }
{
h: ~0 o) j8 t; z) y6 J; z0 j2 B" Z: d report=0;
, v g/ q2 y7 {1 E8 G9 P5 H break;3 L" z$ d* ^ Q; u
}
) A# w% }' p) J }
$ ^1 \' W+ `- z* T# r }
9 ?5 O% F* |- W. J8 i( e$ V, W; K else/ q2 }0 j2 a: p* q. y
{4 \) A4 j1 q$ u+ v5 d$ U# b
for(i=2;i<8;i++)
4 I8 A& A1 h" v' D {
h/ e8 _/ A2 F- B if(report==key) D1 F; p P/ I2 @7 z7 g
break;
) c' e1 Y2 @3 d! [ [+ u+ ]5 p if(report==0)
/ D8 Y$ p. r `) X+ W& R7 ~ {
& b* E* _/ l9 m) U9 a$ f3 p X report=key;! G B* q! O: |: r; f, c% t: E9 x
break;
% S; s; I1 {( d1 e }* V. I0 j" P- X" h+ M4 W1 E
}
5 G& y0 ?: ^7 b$ B } x7 \* O% U/ C2 b; U! @/ Z
}4 J; {0 g0 K: C5 H' p8 [! }, X' h
for(i=0;i<4;i++)1 F1 J$ e/ s# k Q$ ~# P8 v5 C5 [
USB_PMA[192+i]=hid_report;0 N; X0 M/ t1 ?6 h& I$ F8 o
USB_PMA[5]=8; //COUNT1_TX
: w# _) |" R2 ?) ? if(ep1_wait==0)
; N3 g% h/ H% E& z# S% i# Z {- b+ e4 c4 }" j8 _
USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
" ]8 u0 o" { d: w) t9 V ep1_wait=1;
/ b/ O7 u1 d+ A& y* s' ?# q: Q$ ~* l }& @9 z1 z& c9 T$ r
}8 o+ u" h0 ]( Z# d! n, X4 W
( X+ x: q! b# b" o! X
+ t8 [/ U* I, ~* w8 |& k完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。3 ]; b" y- J( g7 m
keyboard.zip
(8.7 KB, 下载次数: 6043)
0 i, C7 S+ l1 F1 X4 _
' _" N" x& T+ s! O! `
8 o3 W y- q. K! c* W; v$ m& v, j/ ]: f
4 ^' {* f2 k" Z |
|