|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 ! O% S5 |+ b# e0 v
' Z$ E4 T% h) M扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。+ X' O; d: i, h5 i+ G" B
; ~0 N! @ p$ L8 q# F要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
- y( e# _( L9 F W% o
^9 ^6 A. v I3 j- f
3 p T7 E3 h! [3 s; z8 vconst char hid_keymap_qwerty[14][8]={
, g8 [6 F# q5 \7 K, b( g0 E( F {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
) M' ~6 |! K7 K8 Z* C. v) V9 C {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},4 F! w) R: \; G1 _+ b9 C
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
3 i9 {/ N/ k3 O3 C( |7 G {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
/ l8 u) Q9 x/ c& w/ f6 O/ @ {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},; p$ w. f2 c5 H! H* F
{HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},7 G" Z- v+ d) h3 F k4 U; ]+ y
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
' q: {0 b; L/ o* ]7 l4 @ {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
' y$ m2 e; e3 C3 Y {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
, d( z% @$ N# X {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},$ M% C/ m" H2 D$ E) p) o
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},: B7 Q2 F. o5 d* C
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
' L5 i1 [, V$ f8 S* c" V1 D: k {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7}, c; m6 r: b3 [% [$ I$ W
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}6 K+ s6 Y2 I; h! C& F$ W
};
# k: I! B9 q- z1 p* \
% L2 y; h' V8 S$ O# A$ I) L/ S" G1 K
const char hid_keymap_dvorak[14][8]={( T) L* ~" }5 M* U- U
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},' j4 ^# S/ ?6 @( W; j$ X! U
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
6 Y& B5 t# i6 U/ o {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},; T' r0 X- Q& v; a; b
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
+ M" a9 Z: P' g8 B# W& _: z* ^ {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
& g3 K# ]; D T7 [4 t {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
. Y4 w( A0 @6 I+ T {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},' L' U+ z" k) [, i% j$ N' Y0 U
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},# y# [8 d: l, ~. @' {
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},9 S2 C0 P) |; c& g) m9 j: r+ m
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
0 B# {- E9 Z6 M9 ` {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},4 |: j% @+ D$ i8 ]8 Q
{HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
4 W7 z+ e9 z! i% } j4 n6 t {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},7 H! `0 R, z9 B; P/ F
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}8 r' x2 o. p6 Y; M# D
};1 Y: {5 @6 }& F. [/ c W7 {$ V% y0 c
$ ^% ` u, g1 \$ C; ~
# p1 C! k# d: T% C+ t! P上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。4 j/ |! }& v7 b" t( T2 W% y
/ P. s, X4 W: x" X$ UHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序: t4 Z# u8 \+ W0 b( ?
3 Y( G- `& `$ _* z0 t* fvoid update_key_matrix(char row, char col, char onoff)9 @0 R( D, f% b1 l* B
{
" l4 S' G& \0 E: U4 F& P# n+ | static uint16_t hid_report[4]={0,0,0,0};
8 h( t& V/ u$ ?* [6 | static char (*hid_keymap)[8]=hid_keymap_dvorak;
o7 U# n. Y; a2 Z# c3 i, S" I3 \& p& G2 k. P
- i8 @' T/ @# }( w unsigned char key=hid_keymap[row][col];
$ o0 ?. q. u, |; X% j unsigned char *report =(unsigned char *)hid_report;# u* M% r( U I) e1 J; v
char i;* w. _2 ^9 z5 W% G& w: m. ~- P
& \# e3 ?0 H2 `
7 C' ^% h+ C2 u: m, p8 L- ~ if(key==HK_MODE)8 d2 i+ j7 I" ] C
{
) P7 ^* @8 z4 Y if(!onoff)
4 Q* N1 b6 u& X2 }! w8 N {
3 Z$ X# O3 ^! r if(hid_keymap==hid_keymap_dvorak); v" U/ @+ R6 _( H# E3 z1 S
{$ Z' k4 x$ |7 p+ c9 |0 o l
hid_keymap=hid_keymap_qwerty;
! c! \9 Z; M+ D0 R% l1 m4 c GPIOB->BSRR = (1<<2);
6 F+ J5 V' t2 W. }# { }( v9 O4 g5 f, n8 z/ F
else
- t6 x; X9 q+ e, K8 x {1 M* A. Y1 n" D0 I8 r) M
hid_keymap=hid_keymap_dvorak;0 ^; }/ a4 t) A9 Q2 U3 {
GPIOB->BRR = (1<<2);, h. M$ w7 M. B, B) w/ J
}- L* v5 ?! w; G! f: h
}
M* R; @3 q" O- d" t# K2 ~( @ return;
+ A& `" w0 i. I1 j6 N1 T$ F }) W! s+ j. i2 @: {& b, n; A
; i3 s, H; \0 X) ]. o
' Y& r* |) ^3 @3 Y# l+ c( g4 @ if(key>=0x80) // Alt, Ctrl, Shift2 r2 W/ x3 i0 g+ |
{! g8 _4 {, f% g/ O9 F2 ~+ Q
uint8_t bitset = 1<<(key&7);
; ]1 o. Z3 K1 T1 N; b% @5 Q if(onoff) // non-zero is key up; d$ Y+ {" S% [) ~" }
report[0] &= (~bitset);
/ T, X3 e/ r5 a else3 P- D2 |5 ?) x3 y* u
report[0] |= bitset;
: A% X) X7 x9 D& C. |7 f }: k |8 v3 M5 d2 k
else
3 i0 b& d/ J6 }4 k {
3 w% F9 Q; S% h4 c6 {; H+ s! y" B if(onoff) // non-zero is key up1 D# w+ @. L2 {
{
5 `0 Z9 U9 p' n for(i=2;i<8;i++)* q( ~9 b7 ~, v' t% F6 C
{1 `- l7 V2 }- f
if(report==key)
0 Q5 n7 i& Y& r/ B9 Y {7 T5 m4 j* U' X5 |2 s
report=0;
! b8 Q; @7 j& ] y V break;
2 S; a! h8 Z& S) a0 R3 u }
3 ^, P( ^% Y) Z5 |1 ]* F }$ ^4 r6 L8 ?3 {2 Z+ g
}
( \/ s3 h' K, V! {2 g; W4 v else
1 ^& W% i# r' ]1 j {2 U0 R, v& F$ [9 z0 |4 g4 b
for(i=2;i<8;i++)* K, U' m9 f: |4 V Z' f$ u# t
{- N `4 N7 g) {+ u1 P0 [
if(report==key)
1 Y T3 E& } I4 |; G, K break;5 a+ Q! f! h& s. F* ]' ~2 E0 ~
if(report==0)& B1 J2 T8 h" C
{
( I8 F. g2 }% p$ x7 n$ S) H report=key;' [) R# j5 E7 F( j. D1 z) O
break;" P5 G& ?! d- o% _; I4 \
}
* A2 S+ P( G$ G2 h4 ~ }
* y$ u2 F' a; ^& S# J }
/ M2 }& s, n0 F( Q: T" ~7 n }! K# j. T$ z1 B9 d9 M, z' Z2 q' E
for(i=0;i<4;i++)* B+ a" N0 F/ A' r5 U
USB_PMA[192+i]=hid_report;
' v0 X$ \& u2 R# O/ i USB_PMA[5]=8; //COUNT1_TX3 o& I1 r5 ^: y6 D1 P9 o# T" H& D
if(ep1_wait==0)! E! W" x0 c9 E1 O
{
. T/ m" k9 g+ ?1 ~ USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;# M& ?0 f) R' V8 a" ~
ep1_wait=1;8 j: u$ r! n" E4 }
}
& z4 U) W1 `. w# q% Y+ z- y}
% B' |3 M# B& r& O [7 L5 e m$ \ n
) s. p! b) \1 u/ e0 ]8 f- c; K& {" K
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。+ l- X @- ?8 L% |8 {% T, ]" M9 c. n
keyboard.zip
(8.7 KB, 下载次数: 6464)
$ W; ?: K1 ?* q4 L4 x/ y
4 H% N5 H/ C6 q: l
3 r% ^* E. }8 u+ T" s4 W! p8 ?- P, D( z: n2 B/ t( ^' F: ~5 _5 Z. }
, m! F; x7 R$ W* u2 m6 ^
|
|