找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 22799|回复: 9

【灌水无罪】老机械键盘改造USB,QWERTY/Dvorak一键切换

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑 , _" F4 f  q* q8 P4 }+ T+ F

4 a7 {; o* W0 H$ C& v9 \http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D12 I. E+ p* J9 `$ A. P
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。8 u, S7 A4 F" c+ \5 k

' L5 c+ _& \. a4 b1 i在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。
0 ?& u5 j+ M# ~, L" D! U: ~

% w- d& S! m% g. W& a 235140i3a36qivqzuvmt5q.jpg.thumb.jpg
1 b  }6 M, T' I: k
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。7 |  }. C2 z$ h0 K' l0 j4 n
001734klbyoluenuwz4h4b.png.thumb.jpg
$ A7 ?0 `$ |7 P( c/ \0 |为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:
% Q( b5 y. {; h6 O 003625r2agx2f5v922cf2f.png.thumb.jpg
/ J1 @$ L- H: @0 }其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.+ c9 M% y& Z) @' W4 |" J
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
0 }3 d# @$ U5 r5 j: u3 Z
005836yvs0wvovwsssgd3o.png.thumb.jpg
. W$ g! C) p6 n, f& I1 ]3 SDvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。  h7 J5 r1 I  [+ q7 D
% N! f  W2 J' x. P$ U$ K) X! @3 b
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。) D5 ~: P* M2 V& d* l7 ]

- m0 Y! j  R9 E到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
" @" m) ], V$ Q4 r. b. s2 z
* H0 U* Y$ }. R% m( I+ e2 ?机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:

  t' z% k5 ?; r* @3 H 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg # t% P4 q0 A8 h0 M( V0 W  T
----------------------------------------------------  分割线 ----------------------------------------------------------
8 \0 R$ g3 o+ n
% m9 w9 X. X0 Z% U1 v+ W先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
& \) N8 E( [# N8 {+ O+ o; U
020011osionbunl4ui44vi.jpg.thumb.jpg 3 ~& i* \  W5 i8 Q+ m* A2 k) H
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
( b2 M8 ]9 K0 `4 A, e( B' u 020017j8ycmnv7788bqv52.jpg.thumb.jpg
. ?( w2 D8 K! i/ T7 B特写,80C49
& B( `# }+ U- V5 C; F! ^4 a 021040oujzuvtut6iujtvz.jpg.thumb.jpg * s9 j) l: |% e' w
LED部分,使用了一片D触发器锁存指示灯状态.
& G5 J7 k7 j! c$ A7 I 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg   Y: k  z: o: f
暴力破坏,将80C49拆掉
8 L- q  V% T3 x! k1 i) x 021113e48qq98vyohvhzzh.jpg.thumb.jpg " \! i9 D) {/ M9 J3 x) s1 J; }
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
- X2 H8 O) [+ _( s7 v1 k 021125nc9az6dj33rlds2r.jpg.thumb.jpg
5 v1 ^9 l) b( Y  g; _$ z
焊好元件后的板子,准备替换80C492 J7 B4 t* {* v  U
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg - X5 t. I( e1 U2 N  ]
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。1 T& L' P9 y  q$ L$ M
021104shifhnrqbr3o5nlo.jpg.thumb.jpg   L: A4 ]4 B* q3 T- _1 Y
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.
  C8 \6 U2 K) _: r/ a 022003ym1p9u4ug40280uu.jpg.thumb.jpg
- a0 v/ l" q5 z1 W4 n* \* R  J) l开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。: H6 h& @8 X" C8 V6 Y
023313kt141q9qajtol7ma.jpg.thumb.jpg
- ~8 y9 ^# c( [我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。1 P/ l! ~3 @; S# e
023322nt7l5xb3ltttkltt.jpg.thumb.jpg ! x- o8 Y" e- T) B
主键区键帽就位& ]$ I- c9 m9 x, s# ?2 q. t
023331hin88e8wkrwzwikx.jpg.thumb.jpg - s& C/ E4 f2 g5 e$ @
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。: Y5 a3 c. c6 ^) \: F
023336wjzlgopugg1jyy79.jpg.thumb.jpg
3 I! o) Q5 ?* r7 l8 _  b: M最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。% S; m+ p" }7 d  K% f
023341sffu4j3g2323h6fl.jpg.thumb.jpg 1 s3 Z  D- f1 ^

# y0 S0 |. b, y& w8 W----------------------------------------------------- 分割线 --------------------------------------------------7 g- c: b( U' O1 p" P

3 C8 A1 R# g1 ]$ N% _. i( \

& R/ D& E! ~5 l9 Y; b
8 U. j/ z5 R+ c) X  T/ z
* |$ }& G: n: I/ E# h: t  X3 S& ^/ q+ `+ y# B& y

5 r# S3 z. }( }0 N3 z2 l7 D" Q; I3 R3 F: J, P, V: g  L, t' y

2 E4 f) Q* Y8 C5 R) ]2 L6 K7 V7 G' C* e3 D& x! c) M

: Y- R5 L2 _1 r. v2 n0 M& Y1 V) R/ C. `1 v6 Z5 f
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
- X3 ~7 G- H2 n+ N/ c" @2 p" a. \80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:: j  _9 F9 b6 n% ^- Z, k+ p
104025nzibm2rmiomhyirm.png.thumb.jpg
  m: `5 b5 T6 d" H

4 X* v  s: N6 g, }) u其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
% j; k9 ?# z5 }- O 105004zkrez5houvkkznko.jpg.thumb.jpg   x( |. D) Y: z. d$ p* C

- J% [7 I& J6 p: `3 q+ B+ Y扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
' w; g/ o* J8 t3 ^( B) W: d1 u4 r9 a  U
这是我设计的电路图:; Y4 d4 w4 h( O' L" D( j+ C+ c
110344ej2z2oo2rflo7oe7.png.thumb.jpg
/ x1 N) J# t2 j% C! \, I& j
6 x* O% o; T/ ]' B
PCB Layout:8 v) y  B, y) X" r: X  w% j
110847jjbjvt34vwt3v5bb.png.thumb.jpg
4 h+ W+ N  Q( p& F, G. V8 y2 X' v
' l% o! [8 F* I
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
) a) q6 b" m9 O' ^  G; n7 V5 F* p% v& g1 ]$ Y& W7 K; f# v  V
& m0 J, L3 ]$ w
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 , z  E) e# }1 Z% Q$ {6 w

! x1 D* R/ T. \4 s1 ?& S软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。" i7 S. P, z' |+ T% S

4 I6 M1 L0 J4 Z- c0 h8 w* @* ^总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:2 |1 O1 @! I  O" O, e4 x
113818pmrfsb6z0byt6t06.png.thumb.jpg 6 t! M3 O  o. R0 E

2 a" v/ H1 y# |5 j. C  a$ V  l" O其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)# |. d$ s  S+ l6 A
! s4 y) O  R( I0 h
USB的中断ISR,bare metal哦( X1 X1 \) C5 ~5 S. t  p0 ]

9 ]$ _8 m. Y% I; hvoid USB_IRQHandler(void)- v$ O) H- ^( r, x
{
/ a# n4 w; Q5 Q& y3 g) m    if(USB->ISTR & USB_ISTR_CTR)/ F) C9 [/ D  g5 m& S" g% ?% Z
    {
3 m/ o* }4 U, J7 V" E" g7 p        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
* R2 F- G( Z' B# H        {
( x; Q- V& [9 y( Q9 a' t# D- o            switch(ep0_state)7 d$ r2 e7 x: E9 Q; R2 L+ r$ b
            {8 l- M/ w/ ]% q" \$ o( L. _
                case 0: ep0_state |= 0x80;* s- K0 h/ i3 u6 L+ X% ]9 `
                        break;7 W" ]* N( h9 _6 Z) O5 j' p
                case 1: if(USB->EP0R & USB_EP_CTR_TX). s' h3 A7 \4 n. M
                        {
) ~- a7 R% \, i! A5 d                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;% Z6 p% x; }1 L/ A, Q
                            ep0_state=3;
$ l' Y4 Q! h; |  p( ~                            return;0 P" K) u2 Z9 {4 Y
                        }+ `0 J4 B% k+ n6 }9 n( M% z! n
                        else
1 h3 e7 O) J9 ~. F8 I% f( i                            ep0_state=0;
3 v+ C; d" E" P& D5 ], J9 f3 b                        break;
/ F9 u, h3 Q7 K3 z                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet# S; r3 ~, K" f+ S# L# `
                            ep0_state |= 0x80;' S3 q* r# L2 Q( o- s; \
                        else
7 C4 U1 l5 V- E9 b                            ep0_state=0;
4 g0 E3 a* P+ f                        break;; r3 x+ I% a1 G/ n1 E% V
                case 3: ep0_state=0;
7 t& {$ [8 U* m/ P                        break;
+ X" t0 X  z: P8 m                case 4: ep0_state=0;
( [4 p8 b: f  Q% `* `                        break;4 }' a) L0 l3 A+ K
                default:ep0_state=0;5 X% `& F7 w0 `# Z" d6 K
                        break;
* y+ X) W; i8 F9 ~4 P7 E2 ?            }0 Z* I9 J# T, M6 F/ x5 Y
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag& X/ |& L% }* d, `
            return;" F7 x2 x" U: F; m9 |
        }
6 _7 y7 V9 W# N$ [2 `* }        else    // EP_ID can be 1
1 ~4 X5 R' C/ i, N2 p( s8 c        {  S( Q4 P% M. m+ y% r
            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
; V8 c3 }: p. k$ b' P. C8 K5 V0 C' [9 L" R            ep1_wait=0;
  m  c: W# P2 z+ R8 w, y! x' g            return;
/ X9 }( B2 `! i2 U        }
$ M9 i  W0 z' R# j  \1 s1 r$ ?    }. i. g& V+ u6 V
    if(USB->ISTR & USB_ISTR_PMAOVR)
" M3 Z$ O5 w4 @( x6 k6 ]7 f0 ?7 d    {
9 }0 J  S4 ^0 s$ R4 o; y. y; @: T        USB->ISTR = ~USB_ISTR_PMAOVR;
% U' Z/ d% K+ D- P! R+ x% R+ r    }
, h* P' E$ L+ @    if(USB->ISTR & USB_ISTR_ERR)
: t3 r1 ~" W: q! A7 s    {% I; L# F6 o3 L' w
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear# w+ m. N& |: `. d
    }
- Z0 A. {$ q1 Y: ?7 `: m: i" G' p    if(USB->ISTR & USB_ISTR_WKUP)
( q; f% ]- R. g; Y  h    {& v+ X, d% a; t" b# {- L- H# ~
        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
8 H4 P6 C- z& _8 E& T, T7 }        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear/ ^/ f. I( M6 g( H9 g! g- b
    }# `& a# K( o8 Z! j% B! F: C
    if(USB->ISTR & USB_ISTR_SUSP)
) @5 ~9 l% ]" R& b7 U$ |1 a- X    {
% t' x* A& ?6 z/ X2 p. v1 U        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
  M' ^! d; k* S9 ?! Q" N2 a) p7 N2 n" b' \        USB->CNTR |= USB_CNTR_LPMODE;   // low power0 Y6 P# c2 u) B% J9 C% B
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear% a& s. Z' h+ [( p- c
    }
0 {( o  ~, v: z    if(USB->ISTR & USB_ISTR_RESET)( f/ |, X; P) C, J
    {' `# C7 u, \0 g  S: |
        USB->BTABLE = 0;    // buffer table at bottom of PMA
+ \0 \& e6 e; ]# A        USB_PMA[0]=128; //ADDR0_TX9 s- z- f# l' d3 f1 I
        USB_PMA[1]=0;   //COUNT0_TX
/ B# o& t0 d* j, {4 c% \        USB_PMA[2]=256; //ADDR0_RX* R( d/ e. \$ d6 Q
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
+ {7 C8 {9 ~$ V5 F3 \2 f: h        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
5 \2 ], _: T/ x! f* f$ _- Y        ep0_state=0;
6 C" N0 |" \! s8 ?        USB_PMA[4]=384; //ADDR1_TX
- z  R- ^8 J: y9 q3 k- a$ o8 W$ K        USB_PMA[5]=0;   //COUNT1_TX
. a; P1 ]- t" R- \/ W" ?        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
# P1 d6 a( Z& p. G* m& {3 W  A# V2 G        ep1_wait=0;
+ @' ~1 M1 H, y7 V0 R        USB->DADDR = USB_DADDR_EF;      // enable function# h) O$ q' U. `, D, q; b8 M
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
/ y7 l: f# X: X2 Q+ N! l( m) Q, m    }
8 C- {3 n# D5 X, Z    if(USB->ISTR & USB_ISTR_SOF)
9 R9 X- y8 N& s) T/ w* ^( f! F6 n    {
# o3 X. k6 w8 J7 W- G5 H! P' E2 @+ O        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear. ^! x' H3 @) M& {8 P( Y' ]7 p
    }
+ n4 u2 y2 v' H# M/ E5 @- ^5 H}
( ?( B- G$ N7 a. O" U+ A+ {/ t* `; n( x
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
+ v- `2 @# I8 s  m- B8 ?$ s# n: T# V: v' D; r
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。" ~2 I' h5 c8 b- _: h
* g8 N+ w6 f" B6 _4 Y! e6 D+ H
    while(1)
0 S. [7 @1 a! U7 z4 v) l6 L* g    {5 A5 q, k+ T. \/ k6 Q  K' R$ Q
        static char row=0;% z9 K! I1 l0 _7 ^! v
        __WFI();
* [6 p6 S  o, ~; Z        if(ep0_state & 0x80)    // request data processing# O8 }! ^1 O6 Y4 _
        {
4 G& [- C3 |& ~  Q1 T) h/ D            if(ep0_state==0x80) // SETUP phase
% X% c  x/ f9 u4 O+ Y            {* @  i" t% z2 L( X, ^' `
                if(!setup_packet_service())
$ s0 d" L% O8 r- U/ v0 T# W, h. F                {, g0 X- P' L/ u8 v! L$ C- s: p
                    ep0_state=0;
  j- l" v/ e4 w. q% R6 Z                    // not supported- ~& d/ s/ r! ~8 i' O- d
                }; C( o9 B# I' K6 X8 f" [
                // ep0_state should be set to 1 or 2, if processed
: `( R4 p* O/ d+ l+ D            }9 {* a- {" x# i; e2 [( [
            else    // OUT phase
$ M0 c4 ~: ~! G# O( Y            {
" l  o; t2 P; v( \                // process data
# l( t% W/ n! R7 v8 ]" p                show_LED(*(uint8_t *)(USB_PMA+128));
! ?2 Y- a. a" L( }                ep0_state=4;
5 p" M. s/ k# \% I( h; |                USB_PMA[1]=0;       // Zero length DATA0& w* m$ L: n, E3 t4 |  o
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
4 W. C8 ]6 w# U, q  @8 H            }
5 z. o2 W0 ]/ d6 g3 u        }
5 U; U3 o% w# O3 m. I$ O        else0 D6 F5 h9 d  {- F, h# k
        {
# t! U. ^/ Z) S8 G, i; k9 S            if(usb_address && ep0_state==0)
4 r* E1 c$ B! g4 i            {
) z7 N. t& u: [. p7 s                USB->DADDR = USB_DADDR_EF|usb_address;" E  N. I0 p" i8 @  d. C9 b+ s
                usb_address=0;
: v( r4 C: M! r$ g8 R, `            }
2 Y1 U5 R9 D, V" t. K$ @        }2 H5 s- K* S- N% w/ {8 b7 \
        if(row!=scan_row)   // new scan line* b9 @' E/ X2 Q* J$ k6 _$ o
        {
2 `# g* P# O8 T) N7 Y9 ]            if(key_state[row]!=prev_key_state[row])
6 i. O2 T+ ^8 r8 Y+ y2 z            {
4 M; p6 S" Y7 S+ N9 _5 Z                uint8_t test=0x80;
4 h& S  v1 |4 J; G  A                uint8_t diff=key_state[row]^prev_key_state[row];
3 E7 o$ z. K3 g( i$ `4 I                for(i=0;i<8;i++)
9 M9 _6 ^$ m& `& ]/ }: g                {
6 z+ B6 K" f% N* N% s& \                    if(diff & test)8 j7 x" v: e$ G4 f: a: k8 J
                        update_key_matrix(row,i,key_state[row]&test);5 L" y: p& Z5 x' E9 k+ b  D
                    test>>=1;/ x: a" p( g1 j: S0 E
                }( h( e3 d' w- _  Z) Z
            }9 y, U4 I, T* ~1 Q1 m6 x
            row=scan_row;
5 t$ v$ d0 z6 [; g5 W' U; U, U3 Y        }
7 {3 r; C( B: g/ j* c$ ~    }% ]! h, r" z' w6 y% E. d1 b

* j9 i# X3 h$ `' J0 w# W& V4 ]: c5 W# Z" E4 p* Y! q
EP0的控制传输,把用到的请求处理一下
$ a1 q9 L4 [% u4 f/ J+ Echar setup_packet_service(void)/ g9 W6 T6 h8 m9 \, |+ q/ z# ~& E* M: b
{8 a( Z! s2 w- v6 {
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific% }/ Z8 t% r3 N/ H" j  r* L
    {
: R/ n) n( a; Y        switch(ep0_std_req->bRequest)( u. E( r" h2 @6 [) x
        {
: B# w# L4 d  G0 _            case REQ_GET_REPORT: break;
: U9 P5 S8 v2 _: y) v" a3 L            case REQ_GET_IDLE:
; e5 Z" b% w- B! F2 y9 \* c% Z. S                USB_PMA[64]=0xfa;   // return 1 byte
$ y3 ]% m" _7 ~8 Q$ `4 Q                USB_PMA[1]=1;% R. M" D4 e" U" M; `. X
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
6 |0 k. ]( l  o/ _4 v# q                ep0_state=1;, ~; y: B+ Y1 ~! C3 o1 q# r
                return 1;5 i4 q6 |- b% i
                break;* ?& r( g5 u( B& ~% r' ]8 p+ R1 j: q
            case REQ_SET_REPORT:) F7 w! q, b, \% Q4 c. s8 c: Q
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;6 G7 y# y' K3 d' `
                ep0_state=2;: ]1 s5 V$ e$ J( g6 j. m& n4 V
                return 1;0 J+ \- j2 S) K" Y/ b
                break;
2 ^* V; _( W4 r* N- _2 L            case REQ_SET_IDLE:# O4 g) O" j% m' g4 F" x
                USB_PMA[1]=0;   // Zero DATA
0 b0 V9 F2 d( Z, J% O: O/ ?                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
# P3 K3 e: r4 \) N. Y3 ~3 _                ep0_state=4;# I. ?6 N4 J$ }( b
                return 1;
/ R$ [* P1 u4 d( e3 O# o                break;% f- C7 X  G1 {7 p2 L
        }, C& j0 |6 F( G& w2 W0 P6 O- Y7 G
        return 0;8 e, U6 e" I* w) _* r# [# L
    }* G* r3 }6 u3 |& P1 J
    else    // standard
8 X* ?! b3 D) r9 f0 a& D    {
' l1 i4 t) d6 k* X, `8 M8 l        switch(ep0_std_req->bRequest)
. h1 d$ f( z* M        {" d/ g8 s0 u2 L) X' A
            case REQ_GET_DESCRIPTOR:
8 x* k4 H% V4 o! z4 w& T, d                return descriptor_service();
; ]" o8 o2 z: \3 G. I4 `# u                break;
  P: |4 ~9 m, W3 w4 T" c% l3 f            case REQ_SET_ADDRESS:
4 d) e. v5 D6 |2 h# [  p. L                if(ep0_std_req->bmRequestType!=0x00)
  o2 K2 d5 f& I' d4 d                    return 0;
  i; u; C) _1 i9 Y! D0 Z3 m                usb_address=ep0_std_req->wValue;4 t, V- M0 A( Q6 f; [$ c+ X' @7 Z
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;9 Y: W) o$ j2 O9 ~, U: S
                USB_PMA[1]=0;       // Zero length DATA0
! U( [% J1 v$ P- e                ep0_state=4;    // No Data phase
3 o. ^  Y2 |0 K& U" P! l0 V/ T                return 1;) X. |; Q1 k" X
            case REQ_SET_CONFIGURATION:
) E$ s5 H  g2 ?$ b6 h5 {0 y$ O: }' |                if(ep0_std_req->bmRequestType!=0x00). N8 p5 ^: `/ @
                    return 0;0 }0 r$ c3 D: Q6 e
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;. |" R9 d6 k0 y
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;6 Q  l9 _3 y9 p( T: m: U
                USB_PMA[1]=0;   // Zero DATA
0 |3 N& c) E# k0 U                ep0_state=4;    // No DATA phase
0 }3 j4 Q0 {; g4 Z( D9 p                return 1;# d$ J9 w) E7 K# U9 b# T+ E1 S
            default: return 0;
2 k& _) t8 b2 S% q( q( ]( E" d9 J8 r        }
: l0 _) K9 f' ^( f    }" z- d6 K! |  h5 S7 G
}+ ^! A: \( m8 P

3 V) N9 X6 K5 z/ j) u  J9 C6 D0 v
# D- j, C1 a0 j7 C
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
  L! r- Y) b# O0 Hchar descriptor_service(void)
3 D& f7 x% I: d% \3 K+ {/ P# D" w/ J{
3 x. V! B2 w* j, @, U! G    switch((ep0_std_req->wValue)>>8)* f+ y! `( u2 W: F
    {' l' Q: @% B! |0 F* c5 P& g* h
        case DESC_TYPE_DEVICE:
. @) h8 w5 U; y- r. a& Y; r            return ep0_preparedata(&DevDesc, sizeof(DevDesc));
! ~9 `/ U+ k% l1 a# t8 g- i( W, {            break;
" D* h3 a1 v  N2 o- L* D& x  l        case DESC_TYPE_CONFIG:
0 B: x- J) d6 b1 Z9 h$ F  b            if(sizeof(ConfigDescData)>ep0_std_req->wLength)
8 }3 I2 w" ~% h8 V                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);1 b9 L0 x* e6 Q, e4 V% X
            else" m  G+ p7 M$ M7 `0 f0 ~1 j4 ^
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
# g8 U; u- n9 P( s5 s! f            break;8 @9 f+ \; W# u
        case DESC_TYPE_STRING:) ~! w/ A% L2 f2 ~. s) B# y
            switch(ep0_std_req->wValue &0xff)% t$ ?( {! G9 f" }0 H
            {
0 ?  p' q- U) d( n6 T1 ~/ \! q                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));) u2 {% D- o- a2 ?' @3 o1 w% H
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
& E! c! i/ y9 y7 Q5 T% o5 D                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
$ [: }, l4 o$ l8 m9 P) j  \, f9 K                default: return 0;
4 Z, q2 N% }0 H+ ~            }5 Z+ G; b' y. A' s5 F7 a9 S
            break;) b7 A8 a! g  B( l- g; Y
        case REPORT_DESC_TYPE:6 C  b: u- \7 v- m- Y( F
            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
/ }6 O& n7 K/ [* U$ Q7 _1 a        default:
) h5 y0 E+ T' M2 @9 G            return 0;3 F( x4 h1 k4 r9 R6 v
    }4 D4 ?9 d) `% u& p' Y7 o
}/ q. @0 I% j0 \6 S: Z9 c
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
8 v9 j9 S) r+ G- R9 O/ Q/ wvoid TIM6_DAC_IRQHandler(void)3 D- s7 u% d& z3 y9 d4 K4 p1 }
{# Q' n5 X" Z' O8 M7 f3 M
    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);& [" r4 B7 u/ D5 i

$ ?% d# P- M1 Q/ \- A" t0 e

( i; T# C) M  r+ C$ }* O% e8 A    TIM6->SR &= ~TIM_SR_UIF;' @+ ~( X' G1 l2 M6 \; X5 Y
    prev_key_state[scan_row]=key_state[scan_row];
5 x. r# ]# s3 v" ?3 N    key_state[scan_row]= *PA_IDR;   // update key states& ~6 R) ?% M& d7 g* \4 m# @
    switch(scan_row). M1 `: b; n1 X9 R
    {
4 Q" P. ^4 _0 X) a6 N9 Y. }        case 13: // next row PB14& M4 Q# i: A- Z4 |8 _
                GPIOC->MODER = GPIOC_DEFAULT;: y4 f9 I' i; C
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;  R) O- @7 u' i% i  J
                break;. i' P& k2 i7 A6 G
        case  0: // next row PB15: B6 K* z2 H8 @8 B, H4 L! o
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;% Y1 Y! q; F: o
                break;& S% ~: m. [6 u7 T; ^/ R2 ]
        case  1: // next row PB3: e$ [: U2 t9 L  p0 v1 {
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
( a) k( `" Z& o% ^! T: T6 h                break;
4 P6 c& W8 `, m! Y        case  2: // next row PB4
- z3 N' |; M/ z- _/ B# W8 X; C                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;1 M, Q6 s/ v: K. r$ v! e5 b& C
                break;& _% O. X- O$ m
        case  3: // next row PB5
" B4 d# v$ O% {0 L! c" r                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;
9 o; ]7 ?/ B5 w8 w                break;8 W) I- o% m9 b# E8 G( L
        case  4: // next row PB6
  L2 p2 `0 p+ z                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
- ^: S6 O) l! r: @) L                break;
: i0 n7 ?$ V. O) O/ C& N        case  5: // next row PB7" G" n8 W8 ]7 Z6 c/ g9 M
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;& ^: ]1 |5 o% P/ d  \& A; x! ^5 J
                break;
% g/ r9 ?8 k2 n0 L2 c        case  6: // next row PB80 f) L8 d0 K7 G* u; F1 e
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
. D1 S/ s7 m- T+ y                break;5 }; L4 |, E4 N4 _! h
        case  7: // next row PB91 Y' K6 k2 r! W" ?# ]7 N+ @0 X
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;8 P6 z7 {* O6 l; ]% ?
                break;
4 K/ I- R6 _5 t3 C! d  P- j0 l        case  8: // next row PA8$ l! ^5 g0 B& W0 S. Q- R+ b
                GPIOB->MODER = GPIOB_DEFAULT;
5 D! F0 g" h( `8 H2 ?# ^5 g                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;: @8 E$ I6 `0 |3 c# }2 s
                break;
+ B3 U7 L  J9 L  r        case  9: // next row PA9
% A8 l* t2 j" M2 ^( z                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;. y) k+ Z% v& i, ?
                break;
3 ~) E) E/ N8 g% r" H        case 10: // next row PA10; w3 [1 N" B9 P0 W! C0 a( f
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
" ]. S+ D; [& y                break;" X: c, M; p; v" _0 r8 P, t- F
        case 11: // next row PA15
$ ]! m4 x$ J  x& E& O0 ~. d% P4 }                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
) a; X$ w$ ^( Z9 L4 Q# P                break;
0 }. M# r7 ]' O/ J        case 12: // next row PC13' n; s* p5 b8 ]3 |  h. b+ |
                GPIOA->MODER = GPIOA_DEFAULT;
" [# j3 H& }  h$ V' D/ Q) Y7 v; L                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
1 P  I# I$ E8 g' d7 `6 P  t                break;
/ P) k3 \, S. u9 r: G+ z1 m    }" V& I6 F4 T6 k8 r
    if(scan_row<13)9 c$ G. S) B  G& z! s
        scan_row++;
! ^- g/ m" i! o2 V% {7 M" H    else2 m% i9 J; I) E7 u: `& }/ m8 }' [
        scan_row=0;: }* O7 @0 W( Z! T' ]# S
}1 q3 C4 i+ W! ?

' f0 q$ }3 H' }0 j. A! n0 n, m' S9 |; P: E$ }) U0 j
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 ! s; ?% K; k5 d' R" k

+ T5 Z4 {% \! X9 M# g$ E4 z扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
7 n$ x0 c8 ~5 n7 V; X- f5 E: g- f, r& r( S4 \8 ^- d; f, ], l. ^
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
% J; r9 L5 W6 n' g  v8 I4 B

6 s. p- c- r( e; h
: v* P# K  g  Qconst char hid_keymap_qwerty[14][8]={
& V! i5 F6 Q0 k9 K    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
8 p5 b1 _8 k  ]  J" r    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},8 K. |% L5 c, _( U3 P: }
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
0 }% F1 e# p5 A% Y2 q    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},3 I' }# X/ T: ]) @6 q- e
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
4 {- ^, y/ U- ^! @    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
( U3 t9 @+ F0 s$ }+ Y7 h0 k. V    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
9 L9 A% L" U6 q7 x/ g4 {* [4 u    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},: ^: h/ P( U, `  P2 j2 n( J
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},- y3 D* T3 c+ \% t
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
* |8 G& ?* X7 |% V1 V! }+ y- l    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},4 S6 f3 O. y2 o+ g: T
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
/ }2 H: \0 K" o( a    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},: h9 `' A2 _1 Q2 D3 \& u
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}0 f" k- \% I! j; v
};
( F" v( N" j9 f; F& P. e
, ?! g, L* [7 y% c* `
" ~" N2 c$ x7 L
const char hid_keymap_dvorak[14][8]={$ H# o; w# t2 ]
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},: }1 C! _0 H( N, D
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
1 k$ f8 m! `2 {0 s& Q1 r    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
- L: \0 T/ P! S4 T4 ]# Y, L    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},: }$ P+ U$ n3 y. A/ q! a" e9 ^
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},: T% o/ L% |, x& v% h0 D1 O  R
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
- d% F4 q& c% d. ]6 X; w5 s1 J    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
7 L+ I0 s  A, a# \$ l4 L, b4 _" j, j    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},/ G; {% R# ?, D$ G/ J+ V
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
( f, [( D6 h7 k6 ]# b    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},: m5 D; G" N5 q) M& s! B
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
. q1 u* G: C! _1 j6 [& p    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
& F) H" I+ R1 R) g" f& E8 T' M' ~# i    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},- ~. r& H; [6 }. t
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}; b+ m+ c) J1 d8 N. Y5 {* C
};- F9 X6 d9 Q+ a' {, Z5 N

; \( `, q6 o( X) o$ S- I, f8 A/ o1 ]8 S- I8 i
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。4 h6 W9 a0 K. ?5 I0 C
% `# s5 v4 d( G* y8 R
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
7 |( X/ o7 |$ L' [3 v! e

7 W( h% ^! f" E# Hvoid update_key_matrix(char row, char col, char onoff), Q) z& ~4 l$ P4 a% \! I; F
{
# J2 f! c* _8 x0 b% \" A5 N    static uint16_t hid_report[4]={0,0,0,0};/ d- Q6 G& ~+ L4 I- U% c% Z
    static char (*hid_keymap)[8]=hid_keymap_dvorak;# _9 o0 u4 X, x4 j* s6 h* o- w. z0 }

2 d; X1 {$ u1 I$ t5 K) U
% L- l% B5 H/ }3 E9 R5 g8 r: B
    unsigned char key=hid_keymap[row][col];9 U6 ?  H3 f; y4 Y; k
    unsigned char *report =(unsigned char *)hid_report;& i. P" O6 F/ V# k6 }9 I
    char i;
$ }/ Q' q* g+ H0 Q0 }; X1 n% ]3 t, j  j* @% F
' j* K3 E- A% v, [: B: U
    if(key==HK_MODE)  ~, l( `  P7 ~5 |+ o
    {
1 P/ }5 O2 }+ j$ F        if(!onoff)& j9 q! w/ r/ h% \4 X$ h
        {
+ E, ]9 t$ h; R$ W! S            if(hid_keymap==hid_keymap_dvorak)
* y/ e0 H1 l& j            {  O. m2 Z0 {$ Z% P- l
                hid_keymap=hid_keymap_qwerty;
. M! Y2 \6 m, w* M  d% Y' {                GPIOB->BSRR = (1<<2);$ a6 {# g( x& @
            }
& Y) E# P9 j. m6 \' L            else# L4 @9 q3 o" A% f! q
            {
* ^& O: a6 T( _: l7 G% B                hid_keymap=hid_keymap_dvorak;2 D* H* _" w" }: e2 u
                GPIOB->BRR = (1<<2);/ i6 T$ e- }5 C; W( d+ D
            }
6 E+ r4 v" g* W( ?5 I- M        }
3 V3 Z  ^) ~! N; D; x! X        return;0 n/ Q5 |8 ]. T+ F8 m
    }
1 H4 T6 j4 Y- j/ }- l" }/ h5 q4 ]" l( F2 t( C* s  O/ H6 w9 R# b

2 q( _+ h: U& X+ }1 q. q/ K( m    if(key>=0x80)   // Alt, Ctrl, Shift( ^% [8 i& \" h, @7 a0 K
    {
3 R1 b4 [  e- j% o$ H: h( B        uint8_t bitset = 1<<(key&7);
# L! J& O$ D; R& u$ y  r/ ]6 E, Z        if(onoff)   // non-zero is key up( [1 f% z; ?, l( a4 E9 y/ z6 f; f
            report[0] &= (~bitset);
* P: e. s4 _% u9 ~( S  b        else6 \- t$ ]# ~9 Y6 E' G( y) C% d
            report[0] |= bitset;2 ]( ^9 G- P+ w: _8 w9 Q
    }9 I* c# ~( }7 ], S
    else
4 f5 c7 H/ S1 F% Y4 f    {
7 a) C& }6 X8 D% x        if(onoff)   // non-zero is key up# L; E) p4 M! K
        {- e9 m4 o3 k, t) {' K4 l4 X
            for(i=2;i<8;i++)
& D( _0 g6 S7 p% C8 c* |            {1 Y/ d5 g# U5 q. F
                if(report==key)
) C4 m! k9 `1 [, w                {
+ c# J% c( A7 V/ |; C4 Y                    report=0;6 S8 J0 F, A; V/ x/ v& z2 H. A
                    break;% ~0 x5 c  g' x1 H. m; i5 B
                }; R8 R* x5 X* h; z( ^
            }
0 }5 o3 x% a' u/ {        }9 C; S; [. o# S: D) U
        else$ @" y- J0 Z% W4 K# L& I3 H. C* ^
        {
- E  K1 W, t, b9 a2 ~2 b2 l            for(i=2;i<8;i++)1 T$ q( C% A4 u4 m% p
            {
5 d6 U8 n% }" E                if(report==key)- K$ ^8 r$ g- o2 B6 ]) [
                    break;8 v8 C) I$ _. F
                if(report==0)
" C. i5 V3 v: u' [1 }8 e. `1 V2 s                {! r& p7 l& D- g+ o5 C$ X6 U9 P
                    report=key;
$ `- O& `/ |& ]  C$ b                    break;3 C7 g) G. }1 D6 @$ _5 c$ |
                }
7 A+ S9 K% ^- S4 g3 U/ T            }
5 e! w1 W# A( O( n        }
' A! u  M0 E- o* p0 D; s    }% L4 J6 L/ p1 g! R
    for(i=0;i<4;i++)
! i; o. N4 W2 j  ^* ]) l6 K        USB_PMA[192+i]=hid_report;
, ], H5 p" L2 G1 j1 ?: L    USB_PMA[5]=8;   //COUNT1_TX# T, _5 R/ a- _# G3 \- \3 j. P. y
    if(ep1_wait==0)
4 @# f; O( b* l    {
4 T+ ^5 ]$ K- T5 w        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;0 M) H6 L+ g" ~. F. ~! G
        ep1_wait=1;% O* e. }. j6 R& W, N+ ]! s
    }& I1 d! {0 U/ I7 {9 ?7 U" N. a
}
) \" }+ w8 B* n6 E. b" h, E  [8 ~
0 R, v) j4 |8 \
( D- ^. x3 |0 J* S( M2 t完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。, P% u$ t# P6 |8 Y
keyboard.zip (8.7 KB, 下载次数: 6306)   h( y) G- h/ J- Z0 |
9 d) ]3 ^. N8 B1 U+ q& D5 R) r
2 L1 Z1 G- ^, E+ q1 Y: {
7 l' ?4 L! r+ N$ x+ G+ x+ z! [
8 Q$ F: M5 y6 u+ y( K2 h
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。' C* Y: F4 Q: v+ i
回复

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
) Z+ E* x2 E9 v# A5 I6 w) J, b( i8 N不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48( {8 u/ j- g- d" S
刚开始我以为要把打字机改造成电脑键盘, F$ P/ z+ h5 ]; O/ D4 |9 F
不过楼主也很厉害!

' }5 s7 S, m! ]' w" l哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
$ `# [' X( a6 E$ r( S5 B3 E8 m
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

积分
2
发表于 2019-7-30 15:50:27 | 显示全部楼层
这个看着真硬核
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|MINIWARE产品技术交流 迷你工具-智能烙铁-加热平台-示波器-体感电动螺丝刀-数字电源-智能镊子 ( 粤ICP备07030012号-1 )

GMT+8, 2025-9-19 13:46 , Processed in 0.168564 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表