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

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

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

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

[复制链接]

24

主题

63

帖子

286

积分

vip

Rank: 9Rank: 9Rank: 9

积分
286
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑 - K7 ^1 W) g% N

: V$ _9 I  h. O3 Zhttp://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1' x5 ?. H; c* t" K  e
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。5 w) c3 U0 O" S9 q  j# Y- H, D7 m
- m- b! p% s# T/ ~( t/ j
在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。

" |9 g7 K2 Z7 X0 {! e
/ m( u  A( y0 b# I
235140i3a36qivqzuvmt5q.jpg.thumb.jpg 7 _; ~& v# y" n0 w5 t
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。9 |8 G' \. h  e' B% F
001734klbyoluenuwz4h4b.png.thumb.jpg ! @7 h& y' d+ z' e
为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:8 R9 {# k# V' D. ^! o
003625r2agx2f5v922cf2f.png.thumb.jpg
" h/ `( c8 o0 B其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.
- B( @8 w% Q- ]" o" b0 A2 |9 p( QDvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:

1 N) E$ A  w( N4 o4 M3 ^ 005836yvs0wvovwsssgd3o.png.thumb.jpg 0 s7 P! p, H! v% _1 H
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。+ B, m# g3 V/ a8 w
; S$ n8 |% ^8 c8 ^) ^; ^
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。, R% F5 o! @' d
) f5 D8 i+ O/ g# }, f+ A2 \
到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
( W0 u; j* j) k& g  w) [. I% f4 {( m4 S& n
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:

$ q, r& W, h" C, B5 R1 w% I, {1 D 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg 2 u% S7 _  h$ u% \* `% o
----------------------------------------------------  分割线 ----------------------------------------------------------  p. X$ z' I3 I# J

" s- B3 |: t4 |4 ], q5 K5 z先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

5 a1 u" o& C, p' n8 ~2 w: n 020011osionbunl4ui44vi.jpg.thumb.jpg   `2 Q3 x) s# w7 f2 w, S$ U/ G  V5 j2 t
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。  Q# h; ^+ }; t& K' x9 v! C1 d
020017j8ycmnv7788bqv52.jpg.thumb.jpg
0 \7 f8 \6 b! V8 f" X特写,80C49
3 X4 @$ e6 n( F- h: i( g 021040oujzuvtut6iujtvz.jpg.thumb.jpg 7 O6 n3 z) h, q1 @. Z! P% {
LED部分,使用了一片D触发器锁存指示灯状态.
$ H/ ], \! o. v 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg ; m5 ]" [9 H* a. s6 D* |  S) |: v
暴力破坏,将80C49拆掉
0 J! a: o+ d1 t$ e1 a 021113e48qq98vyohvhzzh.jpg.thumb.jpg
2 I. \9 e1 [1 ^4 K  \+ s/ i拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。& @, Y6 ~8 W  s9 Q
021125nc9az6dj33rlds2r.jpg.thumb.jpg
" L4 [7 P. d! a1 E- I" Y, {" A
焊好元件后的板子,准备替换80C49' h  o+ T% c% D/ B. M1 Q
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
: {* l) Q( r8 G' f1 A( p% U0 V用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。
5 X/ O) b9 x( Y9 V5 @' ` 021104shifhnrqbr3o5nlo.jpg.thumb.jpg : g: T. F" `2 W7 S; S+ v
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.
+ r+ q, U, F, n- _2 r 022003ym1p9u4ug40280uu.jpg.thumb.jpg
/ j( }! w2 w, h4 G开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。0 C# B+ n- w4 |, F& b
023313kt141q9qajtol7ma.jpg.thumb.jpg - r% }! B( v9 a# I6 T. o3 O
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。  w/ Q1 j2 e- D% c. d) Z% r' w
023322nt7l5xb3ltttkltt.jpg.thumb.jpg ) j8 W1 d$ J' `$ {# d) s% i
主键区键帽就位7 S' k2 a7 l9 ^9 E* @: _$ \
023331hin88e8wkrwzwikx.jpg.thumb.jpg
( ~0 \: j" p- D" r: D" s( F6 v编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。: T4 I0 M. W, [: i( `/ K# c
023336wjzlgopugg1jyy79.jpg.thumb.jpg / n9 x/ c8 q7 f1 a1 C2 F
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。
4 I& q. E# [' N0 q, s 023341sffu4j3g2323h6fl.jpg.thumb.jpg * p0 _$ G  i$ U# W8 H8 f# M: p

/ d& K  H# I* n$ N" ?* A& h, x6 R2 f----------------------------------------------------- 分割线 --------------------------------------------------6 z" }+ C* U, ^% q- ~3 k- d

+ _/ [+ Z! k5 E( \1 t+ m
5 v  w0 [1 U# t- j( k
6 E9 s" \5 X' ~( K) g- d

$ f5 Z7 ^% M1 M7 ~1 z+ ~2 S5 f1 l% d
+ b* @' }. O$ a  L
. K$ w: y1 Q8 G$ n

% R3 s7 ?( s# V- a& t' w- @  f  ~
* d% y3 ?: q4 ?' x$ ^5 C/ [4 M$ j' p
2 }. V, [% L8 g& R; O( K2 O# J2 W! k0 _, a6 C& e5 k3 `
; q( W: D( f! S  c5 r9 S
回复

使用道具 举报

24

主题

63

帖子

286

积分

vip

Rank: 9Rank: 9Rank: 9

积分
286
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
9 s0 b( p4 @# J/ [80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:: x1 H- N7 X2 G4 U% ]6 E
104025nzibm2rmiomhyirm.png.thumb.jpg % z; ], y- J5 w9 ]  l

) x) }7 v6 T' n4 T  `* f, n其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)+ p/ o& \0 V* @# I2 E
105004zkrez5houvkkznko.jpg.thumb.jpg   v' ?6 ?+ `9 y9 p) v6 a! b, O" w; l7 h
7 Q! S+ U/ m+ @0 t6 R; J) s" m1 Z0 r& [) x
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
( [) t$ h9 Y$ [1 e/ x; E4 o1 Q& e8 F
; f+ t- }4 e4 D. y这是我设计的电路图:. ?4 ~0 r* c- w- \
110344ej2z2oo2rflo7oe7.png.thumb.jpg ; P7 W( C7 [( y" g2 b! G

; u1 U% r. A+ c# N. |  M- gPCB Layout:. W, V  @( F5 R4 s8 c8 ^5 `- K- y
110847jjbjvt34vwt3v5bb.png.thumb.jpg ) t0 v2 x8 U4 ^$ U* E/ J7 g8 {
& m4 ?* S; V5 n9 n) x
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
. m( U8 u; T% y" l$ m
5 z+ m8 G/ M; E% S2 F7 m% }; V3 w9 N
  g# Z2 N: O! s% n
回复 支持 反对

使用道具 举报

24

主题

63

帖子

286

积分

vip

Rank: 9Rank: 9Rank: 9

积分
286
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 3 N, V9 Y2 `& D" q8 U8 A
; F/ C5 z; p4 q- @) m
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
$ V6 p6 K1 U5 h7 f5 X& C
2 D. ?9 q% i2 W3 F) \4 [5 T0 b- Z总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:
/ f3 W) L* B6 t& y6 r) ]  t 113818pmrfsb6z0byt6t06.png.thumb.jpg 7 {- O& A$ a& O  P! V, U
% Q0 Q4 t/ i0 r7 r' o1 s" S4 M
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛); Q/ {, r, E# u8 N; v
" d0 I  |6 E# i" H6 w+ B! y* C
USB的中断ISR,bare metal哦* C8 \$ x0 F4 `3 l  i. |
: K# Q2 O0 D) E1 k/ \
void USB_IRQHandler(void)
4 Q6 _$ [- X& W+ j{
- ~% I7 \( c) ~2 a! v4 Z8 X# b: m    if(USB->ISTR & USB_ISTR_CTR)
+ {2 B8 O- ]# w. s2 @    {
5 Q$ W& e! ?1 N        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
% l7 E/ C% g, X8 D* \: Q5 x2 I        {9 K. h8 p* d/ C+ n7 z7 Z# m+ u
            switch(ep0_state)$ a1 C& w/ s! T5 E0 b1 y1 P
            {: o6 v$ K( c+ L5 V' ]" c5 u6 [
                case 0: ep0_state |= 0x80;6 O: n5 O( W9 O
                        break;4 k7 D' f2 Y: M& L- l1 P
                case 1: if(USB->EP0R & USB_EP_CTR_TX)) c. i, v, e3 f# J+ C$ U
                        {4 h. f! o: s  u
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
7 U9 Q% C6 e3 X6 Z' S3 u                            ep0_state=3;# y- A  [$ _; c* L' n
                            return;4 ]- c: G( m( G
                        }8 N! T( p8 l+ q  g' n$ y: b! Q, X
                        else$ E/ E) n- L3 }) A+ `
                            ep0_state=0;5 m& K7 T6 m6 q+ s+ N% p; _
                        break;1 J6 A2 T( c; u# W$ [0 m
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet, Y, C1 q( ^5 c! ]! C/ \7 y
                            ep0_state |= 0x80;' Q) T, p7 p4 J1 @4 t% C
                        else
7 q) ~. r# Q" A                            ep0_state=0;
9 D( J: W) ]& O7 t, l                        break;- X- [3 s/ Z; U! \: g  e6 b# `
                case 3: ep0_state=0;
. j* M" y& }2 }                        break;
9 j% Y( h  E) ~0 h% q# |. Z                case 4: ep0_state=0;
. T) e/ K! |: M9 g' B: R! a& G                        break;( X! Z' ~5 u- ~! m
                default:ep0_state=0;, W  \, f0 f" x1 c  u7 A/ U: ^2 U! j) @( b
                        break;
& F" V% U9 \# e. \3 j! W            }( }6 ]2 O7 l' M$ H
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag' r4 W: j/ L1 i4 Q, s& {
            return;
2 `. s& b3 s0 _5 o- w- T6 E        }6 ?( P* i2 B, d' t
        else    // EP_ID can be 14 U. a7 y4 _8 G
        {
5 q: g7 r. a8 p6 Z. Y+ Z            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag3 s; G3 w3 m: `
            ep1_wait=0;8 U$ c+ y2 @* v% A
            return;
5 U- m( e/ c* F- R        }% F0 i+ X- B0 f) [" ^
    }
2 M/ e* u; F. A    if(USB->ISTR & USB_ISTR_PMAOVR)& T) V) D5 H! n; T. `
    {- f8 E; N8 U$ [: z) C
        USB->ISTR = ~USB_ISTR_PMAOVR;$ K) @8 n: ~! ^1 X
    }
/ f, [( Z9 m% _& p* k. \! g    if(USB->ISTR & USB_ISTR_ERR)4 u+ [: u0 x7 U3 k- T/ L0 Z
    {
( [; ?) m# \+ y        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
& }3 C- `- Z/ N7 X& ~$ G    }6 s" z1 z7 k" R/ F
    if(USB->ISTR & USB_ISTR_WKUP)
; b. h! @  t/ z% `( m2 J9 B! D& X    {
% [' ^& p$ t* }; B+ @+ g        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend% k  t. X+ g/ L; U! p7 f8 S7 f8 N
        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
7 g9 r  S6 j. M4 R; ?0 M/ \    }
2 `0 R5 _, c& ]( ]; G    if(USB->ISTR & USB_ISTR_SUSP)
% H! T5 Z6 x6 c' Z    {- ?3 F- D! ~6 }
        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend9 O. n* t+ N  ~! J, ]% k
        USB->CNTR |= USB_CNTR_LPMODE;   // low power/ M. J6 W: P5 b& @1 o
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
3 w, |% U. O* h$ Y8 j    }3 \5 `( E" ], B0 ~; z7 V
    if(USB->ISTR & USB_ISTR_RESET)
+ |) V! g# Q. t3 e* Z6 h    {
% ?$ ^% w& k7 X4 p. i        USB->BTABLE = 0;    // buffer table at bottom of PMA- d( t3 q6 p3 `! F% d
        USB_PMA[0]=128; //ADDR0_TX( d5 C& c. K7 p$ w+ R8 g  w5 M" E# y
        USB_PMA[1]=0;   //COUNT0_TX' O: ~% D! o! ~! g5 ^/ A  v
        USB_PMA[2]=256; //ADDR0_RX9 |8 F, M1 c/ D- q$ y& \% r( _* M
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes7 f; ?0 E) O4 G! e
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
8 t5 V# n8 k4 F2 ~- g        ep0_state=0;
; C% ~- d( [6 g- ^& w3 o* U        USB_PMA[4]=384; //ADDR1_TX+ z4 g+ v) ?4 s4 W1 v
        USB_PMA[5]=0;   //COUNT1_TX# v7 g0 E, b  }
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
. h- B$ @# K. p: a# K$ ?: C        ep1_wait=0;
- C4 F" u: @7 K9 Q- d: T' G        USB->DADDR = USB_DADDR_EF;      // enable function
( c5 s* U0 a7 h& }        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear. s3 l' z. p/ G
    }0 J% W& H( t/ b  ~
    if(USB->ISTR & USB_ISTR_SOF)
8 ^; J- N$ _7 S& J/ n' H/ J  g    {1 P; q  G( q/ [+ g8 P0 y; W
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear% P/ T8 g2 }. H3 L; C5 Q
    }: D$ F! [9 Y) V7 T0 m" o& N. }
}6 O- Q7 C1 `9 r# F6 i1 E6 @5 o

, P+ T2 V5 b: x1 ^" _0 o' e+ `
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。$ [5 Z5 g2 E7 O; N6 S! I1 h
  Z9 P9 }  W% K1 V3 }
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。1 g+ G/ w- F/ W- q: j9 a

4 |9 \4 A: t" |! t9 f5 Y
    while(1)
. ?0 O  o+ L% ?6 P" \5 V    {1 A2 A' C: D; ~8 b4 p& G! O7 }
        static char row=0;
- d' `& o( C* O' P- c" R7 O# Z        __WFI();
. R# i# U8 \4 M* G* z        if(ep0_state & 0x80)    // request data processing
8 V6 E6 r8 N( E( y5 `4 `        {
' x/ \5 t5 n! g6 t9 c0 _' t            if(ep0_state==0x80) // SETUP phase
" R9 Q( ]" F1 {2 y6 {: v            {
, I1 j2 p; D& Y/ j2 u8 M4 U                if(!setup_packet_service()). t& v5 a* \: S+ ~# l! u
                {
. \- k" J1 @. g# m                    ep0_state=0;' V, e4 q- A5 y" @& G; G
                    // not supported/ b7 I) s9 O6 a  m
                }; b' {$ {  f. J, G
                // ep0_state should be set to 1 or 2, if processed
3 x0 x3 ]' w; W0 U0 z            }
8 G4 Z% b! _) c) G            else    // OUT phase
; t; K4 o' u9 d, I( w! ^* t  U# Y' `            {
+ e# x; {9 b% Y/ d                // process data
! I0 l, O: L& [) v9 ^7 h8 s& S8 c                show_LED(*(uint8_t *)(USB_PMA+128));7 D, \* A2 D* C9 q+ v" g) A; j
                ep0_state=4;
8 S7 ]; N) f8 ~* H$ K2 j5 v! W                USB_PMA[1]=0;       // Zero length DATA0) W" A5 J6 K4 n
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
; \9 m) x, v4 i            }" l" Y# W- x2 ?; S* }& g& y1 |
        }7 `& a4 t& x0 V; Z; |$ I
        else* U, x' N) P8 T" q" E" _
        {# `! d+ n2 h  M: e6 i
            if(usb_address && ep0_state==0)
% Z9 r  u" S* Z* Y            {
, r+ O$ k- z# D4 T, I                USB->DADDR = USB_DADDR_EF|usb_address;
% @8 k: {. m& [* d9 a8 j                usb_address=0;
' C2 [  ?% d3 _4 F. _            }
+ }) ?: p! l$ C7 x8 }        }& P) ]" c# F8 Y+ F/ O2 e! N
        if(row!=scan_row)   // new scan line/ o& E% D1 f" z( y, C0 P- \, L
        {. L/ c+ C9 m, m# v$ R
            if(key_state[row]!=prev_key_state[row])7 f. t$ R6 O! z9 I* k
            {
5 F3 a: i7 ~) s7 y4 G0 W                uint8_t test=0x80;* @, D% V- o* m$ n/ T* r
                uint8_t diff=key_state[row]^prev_key_state[row];
2 X  T8 v9 J' k! N3 @" e                for(i=0;i<8;i++)+ t8 p2 ^0 N0 b7 \/ E
                {
, b1 j1 x  o5 e                    if(diff & test)0 z' J4 |0 s) ?* N- y9 Y1 Q( c
                        update_key_matrix(row,i,key_state[row]&test);- Q" T5 }! w+ B: D1 j' _# l
                    test>>=1;
3 l- w7 C3 V2 Q2 ?2 C  j% S; B                }
3 s8 P1 T4 Y5 O) {7 S            }. n0 G& [7 ]9 s' T) ]
            row=scan_row;
; v" P3 T& ^7 Y) I        }
! Q, j2 d9 P) ~6 F+ \9 K0 {    }8 u4 U1 D! U, e9 n+ Y+ o& v

. v3 ?4 f/ o( G8 M8 _0 B
; E8 }3 \# f- g  N! XEP0的控制传输,把用到的请求处理一下5 {: V2 p% }  [. h. v) ?% F
char setup_packet_service(void)3 z" A% U  V, d
{
" J% A* U+ f& @( W0 g    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
# S2 ?. n. _/ T/ _& {9 W    {2 R0 Y2 {) k3 S( d3 Y* b: ~
        switch(ep0_std_req->bRequest)% s4 e, r& i9 }/ `" }& F
        {
' E/ {, h( w$ E9 [( a6 E* G' U, C            case REQ_GET_REPORT: break;1 O6 ]3 b1 i. @& u
            case REQ_GET_IDLE:
$ K% ]4 P/ F1 b                USB_PMA[64]=0xfa;   // return 1 byte( x3 {! l; B2 a. X7 Q0 c& A
                USB_PMA[1]=1;- p! f' R/ f% J" Z$ e9 c! W5 Z
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
  P; G" q7 q0 Y' J+ g                ep0_state=1;: P$ X  x8 T- q* A
                return 1;3 |3 ?  I1 p1 Y  ?/ Z6 T
                break;$ M8 J( E3 N9 g
            case REQ_SET_REPORT:
+ B& U6 ]7 \3 F& s1 v& U( ~                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;3 v0 j7 m3 @$ }5 [" q
                ep0_state=2;: z9 A. `3 ^7 U; w: |) z1 R7 K
                return 1;
: @+ ~: O8 V4 j, ~                break;' C6 w0 _' R* m5 J
            case REQ_SET_IDLE:) p3 \$ |( b* @. [$ N0 S* P1 R6 |: F
                USB_PMA[1]=0;   // Zero DATA: H1 U9 r: x4 X) R& Q
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
9 z: O# ]7 k2 o1 M0 [                ep0_state=4;' P( ], Q* G3 g. n) b( f
                return 1;
) h. K- J4 P6 x$ F/ K                break;
2 l% h; t+ G+ m( ~5 s        }
8 I" l: P/ a: E* Z- p        return 0;
% e. v! b% O: g4 j    }
* z$ J- Y" {8 I! W' O- `( b    else    // standard
# Y: W; J- z' j8 V& ]    {* Y, F  ]. l; d: ?5 G% w- g
        switch(ep0_std_req->bRequest)
5 q: T; `# a; p, N- l/ L        {' E! l0 ]4 W" a2 \6 v, ?9 @/ k) |$ b
            case REQ_GET_DESCRIPTOR:
8 b1 r# ?2 a9 A                return descriptor_service();! {# {- p. e4 _8 K' \9 K4 Q% ^+ j
                break;  f  s+ t- v+ K
            case REQ_SET_ADDRESS:# n, u* ?; l+ P
                if(ep0_std_req->bmRequestType!=0x00)
  k4 y/ p! I; e! Q) f3 h( U0 }                    return 0;
: O. a8 j) A  Q: f- @8 M                usb_address=ep0_std_req->wValue;6 W, B0 K* a1 _8 p+ w$ P1 {0 A9 R
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;/ x8 j, s) {1 H6 s- ?* D8 n( h4 o
                USB_PMA[1]=0;       // Zero length DATA0
8 c5 U# {* b  f" O! S6 X2 W- j& s1 O                ep0_state=4;    // No Data phase
  k, u+ s4 \( \8 [                return 1;: B: D# X! b* W. ^. y
            case REQ_SET_CONFIGURATION:3 X& f; @# W. g1 Y* p7 A, u
                if(ep0_std_req->bmRequestType!=0x00)
/ t! @8 B* a, v  b1 K& D$ m( x                    return 0;5 E$ n+ i/ L3 f, ?6 a
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;. w) c9 Z! H; L. J* p( Q  e! M
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
+ \* p( L6 P3 r% ?$ Y  y" ^2 x4 e                USB_PMA[1]=0;   // Zero DATA
% I( K5 T; r  M- X                ep0_state=4;    // No DATA phase
2 b7 J9 n  L" y" [                return 1;5 m( o) |" y+ O
            default: return 0;
& H7 L# f  a" e0 X        }
2 r3 g1 x1 k$ O. _    }
/ }$ b. o# i/ K3 j1 `$ M# L7 p}- g) g/ j# b* t$ e4 v9 j
( [; G! z8 A5 u# B9 b) @

, C1 P6 z( [; W- b6 B
1 n. a0 P' [! B# P: J
回复 支持 反对

使用道具 举报

24

主题

63

帖子

286

积分

vip

Rank: 9Rank: 9Rank: 9

积分
286
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的( Y, q4 x3 S1 Y: B
char descriptor_service(void)+ G9 A1 d3 x( }& f
{
/ M6 [8 j$ f$ T* @    switch((ep0_std_req->wValue)>>8)
# ]7 ?8 Q; J: ]9 B' a9 k) F5 }    {, n- m4 j, r* s/ q& ^
        case DESC_TYPE_DEVICE:: v0 @' l; w8 i+ Z; }. {' M
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));3 M3 |2 H$ _- ^* X) ^
            break;
5 A+ h1 n+ a! y        case DESC_TYPE_CONFIG:
7 O$ _' [3 P' y7 S            if(sizeof(ConfigDescData)>ep0_std_req->wLength)& E0 O: J8 K9 }9 t# c
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);- R( |+ _) F; I( D  P
            else, ~* j! J6 @9 {! Z7 {$ `1 h
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
4 `! f0 P4 B5 `; x: l            break;" _) H, u5 E$ b  j: x' p7 N' p
        case DESC_TYPE_STRING:, {1 N2 w- \: V- v9 d
            switch(ep0_std_req->wValue &0xff)
4 E8 k6 O# R0 W! A) k: R            {: U* R- l3 a- p$ D- V
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));+ }% U: k: G- d! z/ R+ J9 ~
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
, T) H+ b; _8 [2 `                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));% k  G% `0 |  W  _; c: d
                default: return 0;
/ c9 O" d* ]5 q6 v" [            }/ W7 \  j0 A7 a, e5 }9 P$ O
            break;1 r/ W: h, P$ T; u
        case REPORT_DESC_TYPE:
: h5 t9 e% v/ s) X# J2 l            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));/ v" ]: f# n, ?; o) t- B* z
        default:
6 W  m+ i2 a- i" O- v            return 0;
+ g9 X4 T) x, k4 I3 x2 P2 T$ P    }
/ [; S# ^2 v* {2 e9 k$ n6 r}' a% P' S2 C1 ^. _
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.& p( C$ p% l# ?, b( }: H7 b& [% y: l
void TIM6_DAC_IRQHandler(void)6 C# p$ \+ F! M. a4 }
{
) X) i, C4 G8 T. R3 v: [1 e( x0 ~2 I    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
& a% {6 s6 Y& y( ~* C4 _$ \3 f6 [; Q# W# {7 K( [3 n6 C
- u% n. _0 J. o% B" A4 Y
    TIM6->SR &= ~TIM_SR_UIF;
' m$ Q4 q- ~8 L6 V    prev_key_state[scan_row]=key_state[scan_row];6 d) G) t7 U8 z  Q& N* R$ e$ H/ n- Q
    key_state[scan_row]= *PA_IDR;   // update key states* }5 z! {- i6 O  r% p" @9 Q$ C
    switch(scan_row)0 }$ Q( b; ~( S! ^; \; f% ~+ Z. ^
    {
3 }/ n/ O. `2 h        case 13: // next row PB146 Z  }8 E& \  Y- t. A$ v
                GPIOC->MODER = GPIOC_DEFAULT;
; r, M4 Z$ u" b! V& b" Y                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
- f8 t6 `9 ?( X" J                break;9 ]8 |5 z  U. V: S# V) S) K3 d. E
        case  0: // next row PB15* `% X+ m, G4 W. F/ i  ]* a
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;9 }% [/ u' W, O
                break;
, O! _" _6 p; \  ?& y) a        case  1: // next row PB3  Z+ E' |/ Z  T/ K
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
( Q7 h) Q; G; u7 z( Y' R5 s                break;
# E$ Z/ n0 C8 B( C8 B: e. r: G        case  2: // next row PB4
# l2 |" O" x2 K# b* L0 W                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
  E) |0 ]6 W% a- F" M                break;
$ M- L1 E# z; K4 a( \( G, F& [1 V2 t        case  3: // next row PB5- A9 ?5 t9 b4 U: G
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;! {- f8 ~, e5 B' t- n( V
                break;# D% U* \4 B% X# t1 b# d, `9 }! l# y
        case  4: // next row PB6
% s& ?5 ]" ^* S6 w, V/ k- p, x9 i                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;( V$ s- E8 X8 H; J
                break;
+ M; ^) N* p  N5 b6 T2 ~% |4 R* ~        case  5: // next row PB7
* J& L2 t3 m" ~) x# [- C' l                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;" j% z4 X0 v1 x
                break;9 n0 k4 }/ e" b4 R
        case  6: // next row PB8
# l: G: E6 _" P" d- M                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;0 Y- G" h. g0 T- ]
                break;8 I6 {  @2 ]; j
        case  7: // next row PB9
9 S2 w5 n& R  k  v( I                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;
+ s+ M; T2 q2 b0 [. F                break;) U# v* R0 i* y' O2 ~. l
        case  8: // next row PA8/ w3 K! @' D9 t$ ~. c: k9 C. g
                GPIOB->MODER = GPIOB_DEFAULT;) W6 l' ^5 C- w' ?
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;
  M5 c2 z5 [$ J# x" O) A                break;
2 U* l" i( U' N1 R  V4 }        case  9: // next row PA9  b/ U) Q9 }7 `, F
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
# c7 k2 y5 V1 ~- X8 u: V                break;
* q' N0 m0 ]9 H4 t5 l' _$ {& Y        case 10: // next row PA10
0 i$ {# q, }# S4 s- O8 c                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;; J2 i4 [- f8 t" c, h% o
                break;3 ~! y5 M2 N+ b% O  h7 U1 ]/ [& m
        case 11: // next row PA155 w" k) K. S8 ]( t+ g8 G/ X( M
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
, Q, d; `+ K0 c6 j' K* G& m  h                break;
  A2 v% g' x- c: i- m3 Q' r        case 12: // next row PC13; ?2 c/ s* t- D: U3 T% t8 q# R
                GPIOA->MODER = GPIOA_DEFAULT;
4 d* L. Z5 p2 ?+ J! h1 y                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
  f3 C/ y9 g- o. P) z# k# ?! q) C                break;9 H7 o8 g6 p3 V
    }
9 u. e9 p* \4 l* k5 I    if(scan_row<13)+ D. m8 j3 v; V8 v/ u' B2 j! ?
        scan_row++;( _. C" }, v; V
    else
) U- @. @  m3 e1 u0 s$ c        scan_row=0;
0 E6 Y' ^0 Q& d1 Y" t" I, \  g}' l; G; w5 `5 _" h

% B+ |) c" P( j0 ?- q% Z4 A. B
% M  f5 ^# a0 H6 b' F
回复 支持 反对

使用道具 举报

24

主题

63

帖子

286

积分

vip

Rank: 9Rank: 9Rank: 9

积分
286
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
5 r6 S3 B: V$ G6 ]+ {
5 i+ u! V+ y! n- S# ^2 q6 y扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。  a; i+ l- u+ S6 H$ T7 n
/ b4 b3 e: r' W7 n4 A) g& _, o4 r
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
9 E  |* T2 I( L' G7 m' |5 y

8 T6 y& h6 i$ b
8 E- g8 F1 d- R0 D+ q& b2 Y* a# W3 Gconst char hid_keymap_qwerty[14][8]={' ?5 m! C4 c; I# N4 Y4 i
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
0 g: E) h) z! `' T4 J    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},2 G8 r' ]% {' c, S
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},2 q1 s3 n7 p; C* ?* ^/ H
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
1 v: [2 s+ M" B& P- N- }$ w8 e5 Y6 z* p    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
4 w8 H: G" p9 x+ P  Y# `& Q5 `% v; u    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},6 O9 c# K5 o5 ?4 M/ X; M
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
/ J3 \& m* f& c) t    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},; L' U1 x9 C, K' \
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
7 e- E" Q5 ?8 j% ]# ~3 }    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
- m: r4 W8 I# d1 `) }    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},8 g  U; H% d/ o" h+ }5 o+ Q
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},/ g2 ?9 d) L* a: Y' K: {. }9 C
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},0 g/ T$ _+ {" O$ I3 z
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
  B6 x0 Q3 r, Y# f; g};
& f" }9 Y7 {# K" E- R
% L+ J( ^/ u& U" ?2 c

1 _) p: I. Q8 hconst char hid_keymap_dvorak[14][8]={1 i0 b3 H# G: I8 y" r8 w
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
7 v3 o9 G5 q- W% A" {/ Q! ]    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},/ r, _7 {' }( `2 \; _. D! n( P) k
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},) a. ]& R" Q3 o, F
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
1 \$ ^3 m! |+ K    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},1 f1 H: z' ?( x$ p  H) x( M
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},5 z& O; ]+ E2 I" _8 r* I9 ]
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
3 E5 f4 `2 v9 t5 ^' L* y( u    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},; u. Y) N2 e6 M% w( {) o: b: L
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
, a% z( A9 V: e3 ~( G- `* Q    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
2 I+ r' q* Q3 W* p( ]# j* @    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
* Y' k) k9 D0 R$ o1 K    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},. C6 J. v, \# S) Y, m
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
) t# ^1 c) v1 f    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
- b4 Q3 x8 K7 {};' g! O! U6 `  ?- ~5 W
- o9 I$ t9 i; z' B, ]! B0 T2 t
, K! B6 @5 }/ K: B4 |$ I
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。7 |( t6 J  ]0 }+ w( w

4 E4 A& ?: F: S& I1 t& IHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
8 [5 F+ O0 B1 t( j; H5 m

: r. c$ o5 h7 F% I& @* Zvoid update_key_matrix(char row, char col, char onoff)
+ I% j" t1 z+ r8 k7 J{
  s2 V2 `) Z+ m    static uint16_t hid_report[4]={0,0,0,0};
( i4 C; c- w  u: Z    static char (*hid_keymap)[8]=hid_keymap_dvorak;0 e) D$ _' {  X! W
. @. T! J2 J8 z/ C3 L0 x4 I
$ Q% R8 u$ m: N# b# Q
    unsigned char key=hid_keymap[row][col];* l! J2 ^4 f3 e3 j4 I! _: g
    unsigned char *report =(unsigned char *)hid_report;! Q: K( i5 l1 }
    char i;, F/ M6 W1 L+ ]) F* c

* A  H, d0 t' J3 F

  n" E5 |$ V% r8 {/ j& u    if(key==HK_MODE)6 ?0 n) @7 e. T$ N2 w" w
    {
, @9 l' ~" k, Y2 e# A        if(!onoff)9 `7 Q1 t0 p+ s8 b
        {
6 q0 w" u% P# v6 g. d( l% _: i- L            if(hid_keymap==hid_keymap_dvorak)5 L2 _1 V( l/ A9 `3 ?
            {
1 Q9 J  c) \& I, J* h! _                hid_keymap=hid_keymap_qwerty;9 V+ a: |0 W( S) K( N
                GPIOB->BSRR = (1<<2);+ {; `; n9 ~5 w$ {7 g/ G
            }
; B& I. H, Z% r- C( m" g+ v! p            else+ B- q$ n2 d1 P/ ?$ D" s
            {
, h9 o1 E, n- r- L0 h! k8 p                hid_keymap=hid_keymap_dvorak;( S; Z* W, k# C6 m& }% f
                GPIOB->BRR = (1<<2);, Y$ o8 {2 w8 T! }
            }
' N. Q5 H! i2 N1 N$ q$ P        }
# P" v3 E: g0 {, o4 b        return;
. d! H/ V3 k. V    }4 Z! Y7 G4 {8 z6 K
. r/ @8 s, }2 U# z) O) `; t
% \5 [% p' F8 O$ c0 J3 E
    if(key>=0x80)   // Alt, Ctrl, Shift
  J; h& `1 ]& _    {: v; @/ o  k# g5 a& O
        uint8_t bitset = 1<<(key&7);
# I  a) a* I6 ^6 N" N        if(onoff)   // non-zero is key up
( r  x, x; C- v8 n1 N6 S            report[0] &= (~bitset);8 w$ u- k& f% q* q  x8 C
        else0 k9 m4 {# X8 x2 ~- {: B) ^; k$ c
            report[0] |= bitset;
2 L7 ?* c$ |9 ?. U    }
. y9 {+ y0 R2 \    else
/ I0 O0 _6 }8 }    {
3 H$ G6 }1 [& T, I8 \# L        if(onoff)   // non-zero is key up
5 \& M/ |% t* F& U        {. i2 y. b: V- H8 R1 u2 T1 j! \% @
            for(i=2;i<8;i++)0 X! q" f+ Y# V7 Y% b5 U
            {0 M3 n5 t- k6 h1 R4 g
                if(report==key)
  K6 s0 j) |2 ^/ _! X+ w5 V( O# h                {
7 @! i  t, s% b! q/ n  l                    report=0;
  `3 z; U" w& }+ s4 W                    break;
0 h, e: {9 V0 ^1 p# f! z$ I0 M                }
7 n7 M+ }1 s4 F7 Q- O" c% W$ J            }0 ]3 x* X& D- [* ]3 X4 k  D# n/ D1 P
        }
3 x0 |" V/ L/ R1 d+ p5 f        else
) n1 f  e  o: E6 S0 ~8 G0 G        {( ^7 u( x5 v& j; `
            for(i=2;i<8;i++)& G1 `! b+ ]' A1 l) c! E
            {  J# \3 Y. u9 T- u
                if(report==key)
0 t" l2 w/ H$ W2 }! b                    break;
. G; o, M& G  j; Y3 |) r                if(report==0)5 s$ {" _7 w/ O1 r  q  G# e
                {
" q; o$ H6 M7 |1 F* e                    report=key;
5 M! B# w$ Z. l" T                    break;; v2 O  V9 d/ ~, p0 E( ^) o. E
                }
. E2 g7 L) Q, @$ v' K. M. t0 N; Y            }
6 a) |  m' z6 |: @; b        }" ^: k% J2 l( m0 |" _) b
    }
6 {, J- }& n1 n- U    for(i=0;i<4;i++)
7 m" F' B6 m! x- U& i        USB_PMA[192+i]=hid_report;
4 ~: t5 L/ G/ @3 j  K    USB_PMA[5]=8;   //COUNT1_TX
3 `% H+ d" u. t/ H    if(ep1_wait==0)
' f0 I5 W* t( \" z# W% ], R    {
: @# s9 v% ~3 k; y        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
  N* l; I" Q8 m" l, V        ep1_wait=1;
$ Y' z6 S2 X3 X: G5 t( r    }* E1 A: ~5 l; h. B
}0 I0 L2 a$ b! e* |4 l) e
$ {+ X" ?) I1 L0 L( X7 s( y
6 B0 T3 Y) ]3 X' a4 _6 n
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
# K9 t5 v, d! F2 n( U keyboard.zip (8.7 KB, 下载次数: 3586)
回复 支持 反对

使用道具 举报

24

主题

63

帖子

286

积分

vip

Rank: 9Rank: 9Rank: 9

积分
286
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。
( r  X- d$ @: z+ k5 @- e! B
回复 支持 反对

使用道具 举报

8

主题

150

帖子

212

积分

中级会员

Rank: 3Rank: 3

积分
212

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘: j$ h# m4 e! Y: ]! G0 g  Z
不过楼主也很厉害!
回复 支持 反对

使用道具 举报

24

主题

63

帖子

286

积分

vip

Rank: 9Rank: 9Rank: 9

积分
286
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
, {, Z" W1 C1 `) ^& \5 L刚开始我以为要把打字机改造成电脑键盘
2 f  ~/ n3 \) M) D1 ~4 C  r$ R不过楼主也很厉害!
9 R/ N! W. p2 Y( S
哈哈  是有点像奥
回复 支持 反对

使用道具 举报

0

主题

3

帖子

4

积分

新手上路

Rank: 1

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害2 R! S+ J# ], r" A7 b1 C* S& q( w
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2019-5-26 21:53 , Processed in 0.212470 second(s), 27 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

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