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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
* `+ f# ]6 U' F  R+ u
( S9 A& r  h* ]1 {7 A$ Ihttp://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
/ v6 ]2 u2 _8 [4 f这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。" u% I  W7 _+ W7 R6 z# H$ E
6 R' l, t* r  @, q$ ?) E
在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。
- p7 Z" i: k& O2 N
7 U( T; `- [; k; ~2 J
235140i3a36qivqzuvmt5q.jpg.thumb.jpg
8 Z1 a& ?) e2 ^% @( o- O# n
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。0 L2 O2 ]0 J2 }3 F
001734klbyoluenuwz4h4b.png.thumb.jpg / W+ X; E% X2 J9 A& B8 K
为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:
9 u+ a( N8 `" y! k4 g 003625r2agx2f5v922cf2f.png.thumb.jpg
5 m+ a/ K% M& D. c) N其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.( y8 z: l# L  r! K' W
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
- e$ Y4 Z6 d9 t  i0 U4 B
005836yvs0wvovwsssgd3o.png.thumb.jpg
) c' v- {; b3 y8 A* aDvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。0 g  W. D! a' `& p5 S. g) }! B
  J+ `, M5 a  L/ L. X% B
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
6 _3 G/ Z6 T/ q! u6 l9 l
: ^6 R" p4 g* S( y7 r8 J$ p到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
$ p+ ]/ {8 _1 e7 Y+ b; R% Q
9 X; e' C1 |$ z* x7 Z机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
1 M/ `4 ~; x6 [( y* C4 h# C
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
) r$ A4 ?5 _7 W) }/ K----------------------------------------------------  分割线 ----------------------------------------------------------' b* C2 x; K/ }' S- [6 z# O
. `  G& i% P4 t$ w" o- Z/ w
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

) d4 p3 w& L2 [7 `  }: b 020011osionbunl4ui44vi.jpg.thumb.jpg % @$ h$ ~/ K, q' ^3 s4 X! f
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
2 X. h  J/ H* @ 020017j8ycmnv7788bqv52.jpg.thumb.jpg 6 Y+ o( r, r( R. o: e) L8 [( V
特写,80C49
' v0 F) A& Q2 b+ |7 [& p# } 021040oujzuvtut6iujtvz.jpg.thumb.jpg 7 }. L9 ~! l' S8 ^
LED部分,使用了一片D触发器锁存指示灯状态.
) }" f. b; ?- s" I4 V 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
  T+ O* }0 q+ J' I/ H0 c, _/ \7 i暴力破坏,将80C49拆掉
& z7 m! g" J  T9 T# W 021113e48qq98vyohvhzzh.jpg.thumb.jpg
& B# d: j  c0 a2 N. T拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。5 |' n$ x4 r, V# c* j
021125nc9az6dj33rlds2r.jpg.thumb.jpg
1 b& ~. C; D6 t- R  `, n
焊好元件后的板子,准备替换80C49
$ t7 g" t( N$ n1 D8 }( \) {+ h5 z 021146zmmpmh4pav4o7g6a.jpg.thumb.jpg " d1 k2 P) T( p1 ]) v
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。
: X+ K& P! r( e4 u 021104shifhnrqbr3o5nlo.jpg.thumb.jpg : i6 e& d+ }7 t% U: I( m0 D9 F
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.$ a* B; ?* T) r
022003ym1p9u4ug40280uu.jpg.thumb.jpg 0 g% R& U# g$ Z2 J+ q+ m; |
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。! g' ^9 C. K' q9 ], @% B2 N
023313kt141q9qajtol7ma.jpg.thumb.jpg ! B6 _; [6 B# U3 r9 H  a6 U
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
- j) k, k* L; m! r0 U 023322nt7l5xb3ltttkltt.jpg.thumb.jpg
0 I2 r: U* ?1 |6 w: w9 V
主键区键帽就位
+ c2 Z, j/ J* J4 y8 W 023331hin88e8wkrwzwikx.jpg.thumb.jpg & a' c; R! n% q, y
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
9 g; C3 `- E8 E# t5 M" O4 _' Z 023336wjzlgopugg1jyy79.jpg.thumb.jpg
% d# f3 N0 O: j8 F6 E) Y8 A4 n$ N最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。
) p6 a- U2 c! ~1 `' p$ F 023341sffu4j3g2323h6fl.jpg.thumb.jpg
6 n. Q; W7 {' d4 S: F* Z

# [3 _) \# W* P! {( w/ k& E----------------------------------------------------- 分割线 --------------------------------------------------
) N  T- E  Y, q0 S5 X

" a$ w8 B; s9 r+ S- Y6 ^
" r& j: F  s1 u; d8 N7 t, y* D
& P6 @( \: T/ F# }

' |9 k0 y; |* v- J6 y) @
. q! j8 A% j/ Z5 N- r+ ]/ V
4 ?8 ?' m! R0 v5 Q, u

# l4 p6 [  h* I$ b" Q. B6 `7 ?  s: x
2 r5 f2 A9 i+ [

% [4 C& c) o: v( d- |* [1 J* K! Q( m) H1 j
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
7 z, v: I) Q8 [7 Y80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:4 X. b8 r) C4 {5 {4 I% ?
104025nzibm2rmiomhyirm.png.thumb.jpg . X* w5 [& B4 k2 B- o) ~5 `/ \
% K7 ^' E7 j3 r# A
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
, K( f7 D+ e/ |0 V/ | 105004zkrez5houvkkznko.jpg.thumb.jpg , l5 M% x( F+ ?9 x

  k0 G& X; i' b2 K8 ^2 P5 _扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
! u- T: U" E1 J' {: {6 }
4 r6 l& B& {0 G' Y这是我设计的电路图:# [4 ]" J6 N' S# W
110344ej2z2oo2rflo7oe7.png.thumb.jpg
8 |4 ^/ I# r+ C# x: B# c6 a4 E# ~: }

& s+ }' X& e3 M1 d7 H( APCB Layout:
- b( l" l. G2 ?8 \* S 110847jjbjvt34vwt3v5bb.png.thumb.jpg 6 L# f. I4 |  D5 A6 ^, [4 h

6 N) [% _. O7 `1 y, D2 N不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
4 N. D; @+ ?" G: I. P9 I: _. D; F4 O" ?3 b: R/ ^
& |0 ]- e# e& a$ t
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 / O1 n; d- p2 R7 `( J
' \, L- }" v. A' S
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。* y4 X* w( \' r% ]$ j/ J* k1 S: g

& `" X  o3 r$ W总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:9 T+ L0 q3 N" b" W
113818pmrfsb6z0byt6t06.png.thumb.jpg 8 J& x6 I' k6 J1 m" e

$ p& N, p3 W( ^+ t8 s其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
: z' f% q7 H4 ]: b! k- }% _2 z9 D1 k7 @' L9 g2 k
USB的中断ISR,bare metal哦7 i- k3 y0 r1 b- }
7 q/ Y7 c# k/ w' C( i+ Z
void USB_IRQHandler(void)
) ~8 F6 ]$ N" a+ i% n{) E* f* p3 ^* q. `6 e5 c# M
    if(USB->ISTR & USB_ISTR_CTR)
5 U5 L5 W7 ]; I8 x9 D    {
! I1 e( t  b2 Z( P        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
9 M, `) N# l6 u/ w$ h& y& P+ _        {
/ u; |6 }7 S3 }9 d3 ^; v            switch(ep0_state)) r% j' r8 t" U9 R7 a1 V1 _
            {
& U2 k5 e! N" S! Y! A                case 0: ep0_state |= 0x80;
& V7 w' |! C8 s4 c9 y                        break;
9 _% c4 L2 E) e5 C' v( f% e                case 1: if(USB->EP0R & USB_EP_CTR_TX)
. \  q- p, J+ c/ y                        {
* \& h/ h& L; X: O2 g                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
1 {- i8 d9 J- K1 d9 m; Z* C                            ep0_state=3;
8 L# d: u# p6 k/ J! a: X                            return;. M( \# l# }) G! u/ |. k
                        }5 ~8 X( d' s. O9 F# ?/ \5 {
                        else7 \1 G4 G: v3 C- o" B
                            ep0_state=0;" b1 |# C7 u% \' K
                        break;
! r3 C4 P" \1 p                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
2 l5 {$ L  `) h4 c0 `1 l* @0 i. E                            ep0_state |= 0x80;
/ b7 S% x5 [- `5 K2 i                        else9 `/ I2 B# g$ J. W! C9 |
                            ep0_state=0;
* R8 w, _2 A: n2 ~1 H2 t: o                        break;7 x  A, f6 e; {, C+ U7 [6 L
                case 3: ep0_state=0;  P( G8 a8 v# x% i0 U, e: j, r
                        break;9 |9 K0 F1 c2 d+ J: H! N. d
                case 4: ep0_state=0;6 o( @4 z- e  x% b& Y3 z
                        break;
5 {9 H' b% S& a, t6 g0 D5 w                default:ep0_state=0;2 k3 I0 i- h" d9 f& u  M9 T$ ]
                        break;/ c$ e: f. e$ ^2 `
            }
% q: P9 L3 p. s) [, o4 C3 g. {; G: b            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag3 g0 ?( _* u6 T
            return;# j7 W. H+ o1 j' p0 v; A
        }
) u* _7 ]! m3 k: z, q( d  t        else    // EP_ID can be 1/ H6 A  T% C2 `- C1 P* |2 f) |
        {$ }$ m: s) `2 H
            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
6 T' g) E0 b  `7 I  B0 |& j9 W& n            ep1_wait=0;2 S9 g5 N) d7 M* [2 h
            return;
9 m7 [' [0 }0 y+ Q, t+ e9 Z2 B        }# ^9 N: N- A, K! ?1 S  A
    }
; a. h  V) s5 H; \4 ?    if(USB->ISTR & USB_ISTR_PMAOVR)3 |& K, b+ c5 Z, r
    {6 `" o3 J; r: h% U. u% f
        USB->ISTR = ~USB_ISTR_PMAOVR;
0 N7 m2 f' ^" l' a0 D2 s    }
; H4 z2 _5 d# S1 }5 Y8 g    if(USB->ISTR & USB_ISTR_ERR)
- x; j5 B% C8 h, b; x    {
( i* v8 u! V0 b, y" s- ~% Z, ]" L        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
* Y+ p* G$ n) m; c4 M1 |5 z    }: a) d% ^4 d- l- r) P
    if(USB->ISTR & USB_ISTR_WKUP)7 x) u7 ]- q) {' C5 r4 j
    {
; D" d. n8 R& ~0 K4 r4 F. K        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend9 B$ x: y. o$ |
        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
& y  ^' h7 m/ ^4 t    }- w( e( ?3 g" {- F0 H1 \
    if(USB->ISTR & USB_ISTR_SUSP)
  ]7 H9 T# g+ a( k- I    {
8 h' D6 U& v+ z; U- J& x' C' l+ c        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
; G/ Q" b0 s+ i0 l* R3 J6 l        USB->CNTR |= USB_CNTR_LPMODE;   // low power
2 _1 X/ ~) r; T        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
0 Q  \" O2 \" E% c4 W    }  [' Z# ?" d* W  _
    if(USB->ISTR & USB_ISTR_RESET)
0 ]9 s: A$ J" S- x    {. |& U  v  Z) b# Y( X
        USB->BTABLE = 0;    // buffer table at bottom of PMA- o. V9 [* u1 a* @
        USB_PMA[0]=128; //ADDR0_TX# \& r/ o& J0 w8 y$ F# a0 W" W
        USB_PMA[1]=0;   //COUNT0_TX  c3 l/ h# V( v' `3 I
        USB_PMA[2]=256; //ADDR0_RX9 H: \8 p# P# L; u
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes0 n( E' s  z7 @, C; z! C& B
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
* b2 `+ e* d  r) y5 e4 {( [        ep0_state=0;5 j5 ?' V4 y" f
        USB_PMA[4]=384; //ADDR1_TX' `7 R; z0 z3 x8 p
        USB_PMA[5]=0;   //COUNT1_TX5 d( k- t$ E. ?5 a) X
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
3 y2 y- N% }9 {' }) D+ g( x        ep1_wait=0;5 `# t; w, o/ S+ Q3 y7 M/ s8 W7 \2 Z
        USB->DADDR = USB_DADDR_EF;      // enable function
) c' _* C7 ~" ?9 @        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear8 S" C. P1 O2 d( s; Z: ^
    }
3 p" q2 u, C) S- l5 b. A- v( }    if(USB->ISTR & USB_ISTR_SOF)& x: j5 A4 b  _: S/ S/ j
    {6 t7 }) V: N8 k2 n
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear) j& `2 Z+ ~8 C  |: A# M  B
    }
. m6 F! b/ m: {/ f& z& c}0 _& i' G7 |5 B  g0 A9 N8 Q1 z

, w, v+ H( v- z8 b: k
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。. Z$ W  x) I, V. j$ V5 U6 i

9 M$ D9 _( r/ P! W8 w主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。( g/ b1 P5 F1 @

1 B  Q) _0 I, V8 l" r" \
    while(1)) t! V# L2 x8 H  |5 u6 }
    {- ~! J8 d$ x/ c' d" x) v+ _
        static char row=0;; h! M. Q8 p' A% V# E  l
        __WFI();3 N# y! D. v  |
        if(ep0_state & 0x80)    // request data processing# V& Z7 ^& B7 E& x
        {
! J# F& _* w# E8 J* z- X) g7 d            if(ep0_state==0x80) // SETUP phase) L, m; D* O/ F8 s
            {
/ B5 t& w1 A$ ]4 B0 Y/ o                if(!setup_packet_service())
4 \5 u: q% Y: h/ C: J: G& ~4 ?' W                {
8 n# N: E7 e! z, v* ]                    ep0_state=0;% j+ a* l# V9 G8 y! A, o. Q3 A3 ~
                    // not supported
: j; H. a& e/ q. A3 o                }! N0 F% i+ C4 {; k. u
                // ep0_state should be set to 1 or 2, if processed
5 q) W+ c) t1 t/ v            }
  `; U! F( z, d/ L  z! g            else    // OUT phase
/ R2 d' B4 L) f$ p8 r, H5 {            {
$ U" b( g% ^2 C3 ]# O3 K% H7 r                // process data% v5 n6 M  s: o- C; g/ Z
                show_LED(*(uint8_t *)(USB_PMA+128));
* o5 S$ d5 N" l2 ^9 u3 L                ep0_state=4;$ p* j( V$ K3 C& G
                USB_PMA[1]=0;       // Zero length DATA07 n, C$ \1 ]' s8 b& S
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;, ~" T' l% n: [! f1 {
            }& ?: {: |" g( ^2 j! F
        }* x. Q; W4 l, ^1 [& D
        else
! E  i# T9 Z0 ?1 w' ^1 h        {* |! R* F% @; e9 d
            if(usb_address && ep0_state==0)
! C% f; L! t2 c            {
+ G8 W0 X/ g3 r* {                USB->DADDR = USB_DADDR_EF|usb_address;: F) X, b- @$ R
                usb_address=0;( \7 R( r) G, T* Q
            }
" K4 @# t) h* c/ N8 |/ e, F! K# }        }
, H8 ^# [- V1 f( G+ C        if(row!=scan_row)   // new scan line
' Z# W6 i8 [% \' C7 o  ^- k        {/ H( L5 R  b+ E6 _. F. W( ?
            if(key_state[row]!=prev_key_state[row])
1 c2 M' x" \5 y3 g9 I8 x5 Q            {- ]5 a/ ?( i! }) t; L$ ^
                uint8_t test=0x80;
( c6 s/ Q! H5 T* n: U1 J! n                uint8_t diff=key_state[row]^prev_key_state[row];
& U7 U" Z, f! r$ n9 \                for(i=0;i<8;i++)/ B. I) G- t6 Q* Y& Y6 K
                {
/ V( _  t; M, f2 h                    if(diff & test)6 l  H' K, h+ Y0 H
                        update_key_matrix(row,i,key_state[row]&test);$ u4 u8 P6 d* b' A$ j
                    test>>=1;
, n5 q2 b  B2 M* `; ~                }1 @% F. n$ i9 h! |# o: H
            }) {3 F* u6 _* @# ]( W- D
            row=scan_row;/ n+ {% d+ [; n% ?9 j
        }# z  _9 F4 N) `' x4 o7 R
    }# g& X8 R0 G7 R! _# C* @. O* ]2 T

, Q" e+ s' c& W* ]: h1 F/ q/ C4 z* y/ w7 X0 Z
EP0的控制传输,把用到的请求处理一下( P8 f4 Y' e7 O; ]; B/ M2 T
char setup_packet_service(void)
. g* S  r7 H2 l" M; e{
# J: V- q# b% H/ {1 R    if(ep0_std_req->bmRequestType & 0x20)   // class-specific1 |0 V+ b2 u3 }
    {
7 _+ X+ O& ~1 ]# x% D        switch(ep0_std_req->bRequest)( |) z- a5 S/ }2 s
        {
# P; ]' t& [8 d. r# y" n- |            case REQ_GET_REPORT: break;: ^' x5 h) E8 `. k  ?
            case REQ_GET_IDLE:! ~  [) s- t1 Q
                USB_PMA[64]=0xfa;   // return 1 byte
- \! {& S0 s: q" a% x                USB_PMA[1]=1;
8 L( A. ^+ v( v& g5 @' J3 `7 I* ?                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
, m+ ~+ _) d5 z3 I1 E                ep0_state=1;
" V: Z9 O1 x: a' ^- `4 r6 u                return 1;2 U. v! E) A- T( t
                break;
7 u: w) z3 l2 e5 M7 a" a            case REQ_SET_REPORT:
+ ~. d2 E: g( C# l; s                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
4 @4 r' P' i, m( }  F4 F                ep0_state=2;
! t* n1 T% }9 p                return 1;( Q# h6 ]/ |; T% Y3 s" ]0 E
                break;
) e. A0 U$ u# R" d0 n; f$ x- E            case REQ_SET_IDLE:
# L# [& i- y* Z0 E4 X& W) v                USB_PMA[1]=0;   // Zero DATA
% ~9 \# n+ }* m2 L# _                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;' F  Q9 l0 ]- q
                ep0_state=4;
6 @4 g/ Z! X0 \$ y3 e6 d                return 1;
7 x/ @# }+ I% @$ O* p                break;5 D, n5 {( p) Z$ P% e* e) h0 _
        }
, {! J! C, m% V1 ~        return 0;4 ^6 ~/ K3 w& }. |9 M
    }
6 D0 J( I, p- ]    else    // standard
4 o# T, f2 T+ w' H9 L+ W    {2 F. E! l3 K- w( U: X; ~9 c
        switch(ep0_std_req->bRequest)) c6 @  g& n* \, r# w/ p, h4 z
        {& h8 X& `' ?$ J
            case REQ_GET_DESCRIPTOR:
% h4 J; j0 b4 {& k  Z7 F                return descriptor_service();
3 F7 M8 o7 D0 k. l3 H                break;
  b7 k. b6 C' V1 @" b( I) K' S$ W            case REQ_SET_ADDRESS:: C7 y; W2 T4 N8 q' Z: M5 l4 `  K/ g
                if(ep0_std_req->bmRequestType!=0x00)3 C7 @0 b1 `# R+ j( d6 a) P
                    return 0;
# K' b( D, f* `) U% n# m                usb_address=ep0_std_req->wValue;
" y5 v2 |+ X1 r9 F9 x/ l: V; Y                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;& N4 g2 Y) d6 l
                USB_PMA[1]=0;       // Zero length DATA0
) C( h8 y/ X  E3 o7 H                ep0_state=4;    // No Data phase  b; N, \( ~$ ?" ~. v) }( C
                return 1;
8 t/ L+ Y  H- n0 Y/ h            case REQ_SET_CONFIGURATION:
0 i  G8 {/ S) z4 |$ i                if(ep0_std_req->bmRequestType!=0x00)
+ ^' `0 K0 C+ f6 T. F                    return 0;
( m' c6 d9 B' d) n1 i% o; o                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;5 v/ l; w1 t% a5 m/ l
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;$ l0 }) z* L. j
                USB_PMA[1]=0;   // Zero DATA% h: @* b. r& t
                ep0_state=4;    // No DATA phase& s1 J+ t0 p; L
                return 1;9 [' g. y) g0 G
            default: return 0;$ ^! X  U( }& p9 B( k4 z/ C
        }
  f8 b8 T: \) r; I" w6 E    }4 N( }1 H' o* C6 r$ |. v; j
}* e1 U: O+ z+ }1 m
- [# U* T1 x( Y  M6 C& k

0 F* K& n  r5 W) u0 }# Y5 s6 H' u$ j
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的- g' ?$ f3 X2 R( s$ n: P- E
char descriptor_service(void)
3 N3 m3 F9 U' |7 _- `5 G3 N% o{
! b$ v. T$ @& J" U- D% e. V7 m    switch((ep0_std_req->wValue)>>8)% W( a0 t' e0 l. ]5 O
    {  I, P- y- Q5 _* {% D9 }
        case DESC_TYPE_DEVICE:
% ~" L) B, U; Y3 h            return ep0_preparedata(&DevDesc, sizeof(DevDesc));
, G" [/ i" R' V3 D# l            break;
5 W) G  b$ L4 e8 Z3 o        case DESC_TYPE_CONFIG:
, V: T, h0 N" e# `            if(sizeof(ConfigDescData)>ep0_std_req->wLength)
. P( B6 F: \0 b# Q                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
8 I, R6 e, S% B8 t7 B            else
9 b1 w" y) U  Q/ J) P2 U                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
; M$ Z- N& y: }' G; d            break;+ L- O8 m4 i" K3 I, h
        case DESC_TYPE_STRING:) @0 A& e" p7 \7 O4 _6 s- E3 C
            switch(ep0_std_req->wValue &0xff)
3 n& l/ {9 i( Z9 o2 s$ X            {1 c) q' ]' U& T) n
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));! N* }) Z( X8 P
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
2 b( _# r( `: w, _; S$ E9 \                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
$ g7 \9 D! w+ v                default: return 0;  X' g' l. ^- j2 f2 X' X  k8 H5 `
            }. d9 G2 {4 R& o4 w* o8 m4 y. I
            break;
1 _5 `& x# ]- D1 J" E9 k) i        case REPORT_DESC_TYPE:7 v9 q- G/ J' y% @/ K
            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));0 o  b  I6 `$ h1 Q' k. r6 N
        default:
- u# D& @, U3 |5 t            return 0;
: l) R" j. D, B+ V9 i- M$ X( P    }
. B5 I  @8 B! R& A}
1 g$ r2 A4 t$ w; U! P下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.$ ^$ e- D: M6 U# O( d- w4 Q' n3 Y
void TIM6_DAC_IRQHandler(void)
: ^' O" V+ H' H3 O4 s, d: C{3 Z8 L0 y1 S8 g  m& ]$ |
    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
( S+ @! L& D/ c1 s
5 o; E# g  ~& M8 j( D% W4 q) Y

5 l$ J+ R2 e  Q" ^    TIM6->SR &= ~TIM_SR_UIF;
& q) {' {, z/ l; @    prev_key_state[scan_row]=key_state[scan_row];- K1 x2 ?3 h8 v7 i
    key_state[scan_row]= *PA_IDR;   // update key states
& \' c$ B1 T+ d4 a    switch(scan_row)2 q  v" C5 f+ B- f9 A8 W9 a% _3 L
    {
. g4 L4 S/ c8 p$ R% T3 |( ^        case 13: // next row PB14
1 ^1 O: e. e* c3 L7 K& Y/ k; e$ F                GPIOC->MODER = GPIOC_DEFAULT;
2 e0 p; ]# P# l# }8 F& N$ x                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
/ O7 l" P0 x5 H1 V# p) f: o* }                break;
! V! L/ ?9 I* D  o% L% Y        case  0: // next row PB15
/ y# E) _9 L6 g+ b; w2 ?3 B                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;* W4 l/ Y" w8 u. R
                break;3 n8 U$ p- j8 @6 ]$ M5 r6 [
        case  1: // next row PB3/ \, X8 U3 }  V! q  H
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;6 l8 t, |1 X4 s4 i# F! ~2 i8 s2 ^
                break;( T2 Q7 c7 I- h; I  r6 c$ A
        case  2: // next row PB4
/ J/ J- ?* ^& w                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;6 ~4 a/ f! d4 m, b: T
                break;0 W) ^6 |' b" f  p/ O% P* |
        case  3: // next row PB5
: L0 ~5 R8 O8 d                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;
, T$ o$ A; ~) I% U4 D" M                break;
8 _" x  V/ g. L- A4 r0 o8 \        case  4: // next row PB6
7 f: H8 ?/ @+ Q# O$ \                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;( Y: J7 ^( v* D9 H& I
                break;
3 k; n; X; \" [9 D, C        case  5: // next row PB7# ?# T( \9 S& O5 {- |) ?* h
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
6 s' P* t: e' A8 q5 j1 [2 d3 F                break;
- I' Z. x7 o& r7 U        case  6: // next row PB8- i: s( Y; m/ l( t* B
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;7 O; [  L. j2 s- ]( U
                break;
% S3 x* n  r6 k0 T& @        case  7: // next row PB9
2 r) ~8 \+ o& N                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;& G' W' I6 [# U9 f- R: s. Z5 B7 B/ ^$ b
                break;8 J8 ^% h6 ]. p" [7 U8 G
        case  8: // next row PA8; y  y  S! a7 p+ s/ J0 q5 f: A
                GPIOB->MODER = GPIOB_DEFAULT;
* d8 B' O$ a; m  ?# v                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;* Y1 y7 U0 L- J7 o/ ?' `
                break;7 V+ h1 K$ u2 _; u* R4 Y! _
        case  9: // next row PA9
' W( C) p, w9 I3 K  h                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
1 S" G" O& S$ i                break;2 w( G# Q: Y# s$ X+ m& u5 w& C
        case 10: // next row PA10/ F- ^& N3 L5 [( I, D
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
% j9 e7 {' E# p! f+ ]/ b) G                break;
4 \! K9 K/ B- k* q- l2 l$ {) N        case 11: // next row PA152 B" {, K+ w1 U6 U" Q1 V; u$ d
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
( B( \) P9 z1 v                break;4 x! x0 e8 y- ~+ }
        case 12: // next row PC13
" u. c& L! v2 |                GPIOA->MODER = GPIOA_DEFAULT;
/ ]' [$ |& ?! z2 y& S8 }: W                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
- [1 t) E# B7 e1 @  W                break;
3 c- u6 h; a5 m+ ^0 R6 J    }
6 b3 G) Y! P: @5 d% w    if(scan_row<13)
6 _" d/ P+ n. P$ @        scan_row++;  c! I  ^0 ]$ c1 ^" J! T
    else
: n5 c8 o. B/ V5 e        scan_row=0;+ K. z4 r/ y* v: T. S1 }5 g+ h: o
}0 m3 i3 o+ V$ T( l  m/ O8 i; `
* l: M: }% ^( Z5 G; Y0 o' C' Y

7 m/ A3 L; C% X7 Y$ S1 a) s
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 3 I1 Z6 z& A7 M6 E

/ _. {; G" X; j& U/ m扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。/ J; W8 w8 ]2 m, F( Z. c7 I% r, ~) z
0 W( P8 T* W8 R6 p& k% s
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
8 P( H7 F* p1 `4 n9 |

: k2 i2 E9 G" ^9 q4 `$ o1 g# O, z' z3 h4 p) v4 R: j" I
const char hid_keymap_qwerty[14][8]={  q' O1 Z5 V, E* \
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},8 C# O+ j' s4 s+ |! j
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
5 T' N8 |; d" Z: J9 `' M    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
: b6 p0 V% l+ k2 V# F    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},- {" B0 ]* H  E, B1 Y# r! @
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},- N& v% [: z+ Z* v  h7 p7 I5 F: i
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
3 P0 W! P& v/ \+ O% `! C3 r% f    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
3 i: ~! |1 ~5 a' o% d% g( Z    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
( D& ^7 v0 P0 j+ X  B. c3 c! }    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
. h" p! s4 Z1 r& k* g0 r8 `# [& K    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
' g/ G% C0 T8 o    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
( m2 U  ]2 G8 S    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},- B" ]1 Z6 e% N
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
; e5 M6 }( S5 p$ K; z9 {    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}1 k" p% r: g9 x' R& r5 y9 c
};7 s4 K. c+ C  U4 ~
, s! G! F# L' j8 s# \
2 v- W( K6 g7 S% h6 O; _: M2 A
const char hid_keymap_dvorak[14][8]={8 w: Z9 `/ l1 j5 s% [) ^4 Z
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},, u  j% i* w1 x' h# F" U6 ~- w' t
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
4 r& r1 d! C1 e% g; t/ S. `    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},' ?! v# E' Q0 t. n( I6 M! I
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},; N7 j( n0 N  T; h1 b8 B; ]' E
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},8 x% D3 ]$ K  b. I
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},6 V9 h3 Y8 a; }" ~
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},' A4 W% s3 {" ]2 E" a
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
4 E: \! |  W+ X7 k2 ]" @: U/ m% Z    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
2 U4 F/ j  M( p& k; i    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},6 J: A& R: z1 N5 c, |% b. A
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
3 q  S2 [! W& P. L, ~    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},& A" u) j8 K. J% {
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},) o7 @: E3 ?( t6 }
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
( A2 S: S& R1 w" ^$ X0 Y& C/ d};" c+ s. e- {' I6 n3 H- |. ?

7 }1 ?) w. b. ]! B$ q3 l; H- Z9 a8 l7 B: i& N3 U, u5 |% ?
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
' i4 g" Y$ r! ^' q: r- A& b  ?% n# t( A7 f
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

% t4 C# R3 H# m9 c6 i: j! e( V; R0 d  d- W( x) `: E4 F
void update_key_matrix(char row, char col, char onoff). B3 ^, O. H$ K* d% p: V
{7 Q, }: W* l. ~! L2 v1 V1 E
    static uint16_t hid_report[4]={0,0,0,0};
- u0 t# }! K! ^) {    static char (*hid_keymap)[8]=hid_keymap_dvorak;& Y, C2 R9 n5 M: Y9 B, b% P: t0 b
' f, Q  @7 d1 s: _
, \: a/ e2 j9 ]$ C; z' Q
    unsigned char key=hid_keymap[row][col];5 V4 Q. n+ a" ?4 B
    unsigned char *report =(unsigned char *)hid_report;
" J; C4 V! o& _    char i;. \, Z( k2 T# O+ E+ L

  y8 j' l; T+ x

3 ]1 B( q: G8 c- w# W; z    if(key==HK_MODE)
& [9 y9 l: _4 F& y    {' g7 E8 Q1 U9 S5 A: r
        if(!onoff)# D/ V3 i, _- b5 l! B5 ?: s
        {5 k. e% _0 h$ j/ T' f7 e
            if(hid_keymap==hid_keymap_dvorak)! v5 j$ Z! q& ?  [' |* h& ^
            {
8 y) G/ W, W$ r! X                hid_keymap=hid_keymap_qwerty;: L+ `% V2 K3 ~+ v
                GPIOB->BSRR = (1<<2);' X0 \. |4 t# [7 B8 }  b
            }( M! Z/ u2 j8 Z( @" I
            else1 `% X. m  v6 H6 y+ n! R& j
            {
' W2 I8 ?  k$ Q                hid_keymap=hid_keymap_dvorak;+ J# Q& [* K( Q( }. R( V
                GPIOB->BRR = (1<<2);2 F+ o) j7 A2 Z
            }% @3 `5 {& i* }3 ?! I$ A! F
        }6 s2 `- X; a& ]
        return;
5 W% m3 b' x7 j$ e: D1 J, K    }
# c1 ^& x; P  n7 a* I1 I7 d: Q, Q" B  E- z0 J
* X: ^6 M# r" g# j
    if(key>=0x80)   // Alt, Ctrl, Shift+ S) X' G2 I( k) F
    {) }8 R" M" u+ Q8 o: W/ G/ y: Z4 O
        uint8_t bitset = 1<<(key&7);! o$ }! S0 N- V- B4 q2 g
        if(onoff)   // non-zero is key up
: Q) ]$ t& p; w  ~7 ~            report[0] &= (~bitset);
  ?" J2 G, G# i: o        else  b4 U% L9 V* k( E
            report[0] |= bitset;
  [+ m& o, n  U8 `& G    }5 }3 M6 B9 f" {6 m  d: m
    else9 Z2 M, F/ B1 y, ?* A
    {6 B- N, ^; d( R5 j7 q$ z- r
        if(onoff)   // non-zero is key up" ^9 u' f# d: `" }( W+ [7 V' Q
        {* M+ ]/ }1 F9 F! x9 T1 A
            for(i=2;i<8;i++)
# H/ _5 G; i1 M) I4 d1 i            {7 A  K8 e6 G; R  q3 L3 [* A
                if(report==key)9 a3 x! J5 I' j9 x* ~
                {
3 o! K7 t" E" x+ s! S, V                    report=0;; q' d2 ?2 y% E- n( `# P
                    break;
8 o+ N7 y+ ~) e+ F' a* `& @" L                }1 O9 S. }6 o5 H" c& N; e
            }( p2 d' i' v: [& T  B! n
        }
) A) M+ J+ @# |3 @) S        else
2 q( w/ Z$ m! _% p        {5 s* l+ L; M9 K9 o. J
            for(i=2;i<8;i++)) e! Y! H. m4 ]/ U
            {# E2 t' F! N# D5 r. r
                if(report==key)
% Z( z0 r1 O; K                    break;
0 s, v  e1 z) h                if(report==0)
" j& Z# n1 \- g" c0 }' J3 U5 g                {
0 @" ?( B( o6 B/ C$ ]  {% i                    report=key;
  Q- S; M$ F/ _  d/ ?& |& s) @1 N                    break;+ E- i9 H6 M" v* l0 l5 w
                }9 q$ [2 D2 B, b3 j: U8 s
            }: T6 e& S! A6 Q& a* y% ~
        }6 F+ T9 M( W" d
    }
4 d4 {/ L+ ^7 ^# u! Y  c" I4 N    for(i=0;i<4;i++)  l- R" |" R0 l- G/ f0 d$ e
        USB_PMA[192+i]=hid_report;
9 c4 I/ f, k( [* S2 V+ f  Z0 s    USB_PMA[5]=8;   //COUNT1_TX
9 J) {9 K, x6 G& m/ E* |5 G6 K    if(ep1_wait==0)) t4 X# h- t# V; j; U; L# ]9 U; z
    {
5 n! {0 I  U2 Q1 O' W& a, E- }) L        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;. i% _: e- J4 y! n) [  D
        ep1_wait=1;4 S$ E* V9 O& r2 I5 b
    }6 l* Q: U1 b, A6 T! q( c
}
2 z' C) w9 A/ P+ |- o" o2 S( Y- X: W& d, ~  H$ P

! S4 E# C) t- E$ M) E& M* R/ C完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。( U" A; t+ t1 t: r% e% G+ u/ W. R
keyboard.zip (8.7 KB, 下载次数: 6346) - ?0 F6 L, y$ ^- b8 ?

+ g: \& a5 O, ]; c! C' b" A, O5 ^
) A9 [2 A! s2 Q- F8 M
9 }5 O1 |  e) u/ }  n1 z8 Y* U% U- w# {' v0 S) l. o$ h5 n* b
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘! A; ^, z7 p! |) y/ B! F
不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
4 \2 X5 S! l6 P+ |5 _4 }# W刚开始我以为要把打字机改造成电脑键盘
8 _) E' Q/ \9 D: L" q1 C不过楼主也很厉害!

* [2 K2 R7 Z- z# S! F4 O5 S8 ^哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害6 D& v% F( C4 c; o* e7 u
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-11-7 19:00 , Processed in 0.806137 second(s), 27 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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