|
楼主 |
发表于 2016-9-21 21:01:28
|
显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 3 J3 Y, ~6 ^% o) s* D
# j. e) l0 _; w# N$ Q
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。; R- D6 w. e" A$ N5 A7 j3 Q2 A3 S
" e# F9 D% F* a/ J2 {
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
! A- S% _7 M; a) X# y5 \
3 @: y$ g, K" }/ x! d. S( `0 @9 u- Q/ @6 _, ^6 W) T% l
const char hid_keymap_qwerty[14][8]={
; S9 b* b/ h w/ a4 I& [! t {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
+ Y; [: J, q5 w3 H3 s/ @; A {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},/ D& M6 g. y' z$ I
{HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
3 f; q# o8 z7 x* o. a {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},+ `5 H6 s9 p, s4 S. q; x
{HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
% I0 ~( k* [" F9 N: `+ Z {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
5 W7 ?* R8 _3 u2 s; D! B& W {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
f2 z& e; `, u7 r* F7 T {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},' r- F. W/ O! w' A6 {, U
{HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},5 b9 V/ S$ N, d# f2 ]
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
; g S: A( G: B$ ` {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
- g7 J0 U* k+ c4 ? {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},( N& u& R* N2 A, ~
{HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7}," k7 t$ p- v- m: r: C8 ?
{HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
( H3 V/ {) M" D! o};; ^$ |5 E3 D" T; Z& s; b
9 m9 _% ^* \6 m5 H6 Q/ L% M% m
: `+ p. f5 P/ J) R% Yconst char hid_keymap_dvorak[14][8]={( L+ z+ ~1 {% A6 H& c0 i* Y* a
{HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
0 @) \7 d; k9 Q4 y$ d9 } {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},& O& e. i3 H+ u- w) P
{HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
* v) L+ c" W0 O( i1 B {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
) J ?5 a. E6 m5 W' Q {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
! l$ `. G8 w5 c! W0 a& ~ {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
" [# ]/ [" L6 e" l+ x {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},# O* W4 {. I9 G5 ]2 |
{HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
( L" J* v% v; u( c T/ `8 K8 D7 w5 Y {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},: w4 P" G3 V% X/ p2 Z/ `3 P
{HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE}, ]: N2 A' d/ `/ f
{HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
* T) {' `; E( w) Y2 c* t {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},5 ] B/ ]; J3 B3 I# ]* S% l6 n
{HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
" ?( i$ j! t c {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}- E: A( H# u" `
};
' B" l; k) V$ f* \
; |3 _: G: j' }3 G8 z& Q8 x( z7 ?7 d( u4 Y+ U
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。& ?, w1 U* j7 d3 J
: z! O, c" z. y# p5 ?HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
! X! Z+ n0 w' G( r, S9 g) A0 T9 B. @$ `
void update_key_matrix(char row, char col, char onoff)
& Z. I2 T' g x" w# w{; S) @- j- t- V" ?, I
static uint16_t hid_report[4]={0,0,0,0};
0 f5 v; S; a+ T9 y3 g- f static char (*hid_keymap)[8]=hid_keymap_dvorak;/ i0 Y$ }9 C( h5 `7 Z. ~- y
/ I( b# o' c# I5 |& J
& y9 W& ^. N2 P# g8 d( F$ _) I unsigned char key=hid_keymap[row][col];7 W4 h' b' W" Y: I
unsigned char *report =(unsigned char *)hid_report;3 Z3 ` K+ f0 o. [3 b& y# x
char i;. Q P v) O1 f" C
5 e- U6 I% J; r& h0 I* ?
7 ]. E: W+ N9 {! z- d: C6 Y if(key==HK_MODE): R1 o, Y$ }0 J; o7 t7 m; Z) Y
{
3 w/ `' e7 R( V if(!onoff) ]& |' e, G" e' ?
{
: r- q& X+ A5 r: e% n if(hid_keymap==hid_keymap_dvorak)
. y8 m" d( G9 g4 t {4 u) N, {) U' Y0 B# ~6 a* t
hid_keymap=hid_keymap_qwerty;
$ _2 I9 l& v9 h0 a# M1 R: ? GPIOB->BSRR = (1<<2);0 F; K4 b- H9 Z) A; i
}8 M1 x- J* G* c: `7 z4 s& ~8 S
else; |, j0 I5 f% ~
{
" H% @( N7 [- d' c hid_keymap=hid_keymap_dvorak;
# p; a* p0 V! V! B2 e% u& F GPIOB->BRR = (1<<2);! c4 D" `7 T" a9 E& |& Y, C
}
! _: G# ~7 p9 \- w& {; {) v: |0 N }
2 ]% }3 T% g& V9 U$ W( a return;
& ?2 N! L4 F1 y2 [- H' S# f }
8 { N! F; o: C& m( `: C [1 R. Z" W) j9 }+ Z! t
" C# F! p n$ U# A- j; R# m if(key>=0x80) // Alt, Ctrl, Shift
2 C2 }: C0 }! O3 f" j5 h {+ J; _7 i: K# h9 z' H' J, Y; }
uint8_t bitset = 1<<(key&7); a9 B: p, s* C- G3 C4 R S
if(onoff) // non-zero is key up
* D) t' B% D6 r' u; c: i* w% y- G C report[0] &= (~bitset);) z% M6 g7 o2 W! M$ F$ {
else8 H. q1 A, R( [7 v( v" ?
report[0] |= bitset;
F0 \/ q9 N% H1 w' X4 C7 n }
5 H1 v6 D- `: H" L$ b( P9 s8 L else
5 u, k( |1 f. {1 ^/ [ {% N8 F Q$ N. s3 d
if(onoff) // non-zero is key up
) }) N4 l, E1 F3 z c! X {5 h8 t* l/ |1 L* h- B9 C7 R; _
for(i=2;i<8;i++): y! \, c" m; v5 e3 Z u/ d+ v, T% R/ B
{
p1 ~6 S7 B8 r. v# S& J% S if(report==key)
# D9 M- V; k2 h1 t {
+ V- x+ i% [! O4 Q2 Y report=0;
9 Q/ K3 z* H$ y" | break;& D/ W& Y: w, b
}- |, I0 X1 j5 ]# c% d/ r
}8 v$ |$ Q. a( Z* B( A, i
}1 g( g2 g6 r) `$ M2 e5 G1 O0 Q8 M5 P. n! w
else
\) v) t1 J% i1 m {
$ U+ D0 p+ j$ K7 a for(i=2;i<8;i++): K1 K- m5 A- Z8 x4 T
{
- \/ t1 S8 s) Y5 y0 h( u, G$ \ if(report==key)
/ o3 K# j8 ~3 H- e5 R: {1 W; {4 j* z break;6 ]6 v8 {) }" h g0 X( D. c6 `
if(report==0)
4 D( M6 g6 X- d8 R {
3 t) o i! m& [9 F/ q# F$ `6 n) F( m report=key;
3 Q0 r6 [: i0 x+ ]6 \8 k2 a/ Q break;8 v4 B# ` {- \7 l9 u
}
9 u6 L, N3 u" ?1 ` }
. D& y( [+ G$ @ }5 y, P( \0 t3 }0 R N. G! w( ]/ |
} F9 K1 {# f- F* h
for(i=0;i<4;i++)
% ~4 f$ A8 t% l8 ]8 ] USB_PMA[192+i]=hid_report;
+ F! D. g1 K. @7 \9 u USB_PMA[5]=8; //COUNT1_TX
" k5 M ]( w& z# J6 B if(ep1_wait==0)6 r8 @# C& t8 \6 W# \- E
{
: c. Y3 F' i/ o9 [( ] USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;4 K+ y! L; j& g% @% L
ep1_wait=1;
8 ~' {. q0 D' o) E0 Q2 L }
. ~* ]& c; c" J}
5 l8 p/ x! ~8 c0 q
4 d$ t' Z2 d# @8 c) u. E- \$ M- c" `, F- @ m
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
( j; }- w V6 p2 u; n2 j& r
keyboard.zip
(8.7 KB, 下载次数: 6188)
7 y1 c2 t8 Z5 |* w$ A" S
- M! I- j& t6 a2 h
1 I/ _5 I- z! s6 x, G1 `4 z# |
1 c4 J2 q9 ]/ h# l) V$ f
( }8 e0 X/ `5 w" J; F+ o |
|