|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
4 Q8 ^3 O( _* t5 E5 ^' H; ?7 I: h& T( S
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
( d9 E* `4 B8 I6 h; j2 X: ?9 h) Z/ D6 L" Z1 f
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
' n9 E" J) Y4 Z+ h6 i
9 x% ^0 a' \( _3 u' p( r+ o7 N" O4 W# @6 i
const char hid_keymap_qwerty[14][8]={( X7 P H( q# B
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
7 b8 u# }. x' K7 D+ J8 H {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
f0 W7 S8 [6 E3 W- `% W {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},( H3 {% [: F0 ^/ G
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5}, S8 ]2 ]$ i& M$ E" _6 q6 Z
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},: w, f( M+ S. d2 C4 a( ]
{HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},0 \/ b# D) ?: i J# n D ]
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
' z5 [3 n2 J( m. K {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
k4 ]( G7 O8 f9 } {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
0 F3 d& c8 E! O) ]( g& z {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
$ t9 J7 ?- n/ V; D( E {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},, u5 z# X$ }' M. c. E
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},% D& L( j: `. E1 [) Q2 v$ j8 c
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
- k D2 B; g- n g3 q% n/ r1 t4 R' J {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
9 f4 V. M/ s) v+ L. M$ r; X2 W d};7 @# A! j: u8 e/ Y2 O' X
9 A( |8 e1 t( @9 B* ~
' s" Z3 a% _2 _2 y: F* I$ uconst char hid_keymap_dvorak[14][8]={0 h3 r4 w: J9 n x% K* p$ j8 w
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},2 I4 n. F3 Q! k4 \! q
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},# s6 i. p8 p" q6 u& j
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
4 t+ }7 u; ~ f% R/ b {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
4 N" @ q% ^. ^( O, O( O9 W# m {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},& a% S: x* h/ w8 _/ n7 h
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
# M9 M% w s1 `! N* t {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
; d( E3 V L. T2 C1 p- v8 ` {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},9 C$ l, Z6 d8 @! P# W
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
% `1 k! G" P! _2 O% w+ i {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},# U: G: X. t' J4 U
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},8 D& R5 [5 D- T
{HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
0 m) u( x: U8 g' m! ]' { {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
+ \9 F- b9 H6 y8 Q: c {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}' S! @3 Z' K. f" }+ H" U2 j1 |
};; K* C6 o2 X8 t
5 U4 g* a+ t; i) U7 D. {( I- [0 P7 y! z% N6 o
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
9 i9 d) i, t. l0 N
' x, v! w$ w; ?5 a% C0 YHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
: I; `- s, k/ g
; t2 g' e/ s2 @, gvoid update_key_matrix(char row, char col, char onoff)
. z. r- w, s) W# c* f. Y! j! u{
* x! A j! h' V5 @' b4 X, G static uint16_t hid_report[4]={0,0,0,0};* z D% [) Y5 |/ e
static char (*hid_keymap)[8]=hid_keymap_dvorak;4 }" S% h; P$ w9 i& c8 }7 x
) d( o+ d$ O" D% [- X. r& N* c6 d
* [6 `( \. h+ F% U- Z# N unsigned char key=hid_keymap[row][col];
7 S1 L& k) q$ g% N* I! @2 d unsigned char *report =(unsigned char *)hid_report;
6 u' f. j+ I9 s. y- W& w* m$ X& I char i;
6 p/ g1 |* b2 @4 {& g
& D h5 G! M5 d& }- W- M8 i
; L! O8 b/ U) m1 g/ \4 A/ p* J u if(key==HK_MODE)
6 g7 A! `: {& c {
C4 O/ I! D4 ]. v if(!onoff)
( k x9 V% `4 F, [ {
% L+ v; t& P; B/ j2 Q- H if(hid_keymap==hid_keymap_dvorak)
5 |, n0 `) B+ e G! _# h {- Y" \' n, @; H
hid_keymap=hid_keymap_qwerty;
* y* K& x0 I1 L7 h0 g GPIOB->BSRR = (1<<2);$ f) O% n9 l& r3 s3 @) Q
} l- q8 g6 A) p2 c# f$ o0 `
else
6 Q: O( L; y& j: O: @* h& G) K {
7 v" P: G! S; d' S- X* g hid_keymap=hid_keymap_dvorak;
$ e$ l' b2 H5 `4 Y GPIOB->BRR = (1<<2); Q$ y! N. c+ I8 p% r) F" d" `
}
, x+ d8 Q$ T" R3 g/ y. y* a }
% b" I" Q. J! E0 X. ?$ [2 t7 U return;
A- U9 c( }8 g9 i }7 h% w4 C' P8 T7 r- W, M* {3 L6 O& P6 [
% r5 K# h2 \8 [1 t& n. |
& C0 J; e6 A: s2 o: |9 `% v( W8 y if(key>=0x80) // Alt, Ctrl, Shift; X: z) y! [$ M3 F# i6 Y* D
{8 m B& l/ q) X( U3 t
uint8_t bitset = 1<<(key&7);
( V- g; V9 S& y' w& y9 C8 B* j1 k8 I if(onoff) // non-zero is key up
: t3 y0 U$ ]8 ^ F! ]5 k, p, x8 ` report[0] &= (~bitset);
' r7 D2 S. `* i3 B else
2 [$ o' c. b/ O% s/ { report[0] |= bitset;
& E! i# d4 N- z" k }
) P6 Q7 y0 Y; P else
2 [; s/ d/ \. K# n% ~9 H2 Z {0 X' S9 B* H/ L: b9 L
if(onoff) // non-zero is key up t; P( ~6 Z3 G
{
& C/ ^$ R6 Q; I0 s7 f2 v for(i=2;i<8;i++) p" {" Y& }; y7 ~+ ]. e8 g
{* S9 c3 n1 v3 M6 u. L4 U
if(report==key)
% @- _; ?! I6 X. D- C& h" Y {
4 D E5 a% G2 h1 z/ { report=0;
; q( \ ^# }, E break;7 H8 g3 k+ n' y2 d
}
4 j& l' }" V e4 v/ @) d" _! | }3 e( r2 Y* {5 G$ Y
}7 A% P E' C$ j; C' j, A' r6 d& p
else
- \4 u9 r' l6 k z; Q {! L; }; _& X( g1 |& C# I( \
for(i=2;i<8;i++)
: l) l, c7 O- k% a8 A. M' ~ {! x5 P6 D6 D# Q; F8 S& f/ K
if(report==key)* M! \6 e' x$ [8 ~/ w) n$ M# ~
break; V+ k$ Z3 f8 T U9 R- ^
if(report==0): [1 i9 e2 u! a2 R+ w" F% F; F* s
{
" {$ D/ h) W& T- E report=key;! {- F6 ~ i7 O E7 X
break;
7 `7 C" X4 I* O |! L3 ? { }2 [& t; d- ^' L" p/ H! x/ S0 |
}+ S4 l& T3 z1 J9 T$ Q! U+ x+ S( L
}
9 s! R7 [, I) _! J" ?# ? }
$ f2 g" B0 D" b: C1 O% i( U for(i=0;i<4;i++)
( X3 p5 o% o/ G2 b- a5 r USB_PMA[192+i]=hid_report;
3 ~2 V& Q0 [" ]! F- S USB_PMA[5]=8; //COUNT1_TX. W* A9 R8 d8 A, r+ l
if(ep1_wait==0)
y$ e" |" T) s- G3 Q1 E& _- i {
6 K% k- v5 e9 A USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
3 d/ U) H( {1 t4 w% ? ep1_wait=1;, b1 ^4 o& K' H
}
/ Q( d# t% S. f: y7 H}* r/ v6 @' v4 P4 u
- Y+ M( E/ l7 P, l- y5 s
. w, G! a7 h5 u: I/ c
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
( W( N3 c8 P/ f5 o- [
keyboard.zip
(8.7 KB, 下载次数: 6065)
! u1 q H+ t6 X H
8 L+ H* N7 H+ t
' U6 y! R% g" a3 q2 {
0 o/ g& w+ [4 p0 V
7 w [+ h w7 X6 G5 c$ I |
|