|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 2 Y \. Z' g1 r. J/ d0 F m
: V) A4 }3 ^( C/ o( {扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。/ ^" |; q) f {. v# T7 h, S
( H/ A' K3 C8 d2 K6 E) }
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
1 o/ ^$ O. l: q# ]
3 x$ N0 b6 G/ k: E% P0 k& Q8 Y
! u) g& U; ^( [' q. }. lconst char hid_keymap_qwerty[14][8]={
& K% U$ N! S0 s5 l) n8 } {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
1 P' B0 n/ w/ W7 }/ F7 D7 J {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},5 @0 R# @" e, m E
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},# N# j( V8 F; f7 F' v H7 q9 Y1 P
{HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
& }/ O1 U$ ?, k$ J+ B+ `. t {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},! C$ I; T/ d+ x( c/ S* k
{HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
L# i/ a" {1 V1 W. O8 y4 G3 s0 @ {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
" Q8 s4 s' T& x {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},: t, q& T* J, `/ ]) @8 d" l7 K
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
8 E1 d. P- n5 E/ l% E' W2 o( c( l! U {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},# F& a4 E3 d2 L6 z. E: U) {+ b
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},( b+ E) q* E/ z8 @
{HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
% `( g5 }3 o# F* Q* E {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
* Y G2 @6 F- C! D0 [ {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}- x. X- j d3 ]( s% J
};8 e5 K- B2 T* U
2 R& k g6 s$ Y- G+ H
/ p6 A: n* L* B; g) d( Q) g4 [const char hid_keymap_dvorak[14][8]={
+ `5 b$ Z$ ~ @6 `6 K _( x {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
- [- \3 N- k3 b G y! b$ ` {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
* Z/ D# `. r7 j& _; X& \1 D0 l {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},6 F$ ?# X- g$ }$ X" U
{HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
: J6 L* f } {7 S. I7 |$ n( F {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
; I6 ^6 W% j1 D) c: b {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
; H; ~3 g' @, K& K. M {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
q# R4 r9 q s5 b. P. B; n {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},( \/ q/ w) F, R3 E( S) w
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},& J2 k8 l0 W9 b) u6 c% d1 u
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},2 t- w6 `. |2 ^% \5 h
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
% {8 X6 h4 H! H( }/ { {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
- \% t* ^4 i, |& E( ?+ u6 m {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
# e8 R. w5 g& p# E {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}& P; d$ j: Z( U# v
};' R' D7 j5 l0 K
2 H( | i$ s* h+ I
: Y, _0 T8 n: n+ j# Q上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
( Q" N. Y! N8 i
+ s3 o2 e/ J" r4 o; ]! G( tHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
" `$ {1 F0 a+ Z. b$ x, h- |4 K7 ^3 j* D/ R Q+ `2 S
void update_key_matrix(char row, char col, char onoff)
! \: _+ m+ Y' ~* g1 q* H{ S1 G) C$ M- j$ n
static uint16_t hid_report[4]={0,0,0,0};( x# O, R0 o, k
static char (*hid_keymap)[8]=hid_keymap_dvorak;& P* T/ a. ?5 [) G
4 @1 T: @4 C8 W2 w" t, ?
9 M1 f( c$ Y: Y J% W unsigned char key=hid_keymap[row][col];. O2 [; l: l0 G% p' k& U
unsigned char *report =(unsigned char *)hid_report;
/ K: `3 w$ f0 d/ s' ^0 W char i;
: m" p, \ @+ X
|3 N* Q5 R) N. C
_9 n; F0 d) E3 N3 b, I. q if(key==HK_MODE)
3 o2 W5 s7 [, Z, T. c$ X. [ {5 c B# s. G# k% P( z$ U9 R) g
if(!onoff)
3 W2 `+ A; S' f& s7 u {- J( ?7 B( q' m* M
if(hid_keymap==hid_keymap_dvorak)) ~# t/ {- i; @; `
{
% k: m& a% s5 ~2 D2 h hid_keymap=hid_keymap_qwerty;
, W( ^1 L h& ] GPIOB->BSRR = (1<<2); U" v. [. V" ~' S
}0 m# P8 x, S& |2 A
else
0 |9 g1 t h$ }1 s {
P0 p! n( X: E" X" F- w hid_keymap=hid_keymap_dvorak;
- [$ a! Z2 |: {- V8 N% m+ L GPIOB->BRR = (1<<2);
! c$ v* N! B% p3 W8 x }
2 l$ b& S1 f3 `, O }
# C& X" C" ` n9 d; e return;7 X: z( ~0 z0 h: i% A @) i; w
}7 }1 v, V; W+ @1 G! n; @
1 L% r, ?, {* F$ g" G, G6 l% a+ y3 @) C9 ^+ ?
if(key>=0x80) // Alt, Ctrl, Shift
3 L$ ?: z. X' f6 r' F1 |4 y( v( u4 @" w {) h4 @( F/ b0 _% V( k1 v2 U
uint8_t bitset = 1<<(key&7);# H- H2 B! T4 W; K/ v& k* H/ F- @0 A
if(onoff) // non-zero is key up; {4 V( e \- \. k4 m4 \1 |
report[0] &= (~bitset);
9 l! ~+ y6 I2 n0 b0 }/ R9 w else4 U, x, I! d% n: |) f& i3 o( K. t
report[0] |= bitset;! o$ f3 E1 H4 {0 ~- N
}
! h) C+ Z5 U) K. P6 n5 V else6 t. ]* ~# R2 M) z2 ]9 T) m" E" q
{$ g) x& C1 S5 P$ N. h$ E
if(onoff) // non-zero is key up9 ?; R: J- J. U# Z& z; }" b4 \7 Q9 `
{$ t y% H! }6 ]) X& J* B
for(i=2;i<8;i++)
* j0 ]$ n9 b7 @; A4 i {
- @1 S- A ^. Y% `8 K- A6 M+ t if(report==key)( \( d9 `1 f7 d/ Y. F
{
/ Y6 G) Y/ @: a report=0;6 I" e2 q3 m% L, ?. e* |
break;
+ C7 c- o. A0 ~+ X }
# N( k# Z. N6 G9 R( u/ P2 U }
7 T8 K! E3 x( S2 ~* @1 d }/ r3 ~' \5 p* S- W$ E0 j R
else
; d/ K7 C( u2 U1 U5 s' Q# y {; ?& F0 T2 |' u: V6 c5 W
for(i=2;i<8;i++)) e: h9 r5 t7 g0 [
{
$ Z* S! c+ i$ r2 Q if(report==key), T; g) \8 U: q/ t( g
break;+ i8 I) R7 N' p5 w9 @6 h
if(report==0)
3 z- e% s) e0 t( p K; Y {
5 U2 |3 o+ w9 l' s' j) [) J report=key;
1 h; H# J3 I9 U break;1 D8 u( H# _* o( o! p
}
7 _( K6 I1 K4 A h% a }0 A4 B+ i" }9 w: u( P
}2 V0 g- q3 i' U( M( o2 x2 E' v7 @
}
. ^7 `* {3 H. m. C/ c s for(i=0;i<4;i++)
! o7 D, h! _1 C0 ?; w3 R4 b0 }9 M USB_PMA[192+i]=hid_report;
9 s% N. {& O0 ]4 y ]. O USB_PMA[5]=8; //COUNT1_TX/ S; I" C# A0 {" O4 S+ S. k/ N
if(ep1_wait==0), J. f1 Y! M, J
{
1 g, z9 l# L8 p; I2 |) C2 n USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
0 m% j) `7 M0 X4 G: V9 w ep1_wait=1;
3 b8 u7 L9 f# \6 U( z6 { }
2 n' |! |) p i" F* C ?. `}4 `9 \' i( P1 x- S
3 B/ y L2 X8 m1 {8 p$ f# e& u
x* i! |) x0 j
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
3 w% V2 v5 h. q1 \
keyboard.zip
(8.7 KB, 下载次数: 6209)
* o/ u# L) X% t) @+ n
% Z: u' g3 b- F8 t$ z
+ }! Z9 o/ R7 P6 l* J* c/ e, W
" ^( z5 A+ Q- `6 Z4 R; K* @+ t, h& E5 [. e9 c' ~! O: a
|
|