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

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

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

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

[复制链接]

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
5 {7 W- u! D9 x! J/ ?% X! x
3 G% ], D6 O" D1 ^http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
( |1 \; @2 [: ~2 G这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。/ V" t) Y. ?+ H( p, M

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

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

: D7 ?! `7 ]' E6 c到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。# j7 Q' w- \7 x, u
! c& y& w+ h# E7 r3 u* _
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:

8 D) t9 ?' }# T 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg + n: |; u: m3 s: L5 h# {  h! T
----------------------------------------------------  分割线 ----------------------------------------------------------
; M0 m  R7 P3 _6 U; i# [: n2 Z8 `: `: s
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
, a# q  `/ J+ c3 t
020011osionbunl4ui44vi.jpg.thumb.jpg ; m9 s5 h) p& z: D2 i1 r
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
: C) v5 ^# H! p0 E8 ] 020017j8ycmnv7788bqv52.jpg.thumb.jpg
- }- D8 y9 _1 |特写,80C49
+ a' Z: n# P& p9 g% Q3 i 021040oujzuvtut6iujtvz.jpg.thumb.jpg
, V& O4 a5 N1 ~LED部分,使用了一片D触发器锁存指示灯状态.
/ P1 k- z. m& C3 o% C 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg # Z9 b3 [! V1 D" ^
暴力破坏,将80C49拆掉
/ a% k9 r8 b4 L0 e, B9 v# m 021113e48qq98vyohvhzzh.jpg.thumb.jpg 3 _4 X. r! G+ |  X6 f  o
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
! e( v; }) ~4 c5 L" b% z# z% C# | 021125nc9az6dj33rlds2r.jpg.thumb.jpg 7 r4 w: w' Y% x0 b. I
焊好元件后的板子,准备替换80C49$ ?% `/ L, V! m
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
# H3 D" T0 H; h0 p2 E用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。2 N7 P  p1 N3 n1 d; K9 h9 i& W8 U' ^
021104shifhnrqbr3o5nlo.jpg.thumb.jpg   p1 {  k' p: r1 W$ s" k$ \' ]
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.3 \( {9 _9 w: D( C: ?. c3 b$ D
022003ym1p9u4ug40280uu.jpg.thumb.jpg
# X) n* q) I/ j- ]开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。1 V5 R4 [% v+ h9 O; b! r$ Y
023313kt141q9qajtol7ma.jpg.thumb.jpg & E/ f4 h, D! y0 I/ e
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
! U+ ?# n2 Q: k$ w" P5 s 023322nt7l5xb3ltttkltt.jpg.thumb.jpg   r& x# d% k& S  j/ e/ y6 r
主键区键帽就位- o$ a5 w& t) X
023331hin88e8wkrwzwikx.jpg.thumb.jpg 5 H/ J" V- O6 A
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。0 C& H  }& r" P0 U0 w, \7 X: Y
023336wjzlgopugg1jyy79.jpg.thumb.jpg
6 F2 y2 G5 `) x$ L, n最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。2 k+ D% g5 y/ A* J! I
023341sffu4j3g2323h6fl.jpg.thumb.jpg
& O9 B% O4 P) Z5 Z7 l
- X% E* K& i' U- ^! j
----------------------------------------------------- 分割线 --------------------------------------------------5 B5 H7 W5 ?1 f9 H

% k% j& q8 Y/ }/ w; W

  l1 @, P: e9 z6 I
/ t, A: v1 y" J) B9 r
2 Z  E) ^; ]7 J# }" w
3 p( b" f9 |) j1 @0 V& z8 W
9 n, ]- q; a* E' x$ \+ O, g
0 b( e7 G- E: v* {. G' U# [
0 C8 S, ~3 f0 K4 c0 y) a: b% y

% A. [$ Y. F  v  Q* O, h6 ^
* X" j9 i! `- q8 |8 ?+ {5 o
; J, P8 _; ]( m6 E/ n* g
回复

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。; [, T1 e: X' _6 t) p4 q
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:# U, f7 `. y  j: Y2 N
104025nzibm2rmiomhyirm.png.thumb.jpg
; Q* U- o4 {: ^5 i, Z3 A
/ _$ w) W) h9 G8 m  z0 l- u
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)/ T- V* O) f2 H! Q$ W2 h/ S% l+ S2 k% V
105004zkrez5houvkkznko.jpg.thumb.jpg
6 Y( T4 f# Q, [  Z. E

; D; v" M2 n$ i, S0 c* F扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。, r4 g/ u) b" @8 p

/ E4 T/ K& ~* q( t' B" o* V- ^这是我设计的电路图:
: l6 O; V% o8 H3 h( s" H 110344ej2z2oo2rflo7oe7.png.thumb.jpg & s7 b. r$ L$ {
7 Z( p4 w" v3 M  S, v$ U
PCB Layout:* o/ ]' V- h# B% E
110847jjbjvt34vwt3v5bb.png.thumb.jpg
! ^$ L! H3 n/ h6 ^
/ ]& E) c2 g  T" b
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
! @  ^* c/ l- Y; p% ]4 M  a, N8 ^" ?& V4 A- z7 S% |4 R4 g& y
$ m4 [: L9 N. f2 r
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑
' I' t  A$ K1 \# x- d; ~
  D; v& X" |+ H. [% U软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
, F; e0 }, ]5 k7 M! Z: T: p- i0 w9 e% W8 ~: N8 \  h* o
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:; F9 U7 M" {+ a
113818pmrfsb6z0byt6t06.png.thumb.jpg ! y+ M  w8 `! S% n) U
" P+ d  a2 `: T; T
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
/ x9 W: h* A  }: `3 z7 _$ A6 q) I" H! g# z9 V$ ~- Z4 |# r
USB的中断ISR,bare metal哦
; Q: |* u4 W- J9 R! g9 h4 x. T

6 ^6 U( G2 j! g) D- t/ ^void USB_IRQHandler(void)' W& T# T" O) k# }- \
{. A# B; I  O$ y+ `- O; Q: l0 c
    if(USB->ISTR & USB_ISTR_CTR)1 B6 y* G. U# e) l# _5 n; M
    {, R' c/ |- u* q& b) L
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0: m6 c2 e& \+ p- h
        {$ l) k( `2 B- s' p+ e) ]* o
            switch(ep0_state)
' ]" s& W# k1 v3 a1 L            {! I$ m) V' i5 \
                case 0: ep0_state |= 0x80;% R" q5 c$ }9 J
                        break;! s5 z4 w) i: x& D
                case 1: if(USB->EP0R & USB_EP_CTR_TX)
% w+ G6 y9 _, z6 ^4 f, E                        {
  j$ @- d8 G. {                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
5 o( v, M0 q0 ?" e) t                            ep0_state=3;
( Z6 N- m0 Y: W% B5 U                            return;
1 ]* V; j# q/ {( W/ w4 z! u                        }% x: \) L) t6 D8 ^$ I, B
                        else
1 I2 @0 y; x$ v7 T$ {: e                            ep0_state=0;) A0 N, H3 T: o2 r6 @  I1 h! o- v
                        break;( |* w5 p* w$ ?) S% `
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet) k. c. N4 i; j1 |
                            ep0_state |= 0x80;
8 v6 J- r# K! R7 g2 S                        else7 {8 A- U5 {9 A2 }+ G
                            ep0_state=0;
  v4 E  j+ |5 y" ]# W7 n                        break;* r) ?& W4 u$ Y9 O6 m
                case 3: ep0_state=0;$ A! A' c' Z' h6 c; l0 X: g
                        break;2 g, n2 C$ {/ ?+ W
                case 4: ep0_state=0;
6 A# U% {' u; _6 J0 N; Z                        break;
! b& J. X5 L+ s/ z4 u& @! u                default:ep0_state=0;7 r( W* K& w' g' R8 i8 ]
                        break;1 P6 U& O# ^* _& J# k+ ~; T7 w" k; E
            }, _( m4 C# n5 a& s0 H
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag, E) \! a/ @% q' ]1 K
            return;
; b9 Q% V- J! g  p1 D" D& L- B        }
, |  ?' Y  E. f0 A$ G$ t: `2 P        else    // EP_ID can be 1( X( @% M% w! ?5 U$ {9 A; m
        {
* r) T* A* U( C: v1 C0 Z            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
3 d1 e0 A5 G4 H9 s            ep1_wait=0;- {% A  ^0 P2 d: ~3 e0 ^; r
            return;; R" J3 H/ Q4 {# v) H1 B
        }/ f& o" w4 ~) [9 |: z$ i4 E3 n- ^
    }
# ^; o) c2 }" M# k    if(USB->ISTR & USB_ISTR_PMAOVR)
1 }+ \+ ^7 `2 _8 V    {
/ y+ p' z6 G# @: T        USB->ISTR = ~USB_ISTR_PMAOVR;8 n9 x, I8 p6 M' U7 A
    }3 J7 u5 ^0 U6 {/ ]1 ?$ ^: g- T8 t
    if(USB->ISTR & USB_ISTR_ERR): U; d' m7 J" n8 h  p6 z1 t
    {
0 @1 ]; |8 S  @9 [) Z        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
* U5 _) D  R$ D; ?2 W7 C2 T    }2 a, E# t0 K) v( a
    if(USB->ISTR & USB_ISTR_WKUP)/ |4 |% a0 z7 t
    {  I* _8 ]4 ]) R3 e6 ?
        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
9 i% r; |* L$ t        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
6 `  r! y  U! @5 b+ }  A6 u& I    }
* y$ E0 B2 G4 m  U  e2 t    if(USB->ISTR & USB_ISTR_SUSP)8 W0 {: H( a' c, d9 ^) I# c( q( }$ v
    {
& @+ A* q/ Y$ n- l1 y        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend* [% [" `1 L! o9 v, s2 o, V$ o4 o
        USB->CNTR |= USB_CNTR_LPMODE;   // low power
; F# t: O- n% s' R# V. l        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
2 p% P: A- `3 [# z6 a2 Y    }, L, B' ^4 O3 d
    if(USB->ISTR & USB_ISTR_RESET)
# H# J0 G$ D" x+ O9 y$ R; W    {5 u' F. X' r, F4 i2 s8 D+ `+ b
        USB->BTABLE = 0;    // buffer table at bottom of PMA
1 z4 X  }; t0 j0 \% Z% ^$ Y        USB_PMA[0]=128; //ADDR0_TX
" I& S$ \6 M2 H+ ~$ S* c3 @        USB_PMA[1]=0;   //COUNT0_TX
+ g- ^$ H' ?0 r! M1 {6 ?        USB_PMA[2]=256; //ADDR0_RX. w" |6 }9 E5 z, L" i& j- `
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
, ~8 |' g3 S" D; d. h        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
8 z2 g% {8 y- T4 J/ n9 }, K        ep0_state=0;
7 C( c+ H9 W2 P        USB_PMA[4]=384; //ADDR1_TX
6 X+ e* M2 i$ q3 I0 J        USB_PMA[5]=0;   //COUNT1_TX
& {. k; t0 T5 R  ?" B        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type+ |8 p1 [, o$ l8 G3 v. U9 U
        ep1_wait=0;& v* `" L8 B3 c* k7 Y
        USB->DADDR = USB_DADDR_EF;      // enable function& x- ~4 z0 j1 s) z
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear3 H& W" P- N" o* k! R
    }" k9 U" R* Z% b/ b( z$ n( Y
    if(USB->ISTR & USB_ISTR_SOF); p% Q* S& Z, i  @7 {
    {
" [7 I! z& L1 B        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear
$ x. v& T  h8 {0 }; ^    }
9 k  b# e- v+ y$ d}
0 J5 A8 G, \* ]# P' h5 z0 G
7 F# J; K  P) C% t; t+ A
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。; @8 l( R) N! K4 X4 \1 r
/ V+ h1 F4 s$ \: N$ q) Z9 b" ^
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
0 ^+ [2 u4 q/ n9 `1 q; t* d: }% O
    while(1)
2 m2 d2 P  z$ a- w    {
6 ^. m& \* i/ U" y7 H3 _9 Y        static char row=0;- z+ B4 y% f" @+ |9 ^2 W
        __WFI();5 ~- c6 `: A/ g3 q; V0 ~/ N
        if(ep0_state & 0x80)    // request data processing) Q1 ?! x. }- X5 y
        {
$ x, Q: M1 A* l, y& D* N            if(ep0_state==0x80) // SETUP phase
1 t7 x; s/ L2 A0 _, ]            {
% b! ^1 H: x0 [, k9 @% Z                if(!setup_packet_service())
, q  X2 B/ _5 M  n# D/ N                {
) v  x. }' r1 }3 r                    ep0_state=0;
+ R. K8 |) d" ^                    // not supported
" d/ `0 I5 S3 y. S3 [" |* w                }
8 {* a. C" R( w! b* {$ X$ ^9 ~- M3 r                // ep0_state should be set to 1 or 2, if processed. f$ v3 C4 n5 o" K5 @5 O" X
            }* h# i' I% {6 z. W
            else    // OUT phase) H  T' P) T+ O1 |, R4 G
            {; _2 m- t# p% J
                // process data. x- `% @: A8 n; i/ Y! ~  ~6 s
                show_LED(*(uint8_t *)(USB_PMA+128));1 D2 A* P9 m) h2 S' d* S, [- q
                ep0_state=4;
7 ]' i7 M3 J+ X: {! C: i9 W                USB_PMA[1]=0;       // Zero length DATA0% f. v5 s% f( p2 [
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;, T% u  O$ F9 k. [9 C. Y! b
            }
+ c7 Z6 r( \2 F8 q1 N        }% y* @0 g5 B" G" T9 R
        else
' v& Z, ^3 d, E( K% L& {* Y2 n8 a        {9 q) w3 u! X" Y% q* p( ?
            if(usb_address && ep0_state==0)
9 |& F& f6 ]" z4 u" o            {
8 I! w  T! q/ K0 z8 S# R+ G                USB->DADDR = USB_DADDR_EF|usb_address;/ H) k7 x, i  [, A6 l
                usb_address=0;2 I8 R6 D7 ^  L/ O# U
            }1 B" Q7 N; N, f8 D( Z
        }% @8 I- B/ v& _2 ]2 U' [
        if(row!=scan_row)   // new scan line
, U) X7 K: c9 u6 n! ~, c        {0 s# H8 s2 ]9 C$ M3 ~
            if(key_state[row]!=prev_key_state[row])8 Z( [: M9 q7 j
            {
- |5 M0 G4 q4 B% c( v                uint8_t test=0x80;
0 t+ z3 c. f( J% i& n                uint8_t diff=key_state[row]^prev_key_state[row];
( m' ~& s) F$ P& _                for(i=0;i<8;i++)& G; a( L2 s7 C$ T% `9 r- Q
                {$ _) r. P: }7 z( w
                    if(diff & test); b1 X6 P) I( o; l4 b8 J
                        update_key_matrix(row,i,key_state[row]&test);
9 k. C- c5 |5 k$ Z7 ?* I9 v3 j                    test>>=1;
( J: ?( K* y4 ?' s, \                }
7 Y% U4 p( t) d/ }3 f4 b& Q( j9 v            }0 G2 c+ m9 F& h$ Q( E* Z$ X$ |
            row=scan_row;' c, w# s! t) s. M; J
        }
- J$ X+ E* |% z    }! ]( i% z, y7 b& ~) E4 l

+ U+ O% J2 u1 h3 u; V0 N. A( [& C( ]) V  m. l( V
EP0的控制传输,把用到的请求处理一下
5 f4 u9 Z, v6 _2 P' Wchar setup_packet_service(void): x) t7 ^" }9 Q: q" a( j
{
8 [) H  J6 ?+ _    if(ep0_std_req->bmRequestType & 0x20)   // class-specific3 B1 ^8 g9 Y: z2 _
    {% E2 v% y& H& D  n" J! W
        switch(ep0_std_req->bRequest)) F: z2 I9 f/ y0 s9 J, X6 j
        {
! H% m  U* Y: J: H3 A( m            case REQ_GET_REPORT: break;
# a% h* L, _% L6 g4 S$ K: n            case REQ_GET_IDLE:
8 V' R1 W, K, W8 l& p! ]                USB_PMA[64]=0xfa;   // return 1 byte
/ @: u' v) Y; G$ \. _! E* z                USB_PMA[1]=1;2 c! P; ]" c5 l/ s& b9 q
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
( L0 R0 f7 _; |2 I4 o4 S' |5 `; w                ep0_state=1;; D8 }; D- i& m2 E; ?' P' H) _
                return 1;
3 r$ W" t- M" d4 O                break;
2 k4 m% C( U) l1 l, T- m8 s            case REQ_SET_REPORT:- G7 O' h/ a" ^6 ^
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;: H2 L& S* M$ ]1 O- A
                ep0_state=2;6 a6 t5 ?! `' h+ d0 z
                return 1;% O9 S# O- Z# v- B# ?- t
                break;
  l; Z2 i3 B" I0 L1 y            case REQ_SET_IDLE:
; t; e/ ^0 H5 _- x4 E% a                USB_PMA[1]=0;   // Zero DATA
9 Q* f# S# q: @& {  h6 z8 A                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;# z3 S" F* S8 Z! E! v
                ep0_state=4;
* B! a3 O0 l7 b  G. I; r$ K9 d1 A5 q                return 1;3 N  |2 X2 e* f6 X
                break;
  I- B3 C# h- b) v        }
' R" o& [- T& {' j        return 0;
& _7 [# J* ^' f6 U6 S' f/ b    }+ _' \( Z7 o4 M; z! W0 ~
    else    // standard
% v' ?" q; [" o6 D2 {  T/ s    {
3 w, n2 A/ o6 x" L9 H* D5 _        switch(ep0_std_req->bRequest)' U, n& ^# C) D$ v" f
        {/ c2 B2 s  j0 M" G2 ?  n' z
            case REQ_GET_DESCRIPTOR:
  _7 j$ F5 o+ f" H  D, F7 ]$ W) t                return descriptor_service();
8 R3 T7 d3 L: }7 ?' ~- a. O                break;
( H; X8 H: R9 u            case REQ_SET_ADDRESS:
+ x) a/ `! v0 W( s  z$ q                if(ep0_std_req->bmRequestType!=0x00)
8 x% F% ]  P8 b! ?                    return 0;
8 d( r' l% A! G& S                usb_address=ep0_std_req->wValue;& N, A6 z, H9 i+ D! a6 m. x0 E
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;$ e6 H$ x& B0 D5 n  U9 y4 n
                USB_PMA[1]=0;       // Zero length DATA01 V5 L* }) O: l" r9 J! i
                ep0_state=4;    // No Data phase
- P; k6 l/ e2 s/ p' r. J9 X+ Y, y% D                return 1;
' s* l: q, U3 g            case REQ_SET_CONFIGURATION:. V* L5 Y! V( ~. U; R4 }
                if(ep0_std_req->bmRequestType!=0x00)% \3 v& t) O5 P+ q* J, T- c' i9 K
                    return 0;
9 C  Y0 l! p. r! p- g8 u                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
7 v+ {" _7 D$ e                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
- D3 N1 `/ n" }% f( x                USB_PMA[1]=0;   // Zero DATA% @# I5 i3 m7 h9 B  ]8 H! i
                ep0_state=4;    // No DATA phase
* @- Q9 u* k4 K1 y3 _- ^7 p                return 1;; g: ^  c: C8 D; g4 @* C5 l8 R
            default: return 0;3 d* W  S4 z' c8 z% g& E
        }# S' k* _# z( D0 d6 v
    }
5 c2 W8 W( a, V+ i$ p}! O5 ]- N) z9 z, L- _
8 @2 l6 L' E9 c

  L& X7 Y9 i) \3 _1 d3 _% A  z5 C+ F: p- S* E
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
3 t, c. a. K! U" W$ e4 R2 \char descriptor_service(void)
. _7 @+ X+ D  D  t& ]{
5 \, B  |7 k% M3 Y7 f# F    switch((ep0_std_req->wValue)>>8)& \# P3 u; U6 [) u& P, f! ^8 m6 k. ^
    {" r1 ?2 g, ^! O+ V
        case DESC_TYPE_DEVICE:+ {/ z* G+ E* l2 M" u  o
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));
! j" c8 a" @( `+ Q7 F6 G- W            break;# Z( x  S# F. s' D
        case DESC_TYPE_CONFIG:1 u" Y* t9 s; J0 |8 q
            if(sizeof(ConfigDescData)>ep0_std_req->wLength)
6 p) |5 y, s( S+ a8 F- }" y                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
7 \! \- f. i* _, l            else+ c- }% f% n8 e. g3 \
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));, N* ?9 X* Z5 p4 G
            break;$ M  B9 V" n- @- b: j
        case DESC_TYPE_STRING:
# \) J6 E% r- T; H/ O: H9 c            switch(ep0_std_req->wValue &0xff)" e" F) I$ v: u& M  E8 f# |! }3 i
            {; c5 ^$ }, X, i' a& e8 n9 a
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));) q8 x" ?& g3 @- v
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));. G% D8 ]' L. ^1 O
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));8 L- D8 H9 D, ^7 A/ d, P3 D
                default: return 0;
- J: U9 e, @% \0 [3 B. v" R            }
" Y0 |# H9 U. T$ w8 t2 [            break;
" Q9 ^+ {( h) |        case REPORT_DESC_TYPE:# c6 s1 ]. s% m$ d
            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
) ?+ w5 H  t/ \* O+ n7 j        default:) Q6 ^$ i' C) q
            return 0;& G& c3 t8 a9 J0 V
    }
! J. H$ \& H2 Q}# I& ~2 b! `6 z2 E0 }
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms." Z: @1 o, u$ {( d5 g
void TIM6_DAC_IRQHandler(void)
/ F) |" c+ U! n% k9 p{
0 z$ L4 p) D' S- k    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
9 W) Z6 o1 Y( d: e2 n( D0 O+ \5 S5 C" n* N- L! W

3 ~& l/ `5 Y" ^& R. @1 O    TIM6->SR &= ~TIM_SR_UIF;! f' P+ r4 h- m( P$ G3 {; X: A
    prev_key_state[scan_row]=key_state[scan_row];
* t; k; N' b- n. U+ i: B9 R7 Z  q9 }( ?    key_state[scan_row]= *PA_IDR;   // update key states
% b) L+ p( S2 s2 H    switch(scan_row)
6 y) b2 ^/ d, u5 z, O; K3 l' B5 a    {
  x9 D$ C; n/ w" t0 Q# Z        case 13: // next row PB147 j, V7 f) P; h' Y7 J: T
                GPIOC->MODER = GPIOC_DEFAULT;
9 K4 n5 \. w( l8 @' l- Q" q2 b                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
" _. D+ t; S2 R                break;
- w( V  E! z5 P8 E& t9 l! y        case  0: // next row PB15
3 D9 c( S  o, I3 Q                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;+ v  ^; ?* W  d
                break;: J& y3 v) \& a9 P+ I- T+ Q
        case  1: // next row PB3
9 _" u4 Z0 k+ u9 B: H0 u5 U                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;' o4 r1 T/ p" D: y' }" B
                break;
0 r! q8 J  R, ?% P0 t5 B        case  2: // next row PB4% \0 P0 Z* g  I1 ]
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
: F8 I8 W; u2 f9 F                break;
* ~. G) S  k0 |, _; r  N        case  3: // next row PB5
$ U+ J8 `9 Y, f( k2 j                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;
$ `+ {3 S" j- K" P3 j8 [/ K# D                break;
/ j' J" }, D2 S& p7 C        case  4: // next row PB6
. H3 U& p' g' F                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;( }: s: `( a& V) H
                break;8 i9 [# W2 V* d  z
        case  5: // next row PB7
& U% n8 D+ F, i  f5 b                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
. v1 n& A2 _% k( k  ~0 P                break;& O( @0 y+ G6 N' b' p7 b8 [
        case  6: // next row PB87 Y( e% M; @" q0 [. o  e+ p0 C/ @
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
/ X$ r4 M5 |+ U  K! @                break;
! q% G- y" h. E# o        case  7: // next row PB9
& J! p3 m% Z2 e" e                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;; T9 |+ ?& \4 v, U5 @4 ~/ K, Y
                break;
- G$ V. `! j1 ~+ ^; a% e: N8 i$ U  ~2 B        case  8: // next row PA88 o3 b2 m" W, R( B3 a, S! Y8 C
                GPIOB->MODER = GPIOB_DEFAULT;
' z0 k0 M  `8 ^2 ~; b- s+ q                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;
6 n! H6 ]6 i% |* z                break;
8 D+ r+ [3 ~6 h, E        case  9: // next row PA9
/ H9 Y) l# _# t( M- \: _, N  O                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;! ]2 A+ L: @9 C& \
                break;! y% S9 H: ~$ r1 S% Q
        case 10: // next row PA106 \# F# J' W7 E
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
; `. A; b7 M3 Q( ]                break;1 D- g4 ?& o! B" Q+ q
        case 11: // next row PA15
/ ]+ K9 n' C/ E8 c1 M. T/ w2 j8 e                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;3 P/ b7 S! X. O8 X$ R! A# S
                break;
/ v- B5 B6 Y2 m$ D        case 12: // next row PC13/ o0 G3 Y0 D( j. P6 `! ]; @5 a
                GPIOA->MODER = GPIOA_DEFAULT;$ p" C/ O% C( J8 c2 T
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;/ k- Y& Z& B+ u% ]3 K. |
                break;
( O' ~) q2 f( }* Z1 `    }
& A  w2 r5 c- V' I/ X! g    if(scan_row<13)
0 \0 f6 q5 p  V- `6 x/ K- o/ g        scan_row++;" v8 p; {3 Q( \& x* u2 G
    else$ j# c( o) w* E9 r4 W( ?/ Z4 g9 D
        scan_row=0;
6 d: D/ ~, ~/ Z) z  S}3 F* ^' s/ ?# O3 I8 Z. C: U

0 ^5 `2 G8 @& _
+ V' E- ^  u. l* m) L
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
0 F# G- o. f, s; A7 X6 V. j/ ?# O$ @3 f, R6 ^. Z; m
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。' M# E. P$ ?4 d
2 n2 }2 S% F. P
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
8 c4 w6 r: @2 B7 _( q; {5 P& N* ]

' y2 y% u) j; V) T. R9 S4 V/ G7 z3 W: @9 m' S1 k- {& u
const char hid_keymap_qwerty[14][8]={) T' j! w8 D! _8 r3 `+ Q9 b
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
$ n4 c, `1 i! _- C' ?3 c3 B3 t    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
  p' Z! d* P# p! p3 T    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},/ m6 d0 ~3 j# u4 A
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},5 x! C/ L1 h7 ]' m/ H( r
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},1 j; a" s) [* s, S% P" F( f
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},. D% A! t& c3 e' |; N8 E# b
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
0 ]- w* H% V) {    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1}," u$ p. i: Q4 W
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
& N5 [7 L5 t$ X  V/ s$ o    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
& Q" e. l: l5 ~: c( W    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},  H# F# e- S7 z5 I4 D; t
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
" F/ I" x; l) x7 a6 u6 O    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
+ s2 S4 u  S' O2 F    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
( v( \7 [' J! _9 _};
( p( Y# [5 C7 H9 U+ f2 U) _
* {& o2 o' T) `* I7 Z5 e" Q4 u

* o; R2 _  [4 q; p; X8 Uconst char hid_keymap_dvorak[14][8]={
( t+ R* L% O5 |# n. F4 D' t    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
) S4 ~. ~7 u& y, X+ ?& q( c    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
1 ~+ b7 u) h" C1 i    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
( m: p. p/ g( j; r! s/ B+ E/ e    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},( x% W- A0 y2 {; h( `7 J& E
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
6 Q/ P" m: b- U9 T6 r# c; s    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},- ~* G4 Z, Q, x; ?" j4 O4 h# j
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},' @. q  \& C" d" w& G3 k
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
$ @# l4 v" z" n( H: \- I3 ]    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},2 i$ g6 F" f# ]
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},, |. ^8 r/ D& h5 }0 L9 N4 I4 B/ f7 k( J
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
+ h2 }$ @+ v! @3 C$ g% `, l    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
, _- t! Z7 O% E7 f* A8 X    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
" E8 R! Q  Y2 `) j    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
* S* ^7 f5 [4 f};8 t. f% h+ e0 _6 o
) G2 r' X5 Y- Z+ b) W/ _
0 r6 Y% f6 |( c5 r% j" ?
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。4 q2 L5 i& y, q! U
6 K4 X" h- Q( q0 U% a2 _
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
& T3 {; J$ x* F7 O# m( l* E) `" g

8 |. k. l1 s/ H7 H  U, {8 m  ]& ^void update_key_matrix(char row, char col, char onoff); M  C- e" \* g* w' |- H
{
* j: \# _  v2 w% \    static uint16_t hid_report[4]={0,0,0,0};, l  x! ]; L: l; z+ M
    static char (*hid_keymap)[8]=hid_keymap_dvorak;3 V9 x; f7 @6 A
4 r$ E# T6 T& j; i) \3 g6 b
; r' i3 e. F/ |% O( T8 n
    unsigned char key=hid_keymap[row][col];
0 o. S- d/ m8 Y! d! [0 H9 t    unsigned char *report =(unsigned char *)hid_report;
3 M5 d7 _+ W. _    char i;
# L* k( |# X5 A; H/ y9 ?. u$ Z( v; Z% U8 |( s2 T' i

6 \! Q7 B( m2 _    if(key==HK_MODE)
& i; h  l# ~# k7 [    {* {% A" c% o( }/ J" D) J6 o
        if(!onoff)
9 t" E2 `, G0 u) S) N        {
7 }  V0 N# s- m# ~1 w: g0 t            if(hid_keymap==hid_keymap_dvorak)
& U7 A/ I- W; K, H3 L; A* `            {& v5 `8 J0 o8 z/ m( M
                hid_keymap=hid_keymap_qwerty;9 ], A7 L/ P& Y, R& h+ m9 L! V
                GPIOB->BSRR = (1<<2);
1 c$ m2 R( W7 i4 K$ r            }4 _3 p) k  R( S9 f  ?9 P
            else' C& x5 h6 e8 Y+ h& L+ |" o1 D1 F
            {
& A  M, Z, ~* e" @                hid_keymap=hid_keymap_dvorak;6 Y6 [  B+ ?. ~0 M: i( m) a
                GPIOB->BRR = (1<<2);
  A/ b' d0 ?1 D            }3 e0 I' I/ P4 V8 X
        }% D7 a3 T+ F8 }; \$ G) H
        return;
1 v" o7 A' d" ]    }$ l0 `4 b+ G& T. U7 u9 T: M

6 P) X% l6 {$ g8 \7 f" L- k
: l2 v2 d; V4 O0 i2 l$ q
    if(key>=0x80)   // Alt, Ctrl, Shift$ b+ `; Q( I. `/ C$ W; S2 ]
    {: n, A  I6 S6 B* A+ z; u: T
        uint8_t bitset = 1<<(key&7);9 F) L8 n& Y3 R+ j8 Y% O
        if(onoff)   // non-zero is key up; X( y4 j2 P( L- I$ ^  Z. d( @
            report[0] &= (~bitset);
4 |9 B9 R- }$ f# x0 [8 o        else
. g  ^- N! g( \: g            report[0] |= bitset;
; ]) Z1 \  t# m  e& `    }, L4 v$ Y) N" w' ~" C) l1 C
    else3 t4 N1 c. P$ b
    {
. r$ T- X1 J1 O) R/ p+ V( C0 k0 l        if(onoff)   // non-zero is key up
4 X" |6 w, |* U+ h, `5 o4 ?! S        {) f8 ^! A* l( b) X
            for(i=2;i<8;i++)
$ J& V/ U2 V) X0 O" M* {4 x            {
2 w* k5 _' q4 K/ O4 X1 [0 |& a                if(report==key)5 a2 O3 @8 U/ T
                {8 }6 Y* ^% V( M- Z. \
                    report=0;# P9 b% h& T# D
                    break;
( Y' {8 g: c' |0 z) D/ }/ _3 s" y- D3 _                }
0 @. z9 r; S5 H# [, d* F* [            }8 t& x  Y: C, K' t& I; ?+ P9 a
        }
: u. s. [9 h5 f7 D+ `: x2 H0 p        else
. b$ @: o% w" e3 F        {
6 }& H+ |& j0 [+ Q" T$ R            for(i=2;i<8;i++)
* ~% L% q; \2 q6 V: i5 A: S0 X+ \/ R: ^8 `            {
  p, w& s9 e+ s# J! j" k. ]                if(report==key)
% \) i2 R* v* e+ A                    break;: k5 N0 L1 D1 I7 q0 Z
                if(report==0)
+ x6 M( l! D3 X' o0 \! {: R                {7 Y" ~; z3 q, W6 R' d9 F, u+ m4 K
                    report=key;
0 x0 g; t9 r4 z3 \                    break;
/ J! o5 R- R; y8 Z, q2 R3 ]/ T                }
: g! z6 d! w: U2 g, w            }/ K6 E( ]8 M& V8 m
        }$ \) E/ H$ d' r; f( d
    }
$ I! W3 v4 a; r; ]+ y% @    for(i=0;i<4;i++)+ |3 g) e4 q1 S1 A+ T
        USB_PMA[192+i]=hid_report;) w, }2 q- T  h
    USB_PMA[5]=8;   //COUNT1_TX
# ^% \5 K! x& K9 c  v8 a  \    if(ep1_wait==0)
5 U: p, @5 q% T! N% X4 Z( K- m    {
/ N8 D) @! I8 L2 z        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
% q" C( v$ `: A5 q8 ?        ep1_wait=1;- Y5 \. }6 q, @- a4 a
    }
9 k6 v" E4 Z5 ]3 o}  [( x" Q5 R: ?  ^! ~
! U* Q. w& V, S7 ]7 V% _9 I, n

, f, x# H" I! G/ ?$ F7 \- J完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。2 |2 B# f& i+ Q$ B
keyboard.zip (8.7 KB, 下载次数: 2598)
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。3 C! u  x0 q: a. E' N; F
回复 支持 反对

使用道具 举报

8

主题

149

帖子

208

积分

中级会员

Rank: 3Rank: 3

积分
208

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘2 i! s/ v1 `6 B& ^2 C
不过楼主也很厉害!
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
3 q* Y0 _; ]& @, ~2 }刚开始我以为要把打字机改造成电脑键盘
  ?* t  W7 H8 ~/ s% E- G不过楼主也很厉害!
+ S( v  }; a& W& E& }
哈哈  是有点像奥
回复 支持 反对

使用道具 举报

0

主题

3

帖子

4

积分

新手上路

Rank: 1

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害/ Q- x# ^. l: C5 |+ {) b
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2017-8-24 10:47 , Processed in 0.170138 second(s), 28 queries .

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

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