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

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

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

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

[复制链接]

23

主题

62

帖子

266

积分

vip

Rank: 9Rank: 9Rank: 9

积分
266
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
" w0 P8 R/ {2 \; @4 c& Z, y2 \
, }5 q( K6 x. `http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D16 S4 l- u1 D3 T, b' P
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。& M! _. Q% _& g' f# _
1 Z8 L2 `: q6 R3 k
在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。
- I" H: c4 i" u. N0 m  @
  j9 p4 ~1 Z8 i" d% x) P/ J9 d0 o
235140i3a36qivqzuvmt5q.jpg.thumb.jpg
/ M8 D, r; F8 k- n3 |& m7 D- i
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。
6 E) }# Z# r2 D' X* X* c* ]/ } 001734klbyoluenuwz4h4b.png.thumb.jpg ( T' {4 A! L( D7 J1 ^
为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:
: N; I* z# f$ W 003625r2agx2f5v922cf2f.png.thumb.jpg 1 O8 `9 p4 v) k
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.
. }) T- w% f* b8 h9 B, |* LDvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:

5 n( Y% z1 x5 Y 005836yvs0wvovwsssgd3o.png.thumb.jpg
: u8 b1 m% h3 mDvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。# F6 G! K( D6 l; R

4 H1 g+ Z4 ^4 R4 }) p我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
; m0 M+ v$ Y. l* T
- J9 N" Y. p/ F/ f$ Z' u, w4 H到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
6 U- D, w6 K$ u7 n" c% K- ]0 a6 p# r7 k, u. o* k2 F9 Z
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:

; U- v+ q/ B* P3 X* s- T* ?. W 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
1 S5 R6 f2 r& o----------------------------------------------------  分割线 ----------------------------------------------------------$ ?0 a! S% b8 y* R
* \/ P6 F! C3 d  |& I: [) O3 X8 i4 k
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
- }' e) i5 L6 X' K+ A3 N
020011osionbunl4ui44vi.jpg.thumb.jpg
+ j8 J  N: g8 }' L9 c# Y% I5 v: C* u& B轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
# {- a& P$ \0 x. u- m4 P 020017j8ycmnv7788bqv52.jpg.thumb.jpg - _; ]2 [' ?  K3 ~- T# d; W) }9 x
特写,80C49
" D0 Z* ]: I6 R4 z# W2 c 021040oujzuvtut6iujtvz.jpg.thumb.jpg 2 ^5 q# d2 K* P* p" z$ y
LED部分,使用了一片D触发器锁存指示灯状态.
4 k# f' s1 q: d4 C- ]# _ 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
$ V1 m% N9 j2 S$ o* m! A$ }暴力破坏,将80C49拆掉
& L( f+ [: w1 r( K& G 021113e48qq98vyohvhzzh.jpg.thumb.jpg
* v% t- l6 d2 E2 v3 q拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。3 x7 Y2 R8 r4 `
021125nc9az6dj33rlds2r.jpg.thumb.jpg 3 @8 J: m' o5 D4 _0 F! i
焊好元件后的板子,准备替换80C49
* Q6 X' T  z$ b' r5 [" A9 Z% _ 021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
- v6 c7 u3 p" |( L用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。: W, T) _: X5 l: v# u  M9 F
021104shifhnrqbr3o5nlo.jpg.thumb.jpg 4 |) }7 @  W' n" f
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.3 D( u! U" L) l2 q
022003ym1p9u4ug40280uu.jpg.thumb.jpg 2 ]0 r- r8 G) }! C: k# z
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。
- B2 r/ M' L4 b  W$ V 023313kt141q9qajtol7ma.jpg.thumb.jpg
# y) X3 S0 h0 \- b我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
$ T% Z) t  j' O2 S0 d/ U6 J+ p 023322nt7l5xb3ltttkltt.jpg.thumb.jpg 5 C4 k8 @8 l" k  j
主键区键帽就位0 B4 v1 T1 B4 b- x! D
023331hin88e8wkrwzwikx.jpg.thumb.jpg 4 w% r5 Q4 v, M" n' C7 ]& w
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
, R  l0 w) C2 m+ y( z( w2 G( j; } 023336wjzlgopugg1jyy79.jpg.thumb.jpg
* w& X  f( C# Z最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。6 N: t- \  S# M( w1 r
023341sffu4j3g2323h6fl.jpg.thumb.jpg 3 {! G3 l5 h& S2 P3 e

$ Z7 e+ Z# g5 `5 B) d3 ~, j----------------------------------------------------- 分割线 --------------------------------------------------
% i5 c0 g1 b0 M9 o5 b; H& h
1 [; ]  }1 @( `
# s) q5 ^) ?2 F5 Y% ?( S
% n; t# I! E( {0 A( U

- n6 M5 I# i$ b+ w; l$ S% _. `# f, N& e" h; W4 A5 G3 b

6 I/ c8 B1 E0 E2 R5 z3 M& }3 N/ \# |' O
3 M. o  x5 g6 B8 a5 Z  E2 \

; }; I- [& E/ f
! q* i+ \% I+ C5 W! l; g7 E
  \( g& f, b' `2 t
回复

使用道具 举报

23

主题

62

帖子

266

积分

vip

Rank: 9Rank: 9Rank: 9

积分
266
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
/ ?/ K% |8 K0 a4 F  Q1 @' f3 l$ s; {80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
5 W! K! h7 S. c" f5 N. U 104025nzibm2rmiomhyirm.png.thumb.jpg
. m0 \8 U: M& h; r7 e

1 ]: F  }5 u5 d  m2 K其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
6 M9 r5 G" ?! \5 h- _' P+ j 105004zkrez5houvkkznko.jpg.thumb.jpg ! A0 y% G& M# X1 t
  |+ l1 f5 H# p1 _7 N# c( i" B2 g
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。% k' A1 {: u3 X7 Z) j8 a4 L7 o% P

; o; r$ T+ N9 z- I% I这是我设计的电路图:/ ?2 [. e7 u6 M  ~7 w
110344ej2z2oo2rflo7oe7.png.thumb.jpg * X  k- s+ C( `
- r6 ~% R, Z% c0 L& R" m/ f0 S5 J
PCB Layout:
& |- t! D- a+ ]/ k 110847jjbjvt34vwt3v5bb.png.thumb.jpg   \# t+ _) n6 V. h. k7 P" v

/ u( S% H6 T5 m7 H; ~) ?6 z不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. / t8 ^/ }' K+ ]3 ~
3 n8 r5 Z, z) L% o& u. x

& z( k! K: Q. s7 T! N# I
回复 支持 反对

使用道具 举报

23

主题

62

帖子

266

积分

vip

Rank: 9Rank: 9Rank: 9

积分
266
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑
3 |# n& I% {+ I1 `$ a: E+ @$ [0 ?* s+ N, o
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
9 k: d. X: I7 C
% a; m& r. R+ p总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:/ l  G: K( A9 q2 W
113818pmrfsb6z0byt6t06.png.thumb.jpg 7 Y/ h8 a9 L+ {; }- ?; `
  P) a: m9 n5 n  v! Q
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
" ?! v1 [5 g3 C( G* n7 f
$ a- D1 d7 a! y4 i/ LUSB的中断ISR,bare metal哦9 n6 Z! o' _! Q- W0 Z1 ^, }
# i% W6 i0 q  @1 ?$ j- X
void USB_IRQHandler(void)
- X4 `  V2 V0 x3 N+ v% J{9 Y2 \# Q4 J/ a1 d5 K: r: e9 M( o
    if(USB->ISTR & USB_ISTR_CTR)/ U" W; m7 S* B; \; v0 m
    {; H3 V; A% j2 J! {- M
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
+ m* T. M/ B' D: v- J        {) n9 o0 G& B  f2 U
            switch(ep0_state)
0 G  w$ |  O7 d% c; [8 C            {
$ B6 X3 q' h6 `# t. m+ C0 m6 N                case 0: ep0_state |= 0x80;  _: g! f5 B9 d0 v! ^9 X  Y
                        break;; `$ \4 Q- w0 T; m: |9 ^: G
                case 1: if(USB->EP0R & USB_EP_CTR_TX)
' v' c1 M5 I# G- J+ Q1 w/ c                        {. H# I& ?1 v1 u
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;) X5 ?* _' D) ^% P, `
                            ep0_state=3;4 s$ |9 q1 S0 W# m: c
                            return;
/ Q% u9 g( v( I8 b                        }0 L6 }# `) h8 p
                        else
& E7 V1 h5 @% ~+ n; b$ [9 e% x* z% G                            ep0_state=0;
9 w" E1 j0 C6 n2 V- \1 p                        break;; L! x# d' d9 T( ]8 t0 L
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
3 i# Q; _$ n; L$ V! l, ^                            ep0_state |= 0x80;: V2 Q  T" I: ^+ i0 ^
                        else
- ]$ X2 E+ ]' f* ]4 @7 ~% x                            ep0_state=0;
$ _5 S  v; `/ z" X2 c/ O% I& C                        break;; }- {6 C' Z5 V1 W3 K  Z! h- H
                case 3: ep0_state=0;. o) Q) i$ t# t# n7 B' Z+ I
                        break;" E; l2 |5 n  F0 L% H" t
                case 4: ep0_state=0;! P9 w: G) i6 w& e- j7 \: o8 u, D
                        break;
- D8 |* T# R/ d; t! B                default:ep0_state=0;) b$ k/ B' B2 J* P8 @" S
                        break;
% U1 X7 E  ~/ a# D  j3 U5 J% H- M% N$ }            }
2 f) u: V1 s8 ]0 h6 @8 ^- s            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag- B0 i6 v9 f& U5 ^
            return;* e" m9 Z7 a% n
        }
; f, L! o8 [- g8 V( y9 Z! U        else    // EP_ID can be 1
1 `" v! L) e5 b8 J2 N. Q$ p( c        {$ E4 E- ?/ k! D
            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag& M4 T- b3 W" O6 s2 T% P( R/ t) G
            ep1_wait=0;7 R$ G4 S+ e* M4 y- G
            return;. Z) `% {) n  p3 E
        }
9 j* H* x# x) x    }
; W* l, ^0 [0 t& {    if(USB->ISTR & USB_ISTR_PMAOVR)
2 u, c4 q; q7 u; N    {: y3 X( p8 H' D5 M" v' H
        USB->ISTR = ~USB_ISTR_PMAOVR;( R9 B% S7 y8 J
    }1 g$ F) r% `% A: d1 {
    if(USB->ISTR & USB_ISTR_ERR)- }2 e5 d& V3 E6 U/ w) i0 q
    {/ z) O  d! a9 R
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
$ G. r2 l& W6 q. M0 c    }
2 C0 L8 m! z( {    if(USB->ISTR & USB_ISTR_WKUP)
- w: ?. |6 @5 j7 ?$ D/ s    {
% O/ i1 G) @+ n0 }7 l        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend$ e% o) y: s5 @" a7 \% `
        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear9 Q$ z9 k+ P! y4 P
    }
# m/ w4 ?3 @7 b! p, Y) Q    if(USB->ISTR & USB_ISTR_SUSP)2 G/ G" a2 J7 {/ y+ P* v
    {
2 ^! l6 B; U/ P- x' d+ ?6 _0 g        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend' N# x9 f# L- N6 t0 m* v) C3 A8 f+ M
        USB->CNTR |= USB_CNTR_LPMODE;   // low power
) ]. L3 Z8 F5 a* v        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear" z$ D4 J8 h& ]! h
    }  n  |7 {; p  }' E( D6 I
    if(USB->ISTR & USB_ISTR_RESET)
& q; w5 E4 n0 g8 K9 f* X0 y. Y    {
( z; F# D  V5 M4 R0 o% K7 w5 n2 N        USB->BTABLE = 0;    // buffer table at bottom of PMA
% q2 ?+ g4 L8 N; @+ P/ ?: R/ a3 ?6 {        USB_PMA[0]=128; //ADDR0_TX3 l+ o; R1 W$ Y
        USB_PMA[1]=0;   //COUNT0_TX
/ X0 Q) }% v  `$ M' o: d" m        USB_PMA[2]=256; //ADDR0_RX  H( b3 n) ]; L* \1 e9 y
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
) Z- ^* u# g- T0 p  z        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
5 F" o, ^" A+ X; D2 O: W: _+ \1 @        ep0_state=0;
' f$ a0 }* [# f2 y6 S" l        USB_PMA[4]=384; //ADDR1_TX
0 K4 @) y, @* \+ [5 ^/ I        USB_PMA[5]=0;   //COUNT1_TX
) ^. ^1 n9 ?7 [1 F4 Q5 N$ [; T( p        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type+ @* O* a! B1 x6 X3 ]3 \9 f
        ep1_wait=0;. k1 c+ \  e2 x2 S
        USB->DADDR = USB_DADDR_EF;      // enable function
* x7 j& ?  F& w9 h) v: w        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
  V7 s  D$ {3 q& U4 Z3 M/ u    }
0 J! W: ^- a' J0 [  t. D4 J    if(USB->ISTR & USB_ISTR_SOF). o) l& }7 {% U' T* P. b- d0 l
    {
* O' [9 p9 F! J        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear, i/ A+ {/ y% m5 c' c
    }* N! L) {* s0 D3 ?1 B9 f7 [" o: T
}6 p+ r8 ]0 o3 O! B9 }7 a
& y" A5 \7 ?; @. j# {6 f, x
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。) J, f; R# S8 z/ c/ w+ Q
: n% d2 `2 I+ [9 g
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
3 t# @* O- }0 u9 K$ n( z! e
. F% Z* W# x) G% H% p: P' D# `: U+ z1 b
    while(1)2 r: F. K5 |7 z0 l5 \: }! ~* l
    {7 M: n4 O; q' S- b7 U! w3 m
        static char row=0;; c+ C, s" m3 G1 G# `8 M+ Z5 f, S
        __WFI();
/ `6 j. k5 X4 e' {5 J        if(ep0_state & 0x80)    // request data processing, Y1 i+ i" n! C/ r& n* t& _0 P6 i  l
        {
# m8 s% ?; L) H            if(ep0_state==0x80) // SETUP phase
+ R1 `7 O% H6 J# c: B( b            {
$ z- K8 O) t* [                if(!setup_packet_service())/ ~+ L$ k# P6 C3 I7 V  G. y
                {9 t  y+ ~' I7 U3 l7 G
                    ep0_state=0;
+ x- N! [" F8 i3 j( O% o: g                    // not supported8 k2 k% C' }& o) Q
                }
% H6 X$ g0 H+ `! Y- W9 @: ~                // ep0_state should be set to 1 or 2, if processed
2 j5 p  Q/ x6 i. p: X4 ~# }, L7 t            }
. }& y: F: Y. T) k2 |! d            else    // OUT phase; g3 H# M3 f; Z. z- {  F8 n2 p
            {% \- }8 {- p' y
                // process data# }0 `2 c) p1 }1 a$ p) G" O( @
                show_LED(*(uint8_t *)(USB_PMA+128));
3 }) m2 r& P/ w& G                ep0_state=4;
9 L8 R" _3 C) C/ D                USB_PMA[1]=0;       // Zero length DATA0
3 ~% ?/ v2 G( g                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
9 l: k$ q" f& J; ^, \1 N0 Z& D            }
" Z( p. d' j8 _: ?        }, o1 H; A- {9 x0 }2 |( I% i* R
        else- ~9 m6 k& n7 A0 V2 _, ~0 P! S- Q2 t
        {
8 I& t2 `- j" k            if(usb_address && ep0_state==0)
) F/ }3 U& V. I  C            {. d! x0 e$ i; m5 A6 l( S. P  m
                USB->DADDR = USB_DADDR_EF|usb_address;% H2 `5 r: I, D4 ~9 m0 @$ r$ i# p8 D+ f
                usb_address=0;
# m1 b6 \4 T' ^- |            }' \! p3 V" x) d6 u% N
        }9 N. ^: W: ^9 c; I# @$ x
        if(row!=scan_row)   // new scan line
7 T, ~1 U. f9 K% Y        {
% \! q  A) J. e$ a- f& _            if(key_state[row]!=prev_key_state[row])
5 i: L+ z8 ?/ Z' }( n9 U            {7 ~( C+ W* V" Z+ t0 V2 p6 X5 o1 I
                uint8_t test=0x80;3 L) O5 e" ~* \9 o$ f* a
                uint8_t diff=key_state[row]^prev_key_state[row];/ |7 U- n3 Q( z
                for(i=0;i<8;i++)7 H- M) B6 f$ H( ~$ V& o
                {. j, u2 s4 [' {& G8 |
                    if(diff & test)
; e7 f- @3 S  y                        update_key_matrix(row,i,key_state[row]&test);
& E5 _2 H  y& R; P7 w9 X- [                    test>>=1;, E+ |6 O( x& Q# b
                }
- F' w! Z& [" c; I            }/ O, T* [" c3 E3 [0 t
            row=scan_row;
- ]4 R! h4 ^( R: J9 Y2 h        }- X% Y5 P% c. E
    }+ j2 A4 F  n5 u. X* r" w  k& p
. o2 _" Q8 B1 R1 Z- v" I7 [

9 B0 s  o& b, w( Q$ [EP0的控制传输,把用到的请求处理一下) g0 }  P. G# f7 y! Y) }
char setup_packet_service(void)
7 \9 V8 L1 N- H{
6 o2 b1 g$ ?2 o8 ~1 J5 F( x    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
2 \- k* i1 R' q) P    {
" h% z7 r, g; F( `  L        switch(ep0_std_req->bRequest)# u7 O2 w; _/ h0 u+ p
        {
; c: e) Q2 g! d4 a            case REQ_GET_REPORT: break;$ w- Q& z9 x4 T
            case REQ_GET_IDLE:
& J) T. X( j; j; H5 {( W; ?                USB_PMA[64]=0xfa;   // return 1 byte
. v! d. C! q) ~( D/ K: L/ {                USB_PMA[1]=1;8 n; ^' ~' i& L* p* W3 z  ~( m' ~
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
" e: l+ g' l" `- v6 s                ep0_state=1;
  o3 g1 Q  \  H$ j                return 1;; D) Y1 @$ e- h& p, I" ^. I
                break;2 K5 _; [) |4 g7 b2 k, V4 J
            case REQ_SET_REPORT:, G' X* \# o5 W3 o' O
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;# p: n% L( n+ E  w5 v
                ep0_state=2;! m% G7 U* n/ B1 P& E" v) |: z; ~
                return 1;1 l6 G$ ~" |1 M. \  J
                break;" t: y) ~0 |# w& b
            case REQ_SET_IDLE:
% p. i( D( Q' G                USB_PMA[1]=0;   // Zero DATA! f5 \5 z) n# u) w  p
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;% V6 M' t9 j% K4 J, _
                ep0_state=4;
0 r, l7 u2 h) ]' Y  i. q1 c4 Y% j                return 1;
  A- J$ `0 s' j$ p; x                break;
+ v% F3 ]1 X5 P& H        }0 c9 ^& `6 G3 R2 m# V  o3 Z
        return 0;5 t% M3 B! r0 q5 P
    }
$ I& L& O! D/ E    else    // standard
+ R! `5 |  {4 x) z( U. [    {
9 b( ]* k9 h* z4 Q3 K/ z- x# E/ z0 Z! n        switch(ep0_std_req->bRequest), ?; q- F" B1 d8 \- ^
        {; a0 w& N+ V& S- w3 N7 b. V
            case REQ_GET_DESCRIPTOR:
6 x: c0 V" G4 D3 [, p+ \9 |+ M! _+ A( q                return descriptor_service();1 E5 W0 W5 w8 z( b8 F
                break;, T' U* k0 d1 q5 ^2 F1 ^
            case REQ_SET_ADDRESS:
' q$ \. `# e1 \, I/ O& Y                if(ep0_std_req->bmRequestType!=0x00)9 `% |! G0 v7 m+ x  N
                    return 0;
9 v  G! N" D$ k. \                usb_address=ep0_std_req->wValue;5 M9 k4 {! l9 t% C( I( f! [) m
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
3 j, t3 F( m6 o1 L$ A; b2 g7 r                USB_PMA[1]=0;       // Zero length DATA0
- ~0 ?3 }# O+ ]- x                ep0_state=4;    // No Data phase
0 b; [& z; O6 D1 l9 F- E                return 1;! ?7 h. X- Y8 x  \+ e/ Q! P
            case REQ_SET_CONFIGURATION:
8 h( a7 P1 o) N# H* j                if(ep0_std_req->bmRequestType!=0x00)2 m8 H! `7 r1 j
                    return 0;
; t& l/ y: ?& }0 j/ V4 A- k                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;( I  [, S6 S% G, Z$ o
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;# L1 |' E- J2 L2 w0 d8 h" ~
                USB_PMA[1]=0;   // Zero DATA
# ^- z$ H3 i6 K                ep0_state=4;    // No DATA phase
: f7 x2 ^' ]2 o# ^- n4 k( V7 H/ H                return 1;
; E; j: W* ]' }            default: return 0;9 b* D: P( o2 I1 [! I' v% [. e6 g- g
        }( k2 y. Y4 m7 O
    }6 l7 m, p- C# z3 n% e, `% x8 `! @' E
}
  i2 C: Q- Z- q* ^: q; K
3 O* W( c" N& ^. U3 v$ t6 L4 F
8 G7 ~5 {5 }# ]
0 @8 h$ N7 v  G0 Z, a4 h
回复 支持 反对

使用道具 举报

23

主题

62

帖子

266

积分

vip

Rank: 9Rank: 9Rank: 9

积分
266
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
# K& }+ V  S1 M4 zchar descriptor_service(void)
( m& p8 e! t' L, z& }  S: L{
9 A- R0 R. m6 ?, \  A% x7 `    switch((ep0_std_req->wValue)>>8)
  y  S4 O! K, W    {% `8 ~; A+ [5 C) H4 I+ ]  A5 P" ^
        case DESC_TYPE_DEVICE:; S4 g4 r' J% Z
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));/ Z. U0 M# B/ l6 y
            break;+ q  T6 Q3 n4 E: B
        case DESC_TYPE_CONFIG:
$ j$ ~# M9 E2 q            if(sizeof(ConfigDescData)>ep0_std_req->wLength)3 }  J5 o: Y) C& A. E
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
$ H0 _6 G9 h* }            else
5 I, j- j' v6 ^! ?& ]9 R                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));4 }  J$ j5 L8 m8 P% e' d
            break;5 [6 ~5 }' ~/ a7 G! k8 V# C
        case DESC_TYPE_STRING:2 D( Q1 P' P# D0 p% c/ o3 Z0 W: e
            switch(ep0_std_req->wValue &0xff), b* @6 M2 K" [4 l3 s7 J
            {) O: T: R' u5 _( [- g( d3 E
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));) l) m8 M7 |3 c3 _2 u4 b
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));3 G( [1 j0 k) N5 ?1 g0 P
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
' J+ Z8 J0 w" a! W+ y                default: return 0;
! I+ u( }& [9 U. Q/ Y- c; x2 X            }# f- g5 M5 Z' `
            break;
+ ?( Y) O7 G+ U5 A        case REPORT_DESC_TYPE:
7 z1 o! H: Y, J; b& T; e            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
: b7 V' t3 f2 S( O- w. n8 H( m        default:* k1 i- W7 W1 V& o# _
            return 0;
* x$ m8 L7 s; B: D5 \6 M    }
5 f! G/ U* y2 q}9 e1 }' l& H; g4 N  |4 s0 D
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.  X# a: E% l( _" A
void TIM6_DAC_IRQHandler(void)
9 j2 k# N# v1 |; O. l{
/ R( R" L; T$ |! b6 m/ Y! Q    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);$ _# f! h) ^! R# P  F, W3 G
. ^1 d* I- Z, r$ p
( V; C0 B/ w3 e. \( `) i; `
    TIM6->SR &= ~TIM_SR_UIF;
0 b% `+ ~2 S2 r' n3 }    prev_key_state[scan_row]=key_state[scan_row];
- X& V  `* c8 {- K2 A    key_state[scan_row]= *PA_IDR;   // update key states* c; E$ v5 ]) y$ j
    switch(scan_row)
0 F' @3 h% [. x0 a    {
" \; A7 T" h# o- e9 ?1 n# U        case 13: // next row PB144 e0 ]! C' p# `: S0 |' w7 [$ d
                GPIOC->MODER = GPIOC_DEFAULT;& o7 v: Q5 P$ b( `6 a3 B! ^+ f0 P
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
! t' S: g6 B6 w* h2 o                break;
3 _: r  }1 Y: ]3 H" ~1 W        case  0: // next row PB15
  C: s' Z8 F* W                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;1 C) Z  O" c) z, B! \
                break;8 ]' V" O' j2 C
        case  1: // next row PB3
/ h6 k% p+ [1 b( V8 \1 y                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;' t- c( _; \/ C$ {
                break;
4 B, i. W3 x/ q5 q        case  2: // next row PB4, m3 ?6 d) @* c  s6 ^0 e6 p1 o% n' e
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
' ^2 o8 V# t& _9 u0 [7 z                break;( _: H/ o4 t; }! b7 n' ?' L2 S
        case  3: // next row PB5
* o' S# {# n3 {* e6 s" U  ^                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;' P9 M& h# I3 T& ?
                break;+ Q% D- O& p1 Z
        case  4: // next row PB6. C5 F! f! I, I) {' F
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
; \; v1 q. |3 i9 f5 e- q                break;
* ~+ U, u0 G/ i# \        case  5: // next row PB7
3 V# ]0 k9 A5 Q1 `- T6 s# g) d! h                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
& H  e6 e5 l+ J# O' `5 u                break;
) z$ H9 m' h  e2 j' Y, Q        case  6: // next row PB8
, {* U- R4 f8 w& _! l2 F0 v                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;1 t) v- m% ^" J! K6 H3 }
                break;
# v9 D7 g& Y; S' M1 o' Y        case  7: // next row PB9. S7 K( i- G0 w; e6 n3 R- }# v4 [
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;
2 P- ~. X7 c0 n& S: D! G& C                break;4 c4 t6 }7 w& z; E
        case  8: // next row PA8/ \5 y0 f* Q# Z
                GPIOB->MODER = GPIOB_DEFAULT;
  S4 x4 [3 y( G* ~& ]! g; W( n                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;
* i, |! D( J& B1 G5 p( X                break;
. H& I1 ~, W- f6 {! S$ \% Z( J        case  9: // next row PA9
+ ]/ J! D, K6 u4 D  _5 R                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;5 T5 n: o3 A& V# q% R8 I$ |6 s! b
                break;
% X6 n# o2 v2 }' g5 t! l+ m7 K        case 10: // next row PA10
( i! P7 l( M' M) }$ m6 m( [& T                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
6 ?  t6 n& v) H  ?4 b                break;
4 t; s+ ?9 \8 r+ j9 [        case 11: // next row PA15% K8 ^" P( M! @! w6 T& I
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
' E! F. |" B' d                break;( f$ H: x2 q/ V. s
        case 12: // next row PC13; r) p4 `9 g  Y2 F5 t- T% s
                GPIOA->MODER = GPIOA_DEFAULT;. Z9 K7 s) Q. t. F/ {4 @" w
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;  U3 u& p  K- p
                break;
3 Y) f7 w4 Y, n+ d! I: l, w    }) k4 e7 r6 u7 ]7 \. A! n
    if(scan_row<13)4 P! j0 l2 X7 a' y- ]- g+ {5 C
        scan_row++;8 n0 N* \: L0 A- {, I! i9 i5 z- T
    else& Z' l; D+ ^7 o+ k0 ^
        scan_row=0;
, ]$ M& D# x- ~3 V$ e! R  J: I}0 V7 [& j  l3 p/ B
& o6 H5 r" X' r& x$ q" m2 ?  }

9 k6 W% D7 T& _- F* f
回复 支持 反对

使用道具 举报

23

主题

62

帖子

266

积分

vip

Rank: 9Rank: 9Rank: 9

积分
266
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 2 V3 i& T- ]) d+ \/ l
$ W6 ?( D3 g6 @5 }: G) m* A
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
0 Q% u$ |3 v6 j) U0 e0 i, `. k  @; r" G0 t) C4 A, w
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:

+ V1 J; _" \: G. ]6 m2 n9 _# C, p7 x1 H" u0 a* b5 _
* i# a# L" d2 t1 x2 [
const char hid_keymap_qwerty[14][8]={
- r! A5 p" L- j+ v; P    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},% ]: M3 q. z% n6 }$ B
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
2 u# x; j4 C* V: H    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},# L3 P, T, ~: `8 W( ^* ?  E# @# ~
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},% E) ?0 u& Q* ]; T- ^- U, k$ J7 X
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
- j3 ^& F% m# y/ w* Q4 z    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},3 `) y2 j' M! W' f! r
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},1 O6 D  P5 ^3 Q, m: k2 s
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
" _+ J. Y) I+ Y6 |' q/ N& q    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
) {$ \) ^1 i8 M4 q/ {    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
+ P0 z" H* W6 O6 |    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
, a# V  v/ ~  y+ J- C% W* D! m( V7 m    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},+ V2 Y9 S7 T" m2 _
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},) m7 u0 }7 G5 u& M
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}& g8 u, y; L4 ~- Z) M* \
};+ W* P+ }! q; C# A% Z5 [
( ]  Q* [% [' j# {

: _' U/ I' T  F2 D1 kconst char hid_keymap_dvorak[14][8]={
' f1 J2 r; a) g+ ?( y, M    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
3 K4 v- T: ~! i' R9 d6 q    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},. O+ r$ G$ o+ [) v
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
* p8 j$ E& {! Q9 P2 q5 H6 e  Y. x( Y    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
% n8 @0 ]+ \. I0 ]* H' i    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
' ~( C4 ~. g/ b4 ?) d    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},7 ~$ W6 O" L3 j
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
, L! S2 I7 b- K3 _+ j    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1}," \! j: ?1 v& Z
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},2 m, Z1 N- b' u8 x
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
9 g/ r2 _4 v: m/ l9 g    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},1 a$ s& L8 A% L- X+ t4 I, H
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},7 ?  K( i% O5 }8 |. U+ G: n
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},& ?( ~' E- k. u: E7 b
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
* z0 }1 N3 \, Q; X};2 V) S7 _3 V' [- x

8 p0 R4 K3 N" w5 _# J1 G, n
! u& A4 n7 k1 n6 e9 I6 }+ R/ M6 O上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
0 X. _: O) u& x* S  P  r3 e6 Y' u9 _% w5 ?  E
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
! a- \, p" b1 o; Q7 ?9 D9 ~2 e
" t; b6 u7 v# P' O
void update_key_matrix(char row, char col, char onoff)
3 `9 Z0 ~2 H5 b" B{! M2 C$ w9 N+ X% v$ t! i  z( S* x
    static uint16_t hid_report[4]={0,0,0,0};  ?/ B% `4 W7 h+ m( {4 {; b$ t  p
    static char (*hid_keymap)[8]=hid_keymap_dvorak;( Y& {, t+ W. s5 v) F* t

5 S- m1 }1 a1 M2 }9 a6 V( x

2 w3 ?- y& l7 X, r  I    unsigned char key=hid_keymap[row][col];
! X+ {! i: Y( K  i    unsigned char *report =(unsigned char *)hid_report;
9 B& H, `; \1 t7 M' C    char i;
) E9 `: S. i0 ^1 R( L! u. X* v0 P
, T6 m; B. C, L; ?5 d
    if(key==HK_MODE)
" R" [' h$ w5 m2 l    {: t9 ]6 S: ~0 B+ Z( S
        if(!onoff)# l  A" `- T7 X' M7 }8 }
        {. K3 N8 }4 R  C( }; m
            if(hid_keymap==hid_keymap_dvorak)
; g# _$ s! |% N            {
6 a( j6 t# N) _4 w0 N                hid_keymap=hid_keymap_qwerty;5 R2 m4 o* c* \1 o' u
                GPIOB->BSRR = (1<<2);
9 m6 H$ ]2 Q# f# w) e& E3 |            }
! Z- _2 ~, T1 M; W* @4 r/ i            else8 h2 ]8 z8 P0 A( c/ q9 B5 {  m' z4 X
            {9 O$ j2 g, e+ V
                hid_keymap=hid_keymap_dvorak;8 n& c3 J5 u' J. w$ [
                GPIOB->BRR = (1<<2);) A, P  V! q$ A8 _" [
            }' @9 T3 F7 L5 f
        }' v0 Y' s* A4 i( ?- r) ~
        return;
  P% r0 L" G( H3 w3 J; q    }
; g$ Q8 e: C5 C+ S% h& v8 v3 Q
) f) m# L+ g+ w) a0 A
+ |1 J: n3 ?/ _8 b% Z
    if(key>=0x80)   // Alt, Ctrl, Shift
6 u) M6 O* I3 M/ Y3 f    {6 U& J/ u7 ]0 U' E" U3 c' a
        uint8_t bitset = 1<<(key&7);. Q, E  }9 Z( D. Z# ~5 v4 c  n
        if(onoff)   // non-zero is key up
2 T( V+ o) o' Y. f+ I            report[0] &= (~bitset);
4 W( y: K* ]- q4 [3 R: c2 w9 Y: \2 d        else3 V' d9 P# Z7 w" A9 \
            report[0] |= bitset;
# P5 h' M# z' @( f8 j$ d$ {" q- O    }
  H; J2 v) J$ k3 }% n    else1 ?% c9 i9 R' k+ @( b
    {' y! h8 n' P1 s! D0 |
        if(onoff)   // non-zero is key up
  ^# b. L8 v! w        {
- D5 J+ I$ N$ o/ _/ Q! ^            for(i=2;i<8;i++)6 `0 G# g- L! c1 a0 f
            {
6 }- A" z- O2 r8 ~$ n( X5 u% `1 k                if(report==key)5 u- k2 W4 v8 s& k
                {
! h. q3 l# R" \8 l- b: t                    report=0;9 `- S, H3 B; M) ^% H; N7 |$ L
                    break;# t: v. `* _3 q, ]1 M) g  d
                }
+ ^* t) c3 s2 m, S            }
; C9 O- S) K" ]7 f2 t& _0 j% [        }; f' J8 \8 k1 n3 v; ?8 v
        else
3 C/ M! W9 t; ^( ~        {& o6 t1 G" v9 Z* }  H
            for(i=2;i<8;i++): B0 e" N' O4 h; U; [: ]4 t7 T" Z
            {' Y' K$ a: f+ j0 O
                if(report==key)! \& x" k/ \" u- F$ |
                    break;. V1 C9 z* L# m% x
                if(report==0)
. `+ f3 o+ y+ {* D% P; l                {
2 P+ T- I' g& s4 Q                    report=key;+ [) l$ ^, o% ^: g4 x
                    break;7 w+ d. S. q3 Q
                }
8 F; X7 w: I8 @% P; u9 A9 |            }: E7 a5 f( a" A, ~+ m
        }
# L7 m0 T' G! R7 E    }
( d# E9 Z% b$ i    for(i=0;i<4;i++)
; C! `1 T) @" o) A2 h( Y1 z' I        USB_PMA[192+i]=hid_report;+ n3 u7 {+ P6 ]8 K2 a
    USB_PMA[5]=8;   //COUNT1_TX$ F& A( H1 h+ B4 e$ D+ e
    if(ep1_wait==0)
( n  d9 H& T7 |9 f5 o; ~& c$ N* ?" }, k+ P    {
2 h5 a8 r1 w' _! p$ U; g        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;+ Z6 R' _' J& G9 q  \1 w) A
        ep1_wait=1;' o( \( {% ?) j
    }* N8 u* w; [& I8 |) k
}
! {4 E2 {  W+ O# Z, ]( B7 i# S- j
/ r# z* r/ v( @; J6 D* M8 H
( o9 I# a! Q) p5 B( P* ?) |0 ?完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
( ?4 a+ ~8 ^1 V, ~: v. y keyboard.zip (8.7 KB, 下载次数: 1993)
回复 支持 反对

使用道具 举报

23

主题

62

帖子

266

积分

vip

Rank: 9Rank: 9Rank: 9

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

使用道具 举报

8

主题

147

帖子

204

积分

中级会员

Rank: 3Rank: 3

积分
204

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
$ o# K, N6 H6 i3 \8 x% T不过楼主也很厉害!
回复 支持 反对

使用道具 举报

23

主题

62

帖子

266

积分

vip

Rank: 9Rank: 9Rank: 9

积分
266
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48' k% @& Y$ w, K; b
刚开始我以为要把打字机改造成电脑键盘
: c& J3 O0 B, E; X不过楼主也很厉害!
/ j7 W; h, x: u' I# x1 v" E3 z( V
哈哈  是有点像奥
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2017-4-23 23:45 , Processed in 0.158650 second(s), 26 queries .

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

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