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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑 : U6 N4 y* ]; q! o, h: u& @
( ^5 O8 H0 N3 `2 q- J/ s3 w
http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
; X5 n+ J* r4 Q! T; E6 ]4 o这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。
( i$ a+ K$ d& J. @! A# U# x0 h4 E9 m* }
在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。

5 l4 a2 ]( A3 ^  @' @3 x0 W" `
- Y4 o  L% m+ d8 W2 m, \9 ~
235140i3a36qivqzuvmt5q.jpg.thumb.jpg ' o0 _% Z* y4 A: c9 j
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。
2 ~" `4 B2 a5 L: J0 I 001734klbyoluenuwz4h4b.png.thumb.jpg
3 `9 F+ M( D4 H6 X; L/ |; l为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:
, t- M9 v% ?7 N- A( V 003625r2agx2f5v922cf2f.png.thumb.jpg ( X- S! i9 ?( D9 S4 `3 z% G( e( v8 v: {$ U+ k
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.
0 q3 p1 f# ]( S, X1 U. oDvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
/ `9 u; B, H( b5 F
005836yvs0wvovwsssgd3o.png.thumb.jpg $ u" F+ G1 Q; w' S
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
- C* E* m( K" E& w% o( n# m$ U4 r7 i4 r6 N1 D
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
4 J0 _1 T$ n& L  R+ h9 x2 f
$ I* z. G  M2 X3 n1 x$ @到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。% H, F- f$ v8 i, o7 h' E# w* h- j
' H* G+ f+ C! Y  `; J
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:

4 s- L$ l4 f+ F1 Y+ g1 [' C 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg % z, K4 S) X3 ]& F! R1 n2 i
----------------------------------------------------  分割线 ----------------------------------------------------------, c$ ?- c# G0 E5 ?- d

: O3 G' J, s! y, p* F9 Z0 \先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

2 y# S; Z% t3 d6 k 020011osionbunl4ui44vi.jpg.thumb.jpg
( r2 V/ A  k" {7 O轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
2 L, u6 v7 _5 h) s  l 020017j8ycmnv7788bqv52.jpg.thumb.jpg
5 q+ j' U9 X/ u( u. a特写,80C49( o  H( Y/ Z* i; p. m/ Q! b
021040oujzuvtut6iujtvz.jpg.thumb.jpg 3 ?2 \0 b( U1 {- r7 f* N7 ?- _
LED部分,使用了一片D触发器锁存指示灯状态.
7 @* }. R( f+ Y6 A8 T 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg + B0 Y- \6 A% a* h/ u) N- V
暴力破坏,将80C49拆掉' G1 K7 m/ b9 C4 z. b% h
021113e48qq98vyohvhzzh.jpg.thumb.jpg / t0 x  w# \% Y6 z5 e- r. U  }( Y
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。2 [$ ]$ o' X3 ^# B2 i5 q
021125nc9az6dj33rlds2r.jpg.thumb.jpg
7 P) V+ @- c/ N2 {! _2 I
焊好元件后的板子,准备替换80C49  I" @) Q8 e( B
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
3 [. h2 b; m: _" A* C/ t用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。
4 t( H, n7 d: W0 b 021104shifhnrqbr3o5nlo.jpg.thumb.jpg
5 R. F( ~' C4 S2 A1 `1 f& y6 M- z这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.
, Q+ s0 w: }4 n2 l) ^0 O 022003ym1p9u4ug40280uu.jpg.thumb.jpg
4 ?) s. y) B- D6 K- |6 Q) G- s开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。
, z2 v9 G; z8 P# p. M/ j 023313kt141q9qajtol7ma.jpg.thumb.jpg
4 b; W* C& E2 L: h我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。% m' b) g4 k/ W! _
023322nt7l5xb3ltttkltt.jpg.thumb.jpg # ?; L6 X) Y4 s& O& i: _' \2 Q
主键区键帽就位
- O+ W: s+ _8 L& g; r& u 023331hin88e8wkrwzwikx.jpg.thumb.jpg
: g' f$ c# J5 Z$ [# Q编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。6 O# e, I, M" a& r. S6 y
023336wjzlgopugg1jyy79.jpg.thumb.jpg
# o/ H% k& U0 z最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。
- b9 s7 B8 v/ W! i& A  W 023341sffu4j3g2323h6fl.jpg.thumb.jpg
8 o- G) h- }; ^! p2 a0 [0 o
" Z- s6 W4 N: Q; z% w/ Z: C2 l& q3 K
----------------------------------------------------- 分割线 --------------------------------------------------
0 |- _! a, m9 p- k9 ?
3 a3 j3 H5 [: h& G( u
. N% w" g) J5 n5 V( t' o! u
8 F- F0 Y/ L! O- n2 v7 f

9 p. r( B3 ~- O3 b8 V2 u8 U* W3 z  g, T
' e2 J' z5 T  H  n2 T) H4 L

" }$ o4 g+ T& z
" n8 G" @+ j! Q, f$ U+ h9 N+ M/ K( O) w, D- h4 ?" y4 x* }
2 c7 X4 p% c) L

: H: f$ H- b  v+ B
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。, m/ c3 Y# h+ |0 e9 b$ k' `1 z. v' ]) ^
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:3 p. `# G8 `! X) u5 q! U3 C
104025nzibm2rmiomhyirm.png.thumb.jpg 5 X+ b; Z* ~2 N! n1 \, [6 C
2 F. B: q5 R+ v7 W4 v
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
) p7 L1 d0 [% F3 B/ C- u 105004zkrez5houvkkznko.jpg.thumb.jpg 9 \0 i" q1 R) L* a2 [- r

- t' w, j# D6 d$ _扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。9 \! C& B) g+ O2 M3 }" P# L  r
& ]! s6 |/ O; O- E. n
这是我设计的电路图:
9 L0 q% b, D8 k! a7 U- o2 S 110344ej2z2oo2rflo7oe7.png.thumb.jpg
" m% p4 Z# n7 ]. J7 u8 p' \
( l1 O3 x/ _0 ]; U4 t5 Z' d
PCB Layout:% a$ ^- V; d* e7 l% ?* e
110847jjbjvt34vwt3v5bb.png.thumb.jpg 1 e5 ?9 y4 a4 ~" H8 B8 ?
$ _& z( V" Q( U+ D; a5 j
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. " U6 a+ {/ C* \0 [

+ H8 l, C8 b" q1 U

8 a% J+ a$ n' }- [
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 % ^; }# h# s6 L: w  M! U
! a% q6 _6 C% O( j- C# B
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
# e, _* k5 A6 c! N9 ]) b$ _4 ?
& Y% q; u  R( z1 Q, P! U总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:
1 W7 Q8 n# P  t; P 113818pmrfsb6z0byt6t06.png.thumb.jpg % J7 @& O+ i+ q) S" p

' I+ l$ u" o  q3 k/ u其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
5 W) |. D. P$ |5 W; H/ E
4 a8 v0 O6 c! @# l. i7 A/ kUSB的中断ISR,bare metal哦
9 [9 _6 O1 I+ x5 R5 A* H4 v

2 \: R/ U% ^0 `( H4 F7 v0 Dvoid USB_IRQHandler(void)0 I* D+ {+ L& L. m6 m4 t
{' r* Z- J5 ]1 b7 H& f2 h
    if(USB->ISTR & USB_ISTR_CTR)) }' k; C) S5 w6 t3 r/ ]) z- C
    {
9 O: t" a8 n6 ]6 o        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
& X- N# S7 v: P( G3 Y  q" J' t        {) i0 [  d( N9 @
            switch(ep0_state)5 _5 ~2 Z7 Z- j7 v$ D0 d+ ^
            {
; U6 n& u/ o. r( H  [2 V                case 0: ep0_state |= 0x80;0 [: x; v: }( v
                        break;
6 O0 u. T" u8 x1 e. d                case 1: if(USB->EP0R & USB_EP_CTR_TX)
; Q5 Y; u7 G7 f1 u0 ^, n( p3 J                        {( j/ t# g0 s/ c
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
. T0 M+ z8 L* z/ ~$ n" ?: f                            ep0_state=3;6 ^# @6 [! L( s/ t! f
                            return;, r* N) H+ G4 w" m8 Y
                        }
* E1 Q+ r( d/ {                        else  \4 E/ m9 W1 k
                            ep0_state=0;8 c! x, n1 k8 Y9 G. |' Z
                        break;0 n4 X5 x/ L* o5 Y2 Z, d$ Z
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet, {5 y$ M6 d  w& r
                            ep0_state |= 0x80;% O: F1 r, W9 `3 e6 Q% y* X
                        else
0 T0 `% k% P" n& U8 t                            ep0_state=0;+ m  j- \9 H9 ^  F
                        break;# K0 F/ B$ C( B2 T
                case 3: ep0_state=0;3 R4 U6 H3 r3 B" L- X  p' d
                        break;
4 v9 |4 _. y: ^5 |3 J2 i# W  E2 a                case 4: ep0_state=0;
# m) v+ ~7 m& O! Y. B! ^                        break;8 b) Z  m6 u& k' `1 |: D
                default:ep0_state=0;6 \! h! y- G! C+ X
                        break;
, d/ ~$ h, ]  o& q5 f" w) \            }, D& o3 e- Z  t. K$ x
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
2 d+ O1 {* Y: q3 |            return;- ]1 b7 C* R/ Z# a0 f. H- c1 j- Y" p
        }( \) c* j; L" t9 [: k( k- M2 ?
        else    // EP_ID can be 1
7 a0 t1 n4 \" A        {3 `+ j' ^2 d8 Z' t- a
            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
$ ?) Y( U0 u& L3 K, ~3 B) Z; |% K            ep1_wait=0;) C  m7 c. x6 H" s9 G. M
            return;
- a: J* h1 Y6 B" v- U+ J2 a& F        }' l9 T8 H2 f5 N) S8 w- B+ a
    }  e3 z2 u" P$ M7 ]. W
    if(USB->ISTR & USB_ISTR_PMAOVR)
% s% K( j: E4 H% _, e    {0 ~( G! U; {4 g& ?$ V; a. q
        USB->ISTR = ~USB_ISTR_PMAOVR;: u4 j; V* r2 Q5 Z% e2 l
    }9 k, }+ A) @7 o1 h# t
    if(USB->ISTR & USB_ISTR_ERR)7 ?' M. D/ U4 B/ s4 w$ ]
    {2 d, k& {: s3 }  U- f3 n* J  P- j3 r
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear  f: V  t, o) e5 n4 j
    }
2 C; N( R% K/ C; L/ \    if(USB->ISTR & USB_ISTR_WKUP)2 @  a. R5 w: @/ k7 N; E
    {
1 m- i4 z7 p& r5 M. {0 ~9 E, z        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend+ N. x9 R5 Z* X% e, I
        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear" ?; i6 k. Z* z1 P; d; D
    }: V( l0 o& L+ I
    if(USB->ISTR & USB_ISTR_SUSP)
$ R% o& [5 L. L4 B! b4 A4 L& {1 c    {5 c: U% t  ?" ~4 P8 M0 A& Q( h0 ?
        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
  [& o$ F0 B% i7 x        USB->CNTR |= USB_CNTR_LPMODE;   // low power
9 I% z' M# L' a# b% l        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear3 z6 E4 K3 @3 D1 f/ @( H8 u: j5 o
    }
% T# y/ ?7 F5 q  |7 E* e    if(USB->ISTR & USB_ISTR_RESET)! [* f- p6 [; F) v; F% q
    {
9 y# D7 N; r+ M        USB->BTABLE = 0;    // buffer table at bottom of PMA7 H; M  s' Q7 Y' W1 L! z7 d, l* P7 a! _
        USB_PMA[0]=128; //ADDR0_TX4 r& Q. r) ?9 C, \
        USB_PMA[1]=0;   //COUNT0_TX% X" u( l: a* I" g; W; q
        USB_PMA[2]=256; //ADDR0_RX
& l4 e: V9 O1 l& s/ m( |        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
. n( B  a. {- ]        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
; J8 h3 Y  S; X. W( F' A        ep0_state=0;: `$ G( V( @8 C8 h3 @0 y* L
        USB_PMA[4]=384; //ADDR1_TX' Z% n& e  l! y7 I  f5 v, z# W
        USB_PMA[5]=0;   //COUNT1_TX
; K. n/ T3 U5 v1 ]        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type+ B/ @* d7 }: r2 ]
        ep1_wait=0;. C% Q+ k6 m9 P) |/ w8 K4 m
        USB->DADDR = USB_DADDR_EF;      // enable function/ }0 I3 v/ l* X3 Q7 `
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
3 V4 u: w" n8 h) c1 q3 G8 F    }6 `: g3 D) h5 ~, M3 j: n
    if(USB->ISTR & USB_ISTR_SOF)
, Z) i" b# o) z0 H/ i8 D) e    {
2 z; G3 k+ u7 N        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear
, A5 m) ]+ s, `) V: b& z    }  U$ `- y/ P- n3 K) F
}
( t. d: d+ |& D7 q: {
0 x8 c" U1 A! `9 u7 N! H3 Q
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
5 b* M' u4 k. |( g% H) \3 t* L7 v1 [5 H. s
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。) a. p$ y# ^" P$ ?: R# u" y
# k( U0 t8 ?+ U9 C7 r# x8 s7 v
    while(1)
5 u- D) U, X( u% ^& R3 U3 J6 y    {
* c% Y0 D. h: D: {& _, h4 K        static char row=0;0 D+ Y* J: k& W( Q+ X. f2 W/ M
        __WFI();1 L2 p, ~" ]* y; R( \
        if(ep0_state & 0x80)    // request data processing  x. j& D8 W  _- {0 k( b
        {
5 Y( y& W0 z/ G            if(ep0_state==0x80) // SETUP phase6 B% T9 k% [' c" T: w
            {" K$ I7 H: L9 _- x
                if(!setup_packet_service())
! d, v! K4 E: q* b( i% S                {
7 T' Y# S' \" H. n& ^                    ep0_state=0;; [/ Z8 ^' f, `
                    // not supported' S  |$ Y  n  J4 N( D: Q- b$ K5 D
                }' d  M& Q) T* ~# ^. F8 g, U1 Q
                // ep0_state should be set to 1 or 2, if processed% C: n  }& `, O3 z8 R2 l$ {) }
            }
% j8 }4 q% F  n# a6 k' N0 I            else    // OUT phase& @8 x' j) O. n; X
            {0 k+ o/ }' Q% y9 T" J6 P- F6 n: d: m
                // process data
/ m! O# @% T6 K2 T; `6 ~- H0 V( X                show_LED(*(uint8_t *)(USB_PMA+128));* p0 w2 @! S" M/ k" B) u
                ep0_state=4;
! D5 h) u2 W- \! W- i2 d                USB_PMA[1]=0;       // Zero length DATA0" A7 ?. l2 Z, x, \! e& v
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
8 d8 a) L; m7 E# |9 t% D# c            }
) Z1 u/ q: S' S        }
1 K1 j4 `0 z/ h4 q) o6 a# J7 A        else
& L* i# x' C- ~& v- L: w; g% p        {
- B- K6 h# a6 H5 \1 h" j. ^3 j            if(usb_address && ep0_state==0)
9 H; ?( S. P# ~5 M! h            {& P- k5 u4 k* a. J; S
                USB->DADDR = USB_DADDR_EF|usb_address;
% ]2 t5 P# q! ?( H                usb_address=0;
& {: {: S: {/ B9 \3 [/ B  H' v            }- `- V) j5 p1 w; j$ i3 O# P" |* v
        }5 H/ V# l# r- N0 g# U
        if(row!=scan_row)   // new scan line  h- k3 u! P1 ], k. J# p
        {+ _: a$ T1 J5 V4 B) E5 r, G. y! U" o
            if(key_state[row]!=prev_key_state[row])7 }4 ?2 l) S& `
            {
0 |* \1 B; L8 k' E                uint8_t test=0x80;* q- u( r/ L6 [. f5 ~
                uint8_t diff=key_state[row]^prev_key_state[row];
* n4 {: P& m- Z  B. }                for(i=0;i<8;i++)* b2 ]! }7 Q6 e* E/ n
                {
% _4 J/ B$ f+ B0 ^% N& m  _                    if(diff & test)
# j' Y- V; X  B9 ^/ l+ }4 A8 s# |                        update_key_matrix(row,i,key_state[row]&test);
: b+ `- B0 V. |6 \5 [3 z% f: _1 R/ _                    test>>=1;
& _  f' x, ^+ _                }
! @; j* X0 C9 G) D( M1 Y            }0 W) p) a+ Z$ r4 {* I* n
            row=scan_row;
- F3 R1 H. x5 \. P7 }3 w        }
( l1 L( L, y4 a: N& x; A    }8 _: J% C4 L2 o1 C  l1 i

+ t& D' T) E4 X+ W+ Y- v: s3 `1 A6 M5 ?- c: x$ r) l
EP0的控制传输,把用到的请求处理一下3 G  f  q' S* _0 t) c+ {! P
char setup_packet_service(void)
$ P; Z$ @% B% a5 H* K: o{
% e3 \% ^1 i1 w7 i    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
. K- Y+ {9 m9 K3 ~  f3 f    {8 o# M7 B  R7 {- z+ G
        switch(ep0_std_req->bRequest)
, A% B4 `. `8 a        {" O3 s) X! C$ r" c9 K
            case REQ_GET_REPORT: break;/ U# w# H% q! C
            case REQ_GET_IDLE:5 |% C% S3 D% o" |  F
                USB_PMA[64]=0xfa;   // return 1 byte
# Z& |8 o- [( P7 X* U: r                USB_PMA[1]=1;
/ I& r; T: ^# P9 S( s! Y                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;) Q/ I8 B6 V9 \. i
                ep0_state=1;2 ?* T7 H+ ~9 m: F6 d# f
                return 1;
% V( s, ^# N4 w& [( F                break;3 e5 p8 c3 ?! r
            case REQ_SET_REPORT:
9 q) c- P9 F! B3 n                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;0 {  h% j- h# y* X% E
                ep0_state=2;4 v6 ?/ u- F- h! p. K2 Z# y
                return 1;
- {6 \# w/ H) a% F4 T                break;
* p0 U6 S1 R" h- ~: N            case REQ_SET_IDLE:
2 ~& \5 D( E7 p) B2 }                USB_PMA[1]=0;   // Zero DATA
. I! G, s- v4 f( |& v: d! I; @, u9 U% A                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
# H8 T$ c2 z  Y4 n                ep0_state=4;! C7 x+ U$ T" B9 ]
                return 1;
4 T# _4 K, C' J: G                break;
$ @0 e4 H1 h% l; }, J        }
' o1 Y9 S$ y* s% L  p: k0 |        return 0;
% ^9 D2 T! g5 D" X& i5 b    }
8 n& \1 Y2 |. |, y2 R    else    // standard/ g7 p% L+ t" l. S6 o& s/ ?
    {
; t5 S# H' Y) Y9 O        switch(ep0_std_req->bRequest)1 j: w; K: t- \- D6 J' t4 c
        {5 B$ n1 v( |0 P- o. Z1 G
            case REQ_GET_DESCRIPTOR:( c$ D+ l. ~3 O8 k+ |
                return descriptor_service();
2 W" F' d$ W% h# h! P  o7 `; ], u                break;
) H* r8 n0 Q3 w; C/ T            case REQ_SET_ADDRESS:
: |) p: r4 f- H( i4 r! h) c6 k                if(ep0_std_req->bmRequestType!=0x00)
' F0 x8 S' g" n3 \, k( w                    return 0;
, T1 ?+ K4 H  G9 W5 t2 k0 q: c: @                usb_address=ep0_std_req->wValue;
* a& w8 }' Y$ G4 x) X                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;! v+ H* h$ b% F: S1 P: H2 R  V
                USB_PMA[1]=0;       // Zero length DATA0
. [2 A% ^2 Q- q9 G6 n* z# ^5 b                ep0_state=4;    // No Data phase1 V* f- \7 J3 X  P/ b" E
                return 1;
" u+ K9 l' J7 Q# O& o; I" ]            case REQ_SET_CONFIGURATION:
" @# R1 k0 r* w. @$ [7 S/ ?                if(ep0_std_req->bmRequestType!=0x00)0 E/ S' _$ ~* X
                    return 0;
" ]+ p8 _, U$ M% b2 C                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;, W! F) p. w' U1 Y
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;$ `6 {+ \& e, G1 J4 c! I
                USB_PMA[1]=0;   // Zero DATA
- ?4 }0 |+ x' ^! T; L- Z8 O                ep0_state=4;    // No DATA phase
! t( u8 k& X3 r                return 1;
) X/ M6 ^) O, p. [            default: return 0;+ A+ ]8 U' X6 ~" r7 Z
        }
- }) H4 O6 v! r1 g. k    }5 C' w' _" k1 V3 O/ T
}9 Q: x1 Z8 B, p9 I
+ y  D) g+ t: u. v& t  i
2 l  ^( R5 y, x" a) h

# h( J6 ?# _+ \9 G$ t! H7 N
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
7 t6 P3 Q. l( L% H) |char descriptor_service(void)
/ }% c7 y) j- f  t{3 l! G( m6 R  l/ ~( c
    switch((ep0_std_req->wValue)>>8)
7 a4 w6 d4 ~% a& ?8 ?    {
- {, s$ `) N# z, K; [        case DESC_TYPE_DEVICE:; _7 P  s4 R% F' P  [
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));/ O0 C8 U6 i7 Y; q
            break;, p6 R" y' W8 U* e) a
        case DESC_TYPE_CONFIG:
! L' |, \9 _  C% p( @& _            if(sizeof(ConfigDescData)>ep0_std_req->wLength)0 t5 r1 \# [% A
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);, Z5 _+ ?! W/ ^# _
            else; F; \5 g; Y2 |5 G5 w5 ~8 h. f7 i# Q
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));& q* ?, A: M! z
            break;
# L. |* e3 G0 Q( O8 L        case DESC_TYPE_STRING:. Q2 D7 B$ ~# I
            switch(ep0_std_req->wValue &0xff)" p# K) e8 x9 Z, i7 ~* |# O  @
            {
& V# \0 S2 b! S: [5 T                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
5 {- H& K+ B. A& p                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
6 Z  ], R' t" ]# g                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
/ Z) V* {! [+ h* e+ |( o                default: return 0;
/ k3 p3 K( @6 }9 ]+ b# p/ V            }' R' Q& _6 Q6 B+ L1 J2 Q
            break;$ m: Q1 L1 g* z7 n
        case REPORT_DESC_TYPE:
" h& M+ i! A+ L: d) x1 a            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));+ O6 ^# f- B, H* f) B: w
        default:
$ p" ?' T  d$ b6 m- i. w* ?            return 0;
+ N$ U3 I* M! t9 q/ D, A* R# C    }
3 s, V! S2 e( v}
( U) x. Z. v7 m1 M4 n. d* D7 g下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.4 J1 b! B" [8 [  j, |
void TIM6_DAC_IRQHandler(void)
. `* S0 k1 I# [& ]{
) s) c$ V7 s2 j9 i. Q" g    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);# H2 r% [3 _* l! o+ ]
& M7 y  B+ M+ R6 t3 {( N
: L& m" h$ }7 _6 Y" a1 f* a
    TIM6->SR &= ~TIM_SR_UIF;2 J1 Y) G) v/ }- Y$ b, u7 c
    prev_key_state[scan_row]=key_state[scan_row];, `& {! f: R: `% u4 ^) ]: z7 f4 _
    key_state[scan_row]= *PA_IDR;   // update key states
8 `3 M% y5 `9 L    switch(scan_row)
; H+ h* q4 v( M5 W    {& |5 x& ~4 ~( I/ b$ V
        case 13: // next row PB143 _5 d( ~+ s" k/ P* g+ V
                GPIOC->MODER = GPIOC_DEFAULT;7 k9 w& _; u; s% e2 ]. S, K7 s
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
4 |2 m9 {! X9 r- ~                break;  Q9 u3 ?; S9 o. `# O
        case  0: // next row PB154 A6 n4 L9 T; Y& F5 ^3 k0 K
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;
) x: }% E. j0 h4 O4 m# s, w2 t* m                break;
1 x( T, L. n1 \9 h( y5 W        case  1: // next row PB3
4 ^3 f) M5 D7 r: Q" R, a6 P# U% A' |1 ~                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;( S2 N5 t1 W0 p9 }6 K7 y
                break;
/ q# ~2 u+ H" D7 h3 O        case  2: // next row PB4
9 t$ I/ _1 L/ m* Q; U6 p6 B* d6 I                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;( [, @1 G# g) A1 j' q6 Y& v) e
                break;
' S" X5 E' F) Q0 `7 `+ J6 O        case  3: // next row PB5
/ y. b) q1 m0 d+ e8 @: q                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;* p' w& c! u6 T
                break;
( C# s# a. D( v- p) T8 `2 {        case  4: // next row PB6
6 V, c2 f) @8 _5 v& k+ Y                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
3 b1 K' j4 Y8 F% d8 C( [& l) g                break;
* O0 N5 H& o; |/ A. L        case  5: // next row PB7
6 w3 I: O" N" a: ^6 @; S                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;) g8 K; {8 P% E% y
                break;6 G- x! N1 H- n2 l0 b% w9 `
        case  6: // next row PB8+ C- h9 b, e1 u2 N
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
; B; ^, }7 a8 i6 E( k                break;
' |9 Y/ o: g" E+ a0 `, R  W        case  7: // next row PB9
" U5 M+ Q8 A0 m7 K                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;9 B9 l4 r! S3 ~* h% C3 }( q
                break;
; D6 x! P( P# D/ v0 |        case  8: // next row PA8. h5 t7 i* e7 {  E; H$ i% n
                GPIOB->MODER = GPIOB_DEFAULT;% H: c: U7 l( o
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;
* P; V/ r: v# d8 E                break;
% c4 ]% Y- {7 m1 S        case  9: // next row PA9
6 m  h6 {4 G4 h: P  l+ s                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;$ d7 I) m" ]; n8 n
                break;8 o# u/ U# P) W. J) S" U
        case 10: // next row PA10
. R& k3 d/ N. z/ \" w$ V/ t                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
; D; p) ?! M1 k/ U& [                break;
6 X, e6 {' r* j5 ^: C/ r4 c        case 11: // next row PA15
9 k# u$ M) l3 I) ^! X) h                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
6 M! ~  @) B, h2 q. ?; {- Z                break;& ~8 s% _' B6 v0 p- ?' P, d
        case 12: // next row PC13( I0 q+ F6 i3 e
                GPIOA->MODER = GPIOA_DEFAULT;: a7 `( x2 I% {7 \$ J
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;, y" p2 W, m- C3 i8 ]0 y4 f0 q. G( e2 E
                break;: z. }( M+ F) Q  u
    }
, }" G4 _- `& L% ?& S: b    if(scan_row<13)" x: f" E" B+ y  W/ i. F- F% G' o' ]
        scan_row++;- K5 i- t% Q* F+ z
    else
# V5 u# M0 N8 J5 v        scan_row=0;# P; W  q3 O) M) p
}: u4 C' W; g; ~; W
) a6 G/ ?% ~; u  P; M; Z0 A
" e6 p; s. [% P
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 $ h9 Z/ h) q. _! W

9 ^9 B0 i% W7 ~; r) H; j9 F# v扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。1 R; z% p& L: P, K2 _

* P7 b5 n" t. B要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
- M+ a6 E: p# S9 s  {: w
0 {2 |, Q# Y( Y# F8 n9 B
% B# {3 ^  O0 J
const char hid_keymap_qwerty[14][8]={
) x* Z# y+ [- C( e    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
( |  V3 N$ i4 d3 L2 D. Y4 n! \    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},5 B3 }: }% H8 t6 n
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},( j* l& k- |) z+ l( C& C
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
& A* l# [. ]# X5 S5 u3 R    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},5 |8 q" ]* I& V+ T$ ]
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
4 n( T. X0 q7 Q    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},! I' w; T0 @" {! d
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
8 K/ i5 H" Z6 k% P    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},2 L- c; }* q: ?" Z- K
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},- ^% I2 q7 n1 x' E
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},' T4 |, m# ^/ }$ v, R7 n# ?0 s
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},0 J) p( ]. O) G& A. D7 M- ?) [4 N
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
( n, @1 M2 _1 p1 v/ F0 T    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
! j& [* R, M7 e) N2 ^# L};% L* c2 X9 U# u

- L: V: y* [* A! i& l0 V
" J$ _3 p6 r6 e: ]1 g6 J# a8 S9 P
const char hid_keymap_dvorak[14][8]={
9 b% y8 L" Z: v; I    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
2 Q1 H# @5 `' c$ k+ F8 k    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
/ Z0 i# T) `) o. K# `, m/ G0 d1 `    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},! p# z2 y) d8 V
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
# F7 u1 E2 m+ a# N, q6 @5 a( C    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
# _* `- E5 W- ?; s& K4 g# s    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
/ d4 ?- l" p! F    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},6 Y* R- W- ]+ c! r6 h1 @
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
1 k# n0 t) u- I, f3 I    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},8 R9 {) E3 O# Z% v' B
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},2 s0 \4 F1 u- A* `6 s* q
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},- b0 E6 r* N3 r, R0 Q5 R
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
: [) t' S  K, I& Y, S    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},# [5 Z" m* T6 a7 G$ k
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
1 {, n$ A3 l- f& `' e; S/ r};% [4 J$ p3 ]) e! H" [* T

4 a7 c% R9 e% @/ H$ W# H- y. c2 C
, x1 s4 u- Y8 u$ M( x3 Q; {. a上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。0 `2 I- _" j2 u

( s4 z3 g; E7 AHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
. s+ w# w/ [) F  I
: Z: a: ]4 M+ T0 O; H) X0 r6 M
void update_key_matrix(char row, char col, char onoff)
& P8 J+ f/ @0 f$ p9 S0 U) r4 E% p{
6 p0 w, _  {3 F6 B9 D    static uint16_t hid_report[4]={0,0,0,0};
5 P, E. Z4 z' [. M) u    static char (*hid_keymap)[8]=hid_keymap_dvorak;
1 m  g. w. N) ~4 o1 F& K: t. z3 L: w+ R: g3 ^9 L! Y

) t9 l+ T8 ], m' y6 }    unsigned char key=hid_keymap[row][col];6 q( A& r- @/ _. ^8 I
    unsigned char *report =(unsigned char *)hid_report;
5 r0 _' x/ V+ e& a, y/ O8 o9 M) l    char i;
- ^% J6 d0 }5 L1 P7 i
$ T8 |9 W$ h$ B4 ^% W& N

* d2 Q+ N, E% Y; Y4 E  [( u3 R    if(key==HK_MODE)* N$ ^, M3 w; L$ U
    {
) U& v8 {1 e1 ^, N" N0 i9 H        if(!onoff)
; c1 m4 ]7 F- V0 M        {
& F7 X5 k; [5 `5 i            if(hid_keymap==hid_keymap_dvorak); y/ g4 m& g7 J/ v! D( v
            {6 `( q- S% [2 v0 f' H
                hid_keymap=hid_keymap_qwerty;. i8 h! \* y  F) |) O
                GPIOB->BSRR = (1<<2);
( w- l9 q" |4 r( [# H. q" h4 M            }3 s( j4 q6 ~' E" k5 }7 q
            else
7 r) {4 h$ Z( Y! t            {
+ Z/ _) J3 `* t1 b                hid_keymap=hid_keymap_dvorak;
! O: l! C, `1 `9 C- \                GPIOB->BRR = (1<<2);- A/ e- T! w1 {5 \+ D4 G2 P
            }7 c/ m* n2 Q, ^; ~) U- t$ y
        }5 K3 f3 b/ ?2 C+ L! i
        return;
" k* k+ D% [9 p9 K    }
% o* c; r# Y( \" |/ ^) p+ J  ^/ o& O4 l
9 I( M; ~' h1 y+ R$ j: u
    if(key>=0x80)   // Alt, Ctrl, Shift
) f* {7 v7 g" {$ }    {2 [- Q. e6 U: V  f7 ~8 n' q
        uint8_t bitset = 1<<(key&7);  h- L1 O# V8 N; f7 k- }6 i
        if(onoff)   // non-zero is key up; n$ B) u( [- M
            report[0] &= (~bitset);
3 G# v  x3 }# a- K( D0 Z) ~3 j; l        else
- V/ R# N. @( F( l/ h; @/ m            report[0] |= bitset;1 ~) B! n3 k" l) E$ M& K4 W  b% w
    }
3 h, h; s& C# b7 Z    else
1 A* e4 p. v- V6 T5 n; s    {
$ G. T" o1 ^4 N# f        if(onoff)   // non-zero is key up
" L5 A* ~7 N$ w        {4 ?$ o4 Y1 M4 {
            for(i=2;i<8;i++)
* X4 f# D2 n  _: r3 W% j            {$ c. {' A0 Q/ R# H
                if(report==key)( n' T2 t% `1 f) f; G* r' x
                {& I1 K; ?. }; d7 h2 J. c4 C- x2 r
                    report=0;
& @# k: f! f) c                    break;: e+ I( H! |2 k& Q
                }
1 e1 N! R' f( [. l  A( }1 b% e9 }            }
4 k/ Q  n1 L3 ]3 O        }5 ]5 E5 C7 y+ @. n
        else
! f% p9 p% P( ~6 y+ V% b        {* B* d! P) O1 N- c4 X
            for(i=2;i<8;i++)
0 y9 i! T% Y; @5 K  I            {# N, ]  C# M: D2 M% P, D( o8 w$ B
                if(report==key)4 @# j' f+ n" W9 s: H. m. C; f/ j
                    break;9 D  |" ^! n+ ^! B9 o) K
                if(report==0)6 A% q) o& _3 b+ ]# O2 j( `
                {
. P6 H" w5 I  A6 Q+ r0 M: D                    report=key;3 f" F, u+ R' }; O- b6 p* A8 t6 Z
                    break;! e" n  l! r; O* b
                }2 \; D0 X: T! s4 c' L) f7 w) V
            }) Z  o4 M$ S' J
        }' A) Z4 l) L" Q& V7 B# q$ o
    }
5 U4 j% [- o% E    for(i=0;i<4;i++)
  b7 b* y2 a4 p" T( E& C        USB_PMA[192+i]=hid_report;2 B; |2 G& {: |9 x: C( z: C, Q
    USB_PMA[5]=8;   //COUNT1_TX
8 u7 l  a4 e. F    if(ep1_wait==0)
  n. o2 \4 T3 v    {
+ H8 q3 S' y1 \9 r( B0 ^- y. T; k6 d        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
. o8 H. c9 `/ M6 \        ep1_wait=1;
1 Y5 @  x& G$ J& I3 F6 D4 c    }0 O" {5 u- t1 r! h9 E' u8 e
}, s" ^0 V2 f- b
; P! ~' P/ b* N. V, H' O! l
% u$ [+ i& D# Y6 o# Y8 V# X
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。2 ^# b" x2 r' a# k5 k5 B
keyboard.zip (8.7 KB, 下载次数: 6441)
9 F9 I' O6 m3 Q2 n

. S7 z) U8 V% C* v) D/ ?
  t! a5 p8 J2 {# P8 ]8 z* y
3 z' _% V4 |- Z' S
3 i6 d' b2 c2 Z0 {
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
" G% ]* g' N4 v不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
$ Y% B" z, g  Q6 s刚开始我以为要把打字机改造成电脑键盘- D5 w6 v. a2 L
不过楼主也很厉害!

* G8 U% k+ ]* }8 q哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害2 ]! z6 }0 f, m; A0 k* G# `5 u
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-3-12 12:55 , Processed in 0.172244 second(s), 27 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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