|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 # R4 m4 {# ^3 V
! M$ z A, ]& o; u% m
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。) b9 W( A* ]# ~
# e2 \" _2 M5 T8 S( e5 j- ^
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:* E; `" C% Q- l. T
3 \5 j1 M: j. E7 z: d; k; d+ x
' N4 @5 { a. z6 Oconst char hid_keymap_qwerty[14][8]={ F$ `; S+ c7 ?; w" s$ ^3 f
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},, C4 I3 [# R P/ V" q2 |. T% ~/ X3 N
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},# Y3 h# @+ A& W5 E9 O& R
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
. K, e: Q; y# H* } {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
& O. w" Q4 P4 \% X {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
. }7 m9 `4 K' ~) Z+ K {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
4 @3 T% l+ E* N2 t6 @; i) L# X {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
; q: |/ ]# m' I3 M- ]8 E {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
6 |- H! V$ Y. M1 f1 e {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},( ]' M$ J" y% c- }/ F' s
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},5 ~5 o g' g* G4 _
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},- t- r M4 j% F' { H5 ~
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
4 k4 e8 B& p, O! b5 z {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
$ m1 s8 a* B# x4 J* ? {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}/ \0 `- t0 ^, _( v
};: a2 Z6 W' ]& Q0 Z: b' S* g
. s$ g5 Y4 @4 G, H7 G& ~) d9 h! M' Q# D- u. k8 _& @
const char hid_keymap_dvorak[14][8]={. a* t% u' m- k
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},3 ~* Z! C; Z1 {; S
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},& d4 d6 c4 j, b' Z! }, P
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
; G6 P" e( M' U$ h' y: m {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
8 A# Y H. ]" u+ w {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},) j/ q+ ?! ^! `$ P) |5 |
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
8 I3 ?5 c/ h! W* h' Q3 S {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},3 G8 R$ V9 U" j' Y# M; G, b
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
) z" c, I2 r: ^4 S! C {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
# K: k0 M! s$ r7 V8 P- F! L6 ` {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},) E h! e2 x5 V7 n4 P. O
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
3 P! v! V# t/ B4 b# ]& g1 g {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},4 L. `- L+ Y: K7 i2 s
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
4 m2 s6 |% {4 }* f# ?# ?! T- U+ | {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
, S% o; y0 I @# Z. C" A6 y};. X+ |9 a1 R5 z3 E/ J
2 R. @4 x7 `/ Y# |$ o
, W0 G3 Y4 K4 |! Y
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
5 K8 ~5 u9 \) W1 b+ ^3 U& ?0 I/ L2 _! O9 P$ t1 f
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:/ Y6 @/ s6 O9 O5 ^3 B n8 _# L% E* H. t
9 V- ]: L* e; d7 p5 Kvoid update_key_matrix(char row, char col, char onoff): u& \, i0 g7 y0 a( y; \
{
- N {3 M% F3 c& t3 f1 q static uint16_t hid_report[4]={0,0,0,0};
6 A- a7 ~& O, D. {; P) s static char (*hid_keymap)[8]=hid_keymap_dvorak;
# H0 n3 `9 a4 V" y/ f+ ~: L6 R) L- a9 M
" F. Y4 V: D" L8 w
unsigned char key=hid_keymap[row][col];
5 z1 j4 J0 @$ X7 s2 E% E! w unsigned char *report =(unsigned char *)hid_report;( ]1 s# J; a* _# G4 B8 p
char i;
8 W( g4 T! G0 b+ n" T
/ y2 E& `4 S' w& s! A7 ~; U
5 J( i& g* |1 B; l5 A ^% T if(key==HK_MODE)
6 a; k- M# t4 w! V0 y% N& k {) p) J q1 q5 V0 g) R6 ?
if(!onoff)5 n c; \0 Z) w1 ^+ a4 ]' a
{' J- ?8 N6 I9 R2 ?8 d
if(hid_keymap==hid_keymap_dvorak)1 \5 z0 f. y9 W9 }" {6 ]" ]
{. R0 @( g# r% Q" m' A+ B$ u9 [- n- T, J
hid_keymap=hid_keymap_qwerty;( x# f1 t5 b2 o' x S7 A
GPIOB->BSRR = (1<<2);
9 t/ U' C; z( C# c3 L" | }+ |4 w! {# l: f# U1 A) h I6 P4 G+ `
else
. F" Q% Z( {, A I, j {: x/ }1 @( J' F
hid_keymap=hid_keymap_dvorak;7 V) @1 J" |9 z0 g, P( K6 N
GPIOB->BRR = (1<<2);
6 S6 q0 U- H2 _$ v% l/ k/ I4 V% G }
; N2 A9 _( Z( v9 N }
# }- ~5 S+ v# @! F0 U return;3 a- U) F/ _% u' d$ E' X! d
}
6 z. @8 n+ G, \8 X' d! D
6 M. ~7 H" S0 R9 g8 y5 U1 i( ^3 V$ M6 p
if(key>=0x80) // Alt, Ctrl, Shift
' Q2 B' x1 U5 h {4 {% b/ h/ b6 u1 v. I7 O7 e1 \1 j' l
uint8_t bitset = 1<<(key&7);+ C. I$ {5 [3 X. `
if(onoff) // non-zero is key up2 Q# i' c/ t" ?# {" Y* z2 r+ \6 s
report[0] &= (~bitset);
/ C$ ]) O# x& e0 r' S& ~" O4 u7 p else
B# n5 S6 }# |1 E& r( K report[0] |= bitset;
( I/ q' z8 A! o }
9 ?3 z& P3 Y& k$ v+ p+ D7 n8 u else
D6 \( e* s1 _8 Q: H {
5 S1 {. |9 {# ~) x- x if(onoff) // non-zero is key up6 M& E( e( X" ~6 l6 N v& R
{
4 T+ I8 [' V" L/ w for(i=2;i<8;i++)$ K+ d: ]; k$ n' Q# v6 C; J+ U
{3 z( o/ b7 ~, I! J x7 @
if(report==key)0 Y7 D- V$ w0 N% ]" m( X2 F6 }
{2 z8 E. e( E: V; T6 ^3 Y
report=0;
5 }3 w9 u0 D7 _2 {! Q& I) {8 H break;6 C/ X2 y q# Y; I! c
}1 C7 S d c4 ^7 r5 o5 r" U O2 e. H
}
% P1 ?- E/ J9 w# H }
' b1 q. y. B' B1 E( c& R3 Q% w6 V/ \. n else
4 a& N5 f U/ ?3 ` {
: N+ Y6 T* G& ~; q. W0 v% b for(i=2;i<8;i++)! S; ?( n0 C3 r( u
{
! H b3 u6 C* ]2 P. I+ ?- K- h if(report==key)
$ p" l' m4 c; Y6 W& p break;
; q3 {& s; w" [2 I3 g if(report==0)" ` m2 g/ M2 a1 u7 z/ q9 n
{, t; Y" {" [$ v: b
report=key;. z8 b/ p" ]5 ^- ]3 }
break;
. z0 V3 U+ \ B2 a% E }
; Q( c5 H# a3 Z; z; s2 v0 E }! I, c. _6 w- i! |& X- f
}/ V. d+ C Q a! W1 ]3 t
}4 A( x' S* J8 H( Y( D" ~2 ~) P0 {
for(i=0;i<4;i++)
+ n/ K% S: j* j USB_PMA[192+i]=hid_report;
# w$ e: A! v: U) K* i USB_PMA[5]=8; //COUNT1_TX) J" ?, `2 G1 I8 ?
if(ep1_wait==0)7 V" A# _6 V. `8 T7 y
{
$ N" K0 C* i' w, o# U USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;/ _# s/ W; {. f1 T& S- b r" X( a7 t
ep1_wait=1;
6 h3 \- w# @+ x+ l: u( d }
* {+ i7 C5 x! |5 e}
: U; [7 e4 B# |+ v$ ?
+ M* ]) q* a1 o' {* e, ?
. q/ D/ A$ k: m5 _- K7 K. H. ]完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。9 S8 V0 N- D5 Z8 G1 ]5 Z# z5 E, {8 U
keyboard.zip
(8.7 KB, 下载次数: 6484)
2 X& H' b0 Y3 X! h9 N& R& r
e3 i: u! O @3 G2 q5 t5 S4 z2 s( I
( a9 M( W# [' p! G$ E. M+ D
$ f6 b' D8 N+ F
k& K; i/ J4 F2 f. V2 \ |
|