|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 5 E* l3 G R! \6 C3 O0 d! ?
3 `9 s' W( c, S
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
: O, ]( L1 k$ L
- f1 {9 a) r5 k要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
$ @- O6 j4 A( ?) P0 T; b( T% B4 S+ F$ W7 i% y" j
) L' g) u+ B2 t5 s4 x
const char hid_keymap_qwerty[14][8]={! v. {' |6 g$ A! K! x
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
5 l8 Q! b$ C$ N4 _ {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},$ P) U0 r8 r) g3 ^6 {6 U
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
: x9 q' E5 c% L: [% a* l {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},/ Z7 T2 m$ g) N* @
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},4 {' s" m1 Q6 V) G+ n: _
{HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
& O, q. d7 O. l {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
# Z5 a; K0 x' h! E {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
2 Z9 N$ |3 g3 v8 R$ n _1 {( ~ {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
, G. ?$ ~ J }1 R. b/ {+ w {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
2 i* `0 R1 C* \2 C8 V+ `* w {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},- [: e1 |+ `" |) [. V8 _% ]' F& y
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8}," V( R U% I5 G5 s2 G
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
8 B, ]& b3 h6 U- V {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
: Y' Q3 R H# X0 e i};. _! b+ G/ u8 w: n' R
?6 m3 O( r$ ^+ Z, C, X+ x. O/ M, G |! W4 Z
const char hid_keymap_dvorak[14][8]={3 `2 V# T; \3 P2 `4 C
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},% h2 k, ]2 `7 e
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},5 g) T# B3 q! }; x: S) E, d
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
, y0 P& \6 s1 e+ v `+ z, e {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
% H. |; }+ H( l) r$ C* f {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
$ l" S8 t4 Q( R7 r {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},0 N# `* s; o0 P
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},& U+ m5 s( X3 u! @9 N
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
, r8 |. |7 M0 K/ i/ E2 V {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
1 W' } G8 J( `1 g# ~4 ^ {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
- x* i; u u K! n- X {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
( k3 ?2 B2 e( w; F$ h$ s( p {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
1 l# [8 i+ |6 M* Q/ M$ A4 j& r {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
) i. A9 L% k- W% ~ {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}9 |0 y. J, L7 A' f) o! D
};" Y* q8 ]8 a2 Z R5 l' X/ I3 u
' f& n# u9 E6 g* [+ i, N4 m# f& Z* D6 v- R2 d8 a" v3 K$ m
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。8 Z$ T. H% t/ i/ U& ^
3 Z4 G0 y) Q- b1 X) t6 lHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:- y! m0 G+ y8 s$ o. B1 i
8 g* l- W' h% [
void update_key_matrix(char row, char col, char onoff)- H* ~; C* R; o- @3 @% O
{
8 J* _8 Z& E) w, P static uint16_t hid_report[4]={0,0,0,0};4 {. H2 Y. ~4 p5 ?; l4 Z
static char (*hid_keymap)[8]=hid_keymap_dvorak;7 Y# P; x" h8 G
. v2 l5 A$ r" ~1 G: W" `- U
" z! L) w3 A* ]* }4 f6 x2 [ unsigned char key=hid_keymap[row][col];
$ c# L% `1 N3 d: f9 G unsigned char *report =(unsigned char *)hid_report;# W) G A) }' d! F# J
char i;: ?6 L0 C4 f1 S) U
# S* V9 `& F$ I C9 {4 ?
/ y+ f) z8 ~# a5 b0 W, i+ d7 U
if(key==HK_MODE)
" W' N6 P; w* Q) y {
3 Z) g$ ?! {. D: ?- I if(!onoff)4 A' K2 X" r4 J/ y# O
{
0 F7 I. a5 S2 b x, n/ q( l if(hid_keymap==hid_keymap_dvorak)* F8 ^" M, n4 ?% A" f: v% I
{
# A- T# b# x3 @- M/ Q- w8 R* h hid_keymap=hid_keymap_qwerty;
' B% L" D4 s1 G GPIOB->BSRR = (1<<2);% Z# W( E) v. W# l
}6 i1 G$ w+ Y0 D' A( B W% I
else1 R/ I3 ^# e5 g( ]. h
{
* b* p" S) U7 C) `! t; S- a hid_keymap=hid_keymap_dvorak;9 y$ R O; F& F0 E1 [
GPIOB->BRR = (1<<2);
$ L6 T& @# `! h. H }
/ T y' r# k' K7 P4 M0 F) D( G% s }7 o4 T( C) V+ R- ^ b9 B
return;
0 q# O% s, I& |4 c; T, Q" J$ { }; ~, f5 z0 Y( v
+ J# \; C, B. w5 B+ s& X: Z
; e/ M, u" |) s3 x if(key>=0x80) // Alt, Ctrl, Shift. G% \$ ~6 ?( N2 r
{
; \" P9 ?/ a) }* P, r) _1 q( ? uint8_t bitset = 1<<(key&7);
' f" k0 E6 D7 E+ s; F; ^ if(onoff) // non-zero is key up
6 @) P6 `) S# ]$ m( n% U/ x& H- x1 K report[0] &= (~bitset);/ ~8 g: p( U1 Q/ {5 }( Z% h/ S
else
7 L6 j' |6 {- X! `0 a; N! E report[0] |= bitset;
7 {* n- L) m/ S0 n h6 J$ l }# [: Q% w% X0 j9 I9 d
else
6 C& z& v' _% g& g: j2 [ {- i& p' k |! j2 U; A/ L0 E
if(onoff) // non-zero is key up
+ J0 o; k, L4 X4 y G p {
! o) a+ \* F' _1 S# D. T for(i=2;i<8;i++): `, {: v" B; S) Q# @: e7 s
{
6 T' X0 k1 s" m$ `2 r if(report==key); w, ^ w5 ~) ?/ M$ x- L2 i
{
1 u+ j- x$ ?% l+ Z; P/ ? report=0;
@4 H8 p* w: }* g: p, m3 Q9 {" D7 | C break;
+ H+ i, c ^6 T- R, c }
1 M% G& `7 @6 l/ v( m }
! h% Q; g" K* ^% G2 U0 } }0 O9 `+ D# C9 e& i
else, w; L8 k* c% u7 M0 W+ b
{
; W% \$ `) K/ {) b) D& g for(i=2;i<8;i++)- }- m) v; ^# ~9 J
{! Z. u* w! D" \! T5 j$ |' R6 H
if(report==key)
% i3 {2 s* a8 {/ q. f break;$ o% F. q) F: g- y) Y
if(report==0)0 s& V7 S- u$ u9 V9 k! W
{. C9 c# S0 ?( e$ O3 t
report=key;
3 Y$ w7 v2 ]+ K; W+ F$ _- _+ Q- ` break;3 T/ @7 W8 V' w% X
}
" H( D9 V3 i# J$ L/ D }! ^" y1 O% T5 c$ H$ V5 K
}9 e% C1 C, q$ [2 D% o
}
c+ s4 d% Z# n" `% K: y for(i=0;i<4;i++)
- ` O' ?: o6 \ USB_PMA[192+i]=hid_report;# | L( i# ^+ N2 |' t% x
USB_PMA[5]=8; //COUNT1_TX* ?7 x& q8 u# P5 U1 v9 D
if(ep1_wait==0)) } r$ C I4 E# ~, w
{
& E, {$ S4 W7 q0 T8 g USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
/ Y1 a- I! \7 R+ ~ ep1_wait=1;
i9 m/ T$ M5 U2 w J }
1 W# d2 r* E7 ]: d e}
- I' f* \: b* e$ f" d* O- ` K3 e
7 |- P& b7 I( u" f* G" @完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。* W" F# q! f3 O' H: B
keyboard.zip
(8.7 KB, 下载次数: 6302)
* s8 C' J0 o. D) [
- z: Z X, K3 n; M- N0 A5 s) A* A
/ U6 Q3 K' h7 B
; G3 ^ } H Q' q+ ? |
|