|
|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 # p0 i7 I4 l2 B8 o+ z, d* J
H6 L$ A0 u9 S) g1 [- u扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
' v! j! } A$ ^5 @4 G4 Z3 a8 h( p# r1 \: F6 Y# M5 j
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:1 U* M5 j' L+ n# \
|" ?/ X# x9 `% w
" V* |: k1 N$ Y6 @1 w) J3 fconst char hid_keymap_qwerty[14][8]={( x3 c7 C8 `, ]4 [; y
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
/ q( g9 p. T6 ?) p: w0 |( e {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},3 p# {* |% _$ Q& w% C
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},* H* [+ {5 D# I% L: J% g8 s
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
+ I, I, Z) B- h- X {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
& ?2 d+ c0 O8 X4 h- `; W, D% E {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
% u7 Z, Z- Q' j, q. v {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},: A( W6 R8 n. i, P! b5 V" L7 A
{HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
9 h5 L- H6 p$ K {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},. q! v6 B, l; c/ L6 f
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},! k+ f( [8 Z0 |1 n
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},5 F4 j% b0 u+ l9 s' l! a
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
+ x: H# C- u& _3 l) S5 L- [4 C {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},, F6 @: Z- M; u6 J
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
5 e9 f8 p( O8 ^2 B8 ?};
, z. S7 d$ @2 `' B* A4 U' p4 |# j8 b/ G3 ?1 x4 r2 q$ V
, x' s4 p1 v4 N8 @3 l% vconst char hid_keymap_dvorak[14][8]={ q1 Y0 `6 X* t7 E j6 q$ J% G% x- H, r
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
# q* f- j& ~; u# P6 g {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},7 P; l5 h; _) `, w) S* ^0 L) F
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
8 d& G" d6 R- p; ^+ M4 | n {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
0 U; R: k- \- V# P# F O2 }' i6 w {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},* Z7 |5 j, l7 A
{HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},7 J, v5 a8 C2 U, ^
{HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
p. x: p: I& p. K/ Y {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},7 K* p: s8 g' y. E4 H1 y) U" R
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},6 J4 s8 N4 k* R/ X' \
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE}," y+ F4 X- r# U# q
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},$ p* L/ Q0 {/ V" ^( m4 b
{HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
+ h) c5 O) a) a3 h {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},$ t1 J n6 F" Q
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}+ l/ J j: q" h7 _3 I9 R0 G
};. h! g7 e( [: S* n2 P; T, V$ P
1 u& ?# X; A. l4 k' e( X' Y
/ [4 @' M! [+ Q k8 z6 r上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
1 ^5 n- m; h9 g- [) j: b N: N* `. B! `* c8 I! G2 F8 [
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
' Q' E& Y9 r1 `2 N* Q+ o$ P1 |! H5 Y2 R" {) b, J+ m a& u7 Y/ X5 v; w+ Q
void update_key_matrix(char row, char col, char onoff)1 N3 [; j `, J* o" h
{
# s9 B B* G6 d0 m2 E. S static uint16_t hid_report[4]={0,0,0,0};1 M. ]) \. [) I" I% {% d7 }
static char (*hid_keymap)[8]=hid_keymap_dvorak;
m$ ?3 c: d, C, }6 O; L7 g5 e, d: g4 N5 g
+ |% m# @. i, q1 V
unsigned char key=hid_keymap[row][col];
9 p' n5 W$ s: R% i: O+ \ unsigned char *report =(unsigned char *)hid_report;; G& i& ^ f7 A" x
char i;7 ?* S' C. B. @
" Z' C+ J+ U, |* g- W( n
& l* j7 D/ V& m if(key==HK_MODE)
9 Q* y% |$ c8 X* O {
$ v7 N! A U( K3 `6 ? if(!onoff)
y8 {: z& g) S0 P {
/ g5 o. |. o. Q" Q/ {+ t if(hid_keymap==hid_keymap_dvorak)
/ C1 S9 D5 D G# M {5 k( v5 l6 t7 {
hid_keymap=hid_keymap_qwerty;5 l1 L# X( J! _: I: P. ?0 K5 L
GPIOB->BSRR = (1<<2);
. e( p6 y1 a' r }# ~' O4 l: `. W+ [2 r# ~/ u3 d6 `
else& P) j) u2 L' x1 z$ d
{
: O) m3 v, y9 X hid_keymap=hid_keymap_dvorak;, D; D" ]5 Y4 O; A/ L) ?, o
GPIOB->BRR = (1<<2);
4 _# h( f* r* \1 ?0 P F# n4 l/ W% q }
/ L2 Q/ Y/ e% q }
* F' Q X: R* p4 F: w$ }% H return;
3 ~, }2 E; ?8 O$ A5 ]8 G }( ^, p0 X9 S& f. D7 z3 [6 w
6 K6 x3 j- {& k' @; ]
/ r" }9 k" Y2 U- l
if(key>=0x80) // Alt, Ctrl, Shift+ s, S1 }& W8 M1 }+ o& V- T, L) s. d9 t
{% U N; _% V s# R" W
uint8_t bitset = 1<<(key&7); G+ d* Z) A" R; f+ l; w
if(onoff) // non-zero is key up# [* G' G+ h- E/ Y8 c' l% s
report[0] &= (~bitset);5 y5 ~1 K& H( y H" K" @( S
else; A1 E7 o5 G' A4 K3 x5 g1 d; I
report[0] |= bitset;
0 p, B, i4 h2 `4 i4 ^ }
. |; O$ p' P2 _ x3 h- \3 z else; [1 L+ h' h% n! |. |& u
{
1 d. S }9 ^3 ^5 z8 B. s3 A if(onoff) // non-zero is key up
1 }' X3 F* H6 ]; ^ {
4 b/ ~% D/ A- u* ?5 [" j# ` for(i=2;i<8;i++)3 i% T1 T+ f1 r! e) Y6 j
{
0 P% Z, [" w; L2 Y if(report==key)- ^9 F s5 q Y; p2 v. q$ i* T+ ~
{/ ^. B; u* F, u
report=0;; U+ m2 ^/ e8 B- S
break;
' i4 F9 [4 l* |9 p }
! c+ F0 Y, F0 O2 K; j0 c7 o: t }
$ F$ x3 |, V6 j }
* I5 [5 [- Z M: g else
4 F( w" X4 I: t/ Q+ H7 D! R {5 ~2 F3 V$ u2 H$ l- @; R
for(i=2;i<8;i++)- |5 x& ~. l* b0 j2 C1 E) _
{
1 r r/ N3 d: q8 q8 @# \ w if(report==key)
( p8 ~; @. X) |0 _3 i4 H break;
v7 o9 c/ \5 E2 C/ L& ]3 V8 ~ if(report==0)
% a1 g6 y9 T2 @2 I( B) N {
+ J- u. F2 T6 f1 E7 C4 b/ @/ a report=key;
6 ?9 A& D; }; K" |- H4 { break;
& l7 G9 d3 }* o4 b3 u% G, t V }0 C/ w3 ~8 F$ J+ `- q9 w
}
Z/ A( t4 N& B. h! w2 D" Y( e4 J }& w( `$ M( F) Z( A
}
9 y8 ]+ _% Q& w4 S/ D: m/ V for(i=0;i<4;i++)$ e5 B3 B2 K( S4 t: x
USB_PMA[192+i]=hid_report;
& ^% r( U0 d, ?3 M: l' l. p USB_PMA[5]=8; //COUNT1_TX
' C: f% Q3 `0 U# _0 k" _ if(ep1_wait==0)1 I1 N- _7 M* D8 M9 z% ]7 M% x; I
{* G/ U% |3 F) ]
USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
) w4 a% t$ k. v; n* C' o4 s' D ep1_wait=1;
, d: |& ^' P8 w7 `: Q: E }
' [7 ~) I& |& ]: D}
+ O! w, l l! L' p3 {) Q2 e
- y2 M! @- b1 {
4 \1 I- v# n, X x完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
( P; B7 a9 ^1 p$ L8 |
keyboard.zip
(8.7 KB, 下载次数: 6653)
( E8 W$ u9 d _& k8 p
8 K) {, L& d# z# H/ y- f3 t: y0 L. T; M
3 ?1 ~' |& g1 s4 T
) H3 ^7 `5 J: p/ _, ~
|
|