|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
3 Q/ R/ @! J! h, r* E+ H0 r4 p! j5 W
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。5 H, _% {- @- T0 ^
& h9 ~ a5 M' ^7 m4 W8 o- R要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
* |' q5 r5 _/ W! H% N$ X, P9 Z8 T }
' D p# Y6 s) `; b8 `$ W# u0 m
const char hid_keymap_qwerty[14][8]={
$ m; S" V7 R, n. V, H5 g. v/ b {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
- S" P) v8 t, H {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},1 I' |4 _8 g( C. l9 k
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
5 F& ^, E! b* u9 k! t; T {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},7 T8 M, ^7 `* h+ E& w
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
9 t$ J* V4 U, M* |' X {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},7 s! p; t6 l& W) m
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},0 l* }3 i' u! C& c
{HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
* E0 |( c9 t n {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
, S8 ~/ X+ o& ^9 _- v! m4 V {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE}," \, E6 X% n( U/ O
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
/ r4 `5 r8 N/ Q {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
7 v) a2 b" Y0 C* |" V {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
: @+ j7 N; |, L$ z* B1 @ {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}5 Q& [% ^+ E, Z
};' l0 Y) c1 J& _0 H7 p
& i+ B. _. \3 Q2 t' q! m& ^7 z5 O+ p- \. M# q5 O- Q6 k
const char hid_keymap_dvorak[14][8]={8 z* v n( a% d: q9 M8 e- y* l
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
" x9 T- V5 g4 ~, K& T/ c4 B {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
5 H% t! U9 y! N/ W/ g2 l {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},' n0 j: W8 x9 ^ e+ }. q
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
! E! \1 S4 w$ M, K {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},6 N5 ?5 c) A# P
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
: }" K2 v" F- e( E5 I" h {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
, a1 n& q% X$ |2 M5 D {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},, _" j+ U9 Y+ ~' t% q, J; D! ~/ H: }
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},% ?: i; j* V; m2 D: o% n
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
5 E& P( z8 a+ h8 ]2 u7 a. b. o8 x$ I {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
) H* K# n3 A3 i* D2 l5 R {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},5 b& y+ G5 `; F& h3 S
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
/ m' M. N$ \* F' }0 ]% r; H {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
# j; l' _0 y5 C( [, V};$ Y' o4 i+ u' `+ @+ {9 V5 r. W
9 c7 H7 m- K* i- A
2 j. K8 D5 X+ M: h+ P( a
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。7 T$ }# Q2 T: N( l
" y1 C$ P% u; _5 YHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:3 a- D, r0 l# Y+ z
! ]5 v. ?) K. x, `+ X
void update_key_matrix(char row, char col, char onoff)
' }# _6 ]: V( x: \! C# ]/ r" |, `{
3 S! Z- C. S9 R r& e" q static uint16_t hid_report[4]={0,0,0,0};, b+ U! k4 ^! ^
static char (*hid_keymap)[8]=hid_keymap_dvorak;
3 h3 e# E8 w& y: L9 b
D7 D) H0 S, P5 ^& J& Q4 u- t& ~* [/ d: i! [# W
unsigned char key=hid_keymap[row][col];
" ^) h2 A& Z! n( H6 f1 t4 s3 @! N$ d* p unsigned char *report =(unsigned char *)hid_report;
' @4 {7 ?. w o& |( J7 | char i;
3 ^" S9 ?( p9 l2 g& h
$ p( }- m" Z0 y& j. A) B) z% o; {$ Z! e6 T m
if(key==HK_MODE)
: S# w$ `: m5 P3 I$ t {
0 K- `5 i0 V4 x) Q8 w if(!onoff)) k; H* ^! t7 D5 L
{
7 x2 e* o" v( P5 g' w if(hid_keymap==hid_keymap_dvorak)
; |% K( B" W# y4 p {
- w1 Q4 t5 M' I( H. e1 O5 i, \8 d: l hid_keymap=hid_keymap_qwerty;* M5 {/ O/ t0 z6 ^
GPIOB->BSRR = (1<<2);/ q2 c! T4 E$ ^
}
- ?7 [" {2 {1 p' {. P. q( I else
; K( g4 @9 G2 E$ T+ K% e {* J) {2 e3 i$ |7 @9 J. I
hid_keymap=hid_keymap_dvorak;% Q l( u. ^" ^9 M$ s- E+ `
GPIOB->BRR = (1<<2);
! y- e7 j9 Q) Y }# g+ x S C; A3 k" w1 L
}4 o# D2 \; J V5 {# U( |
return;
" S* w0 Q& ^8 f$ @2 u }4 M, n, l& ^8 D$ Q+ e# w
9 |( X t$ Q: x
- Z: v5 _1 y& G+ @3 h if(key>=0x80) // Alt, Ctrl, Shift" m1 M$ l7 U A8 O' O) @$ m5 @& @
{
, u+ c( L ]+ y, | L uint8_t bitset = 1<<(key&7);
2 I- F+ R# l! K4 a" g if(onoff) // non-zero is key up( w2 E- t( P5 k' j% o2 M
report[0] &= (~bitset);
) U2 O6 Y5 J' O# G else
/ \: x2 r7 Z5 D0 L6 [$ M7 p report[0] |= bitset;
& P Z! P' b9 [, I }
7 q' I; x7 T7 U8 Q t2 b else- K. k" ~- ?3 }- U
{5 n; t+ @8 C1 ^7 L# s" z- b
if(onoff) // non-zero is key up( h7 e+ T1 w- O7 O( g1 b
{
- f/ Q8 E; d) _% |0 T. N for(i=2;i<8;i++)9 V0 P% P- S1 M0 U6 @0 T+ t- F) N
{0 F5 o" q- O8 D$ h" E: |$ U' s
if(report==key)
( f" w% i& w/ N7 j# k; s. a7 q% n {4 l Q- T( ]/ |* @- p+ h
report=0;
6 ]7 K* O/ j* S- I2 Z \ break;- D7 k' [9 ?( Q; m' V7 {; p4 L
}
3 ]3 M: w& i5 t& k: |) Z1 h( f; v }; _; u* x6 g9 `0 a c
}8 M& E' v8 R9 C2 u
else+ X$ J; w P: N- t6 w
{
. h w* K' [( b; R9 C* F for(i=2;i<8;i++)) h2 X7 l) k1 K5 K
{
$ S# m6 P- z7 l" G if(report==key)
8 g$ L) ]) [/ e/ o' A break;* w& i& {& n# O: A# m
if(report==0)
- {8 H2 t) w9 S" ~ {
* W( t8 {$ K- ~7 { report=key;1 T! H! O8 z3 Z6 Z: A9 C( c) h( a
break;
) K# o& Z- R; a5 q$ c& } }" T% J2 {( F H* y; h2 h5 c
}8 S/ j/ f0 w5 r, r1 d9 J7 P, \
}3 g0 O1 |7 J9 E* W7 {8 ?
}
4 i: w# [1 J- R T4 T# m for(i=0;i<4;i++)2 m6 s( Y" X9 U) L- n
USB_PMA[192+i]=hid_report;
# i9 S" a2 B6 S" t USB_PMA[5]=8; //COUNT1_TX
' T+ J8 s0 o8 E( r if(ep1_wait==0)" c. o7 n p+ O" g- k6 i5 [
{
' _, i' _7 h( A; t* G! M' x/ [ USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;. K/ ]. E- S F' c) `- O0 M
ep1_wait=1;* L+ l3 z( k. e+ o" h( M5 O
}
7 Y/ [9 D! A/ \$ J}
1 Z, o& e4 ~" |+ T0 S" ~
1 F( U/ H2 a: {7 A6 y* O4 ^
0 Q$ Z7 F2 |& [0 x完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
( W1 p7 P/ K( P. b U1 H
keyboard.zip
(8.7 KB, 下载次数: 6188)
, b' ? G- W7 T1 j
" H {1 M6 e4 x. p9 `2 C: P7 ^# Y9 Z9 D# W$ R' |0 q
4 ]9 U" B0 a' R5 T; b
6 `. t& X" J+ A8 v$ U |
|