|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
& D3 P4 o$ J# q% A! c, K+ L1 F0 e) A/ @/ r' t$ V& V
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。8 \) k- j* f3 u2 e/ ]7 F }! Y
# U! C* N m3 G, K$ Q S/ ^
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:5 h- L: D. P }4 Q2 c
( Y- m, r2 X3 E( d. ]0 B( H# k9 c
! f+ n- G; J# ^& Lconst char hid_keymap_qwerty[14][8]={
p: z/ {5 B% W {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},, C' T4 A9 q5 ^& }6 O$ @
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},: N* z0 l0 w. ~7 ]4 Z; o( f( W" p
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
+ U0 @( w3 n! y* s! a V {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
# M# j9 V. w; u" B1 |1 G {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
q ?" g8 G$ s {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
6 v- I% Z6 X! C, Q h; a' X! ? {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
3 N" w" F6 Y/ K& J& I. \& d {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},) \: C& s, t& R
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
5 z' i- Z. j& W# I) @4 C {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},* H, Y! N9 ~6 S+ r. s! x" @* f
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
0 L6 w) z8 o8 d2 w9 U+ a5 a" e; g5 H {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},& T# ?- {- j6 E
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
1 o# A* x! L, l {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}; L" Q1 M9 a* ]1 P3 C$ A3 X
};
- r0 A3 R4 c, C: A1 n" r3 l
. X6 g2 F% w6 S# D
1 D# O5 q# r0 c2 h" e! [* L* \const char hid_keymap_dvorak[14][8]={8 Y; g) ^8 D3 A
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},2 R7 @! R5 G$ M/ i6 y( i
{HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},8 o* B u8 p, g h
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},( r4 s' @2 C4 j, \
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},+ G- Q9 X5 b# ^% G3 _, m
{HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},- L% n. R( N6 Y
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},; U& ^% l5 o( h! G h7 u
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
- u$ }; G8 C! L0 n% D {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},; a# n/ B5 d Z" j
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
9 E" k; J# G4 R {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
, \8 W# h& z+ r# t {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
. O# m& c. M/ z8 Z( m) G( ^ {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8}, T v( N0 X+ Z( ?, s0 m
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7}," c, Z# u7 u o3 [# v8 \- ]
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}! D# R+ M6 h. ]3 u4 {$ ?4 d
};4 l2 {8 X6 g8 g, o
4 z& P' e. Q. D6 J8 V, r
4 h, m: C" i$ V# S" G
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
* g) H3 y2 O; {! |" M# A D2 ^8 Y( O/ x! W9 R/ E5 T$ }6 e
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:! K3 [5 r( x G1 u
4 w$ i- G9 O+ q! U- f) m
void update_key_matrix(char row, char col, char onoff)& e' t' h. F- s; A" ?) S! p" T8 s
{
' M* [* X* B" `# y( P static uint16_t hid_report[4]={0,0,0,0};
* c& z& _2 ?/ w2 y L! t ^) c static char (*hid_keymap)[8]=hid_keymap_dvorak;4 ?) {% s7 f2 |& z7 m0 V9 u$ y
% T: I) x5 S( \& r9 O" `
( Q: v% m2 o0 k& s) D* s4 W
unsigned char key=hid_keymap[row][col];) o5 m$ \6 p7 Q& p- Y
unsigned char *report =(unsigned char *)hid_report;
8 v9 H' x# j% A' f V( G! @! G char i;$ X* y! ~' E i% P& E' u) r# A* y& _
7 e; j; d3 ]' z4 N8 `
& P6 B( q7 @, |3 k if(key==HK_MODE)' G h8 B) Y( c* a7 F" k4 L
{- N* i j$ W8 f: c4 Z7 e+ x2 v
if(!onoff)
& K" r% b7 L: X% Y* r: ^! C; z {
2 @+ z9 c1 S2 d+ J# w if(hid_keymap==hid_keymap_dvorak)
9 _# @9 I; Z9 P0 j# x' m( p {
: n, r4 N& v% v' T( O, s# g hid_keymap=hid_keymap_qwerty;
- k: M5 U8 c; Z5 n# f7 t9 A2 K GPIOB->BSRR = (1<<2);- f9 e u$ ^9 W& j* O
}
, a9 O/ T' t2 b6 [; N7 v else5 t( I' ?8 h7 s- W1 _- H
{
% @7 G" R3 V" g7 c& M hid_keymap=hid_keymap_dvorak;
, m7 a; {6 x: ]/ Z7 ^" \, L GPIOB->BRR = (1<<2);
6 j V0 O8 T4 @! w }* n8 k5 a/ D. |" Q7 l
}
N3 I$ ~/ N& p8 C+ a" l return;( [' I0 u! `- X4 A O
}
5 |" Q' H, e$ i4 X" T) N; B6 K
' M O% c, ?- T3 M2 j8 z
! S$ U) O D+ E if(key>=0x80) // Alt, Ctrl, Shift7 a5 I3 y1 k" s* y, K
{& [* V" ]& i) T6 \# i9 w3 V/ \
uint8_t bitset = 1<<(key&7);. Q3 w% Z7 ~2 H4 _
if(onoff) // non-zero is key up7 z, i2 h6 `8 h0 Y& I% j
report[0] &= (~bitset);# G. g `4 e" d0 f) w& q
else
5 s. C- ?4 _/ z; ^1 N: e. y& |4 a report[0] |= bitset;
: z1 J. j1 j" T/ n: e' X }; I# `6 T4 M7 E: E' o
else2 }. d* t' T# c. p$ [; a5 T2 j' V
{ e. I/ ^- Y) |* P0 G
if(onoff) // non-zero is key up
9 ?9 H: u7 E' X* W: |4 ^7 S5 Z {& \) X* |" m% k5 M3 m# r
for(i=2;i<8;i++)$ K u, R0 K6 Z8 u9 e
{" d# |+ M+ k* U$ ` {. E8 E$ k& b
if(report==key)) Y- Y; n1 U5 J# h9 @6 O$ j5 |! ?
{
4 m" V! m2 v% E1 t+ z2 {! a Z report=0;
7 Y9 d& \0 z; B- {, V6 _ break;
: r( } N! `8 Z8 _* ? }
( E9 o0 ?" G5 m, f% O$ m }
7 `" Q3 b& O; m' U. S/ l }) L& O# L+ Y# k/ v" g: A5 c
else* K) d8 o- n# M1 Z K* \2 g! [5 \/ t
{
8 r$ C2 R* Y- R: } for(i=2;i<8;i++)4 R3 ~& K2 R+ X
{' y: e2 }6 g7 M/ B* q
if(report==key)
; n! [, R6 V0 _& F0 I break;
& X, F6 U F2 P$ p$ |. t if(report==0)! A( Y# ~ J3 h+ A/ p
{% r' z- U* `2 i j
report=key;; _& ]! L5 ^; V2 H% N$ q
break;
0 F( F/ t+ Z, ]2 C }
J" |0 [2 F1 v }; A: ?$ f z! W& p
}
2 u t9 ?4 Z$ g3 c- o4 |9 U2 k5 y( c }
, u8 m& u4 s! W( F. [0 { for(i=0;i<4;i++)/ G+ T; L) c8 b! ?
USB_PMA[192+i]=hid_report;" @( E3 G- ~: a( p( z
USB_PMA[5]=8; //COUNT1_TX$ U- s# X; t. y
if(ep1_wait==0), E! ~" M1 Z* `1 h5 T8 A$ z
{: n6 n& t* N+ l
USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;* Z' @' a5 \' q) y3 z8 t/ G4 p
ep1_wait=1;( F+ G# w' r! I: m$ Q! K
}: Q. _0 F" }: [* w6 e& w
}
) `0 w; X0 l! z( C3 B1 H$ |/ ~; G7 |& N7 @
, @% \( Q% R6 U. u1 b
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
( B, q) }4 {7 z- c. k# V! d8 Q
keyboard.zip
(8.7 KB, 下载次数: 6685)
$ o) l$ f' X; } X/ R. X/ a7 R9 t5 p* U9 i, ~* {$ n% ]
( M+ B* B. u ]' d% T8 g; g1 j2 ?5 f2 t
3 ?) P6 e8 J7 A a6 d% d
|
|