|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 - l# D2 Q9 ~ L! w* V `- G
" t& n0 \3 r$ y0 k. K
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
* A% K, f. R) C7 k. f: {8 S! e5 m9 s5 A' \3 ?, C" v
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
3 p. F/ n' E3 P
, M( v( o8 p) }# p- z+ D; z, b9 j% v
: p. O! V; d* N f) W1 K! Yconst char hid_keymap_qwerty[14][8]={7 O5 @. }. l; f6 |3 ?
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
6 t6 p7 ?. O8 X# [6 }9 [ {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},- g. r$ n( E7 Z/ ~* E
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},) p1 }( ~/ G- Q T8 K: T
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
+ F6 C3 _# ^0 [7 H2 ~- | {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
3 r0 }# k+ X2 |# t$ w3 ? {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},% p: h$ v* h5 U, I& ?6 c! ]
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
' y% X: `4 q1 i {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},1 P0 I* I" u* r. t/ v
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},0 A# `2 E; f. J
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},& M: z/ Y5 k% i4 H8 q
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
0 K+ I( ?6 l' z {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},3 P% _+ j' u6 P( Z# ^
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},! c/ ]. H1 Z+ ^
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
+ n) Q7 _9 _& d+ f K. L3 _' _0 I};) \6 M8 ] X( J# q9 e% X
2 ~- N6 B1 w" e" J7 v w1 D
/ Z8 H! g5 R- x8 @% y$ |0 f7 y+ H: nconst char hid_keymap_dvorak[14][8]={
/ ~% S4 W" O, O; t9 r {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
) h, u4 _: E; P7 g2 F2 C/ {0 a {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},9 ^7 m) O; T8 x/ Y* {
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},/ U2 G$ ]4 T% L7 _) k. }+ d5 S( u
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
/ F+ c5 f$ W" x3 T' T% w- w {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},8 z. V* D' @, H0 U3 K
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
; {8 t8 H7 I- V, `2 A {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},7 r1 T$ Y2 T& h8 O, B
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
' U1 J( j' X9 Q V5 f5 F! N {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
" N: }2 z7 ?* U6 U% j9 E {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE}," a7 _1 V( P u7 R( K7 w9 Y
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
7 |/ ]( \2 g/ w5 b {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},7 a u4 N5 D8 x: h3 N, }1 b
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},4 n: z7 e g! k3 a: z, \% |% \3 ^
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}: }& n6 ? e8 n& F* T
};
; T9 U2 [2 N- D" T/ |
! ~0 H' v+ \) p1 D9 x, ?+ s0 O0 o) \0 ^7 L8 H8 l8 I ^/ ?
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
% l0 j+ v, g" K) g8 o, b# l( [: t, t# E; X" `/ p$ f
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:5 A5 f( s( K h# G' G( r% Y. a
0 t' V8 u! y! x- w& g! p3 Uvoid update_key_matrix(char row, char col, char onoff)
& @) h# k% |( V7 G4 r{/ A7 H) s" c8 x% ^5 q8 c
static uint16_t hid_report[4]={0,0,0,0};3 g9 I8 X7 p! ]; U% G
static char (*hid_keymap)[8]=hid_keymap_dvorak;3 ]: P9 T& n; P# L+ r% @! L) }
5 G# i( M" R) l% G4 }$ ?' L1 U) {" y- H
unsigned char key=hid_keymap[row][col];
* s5 p4 h- }' ~( D2 Y$ w% F unsigned char *report =(unsigned char *)hid_report;
- I& B1 g% m; r v7 u char i;
: f- I' }: o8 k& G% n: O8 X& P) D4 v' _0 U
5 L9 \. h6 O8 Y. F if(key==HK_MODE)
2 R9 j: R. I* C {
3 S' Z. g4 w# R0 ^ if(!onoff)
B* c' B5 t# w# W7 t. b+ M {
9 u5 W. J7 Z5 q( }* h if(hid_keymap==hid_keymap_dvorak)
, Y. Q% a; ]5 Z. W4 o/ v {/ I! G" m$ q6 Z# y% P) U2 |2 @
hid_keymap=hid_keymap_qwerty;' |0 f, d* C Y3 u
GPIOB->BSRR = (1<<2);4 B% b5 T0 x# e; s' x
}
. n1 I: F" f( q9 u9 ]: p1 W3 i else
! f/ \$ u+ U' g" U! Y& a {% T5 i+ s$ Z$ C1 Q9 u8 L
hid_keymap=hid_keymap_dvorak;
" O! k( S( v" ~( ?5 B. z) Q GPIOB->BRR = (1<<2);# O( e7 W; [6 Z' t
}8 F: S6 P' x. g: k9 D+ q! Y6 T
}6 {! d* p% j, a
return;1 Y/ q, r6 ~+ k+ p9 U9 s, A k
}9 d7 V6 h$ s+ }* I' E
5 b5 f! |0 T# u$ ]$ J. a
4 |& R+ u$ u: ^ if(key>=0x80) // Alt, Ctrl, Shift
% Z( Z. ~5 j4 f. d, x7 l' j9 c {6 b! V: t! |; V4 J2 \% H+ t1 U
uint8_t bitset = 1<<(key&7);1 u; O1 }6 p$ ^% A5 B
if(onoff) // non-zero is key up0 H9 b+ j$ L3 z: P( ]$ W
report[0] &= (~bitset);
9 j" o8 g' U V5 K3 P* G3 r else9 {; B+ s2 G# d( V
report[0] |= bitset;
3 w7 {) t( Q$ u% w. E! M }
7 s( W& y' W8 Y/ u5 i- Q1 v, U else- `" \1 L( i3 t$ ~
{
4 G5 l$ ~- `! P# i if(onoff) // non-zero is key up' `4 ~& V: [3 N0 O2 D
{9 P/ b9 ?9 |' f+ E
for(i=2;i<8;i++)
$ _) q" ~- K. z0 k, m' Y, \3 C { G9 g Z& i X8 B
if(report==key)
3 b, W: \2 F- ` {9 ~, l' L9 r3 p# q" h6 u
report=0;
0 h! J9 v! J4 t6 T5 A6 z1 w break;
* D8 \% f% ]7 W$ ~0 K }
! T. C$ l; q# v( ], e1 m/ V }! c/ E$ ]( o' R- K6 H
}
/ y; R( i1 V- ^! u- z2 { else% p) S5 z6 l* g. R% _; S5 i
{
* e. K# J, d! h for(i=2;i<8;i++) B* ]4 U& I3 B" y
{
3 a& k- w! H1 S* Q6 o( ^ if(report==key)
5 q# _( [7 ] i2 F$ p( e/ T. e break;
' p4 P1 B" c: n; i4 @" t1 O if(report==0)
8 m+ h; K; S l g) ` {
, ~; a7 _* f" f5 j report=key;
$ E; r5 }3 i8 c) K break;
# m6 V7 J5 {$ w. x9 h }. |9 Z0 l/ ?! i9 T0 q
}/ H3 U ^* g# K4 o
}( q j& F: Z' F
}
+ Q" T* E" [' R( H4 A for(i=0;i<4;i++)& i N/ n$ ^$ V, D: N
USB_PMA[192+i]=hid_report;6 N8 ~0 \& Z, y# G! Z# c8 N! k% k) p
USB_PMA[5]=8; //COUNT1_TX
- q C! N' C3 @1 J! ^ if(ep1_wait==0)
: R* ?+ }. O" x; ]) O. Z, P/ X+ G {
4 J9 C; y. R5 j! ^. I$ e USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
7 n. a! K" y% u/ E- ]6 u& z0 ~ ep1_wait=1;- _1 X4 i5 ?3 v- S8 `" e
}
* t' Z! |0 I/ [5 O# q$ K}
+ z0 a; I3 g6 V8 p% Y+ @6 U
; r1 B, w8 U5 m7 | E% Y' o' m/ R/ I# o* S. ?
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。- ^: h; c: ], K( X0 C" I$ j3 J
keyboard.zip
(8.7 KB, 下载次数: 6382)
1 d0 c& O: ~6 Q$ c' {
. {3 u2 L- K# g. E+ W5 f
2 N6 n3 T; G+ L& d: N8 } }$ c& U
4 d- t$ I) k8 C0 G/ a0 z. f2 P& E6 M1 p, [
|
|