|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
: \4 j) ?: O3 q! h* n% b* i6 T# P5 b
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。9 a; N$ E* J" J) F! N8 @
; J( u1 P* N# {2 Y1 X/ p
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
/ U; ?1 M( Y0 U6 W4 F( K6 K+ J) b5 s. ^) Y) N8 z
e: _0 _7 p) o5 ^" c4 d( y
const char hid_keymap_qwerty[14][8]={
2 O! p; b: P S) J$ s {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
/ U: n7 b) y' M1 T, R, ^+ d9 ]7 D {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},9 ]4 Q: m" L; t( [) A1 Y9 Q% K' s
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
6 h5 ^3 j2 k5 s# x' y* ~/ ` R {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
% W' ?1 a6 B% L b$ K% u {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},. h# ~$ l+ C; q* q& \7 v, ]8 F7 O
{HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3}, A- J- J9 g6 L7 W- X9 ^
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},/ p1 D3 X; r2 i0 K1 }. K6 U
{HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
3 J- z I5 ?0 x% N5 V: J {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},( @$ t+ v4 A8 s! O% J0 X }. c% ?
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
H* X3 b' N5 }& p8 P {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},! ^4 D0 S" k u! q
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
- u4 S, ]% j9 _" w$ ^ {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},8 C& H( I7 I8 X
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}2 H# V' C1 @/ l" J$ y, ]6 ]- z
};
; s' h- _$ K+ x: i' s
j2 a" C* {$ p* t& [2 b
. ]3 }0 g; e- H' Aconst char hid_keymap_dvorak[14][8]={
! [0 Z; S$ @; X! d9 t# i {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},2 u* e% W% \. l& m* B: ?4 O3 J
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},/ q( O+ Z% d5 n& ?) c' c& d0 I, E
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
% Z+ ^0 r1 b: U- [9 P, o" t2 h {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
* e/ E8 @# d! n9 m1 k u0 J {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},* P2 V6 `9 |+ x. n) Q
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
. }1 k3 ~, R. I2 x# _ {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},: R" J7 P: f7 M
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},6 I$ @& g/ D v1 E
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
# B5 \7 P: K* \, A' | {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE}," @# u6 M0 `# D2 S( d$ X: p
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
) }& b! t- \! m! [ {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
/ c7 M8 H0 ~& R2 \ {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
3 C1 v5 `1 n! [* {! X3 R! R2 O {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
# Q3 t+ M" \7 ^/ H};
( B2 k# z8 D% p: q1 Y" w( @. q3 u
' l! E* b9 f( `. L# F5 q: X, @9 C. e
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。" [, N$ P: g7 H4 n! L( n& ]. z
; i+ a0 _6 B% L4 j( s Q( w
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
7 g9 V3 W. s# h8 D W! `3 i0 Y, a3 _7 v4 X2 q
void update_key_matrix(char row, char col, char onoff)
0 l) T# H- R" r5 K{0 z; |( I) d8 I! u9 e7 n" D
static uint16_t hid_report[4]={0,0,0,0};
9 |" O7 P- X0 T3 P( [3 l+ [7 T static char (*hid_keymap)[8]=hid_keymap_dvorak;8 d( r% [' _8 o4 i
; e4 s& S5 ]0 ^2 E4 Q; g; ~6 ^
& \8 F4 w6 R" c |: A- D8 I unsigned char key=hid_keymap[row][col];
& t% R2 E3 F& J& Y9 v7 |. l T2 _ unsigned char *report =(unsigned char *)hid_report;. P' Y& H) F7 n& p1 s
char i;0 i4 }6 Q# s7 k6 h3 f1 A
$ N* j% A" j# @$ j0 S4 t/ J
# s# K0 O- e, H0 U if(key==HK_MODE)4 C* P: g9 S, w5 V4 g
{# A( t$ x0 Y6 m" a" K
if(!onoff)
; ~; Y* Z3 |; f$ U# r/ f; ? {
/ S* C; a/ u/ Q8 Z if(hid_keymap==hid_keymap_dvorak)
$ p F! d( s$ [0 e! @* h/ t8 h7 r8 V {
$ l6 e$ x5 o9 O hid_keymap=hid_keymap_qwerty;7 \! G! j& f' O. D8 r( L8 `
GPIOB->BSRR = (1<<2);
/ V/ ?# r1 A \! U, |: p8 f1 X' J }
. j0 |4 y( r9 z. m7 e5 m else" y1 M9 M4 `$ a; S% D0 U
{
1 Y' @/ k6 b4 N: o/ w) E, v/ k& @ hid_keymap=hid_keymap_dvorak;. ]$ z0 O4 R( V6 k1 s, D) a7 m
GPIOB->BRR = (1<<2);4 ^# D. Q# L, i- L& i
}* ~0 K& k0 g/ f% p$ c: H$ _
}
9 S0 ^& |" p6 m% g0 [ return;
, Z$ W! F/ s2 B* ~+ ~- M3 S }: u! p! @9 v% |0 O0 b& r
, d' g. W' E! z, I& V% k7 W3 {; ~2 f# h
4 c- t4 A3 j: X# g if(key>=0x80) // Alt, Ctrl, Shift) W. {2 H6 m9 ]8 X j5 o
{
0 r2 ^# Z) `; L/ U8 e uint8_t bitset = 1<<(key&7);
' ^1 u2 T7 I8 c \) B' h3 l if(onoff) // non-zero is key up
4 C0 {+ D+ a, E5 \6 M+ q& b report[0] &= (~bitset);
4 y9 J; `$ _% W" ? @) X3 J; G$ n7 j o else
2 l) Z' ?1 H: @# Z9 m) N report[0] |= bitset;
7 E* i7 Q1 W9 e2 h }6 a/ f: d- c, X! o
else% p3 G. n3 A- X. r& R; l' a
{6 D/ W |! b9 Q z- ?
if(onoff) // non-zero is key up2 h# K: M. F0 C; z9 Y
{
+ L4 ~" J( L+ _0 i. p/ t- ` for(i=2;i<8;i++)+ }# t& X" T1 N7 X% m$ P, `
{* Z D. q5 y7 C! W/ a% ?
if(report==key)
- \- f" v3 q" ^* F0 T: j {
- m; i$ ]+ v# u; g& e report=0;9 g, ]; Z8 M) j1 W
break;$ E( J1 P7 p2 u7 R- T
}
/ t. {- Y M. q# E6 T }
3 }6 e& Y* H4 ~$ j6 e* F h }
1 f/ @- u% A4 W1 W) }$ a else# b. l+ s$ V$ G+ p
{, v$ N" I }. K
for(i=2;i<8;i++)
3 p: I: I! r% X5 [9 f$ C {' T, q9 t8 m3 H
if(report==key)3 V1 U) ^$ v3 X( _' j8 a
break;
( X' d l6 ^2 u- v, n if(report==0)
7 [+ c2 t, ]1 M k9 G0 N0 A {( r- E r; K( X$ |% {) E
report=key;
5 A7 z; ~* l" S$ v2 o+ C' C$ h) ` break;
6 W' i8 o6 y( N1 A/ n% o }( J: e& @6 b" c1 N! O
}! `" O7 o# P* _. P! V' U
}
2 {" V$ J/ G2 N" n7 g& K }+ |2 `0 j8 W% ]
for(i=0;i<4;i++), `; k6 T6 b8 `- N5 q# i
USB_PMA[192+i]=hid_report;
; V C( ^# R6 z/ M- k. y USB_PMA[5]=8; //COUNT1_TX
% j) O( ? f, x# y7 ]8 \ if(ep1_wait==0)
- }# e) }$ r, D# d {+ T# I/ o6 t R! G1 h! p
USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;2 ]5 j$ H0 I" M
ep1_wait=1; _7 ?* O/ f% m8 ~: S5 B2 V
}/ m* M: W4 U- L# `
}) ?& b. u1 C* @ ?, C
0 B: n0 T" U4 Q1 p8 |$ }, Y
1 A& o5 [3 T) |9 c完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。& D# Q# e# T% z* Z9 u% [
keyboard.zip
(8.7 KB, 下载次数: 6418)
" M/ I& v4 r5 K+ |8 N! c
& N# o/ e* l: K& c3 i7 t/ ~ h6 }( h
+ n+ z8 C, ~4 x& t" Y* J' q N- y' E
0 Z6 o9 q- o8 n0 X: d* ` r |
|