|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 ; t/ J) s* X+ r2 }) r
, ~/ i* H- {. h& R2 P- O. f( V, h扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。& q( D }5 j0 I3 Q: p" X" A8 Q% R
& l' }. e7 H/ P4 K
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
+ p V9 u" f8 X( T. Q
' y' v" X+ J! d( n% n# E/ }+ x( k% {' R8 R% \
const char hid_keymap_qwerty[14][8]={
' ]) W8 q1 h( r x {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},- X! Q: e: q0 d" j
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
) f; ]7 ~" J( F {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},5 m) ~" z( Z0 O l3 F9 j) Y- o+ b
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},' _! `$ d0 ?+ t6 ~
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
% P0 k* a1 j5 W% K: M {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},. f, ]" O. [. T' f9 I: _+ o
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
4 d# b. i! z8 I! W- h* {4 q {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},/ n: _* f; {9 I$ i! K. T# M2 L) E
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
! `& K: [- w5 O$ j% J$ w {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
3 _1 R* }( g% X {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},2 M3 ^0 A s+ p% E
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},- ?) U9 E: S! s7 k d
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
. ^: A0 [+ h Z F {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}& H7 g, k) E* c% z; S2 X
};
7 k1 ?+ Q2 ~8 u( l7 p3 g: f& R, X7 V5 \4 R$ s0 `7 W$ I: W5 P. O
8 Z* t2 F9 u# F7 M! |2 ]
const char hid_keymap_dvorak[14][8]={
# C+ I; F* G$ g8 P( p9 ~2 c$ W- [- o {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
& y1 M% r" r6 Q( t% b {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},/ A T( @# a$ C3 y( P* a, g
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
/ R# F7 i9 Y# }8 y# W$ O4 M {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
# f% U+ Z; ], V& |+ `9 i {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},! O: o Q! j/ M# M. I9 Y$ `
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
" W) C' i3 x* S) [; g" B {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2}," }$ q1 P2 `3 F; I: Q
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
9 u6 P9 `" a! s0 ^: a6 c {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},! Z: ~. z; K5 s2 y6 a5 R- J" }
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},7 e, G+ H- W0 O' v! ^2 D) e+ X/ U
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},1 y9 r3 [/ E' ~7 t5 W
{HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
{! C/ |$ x( H' r# f& g5 c {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},3 \4 J. J6 j! ~/ n
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
6 z3 D' E$ ]/ _' c" c7 n};1 i" @& \( t1 O# S u
0 E. x5 b0 S0 }6 N
& t+ W' U! ^! U5 O" U( @- Z' s/ u上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。+ `* V% ?. a& S% D
# ~# |8 G9 M0 t
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:* f6 y p' ~+ y) ]5 L- c% {
6 h, O: ?$ w1 Fvoid update_key_matrix(char row, char col, char onoff)
/ e D$ F! t# V{# l7 o n; R% }' V
static uint16_t hid_report[4]={0,0,0,0};! a) L4 w: {' |, x+ Z3 v
static char (*hid_keymap)[8]=hid_keymap_dvorak;
! c, |5 D5 w. C* [3 M v: k% v; `1 [7 h- O4 V2 C; @
@7 }- `) t4 e: Y
unsigned char key=hid_keymap[row][col];
+ H% d$ u; M$ d unsigned char *report =(unsigned char *)hid_report;0 j& |9 \# p/ Q2 n9 c
char i;3 P; v2 Z2 _7 Z9 Y* m! L5 U
5 b8 Z0 w) ^9 x- F( o1 T8 O3 {
}! _3 r2 y; f- Z' f; o5 l8 O if(key==HK_MODE)4 P/ V+ o4 f! d4 b6 u
{$ z/ g) q3 W3 u2 A% E' {
if(!onoff)
4 K) j* g; Q7 Y* A2 k' Y0 G8 q {' N L& h7 |% }2 w; ~4 Z
if(hid_keymap==hid_keymap_dvorak), `* Q/ |- _1 o( d7 ]
{; x/ G- M s. z9 g( W
hid_keymap=hid_keymap_qwerty;' S, Q4 Q- P S6 K( ]0 `
GPIOB->BSRR = (1<<2);$ m! y- X% k. h7 k, [$ E: O
}
9 i6 B1 d; D( _. N: p4 N$ @9 N else
! L) d8 ?1 O' y8 ^ {
, ?1 ?5 P1 G( u9 S* x hid_keymap=hid_keymap_dvorak;2 V% R1 z0 Z8 o0 v7 Q
GPIOB->BRR = (1<<2);
2 ~* F2 m4 d1 b+ O7 C }
' S& d; M _# x( I2 h P0 ~ }
9 W- I& U7 G* P8 E1 R' D( O return;9 n, q6 t& c6 N/ z) M
}
2 D4 W0 ?- w8 a* P) |
& b& ^( ]- ^6 m9 p( K* h l: J" L" u1 e
if(key>=0x80) // Alt, Ctrl, Shift3 U/ W- }# O9 p/ J" F
{) G1 @/ C: |3 H3 _$ o6 L
uint8_t bitset = 1<<(key&7);
& J$ H3 o* s; Y$ G8 R0 `; s if(onoff) // non-zero is key up
9 a; g6 t9 C ?3 e4 e# c report[0] &= (~bitset);2 S. e: ^: A* B9 `8 q7 E. V
else N% _2 s1 ^ a. \! ?5 T2 L
report[0] |= bitset;
; w0 U( l( l1 T* `/ h9 _% g }
, e# w1 I4 y# [0 F8 M/ j4 |& S, W else& R1 P7 M9 m" R' L" h- p% O
{' X/ [# K) E: v& B% ^8 f; p
if(onoff) // non-zero is key up
1 r, K2 p5 m+ ?8 `$ M) i. N, ? {+ K' f9 M# k7 d4 i$ K( h0 ?
for(i=2;i<8;i++)
: T# C3 r" j5 z- v e' O& o* [ {
3 v _3 ^1 _, g9 B7 C: o) A if(report==key)# @: r4 h0 T& d N3 Z3 [& @: j
{3 H5 w2 U s k# Y
report=0;
8 `; A' S2 x: h& Q* d+ B break;
- s: {' B/ \7 { }% G3 o+ \9 z- ^/ n, l4 J
}' J5 u8 i2 c' ]1 v$ }8 ]
}! p; O' d$ A* a; k7 B3 u' B, _
else
4 g; L$ s% O! Z: c, I1 Y {
( @$ \ |2 Q: V$ G for(i=2;i<8;i++)
. ^8 _1 d% D# y( B {! W. K) M) R; S% V
if(report==key)
% e6 U$ y+ V- _1 C break;" W7 @% O, p7 Q5 t( U
if(report==0)
1 L/ h9 |, w8 D( E8 G3 z: r( \# z {
5 `& L! D! z" z( ^% H% @: W! n report=key;
3 f) \: }% k7 q# ]4 a break;2 A# p. L l: u$ t
}2 A! b, {2 {( `
}
9 p3 W( T3 v5 ~' ? }7 ]. E3 \ C( N1 j0 T0 i+ j4 U$ ^% \
}
& }/ b! y9 }5 V# R7 @" y- V. l for(i=0;i<4;i++)
. G% w( ?) h' u0 P% O7 g. y3 l USB_PMA[192+i]=hid_report;
% L) V/ _8 L( H" k+ q2 ~% B USB_PMA[5]=8; //COUNT1_TX% x- N5 b5 w2 A0 K7 d+ [
if(ep1_wait==0)
2 w! y* }9 b( ~2 j7 H {/ g! r) e/ ^" A& V
USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;" N8 W% Q* H& e/ `4 t
ep1_wait=1;& q ~8 t" E+ o$ o; U( ~, e) B! @" s( N
}
1 \1 I1 @% J6 S3 r}7 q# Q% Q9 F! r- P& a0 L
' m( c/ D( m- {
9 N/ J J2 H6 Z( \) Q6 K完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。# L- b9 N1 G% ^5 H0 x N
keyboard.zip
(8.7 KB, 下载次数: 6694)
# T$ N0 |5 d7 ~
$ q0 |) R0 R: P# r/ J8 {2 \1 r
$ z/ g1 @# o2 E3 g* q- B f
5 Q6 N& u0 E8 j' g! E) b/ s, O2 K% W% y
|
|