|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
# J- [/ g7 ?5 j7 g$ r4 U/ d9 N( T; A: g( I5 ?4 s3 Z6 q
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。# q5 Z4 ]& w/ \+ i
! E- W5 f/ m2 u+ g, V要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:" n; p0 |0 d/ I, c/ q1 k* K) H
- H% G7 u( T' H
0 k7 o* n' p( gconst char hid_keymap_qwerty[14][8]={( d9 V4 S5 x( f
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},0 L7 g; D+ Q( v
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},- X- \. W. p" j4 ?1 l1 `3 Z. F$ n
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},( v8 G1 B. s$ }' q# x! A- R# R
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},# Y5 w X5 ?8 x" A
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},0 o+ u) l. A# _
{HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},4 M, b* p# v$ d3 }. ~
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
; X4 t' K( R% v* I6 r {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1}," A7 H( N# B. P5 y3 u; S; ?) C! X
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
" ~& e7 B. u- o7 T4 z e {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
3 E, K" C8 J. @& }% g% z {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},* C2 f; B! ?. R/ i# X1 ~+ C
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8}, d) m' K2 d H4 F3 ]' O& Y
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},2 U# ?1 ~, \& Q9 c
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
4 w6 A, r5 l( K; k};3 g- t2 U# c( `" T5 r- y
. M8 n+ S+ \, y8 c) S* _2 H$ N
; u0 [) a* ^7 T7 ]9 o! O+ N! Dconst char hid_keymap_dvorak[14][8]={
( `* X& n0 c& K) |2 M' V( b7 l5 Z {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
3 Y: l) k3 M) E! T* l1 O {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},4 k$ A/ J9 x( r
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
j9 w* c7 ? K {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},# w( f4 b3 B/ O: ]0 ]0 h
{HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},- ~- [ i3 j6 m7 o+ Y# Z9 z
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},, ^; \+ u3 f" ^3 ?
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},5 Z0 Q7 M" S1 W
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},2 G+ M2 T* N0 p
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},: T$ z3 I C8 L1 u
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},4 T; C f9 O3 p( [1 {
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
/ A' h) R2 O; Z3 K& Q8 _. I {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
p; a% ]! t5 B1 U; M {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
: h% R0 M7 _6 e. U, i {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}, h) |# Y* M7 V5 d2 f: m
};
% J: s# c; j0 h2 F/ f# F
7 \5 g6 H! w/ a1 m; m9 S, I3 f$ ]. O+ A2 r) c9 b& P
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
- P: N- |! m( F+ P8 n6 U6 p* a6 E% K
: U& w# w( a2 a; fHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
8 [' k# T; i* s5 e5 I& f# _ g9 x- z! w7 K+ {
void update_key_matrix(char row, char col, char onoff)0 ~5 `2 j0 [* K
{* A3 W# w" i0 t8 G2 b
static uint16_t hid_report[4]={0,0,0,0};
5 S6 m* o( E) w$ u3 d; R3 ~% H static char (*hid_keymap)[8]=hid_keymap_dvorak;3 S( n/ x. a* \ ~% \
7 @8 S. A# L, T3 E8 r. c& [
7 f* A4 J7 c3 [ d9 u5 x: O unsigned char key=hid_keymap[row][col];0 C) S+ @( s$ v( j0 I. _
unsigned char *report =(unsigned char *)hid_report;2 q8 q0 ~+ P. S# Z/ A1 G1 Q
char i;1 t0 ^$ R6 N' d1 B5 \
, F+ j* r' A' h) X- N5 v1 f" K
6 L; Q( T _7 R7 K( a g if(key==HK_MODE)
5 d; {$ D. K7 y9 R {9 l! Q1 Q" \' q+ d9 w
if(!onoff)
% `5 M; a! r8 D. j0 @ {
% ~& ]4 j0 M* s if(hid_keymap==hid_keymap_dvorak)
, a9 I$ t6 A( j) G7 r: C( x4 o" { {$ ? s3 s' J3 z/ c
hid_keymap=hid_keymap_qwerty;
8 x6 S4 [5 t8 X! N$ t3 v5 I* H GPIOB->BSRR = (1<<2);
+ G9 H1 P _/ ^& L( |" u5 e }
H8 G9 ], }9 G! q else$ i2 I V* q( Z" X
{
; \4 H) G5 D1 Q' a$ z0 J9 k hid_keymap=hid_keymap_dvorak;
# {' I: Z& V5 o; ~; i1 ] Q/ V GPIOB->BRR = (1<<2);3 O& J$ M" W6 z! [# H# e( L
}7 W1 _) r7 M$ ]" k0 ?
}
; R% e9 O2 T; l$ Z2 H4 _4 S- i. W return;
3 m' x2 @) @, n5 r' [ }1 i# t; V# R$ D- ^' v5 W6 ?
5 Z- x9 j* _" K4 E! ]' f* u6 R- Z: T& b8 c6 D+ d
if(key>=0x80) // Alt, Ctrl, Shift4 w( [" ~5 }+ K8 J" X( _# l! c
{
7 x5 K! d, W W5 N4 E) ^2 c uint8_t bitset = 1<<(key&7);
1 d% L7 `1 P( A. Y7 a. O2 J i4 A' I if(onoff) // non-zero is key up
( _4 Z' \: O8 [, {& b report[0] &= (~bitset);
; g; K8 i# F4 R9 Y# ^( J3 h6 d: C else& U- `/ W& y. y( |2 S; ?
report[0] |= bitset;
! d; v. ^; Q& A5 G' R/ G }" w5 B! ^) T/ v
else
1 j# O; P( {* l9 G, s9 ^3 u' r% Y' d {
3 S! G( ]3 ~! b0 n7 S$ e if(onoff) // non-zero is key up
* F% j6 o: _! n1 O {: ] f- U0 |& ?0 I- g7 j3 z
for(i=2;i<8;i++)3 n1 e! k; u5 R# n7 u |
{4 G b4 c* `+ h& n5 f- p( M
if(report==key)
C/ q) U; k8 [$ i% }: K) r {8 |9 C# N% H+ p/ W9 G
report=0;
3 c# G9 L; k' ~; v5 ] break;- y" ~0 S5 i6 B( g: K- f
}
! [: J! { o6 j9 _: C q }
' _/ Y( z$ Z4 {# V }
' `+ r; K! _# ^ else3 A6 o' }& n& M! _5 p$ M9 Z# _
{, v. z4 F" e2 Z8 c
for(i=2;i<8;i++)
?9 n& x! b1 P) y, O6 C {
8 Y9 m/ }2 e) ^# J; g. _1 k if(report==key)
( z; k% ^% ` d+ D" ` break;' O9 ]! g B# o% Y1 u& w# e5 c
if(report==0)
4 Z/ C! q3 T% Z# M: h {* X7 R' _0 ]7 \ p1 o m* h0 W# C/ S
report=key;
) j; n4 M3 x3 u+ l0 I break;
, Z- Y# d+ z: _$ W1 @2 H7 w3 N2 F# D }. _- }0 L3 T1 r
}
& B2 D, p, D# d }2 }; B8 H* U) @' N- x
}
( s$ f; }0 F/ B" N for(i=0;i<4;i++)
, L( g1 x: K7 |6 q USB_PMA[192+i]=hid_report;
" {/ x6 ~+ K/ A1 N3 G( E B, x USB_PMA[5]=8; //COUNT1_TX# ~. \+ L) a2 o
if(ep1_wait==0)
8 t5 ^+ ^2 J U' c: @9 u" h; T {; H4 Y; m g* X) i H) F
USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;6 X7 _. V* N4 U7 m: p c, l
ep1_wait=1;
- V- e4 J+ @: C5 r: ~9 X# p }
. o- P+ U% ~3 C1 T" y7 m} Z4 B1 l- F7 d0 _& G
: r/ m3 q; P2 x/ z$ E
' `/ K# N1 N: o/ c9 c( e# X
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
. H/ p Q5 B. s, r+ J
keyboard.zip
(8.7 KB, 下载次数: 6694)
2 R% J* ` M, Z- a2 B
! Y0 e$ n2 y; a( w' H% y& B* l6 L
. W5 g$ \! f$ L$ p) `
6 Q( m3 ], L3 b, Q! U; u' D
, Z1 G( w2 V4 U6 x4 G( K' {2 W |
|