请选择 进入手机版 | 继续访问电脑版

MiniDSO产品技术交流  迷你示波器-袖珍示波器-示波器探头-

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

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

[复制链接]

22

主题

61

帖子

838

积分

vip

Rank: 9Rank: 9Rank: 9

积分
838
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
. z; w* x, e; Q& S/ z* ?8 i. ?. G+ k- U1 ]
http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D16 s$ e) M/ B2 e3 U
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。
# @2 P6 s4 s1 C7 }, c5 u# d
- A9 G# z3 X- Z8 s' u在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。
* V& V( q4 B8 J& l
/ N7 g$ y& r8 V5 J
235140i3a36qivqzuvmt5q.jpg.thumb.jpg
1 T( ^- ^4 M* F2 z6 W% R# ]( {2 w
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。, E" W# ?5 w2 [( q
001734klbyoluenuwz4h4b.png.thumb.jpg 4 J+ w0 p* o8 E1 b, u. E
为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:+ g7 S7 `# j  [' F( \
003625r2agx2f5v922cf2f.png.thumb.jpg ! F- R( v1 f3 l! z7 _8 i- h
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.2 q, d  M# Z6 q
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
" G3 ]/ ^8 U5 }5 b9 k
005836yvs0wvovwsssgd3o.png.thumb.jpg 4 e5 Q; R8 ?! \- U2 D
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。. T  Q  S, r: K2 T

& V8 A- _/ B& g3 \+ X0 e3 T我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
$ J0 V! o6 a% z& h( x3 u/ o
7 {; x$ V# t- Q; U. _4 ?. v+ B! ~" ]到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
% Z; D# D- D& Q( }# o( R! b9 d8 M6 H2 `6 q$ E7 ]' @
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
9 x0 K8 \9 }5 G2 X0 u: s
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg 7 u$ _6 P$ E7 h* D
----------------------------------------------------  分割线 ----------------------------------------------------------
- F& P" t# a- ~: t, A- ]( I$ e: ~2 C' k# {
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
" {8 y3 Q6 `6 R  O5 i7 T
020011osionbunl4ui44vi.jpg.thumb.jpg
4 G2 W9 i9 J8 ]  d# Z- B轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
4 Y, C: c8 z. T' U 020017j8ycmnv7788bqv52.jpg.thumb.jpg
" w3 H6 [( `5 H" h/ a特写,80C49
# v/ g1 a& @' s3 P( G' [. u! ]: `/ e 021040oujzuvtut6iujtvz.jpg.thumb.jpg 4 p: h- x. j+ |1 f2 p  Y
LED部分,使用了一片D触发器锁存指示灯状态.
4 N( q; y% u& w. J( }  R- H 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg 7 l( S3 Z. D  ]7 Q6 ^
暴力破坏,将80C49拆掉
! l- H6 m+ x* S! O+ s1 G6 Q 021113e48qq98vyohvhzzh.jpg.thumb.jpg
- u& o6 H, }6 c7 }5 s# i/ u. T, ~拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。4 ^: W' S& \! W! ]1 e/ w; |
021125nc9az6dj33rlds2r.jpg.thumb.jpg * l: G  g/ H6 T3 t( F/ N
焊好元件后的板子,准备替换80C49* s- w5 T1 K; S4 k& W7 P
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg 4 T, [5 e7 Y* i, v
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。% o0 |) `. o# @) d) G2 I
021104shifhnrqbr3o5nlo.jpg.thumb.jpg 2 v5 `/ q9 e* S0 S' q2 q
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.
5 p. k4 D* d' o 022003ym1p9u4ug40280uu.jpg.thumb.jpg % F; o# D9 c; c
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。6 a: ^+ N8 [; j1 ]4 m; M
023313kt141q9qajtol7ma.jpg.thumb.jpg
0 l) H: ?& ?  ?4 M( \5 N" `我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
! ^( f9 Z, x! X0 k2 {/ j6 i 023322nt7l5xb3ltttkltt.jpg.thumb.jpg " c2 C6 o4 B' y) u
主键区键帽就位
1 v1 l0 X5 T- ` 023331hin88e8wkrwzwikx.jpg.thumb.jpg
( h2 m" s' B$ x$ S编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
8 P( Y- b) N/ @% M& j7 b9 { 023336wjzlgopugg1jyy79.jpg.thumb.jpg , Z; D! x4 P' s( r$ V7 J
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。* X& ^1 V+ c/ M/ [% w
023341sffu4j3g2323h6fl.jpg.thumb.jpg 5 W* M7 w/ d1 E

/ _; ~! x" M, E5 E  Q. [----------------------------------------------------- 分割线 --------------------------------------------------
, l& z2 k) @+ l/ O
& `5 G" e9 Q8 R9 D- x; Q

4 V& w+ J0 ^$ F# _5 S# g- L) W+ c+ M& x! O
% d; x" }$ P- x: M6 m& u* U' B
1 k, i& X6 w$ i3 t3 W

& s( d& @$ e. z* d/ @! F/ i
5 W' o; D5 o+ v2 p8 V
% k- H4 V& O6 H) L; P+ ^
$ J+ i9 @/ c; x9 r  U& `) V' ~) t5 D) B% E* c0 W; e
  c; R, r8 n( H2 F' x
回复

使用道具 举报

22

主题

61

帖子

838

积分

vip

Rank: 9Rank: 9Rank: 9

积分
838
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。& I& L: h6 J: ~4 g, p
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:' ?$ H3 n$ ~( G. b
104025nzibm2rmiomhyirm.png.thumb.jpg + U, g9 d- j! l+ v5 ^

6 p! h& q% d. d" X+ |# Z2 ^其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
) [$ f$ \( \) h  j* l 105004zkrez5houvkkznko.jpg.thumb.jpg ( o5 S# Q" u6 R

6 D. i# s" w- B" P. _扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。  Q* F0 f& D7 X( J* o

; \) m: ~- i% c4 [7 H5 z这是我设计的电路图:
* E# o$ u; f1 C" E0 i1 j5 J 110344ej2z2oo2rflo7oe7.png.thumb.jpg / |' F4 ?1 K5 O) j9 _8 J0 x4 }
0 T, E2 G) {8 I
PCB Layout:
* G7 d- i5 ]; J 110847jjbjvt34vwt3v5bb.png.thumb.jpg ; j6 w+ i0 X/ c* L3 G

: U  s8 q% j: y- ~2 A* a不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. # O5 E, \# @3 w" G- l, Q

. T: a2 X; t0 ^9 ]9 s1 W9 G
' \& o* m# q; c/ n- P- _
回复 支持 反对

使用道具 举报

22

主题

61

帖子

838

积分

vip

Rank: 9Rank: 9Rank: 9

积分
838
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 ( j9 _7 W: y( S3 O2 Y
- a9 R1 V$ H4 @6 r' o" c0 n
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。$ L' J# |. s6 I* d6 W( X

7 W' x* S. I% j总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:
5 t( o  n* e! k 113818pmrfsb6z0byt6t06.png.thumb.jpg , Z3 t. J: }" |

1 u$ C1 j9 [6 |3 N- a, r% J其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)) u' s4 `- |& m4 Z! Q+ j' c5 m

6 O  C6 z, t: ^0 m) h+ x' gUSB的中断ISR,bare metal哦
/ p6 A& Q% F! H

( [/ L: z, c* \) |% |/ P8 tvoid USB_IRQHandler(void)6 t  M% N7 P* D. V
{: J" |" m/ n# `+ _9 r
    if(USB->ISTR & USB_ISTR_CTR)  G4 }8 u7 C1 B/ m8 Q, ]
    {1 g1 \3 H6 Q4 l! \: R& p: d; }% K
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
  @6 g( D6 F7 i- z* }9 K5 G        {. `( c, m! a, ~+ ]' a
            switch(ep0_state)
+ M8 H, z  r6 M) ~( s% b            {
' A9 Z; a( g" M                case 0: ep0_state |= 0x80;- |! \: ~  [3 l. R% d
                        break;5 G* T5 D) c! `  P" b$ C
                case 1: if(USB->EP0R & USB_EP_CTR_TX), B; }: F8 J& Z. S$ c8 V
                        {
3 g& E) G4 _5 S$ p# ]0 R                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;' O3 z7 X  K/ D) ?; H
                            ep0_state=3;3 j% z% M; a4 ~6 U1 H6 G6 D% V
                            return;
1 E9 v$ g) \4 X# A                        }
& A' W# U8 o1 V: z8 r) O                        else
) r+ e0 x' V8 z, n; o1 O+ i                            ep0_state=0;
  {8 X; {# i! p' v                        break;
: ~. ~, ?: a" L2 q# D                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
# @5 F& i0 z0 E, s/ E9 ~9 g9 ]                            ep0_state |= 0x80;9 a$ U1 b1 Y9 Y- z3 i+ F9 ]6 ?
                        else7 ?( M( H" ~% f' K, x7 f
                            ep0_state=0;
& C: I7 Q5 U5 E' _5 g, ]1 g/ y                        break;+ B5 P: B* m& Q, Z
                case 3: ep0_state=0;
+ B3 x  _5 G( D0 b9 g                        break;
) n4 R% Z+ {. J7 i# p                case 4: ep0_state=0;
" C' G, |$ z: o' G9 S9 P                        break;0 R6 i* C* q. c0 ]9 R) P
                default:ep0_state=0;
1 h: ~& U5 K: E9 d0 O                        break;' v% I) y- U& S1 C
            }# v& S$ z( x, [. Y" }- N
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag, G& f$ G- _* o5 c8 Q) a( Z4 K
            return;0 q5 f2 N) B) J4 G7 A" v- \5 J
        }
0 q+ M2 |) h! H, ]  K& R        else    // EP_ID can be 13 `6 z6 O, u0 E9 P1 \. Z
        {
4 L$ x0 L' P8 p1 a            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag$ x* @' E% ?3 f- U' P% [6 r
            ep1_wait=0;; T# I% S) s+ N' U
            return;8 |( B4 c7 l* O
        }
) e0 S7 V* ?) A% m$ B, P    }
( R, ~# u4 ]0 z) t    if(USB->ISTR & USB_ISTR_PMAOVR)
! Y9 _% V! K" z# P6 Z    {
8 V6 v1 f1 J. }+ s: k; a8 L( i* F        USB->ISTR = ~USB_ISTR_PMAOVR;
: i' u- v1 v; a, _; E- Z) A3 j" C    }
1 A, p! R4 D& K- }+ B, d    if(USB->ISTR & USB_ISTR_ERR)) {" |- O# `' V+ _8 ~
    {+ u6 N( K$ G3 m# C) ]& d4 ~; @3 D2 l/ k
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear4 U, \, g, n8 m7 {) O2 M/ H
    }
9 i+ t+ j6 S% J3 {2 n: p( N    if(USB->ISTR & USB_ISTR_WKUP)6 r) O) R$ x2 W7 b5 V/ D5 `
    {$ [3 v1 R9 _2 |# ~
        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend5 D: v: i6 J* ]3 A
        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
" Q: r: v8 D; w, S  `* b6 E    }
$ A  f" T  }, y+ {3 f8 t    if(USB->ISTR & USB_ISTR_SUSP)3 p9 F8 v# D# `
    {
6 c" _6 `* p$ U        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
$ z, p# u; M, F+ q        USB->CNTR |= USB_CNTR_LPMODE;   // low power& R, v5 r( @- d3 w& k* o
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear2 \  B7 C# g2 S0 k" Z
    }0 e( \' I0 [* ^5 }8 o3 v$ z1 c7 \
    if(USB->ISTR & USB_ISTR_RESET)
0 `3 J' A2 c0 v3 z/ r& V/ ]: h3 B4 Q    {: E$ d4 S9 S6 P$ C* D. G* B6 W; v
        USB->BTABLE = 0;    // buffer table at bottom of PMA, I  J2 K7 {0 I- p
        USB_PMA[0]=128; //ADDR0_TX
* ^. a! c8 @1 U! r2 o2 L        USB_PMA[1]=0;   //COUNT0_TX
! R. w, F$ j# W        USB_PMA[2]=256; //ADDR0_RX; c% f6 ^& h$ l6 O" ~' h7 W# |6 m
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
/ A; P8 A- s6 t4 o7 F; a' M* Y        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;; T4 ~6 |, n! R4 X0 A
        ep0_state=0;7 ^% ?3 G4 Q/ c
        USB_PMA[4]=384; //ADDR1_TX% s: j) W' C8 W  X- n5 x
        USB_PMA[5]=0;   //COUNT1_TX
4 R0 B+ P6 @# l! k' S9 G        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
' O' h# R3 h3 ^/ f2 D% U        ep1_wait=0;* w( I0 E) D, T3 N/ ~
        USB->DADDR = USB_DADDR_EF;      // enable function* U" g/ U( S9 \1 {/ x
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear. Y! H! W& a& R8 u9 [$ b. B- X
    }% t1 g4 L6 M4 I+ P- [- `% a
    if(USB->ISTR & USB_ISTR_SOF)' N' E1 B$ j1 S2 u) }3 K2 G, A  }8 ?
    {3 m8 b, F7 j4 |8 M3 g2 b
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear8 c, D5 D/ r7 D1 b4 E" B7 j
    }
+ I. Z# q# }5 S0 I}
7 k- @7 b6 R5 w# Z! r9 F, }* |: ?; N2 |, e' }1 [
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
  d( ]' h( B9 i) p/ Q
: O. @, x% V1 F" e& F# s7 d主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。9 }( U& x$ K5 u- a

# r1 l, w. u: P8 M% O: s
    while(1)
' [- L# \$ ~2 S  V- f5 N7 @' D$ i8 C1 O# C    {4 ^% K7 s) i% ]7 L. F1 z& u
        static char row=0;: j" v& N( [' L5 f* ^
        __WFI();- H& b  X$ |. S& }6 {
        if(ep0_state & 0x80)    // request data processing
+ F0 F) b5 i, F/ [7 O        {
3 g1 n; M+ q9 e+ _3 m: J& F* B            if(ep0_state==0x80) // SETUP phase
& s" p% b* b# a, V7 Y- c            {  S) c( l3 h0 p# @# A
                if(!setup_packet_service())5 ~4 E5 r4 W5 X1 {5 p
                {
* x; G3 [% ]5 r) K/ F# a7 M# l; d                    ep0_state=0;
$ F; P, F. `% b" R7 J% O                    // not supported
) }' C- I: P" e% }$ ^1 P                }/ O2 y3 `( V% |" f
                // ep0_state should be set to 1 or 2, if processed. }; v3 n( X7 e4 V
            }
, h8 t) d7 I' Z/ m' S* Q4 C* E            else    // OUT phase3 q) h' V) J$ G+ }
            {
+ m2 ^( N0 S4 ?9 H1 a                // process data; R% F5 Y, T( N' a& ]
                show_LED(*(uint8_t *)(USB_PMA+128));. W8 D$ @$ P9 x. M
                ep0_state=4;1 Q9 ^7 f1 O2 n0 A+ X! B* i. M
                USB_PMA[1]=0;       // Zero length DATA0
; m9 U6 ^! g) L. v7 k1 L7 r                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
/ g2 g% R' S. e5 Z1 n- s6 ?            }
1 r9 W: H) G- M' x        }( R6 f2 s$ W: V, ]( `9 J
        else* ^/ F+ d3 p# N9 K/ d
        {2 v3 P. ]9 M+ ]4 [+ N5 v
            if(usb_address && ep0_state==0)
% ]5 S+ r5 @5 d            {
7 F* q5 F% B% J9 F( @! G. D                USB->DADDR = USB_DADDR_EF|usb_address;) o5 |0 S( ^- Y* f, B# ]5 y' b
                usb_address=0;
: O" _& U. P( q6 {3 F. n$ ?            }& w/ X* }: x3 Y' p: ~
        }- c; a$ m+ V$ @0 j6 w; p
        if(row!=scan_row)   // new scan line
- K/ o7 c) |% L6 L2 N        {* \: c; @  s- ]4 [5 x5 m3 p
            if(key_state[row]!=prev_key_state[row])5 j# k) C' ?+ C2 s, X) R4 J% v: y
            {
: }) o" l: o) e, L3 b1 F                uint8_t test=0x80;1 \5 w7 |* B* M
                uint8_t diff=key_state[row]^prev_key_state[row];7 c8 r9 P9 V( c# k
                for(i=0;i<8;i++)% S( H; r4 c8 G$ N6 W0 o
                {5 Z: s& `; k" d! O( t' z8 J! \
                    if(diff & test)
5 F/ g) c4 F8 t# i% y                        update_key_matrix(row,i,key_state[row]&test);
' p, e- r9 B) @6 {* m5 G                    test>>=1;
- x7 m9 G% i, R* k                }3 O. R* v7 w- j0 `: F1 c% ?, ?' h: I
            }
5 G* j0 p1 P; _; v, l            row=scan_row;
2 I8 C# k/ M; C9 m' Z        }% s+ {, J& y4 ?2 N8 [
    }
( q# ~: ?; M7 u/ U
; x0 b; g/ R7 y9 L% M' s5 \
) m9 c9 U( M9 h  ?  pEP0的控制传输,把用到的请求处理一下/ K$ l" n7 {2 ~  d; L
char setup_packet_service(void)" l, Q5 k0 j7 e1 p9 b. G$ U
{
  x4 V" m! l! v    if(ep0_std_req->bmRequestType & 0x20)   // class-specific( u2 b; E/ J& l1 W
    {
# q4 [% D/ W& {9 q& V2 ?5 H& Z$ _        switch(ep0_std_req->bRequest)( X- v7 o# `1 ~, s$ e
        {4 p* j% U" Y7 B) Y% k
            case REQ_GET_REPORT: break;6 R& y  p$ ]; |. K
            case REQ_GET_IDLE:
& f" {  P/ e" w5 Y7 u& D4 n2 S' }                USB_PMA[64]=0xfa;   // return 1 byte- T$ Y+ d" o- I( l8 a8 v: u5 ?7 V) h
                USB_PMA[1]=1;$ X: b9 T. W- q* i/ w3 Z
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;. N+ r; T0 x  w, x* B
                ep0_state=1;
" O4 F1 G& G$ U7 d2 R1 _                return 1;
$ _' e9 {" A3 k, b7 h# s                break;/ x2 P% b; l- k6 K
            case REQ_SET_REPORT:4 I' R# a7 S. U  s6 U5 E5 u* [
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
( a' C! Q5 @! h                ep0_state=2;2 v/ D* _  y0 H& A% U( ^0 B7 d
                return 1;+ ~/ h2 a( O( H. e7 B
                break;
! C8 i" Y1 E- B6 }! ?            case REQ_SET_IDLE:/ ?4 Q; {% M/ H' i- {* S
                USB_PMA[1]=0;   // Zero DATA
1 N! @5 N2 I3 }. v  g                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;+ I+ d0 @0 _, t7 K) n  D9 F' R
                ep0_state=4;( I# [+ O; C) E4 s# l
                return 1;
( k  }  s  W) `* J/ Y                break;- e8 o) U; x% i2 ]; k
        }
# U4 w- i  E- w( Z0 b2 x        return 0;* m5 I; N/ V; V& Z( i: Q+ P
    }
/ [$ p) I) b3 Y% k9 ^    else    // standard
5 s; B3 Q8 ?/ @5 R! }    {& G8 |  {5 Q7 f% E2 g
        switch(ep0_std_req->bRequest)
2 [' _1 R$ @) R/ \/ i# ^        {
  c  n) y- g- s' X' {            case REQ_GET_DESCRIPTOR:
8 F' y# ~$ d8 y# A                return descriptor_service();
8 p; K: G$ E$ R: J$ k, o, j9 `! x                break;
- f( i) n/ x( o' V            case REQ_SET_ADDRESS:
) X0 O0 R* B. o                if(ep0_std_req->bmRequestType!=0x00)
, ?# L3 @# q3 A& N                    return 0;
, V+ W1 b) K& S# ~                usb_address=ep0_std_req->wValue;
2 S  b% Z- T9 I) `; d                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;3 Q8 }: ~8 e) u8 P4 f" a
                USB_PMA[1]=0;       // Zero length DATA0
! j( |/ M1 n6 Y3 @5 D" I                ep0_state=4;    // No Data phase! J$ [7 t4 h6 m' M. ]! P$ B3 J
                return 1;# b0 g$ F1 t" m0 o5 R+ m: j
            case REQ_SET_CONFIGURATION:
7 B* L$ t2 J) ^" E* ~. _- V                if(ep0_std_req->bmRequestType!=0x00)
) A2 T% h5 G4 O. L. T7 b                    return 0;$ Z' A, G* L- p: y) M' P
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
8 n1 @8 Q% M3 T7 {/ V                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;+ ~; i" l8 X3 M, M
                USB_PMA[1]=0;   // Zero DATA
$ k1 s' B' A3 j                ep0_state=4;    // No DATA phase
# [" N1 h( l' a5 k                return 1;& e3 @0 H3 x7 h" p
            default: return 0;' p7 f& U7 |/ p( @$ G) F$ q; @
        }
2 o* j5 O; [( K; x( c    }
- i; _3 u) W  o( b6 ?}
" c! @; a5 f+ [' K; _. L5 t) [. {0 l) b* P: j: F( y2 I- ~1 B
  A" O/ P6 B" E( R% k4 l

9 N4 b  K1 [$ `% T* S
回复 支持 反对

使用道具 举报

22

主题

61

帖子

838

积分

vip

Rank: 9Rank: 9Rank: 9

积分
838
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的; \/ x6 {* I  N/ |1 P
char descriptor_service(void): Q& s0 I& Y! j
{7 k4 {- Q$ ]. p, B5 d7 [
    switch((ep0_std_req->wValue)>>8)
& R/ R# Y9 M3 k2 J    {. i7 ^- ?3 b+ t1 c# n# ?
        case DESC_TYPE_DEVICE:$ w% d% [/ W! v" k4 j
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));4 T, E/ N. q3 B* P8 ^( p
            break;
, I# h2 a$ c3 }! \8 N3 ~, R        case DESC_TYPE_CONFIG:" j1 T! E* t, a- m2 F. {' P. {
            if(sizeof(ConfigDescData)>ep0_std_req->wLength)9 U8 `$ F4 g" _( {# _
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
' c" s9 u) j1 h% }2 w            else
  s1 w# g2 B% J; `                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));0 R8 S1 `1 U, \3 b+ @# z
            break;1 n# q3 ^/ h2 ]4 o* B6 B
        case DESC_TYPE_STRING:( z8 p8 O9 r7 p8 _- Y# k
            switch(ep0_std_req->wValue &0xff)
" J- ~7 `8 l4 Z            {3 c3 \2 U9 C  e
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
. {% @! P& Q% H, ~' l  V5 ?( n                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
- i1 x; L4 G; [                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));1 G! ^, w1 w7 U( E8 U$ M+ ?4 `7 Y% d
                default: return 0;
" Q9 C9 a4 b6 Y' |            }
* P& `9 T- Y3 ]( H            break;
4 a: k; Q) b. x" s; [        case REPORT_DESC_TYPE:
- b# m7 L6 |1 H6 G& }3 C6 C            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
: X* p0 J3 p* }: X        default:
/ Z" U7 k; U8 o( I. u' X9 E  p            return 0;9 w$ E0 N$ ~- c1 r' ^+ [
    }) ~; J# s2 N$ B, a6 d
}
! f0 f5 h3 D# M2 a7 E  g3 v下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
9 x" H+ |( \" P! ]void TIM6_DAC_IRQHandler(void)4 [5 m) R9 r6 H7 D8 P
{
5 \, Q8 f6 w' V) S5 j" C2 J, s    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);' [8 n) z5 e0 D7 m
, e" \& `/ j3 ]# X) d$ ?( i# M! a8 r

; U  e1 T& h% y/ J& g    TIM6->SR &= ~TIM_SR_UIF;
. {' v) r$ I8 c- U* t# O9 ?' v    prev_key_state[scan_row]=key_state[scan_row];% a- z& I2 ^, n6 o9 R6 W
    key_state[scan_row]= *PA_IDR;   // update key states6 H3 S* P7 [/ O
    switch(scan_row)
% M( t* i+ Z( v' H& M    {
) m4 R* |4 m* L# k' R& ~        case 13: // next row PB14  I: g8 ^. f/ U9 W# L# d* [$ o
                GPIOC->MODER = GPIOC_DEFAULT;
/ J' }( Q) k9 X                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
9 \/ G) G3 e* D2 i+ P                break;
" k' p! @$ Q7 w, R        case  0: // next row PB15, `" A  W# e3 X$ p3 P$ h6 K) U
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;" O2 w/ E6 h& O+ h! l; J* w' X9 S
                break;* @7 T+ ^# _3 O+ X" @8 m. L8 t* P* f
        case  1: // next row PB3
6 @- S4 f3 L. b! H3 X                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;9 U- f0 W5 f# O
                break;
" `. j' H, W( `  N1 I+ g        case  2: // next row PB44 T, H  Y; E  H- O/ \
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;  H  e$ s  X( Q" \/ Y) y9 [3 I% Y
                break;
8 w$ \  j, F: |% V7 W2 c0 s        case  3: // next row PB5
, r+ @& @* a1 d! E                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;, o( ?8 \0 V$ {. U
                break;
- |  N: R1 t. \. r. \        case  4: // next row PB6
( L! Z" x1 m4 O' |4 a( ?                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
2 A; ]7 r1 q; U                break;( N. E& K' I2 l7 I  X% I
        case  5: // next row PB7! c1 r, ^. `5 n
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;$ f' T' \7 E! ]$ R! a* d
                break;
+ W* t# W' A7 r$ M7 k& E/ f* K& O        case  6: // next row PB8/ z9 s% |% A9 e5 v
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
7 Z: J- o$ j, Q                break;# n: @( f$ ^& Y: m4 a: U
        case  7: // next row PB9
5 }3 \9 z, t" \; U7 F                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;
4 ~& D; ~+ E% X. B# _1 X$ r- ~/ U5 v                break;( B9 c& M6 U2 Q7 x* N, w9 K* a
        case  8: // next row PA8! D: h4 a  s0 q
                GPIOB->MODER = GPIOB_DEFAULT;. I! K5 e, T# t% [1 ^
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;
6 L/ `+ i! {; q- E6 v- r                break;
) O/ @, y- |# c# C7 o7 {0 q! Z        case  9: // next row PA9( N6 r  J3 X1 r  \
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;+ s* f0 v7 r. q9 ~
                break;
  `: }) D9 j" w# w1 r        case 10: // next row PA10: T- v: z: |# B  W; m
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;2 [$ e1 |$ x+ P  n/ G
                break;$ N9 b- u. J1 u, x
        case 11: // next row PA15  g* _( n. p6 X! v
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
8 P0 H# g- S8 Q+ X- M                break;
0 W0 W( \( X5 h1 g2 ^: l        case 12: // next row PC13  u0 C6 \& n5 l) f
                GPIOA->MODER = GPIOA_DEFAULT;5 _2 Z4 L) Z  Z1 M  i, R
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
# d% A: D7 i9 Q5 m                break;, ^' k8 U, E6 b6 D
    }3 H- j0 K$ `7 N
    if(scan_row<13)
3 |3 u7 O. T+ j6 v  A. Q4 q% x        scan_row++;' [; D- f3 [' h1 B& _, t
    else! F  S, e+ n! |' T
        scan_row=0;
8 X) Z' F' P# S; v* h/ S}  i' [7 u' V6 C
6 b( Z! O+ C" o- E, Q
. f/ w) ?8 b! A2 g; Z! I
回复 支持 反对

使用道具 举报

22

主题

61

帖子

838

积分

vip

Rank: 9Rank: 9Rank: 9

积分
838
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 # E1 i2 R* j8 ?2 J5 y8 \. q" z

& g4 f# @& ]6 [+ `* Q9 J扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。) D% j1 F* I& P+ a$ G
+ H: n: l( I: t  t+ U2 z9 _
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
. s7 e5 S- e3 D# n3 c) {
: V! X' r2 k( K( c# W& j
0 |9 X) m3 @4 v, `2 q; [. o0 h6 z& n
const char hid_keymap_qwerty[14][8]={
1 A4 A- \/ n. L  ^& ]8 H* u    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},6 I- l& B4 }) }5 c
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
1 p- t7 @% d2 T    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
( G1 j: W4 G! h  c1 b- S  v    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},; t! ~- |# m) ?' Y. X) Y. ~: w
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4}," _) {1 r* |4 x5 _* n0 T6 ]
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},3 m5 i+ k( L, f% J+ G
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},5 X. w( w# O' I  T, r6 n
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
7 Z. y. r0 }- I% r- k. H    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
' g! I' z, {1 i/ N2 [    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},& G) p0 p" v: q5 U
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},: T% e7 A* F( q. M& h. x- I
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},9 j2 l' v& E8 O/ z
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},. U# |0 y) k% [& I# ?7 v
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}' w  b) H, y( c9 r2 P$ z
};
# P: m% d1 |2 X  o" ^0 U  n6 u; p3 u& ]% D! F# u. T$ U

' i$ @3 x& R5 L& T6 S8 s; J6 g8 Oconst char hid_keymap_dvorak[14][8]={
( Q9 H+ B  z% w/ \4 v6 s) J    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
/ m2 H7 W# x( J" n2 Q# [8 w$ U5 _    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},0 v) K7 [6 f+ w5 F9 A# q- z0 v
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},% V$ U, t# B8 x8 t0 S
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
9 K$ l  i  ]/ }% ~: u4 W7 x+ }0 E    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
# \5 g" F0 W( n  Y( E" l- `- k    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},$ a2 ?3 h5 H- M2 M9 d3 K  ^
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
7 {% S" C) A+ Q; H3 p    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
$ t7 f# B3 \4 u/ Y0 r. E    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},* x5 z; g4 S$ E! u* [, V3 g  j
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},+ g5 A- e  n. X# |/ d+ ~
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},+ [( o1 f0 x7 A# b2 N: S
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},8 Z) p$ Q6 d+ Q  M2 B
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
7 E- u" B( \- T) \( E- e" B! j" e    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
. c" p0 l6 _6 ?};
+ p( u) B$ p+ \: w9 ~7 b( P/ J0 O. }9 N" ^

6 ?, Y! ~3 b$ p2 y( D上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。, `- Q2 o! H' G! P, k

0 K; G7 w# x' {% g' m- w. N  _HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
2 v& Z/ [: _6 A1 d- z  {

8 E1 _7 u# n- B3 J7 ]void update_key_matrix(char row, char col, char onoff)
' t4 M0 ?  _$ u: t" F3 w9 n{5 r8 e" t: T3 s" z/ M* E: I
    static uint16_t hid_report[4]={0,0,0,0};
* x" {/ H6 P# a6 R    static char (*hid_keymap)[8]=hid_keymap_dvorak;
; y2 F( g: y1 m0 a
3 |# K/ V* F9 c0 {
; v( n# f" t) Z& r/ u
    unsigned char key=hid_keymap[row][col];: M: z+ P* \/ O* L$ e) @% Y# {7 l
    unsigned char *report =(unsigned char *)hid_report;
: c8 U  \" P1 G    char i;  L) Q4 W9 C' f) @/ ^
! z  J2 Q2 y8 w; d/ o

! O, O/ y$ |. `" m5 c+ X" y    if(key==HK_MODE)
# Z% ~- A. l( E: o! U    {" }( z$ G. @; u# \. V& ]
        if(!onoff)
/ C; H4 ?; K" f- ^4 @+ ]- h        {- a- I; o* P: e- `, a
            if(hid_keymap==hid_keymap_dvorak)
* B6 n" D; z; Z' F            {
8 w5 O; A' ~# N                hid_keymap=hid_keymap_qwerty;
! M( T! ^$ K- I' d4 y  K                GPIOB->BSRR = (1<<2);9 J7 S) i9 _3 t" L# T  z0 Z
            }
0 F" `+ ?6 |; W; k+ I  }4 y7 v            else1 `1 B# Q' g0 W4 a
            {: {- K3 J; T3 h. ?& K8 q3 W
                hid_keymap=hid_keymap_dvorak;* G% w& O7 c9 b7 R# D+ m4 L5 |# Q
                GPIOB->BRR = (1<<2);2 e5 F( t% m( s" K( V3 z
            }. V* W6 @- v+ v9 f; E
        }
5 [* ]* L& y& j$ c  x% F        return;% a3 S+ x+ A$ {9 K  Q, |
    }
2 L% q+ G2 _2 c& u# i& ]/ e: w) r8 g- v

. U" H% A$ y# T3 B' A    if(key>=0x80)   // Alt, Ctrl, Shift& L) q0 F3 ^6 d. m, E8 m, e1 t+ D+ ?/ S
    {
4 h& N$ {+ \9 O, ?! n        uint8_t bitset = 1<<(key&7);% D/ g. |5 Z5 \/ N8 N; ?% t% C  B
        if(onoff)   // non-zero is key up
# ~7 v6 @9 S0 F! q$ J) t0 q            report[0] &= (~bitset);! `# V' t  d' d& P' l
        else
$ `0 r3 `8 ~( A) N8 j' s/ ?            report[0] |= bitset;) o% G; T1 Q/ g5 U8 g" L6 `( F6 U) ^2 G
    }9 G9 |: Q/ r  U5 |. ~
    else- P: c3 G3 |: s. u6 Q3 O: y
    {
; ^1 A$ V# ]# [3 s' s4 C* C* l9 i7 a- c& ^        if(onoff)   // non-zero is key up  T- ~: Q: u; D, {  _4 n
        {# X% j4 ~$ U% T+ z# B0 L
            for(i=2;i<8;i++)
( {. i3 b; A4 q7 R$ ^1 n7 L( `            {
9 i/ g- |; o* |9 x+ `  G2 P7 \                if(report==key)# u7 d( I1 T1 A# ?1 E7 Y+ V
                {
5 G/ H. A& c! w/ z6 I& ~" C                    report=0;; O  t$ b8 F! @" h+ ]/ B
                    break;
3 s: y: y7 u1 `5 Q                }
8 T1 H8 v6 x* s5 T. L7 G            }
$ k- P! o0 |$ Q7 @% S. V: A        }" O# l- B& ?% `
        else
7 `& Z) S5 I" G! p  B        {
/ F- g/ H  p0 a$ D. T, Y8 F; v            for(i=2;i<8;i++)
- x% ~0 N, Q0 N# C! d$ I            {
" T7 Y4 U# Q/ ]0 @/ T                if(report==key)7 Z8 \0 j/ p& O- f* [" i) [
                    break;( h# _8 Q, W* J: l" a
                if(report==0)& u7 I+ w( c  ^' C; z
                {& U4 B8 A7 f& O. w! ~- q
                    report=key;. M7 `3 b6 M& l, o
                    break;
3 R1 A. @7 |5 P8 M$ N, ]                }
" z* |5 S; \3 C9 V+ n  V* D1 T            }
7 c1 `0 r4 X9 t- u. P0 d# j        }
( V' ~& d- g" K8 l' x    }2 a2 P3 p" h1 k& x
    for(i=0;i<4;i++)4 s( U/ A! v& @  g6 b' b6 T
        USB_PMA[192+i]=hid_report;! _5 I% r# h: N0 V
    USB_PMA[5]=8;   //COUNT1_TX
. P1 c! [/ |* ?& J7 d7 y    if(ep1_wait==0)- o9 a# K9 ^5 X! \0 k4 T
    {
# q7 U% c9 K" U9 v/ Z        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;: r0 P! k5 d% |
        ep1_wait=1;0 k. N+ [) Q$ b5 [8 W
    }
" y0 E, S, e' f$ c$ h}
2 j7 c. t7 \9 A1 h4 o( J6 b0 q0 Q( R) w( b2 x& u5 O
. @0 H) n; K7 V( |3 v3 Q8 P, s
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。1 p* v0 w9 u5 A% ?0 q/ O
keyboard.zip (8.7 KB, 下载次数: 1317)
回复 支持 反对

使用道具 举报

22

主题

61

帖子

838

积分

vip

Rank: 9Rank: 9Rank: 9

积分
838
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。; R4 I# T( D. }) _; Q1 Y
回复 支持 反对

使用道具 举报

8

主题

146

帖子

2053

积分

金牌会员

Rank: 6Rank: 6

积分
2053

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘4 \# Y7 w3 o8 i- Y/ w8 f
不过楼主也很厉害!
回复 支持 反对

使用道具 举报

22

主题

61

帖子

838

积分

vip

Rank: 9Rank: 9Rank: 9

积分
838
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
  p: u% U  J; `+ H1 E9 p$ x刚开始我以为要把打字机改造成电脑键盘
/ N- o2 f% b/ O1 t( f不过楼主也很厉害!

+ h. J1 L2 j) Y1 p: N, O  t; n哈哈  是有点像奥
回复 支持 反对

使用道具 举报

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

本版积分规则

Archiver|手机版|小黑屋|MiniDSO产品技术交流 迷你示波器-袖珍示波器-示波器探头- ( 粤ICP备13039857号 )

GMT+8, 2017-2-21 20:02 , Processed in 0.167483 second(s), 28 queries .

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

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