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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑 5 d2 R' y0 b5 J* j3 O/ y4 T

0 K1 ]& |5 U  s) t( l# Phttp://bbs.eeworld.com.cn/forum. ... digest%26digest%3D11 ^% A8 ], p# }5 g6 j6 X4 V& N# [
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。
' M% D) K' Q/ q" H0 b
- v1 |1 u' g, `: i8 }在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。
7 u, ~) R/ c; K5 `, G6 q5 `

- N7 L* Z2 i1 r( x" X& g- _ 235140i3a36qivqzuvmt5q.jpg.thumb.jpg 4 s: q* N# U7 |
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。
" V- F3 T: ?0 O# F5 i8 K 001734klbyoluenuwz4h4b.png.thumb.jpg
) Q+ B/ w) E$ ]' ^! m为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:
) A8 e( U2 ?3 b7 F) w 003625r2agx2f5v922cf2f.png.thumb.jpg
0 a; o8 f- a) H9 f  ]其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.
9 Z  B% p( S# O+ L. ^6 Z# ODvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
* [" d) {4 A; o7 K: Q+ m, |
005836yvs0wvovwsssgd3o.png.thumb.jpg 7 a. {/ q( T" _$ b6 y. m
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
4 k( N. q! C4 H# o. K1 ^4 F1 b: Y* ^9 l) I
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
$ p# _0 F6 l9 X6 C3 V1 e; Y4 v7 \+ m* x+ o2 E; R( \
到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。5 R8 E" R( ~1 P+ I: q* E& t

5 n0 ?0 V( [& q0 M, j& N机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:

  i: k8 S9 v1 c( ] 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
+ {! q. Q+ z: J! w----------------------------------------------------  分割线 ----------------------------------------------------------3 C, L- P: e3 J7 d# K' C

9 S. B( W6 X6 A* E5 p3 t" D: `* X先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
- e% ~5 l- P4 |. S0 D, G
020011osionbunl4ui44vi.jpg.thumb.jpg & J* r. l2 }9 ]
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
2 P* d1 n# p: P. i 020017j8ycmnv7788bqv52.jpg.thumb.jpg 9 |( l5 Z! S- I
特写,80C49+ F6 v& i2 v( a* |* A
021040oujzuvtut6iujtvz.jpg.thumb.jpg
* t) d( Y0 i1 L+ p( z% u# k' \LED部分,使用了一片D触发器锁存指示灯状态.  r5 V) Y. J3 C" q
022209ixzjfjc9crmrrqjm.jpg.thumb.jpg : ?" d7 a% e1 s* t! }
暴力破坏,将80C49拆掉) O+ Q3 x  y# V4 [; U/ R! P
021113e48qq98vyohvhzzh.jpg.thumb.jpg
4 g- P- u' F# w5 ?拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
; j2 G' B# e* p  |1 G. K 021125nc9az6dj33rlds2r.jpg.thumb.jpg 5 ~/ z7 p6 w2 l) {) ]0 h3 Q
焊好元件后的板子,准备替换80C498 z3 o7 L! T) P9 P2 }  z! j
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg 3 D" q- y! z: r8 W
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。, v& v' v' c2 ?7 U' h/ ?" C1 X
021104shifhnrqbr3o5nlo.jpg.thumb.jpg " K# i# c$ {4 Q7 i/ ?
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.
. D% y3 v! I9 r8 a& a; ?. @& v) F( I4 A 022003ym1p9u4ug40280uu.jpg.thumb.jpg ! A, z* r3 A  {
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。# G0 ?. |6 E6 ^1 i# d$ c8 ^
023313kt141q9qajtol7ma.jpg.thumb.jpg + Z' f: K. o% ^+ O+ [& y- O; Q5 L
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
% N  n; i1 r6 j5 ~2 W' N# O: h 023322nt7l5xb3ltttkltt.jpg.thumb.jpg ' x: N1 h# k, t4 b* [. W
主键区键帽就位0 K9 W) E. q. E( V$ E) h
023331hin88e8wkrwzwikx.jpg.thumb.jpg
1 O0 }2 m# c; ^& C编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。" \& Y8 f" I7 y, y3 W
023336wjzlgopugg1jyy79.jpg.thumb.jpg 3 p" H6 R6 w4 \; p
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。
, y! \2 {! e# g* p' c 023341sffu4j3g2323h6fl.jpg.thumb.jpg
% }0 ~: |' e: s8 d  F& r5 A. T& O
- X5 o' r+ [3 `0 F& a
----------------------------------------------------- 分割线 --------------------------------------------------
) j9 P; |- u) U, E
  U0 _. |! O% p. B- n
8 l+ G1 D7 g* j; J

) P# r0 |5 r3 ?, a4 c. G) B
3 ]8 M/ R9 O2 |, ^' Y5 N- F
! j1 D; I" R& T: n

# E" B- O& }' b
* @  |2 b, A- S% o/ [/ \2 v* n/ ^  g5 U4 d" P. y& }, i

' N1 Z# ?& @7 A' H3 T. q4 D, a, X& c
1 W+ p5 E- Y7 f; T7 `4 D# J5 w/ s% ?: _/ F8 k
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
: X. o7 g/ ?2 z+ v: u, F' e. N80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:2 b/ h# y/ F8 t& }
104025nzibm2rmiomhyirm.png.thumb.jpg 0 I( n! h8 {$ S
# t3 d; M2 W: k5 J7 h3 M$ }' x* n" c
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
+ z; C+ W% L( u9 e# w! h% H 105004zkrez5houvkkznko.jpg.thumb.jpg
0 ?6 D. ~1 b+ N% t

+ g4 R* o( s- y# p: ?2 R扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。" Z/ I  x! f5 Z1 O

4 w. E2 m" W8 ]" l0 j4 \! b这是我设计的电路图:
/ Y0 M# s2 C% F! r- r- U 110344ej2z2oo2rflo7oe7.png.thumb.jpg
) [, Y7 t0 c, Z' V6 d$ m) Z* m
8 N9 M, A8 ^9 x. H- d
PCB Layout:. n8 J& C7 Y8 A9 e8 A' b
110847jjbjvt34vwt3v5bb.png.thumb.jpg
. |# Z9 c# k' Z( N  y0 D$ W7 _

5 c2 t& q, F: G( J8 U不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. & I7 l0 k3 n) o. z* g: {
9 X# `& v( }' z; [+ h
4 ?0 J0 `: L" U" ~) L
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 . m$ s# I; u+ M, N. e0 C4 ~% z- o
5 r5 x6 }, o/ Q# M
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
; }' x$ n9 ~% q6 J5 t5 y/ H& X9 w5 V. s, u/ ]5 S4 F# \! ~
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:% w. r: @4 Y5 C2 `, s. l
113818pmrfsb6z0byt6t06.png.thumb.jpg
! a7 d* @7 v( \/ B: @$ Y
4 W% m  P6 N  J. m% F; v
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)  z! l2 C" t0 r
( y& X, |! c) [& X
USB的中断ISR,bare metal哦" }8 d/ s- H1 ]" l
, ?0 ]: h3 S6 N+ H5 v1 }
void USB_IRQHandler(void)
( j: ]7 F! Q$ \& _  f1 m8 B{! K5 N3 z1 z) ~, z7 \( |3 n2 ~7 m
    if(USB->ISTR & USB_ISTR_CTR)
0 A( L9 V+ \/ P# d4 }- B" K" Y    {9 T- @5 J0 q4 c2 C
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
2 T7 D+ K2 B9 }/ i4 @        {
9 W* c) V& ^. J            switch(ep0_state)
3 E  h: b& b5 X* b9 v/ O            {
: V& A6 r7 q1 K! X                case 0: ep0_state |= 0x80;* u# D2 {8 N; S* b; s) a
                        break;) ~* @- ]2 {+ \! d6 ^
                case 1: if(USB->EP0R & USB_EP_CTR_TX)2 i% }8 y3 G6 x$ \8 A* ^. N! T/ I8 Z3 r
                        {& K) n7 ^9 Z1 [0 T2 {+ w& y
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;3 p- R3 M5 R& l
                            ep0_state=3;* f% j" w) n; r% t
                            return;
2 v" t8 K: ?5 Q" |4 G+ B( i1 K                        }
0 z! n$ T, u" z1 Z" H                        else2 r& R/ s3 K: s
                            ep0_state=0;8 a7 k% |( x& O* n
                        break;  c/ ]  e5 o3 L9 N# U* u/ g
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet, h; k( n, ]  Y( p& k1 ^
                            ep0_state |= 0x80;' O) U7 _3 c' n8 }7 q; K
                        else
0 J3 t9 ]$ R- F; T1 z                            ep0_state=0;
5 A. Q1 s& @; `) Z7 A                        break;
! N2 g) J8 T2 K/ W                case 3: ep0_state=0;
, ?4 g8 y/ q! Q' p& M) U1 _4 L                        break;
4 z9 i; B7 s7 ^# ?                case 4: ep0_state=0;
: [0 I! ^1 p% R+ P. E6 Q6 ?                        break;+ L6 D- e% K+ v* ?5 U
                default:ep0_state=0;( Q  j  d7 I) @- H, y
                        break;5 h6 N. g* i3 I2 H* t% m% {7 u
            }& X1 ^: U. ]( c  B5 I6 a4 c
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag7 n! T$ M# m6 D7 Q4 j
            return;
8 T% w: r) J" x0 X4 }        }* [- j- t" w' O) _3 p
        else    // EP_ID can be 1
( z; ^* D$ L- g! i, ^* h        {
+ }" C. w; N3 B( C8 M, g            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag4 P3 e" s: A4 Y, g( x. k# e1 ?9 W# y
            ep1_wait=0;6 ^/ p% Z9 N: ~! h  `  c3 q: Q- [
            return;6 B$ C" B! s1 f. w2 R4 r
        }* `/ W9 r8 e5 d0 d$ y
    }
2 y( C2 y6 m& L0 H3 z( R" U    if(USB->ISTR & USB_ISTR_PMAOVR)
0 W& U* K" G' S: b' n8 b    {
7 r6 n7 A7 {1 R2 M1 s' ^8 E' k        USB->ISTR = ~USB_ISTR_PMAOVR;
" T* I; x2 J9 w+ n8 b6 O) U    }
4 g5 @9 b/ m2 B1 D; M% ]: D( X    if(USB->ISTR & USB_ISTR_ERR)
1 ~! f) q3 x1 l  B4 S2 x    {6 }( w7 L% ]( G
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
1 b6 D7 t! P% ^7 L    }
; J1 R$ S. T4 w4 h; @    if(USB->ISTR & USB_ISTR_WKUP)4 ~0 i: \( r0 a0 @7 I9 _+ j
    {
+ j3 e  L6 N) L& i* y$ F3 }+ @        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
/ x7 A+ a: ?7 N1 V* ]; I        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear* C& B; E- i; o" h4 ^3 ]
    }" A2 D. P, P0 S& X' M3 d" f! b
    if(USB->ISTR & USB_ISTR_SUSP)0 f, f: j2 D# A( k; g- l. I
    {' I( U- U1 J0 C3 Q+ l& ]; R
        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend  c" |" g: a' z) N. Z
        USB->CNTR |= USB_CNTR_LPMODE;   // low power% y+ t) D1 |: E
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear  f) [, b" g4 |: E2 s4 F' _
    }
$ R0 A# v+ g+ E2 @( f8 v    if(USB->ISTR & USB_ISTR_RESET)2 n& f/ g% H7 |8 f: O) L
    {
! [1 n( Y: m8 U. a        USB->BTABLE = 0;    // buffer table at bottom of PMA
( o2 J6 g  A% R+ ?5 g& d& Z, s0 }        USB_PMA[0]=128; //ADDR0_TX
9 E3 M  O. z3 b+ a        USB_PMA[1]=0;   //COUNT0_TX6 f2 Y" |+ B% f  U5 I
        USB_PMA[2]=256; //ADDR0_RX
, F- q$ P+ Z  {* ~& b% v        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes; }! j  O6 Z4 r
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;# v2 u- ~3 w7 P7 a
        ep0_state=0;/ F9 a, A4 ^) e0 l1 B! n, v4 T: V1 G
        USB_PMA[4]=384; //ADDR1_TX
4 E8 T9 w) j0 M" B& x8 L        USB_PMA[5]=0;   //COUNT1_TX& {4 N8 ]: u6 V+ x4 K3 [( |& Q
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
6 c# `" @! n: w0 U* `        ep1_wait=0;  t  N6 \* L8 B; l/ X
        USB->DADDR = USB_DADDR_EF;      // enable function
' q; V8 D: M5 C' d        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
# N1 Z' ^3 F# X' L    }% J2 M$ Q9 I  K
    if(USB->ISTR & USB_ISTR_SOF)
3 `( x+ `  O1 V* V- ?3 u    {
! X$ V( x6 {1 K' o. y        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear
, d3 j0 L, S' b& }2 P' d) n    }
8 g% q, O9 d. b}
# _; H" g; E6 Z# @$ Y
" a1 [, P/ B' e6 @8 T8 b" [
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。) z  W  Q0 ~( Y$ c  M
: [& m8 F" l! W* b9 y# C6 |
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
+ f# {$ J, j) ]. W7 g: _0 m  x! M( ]6 P: ?. k$ u) f8 d' y1 k3 F
    while(1)( V5 P. w3 k9 U- B
    {
0 |: s' s; R: b9 f" e5 U        static char row=0;( ]* x3 E7 j: b0 \
        __WFI();+ p) d6 Z$ G- K; s: G
        if(ep0_state & 0x80)    // request data processing
- k! W' ?# G% O8 m1 }% B        {' q7 o. s. {4 u) V+ m1 M9 a
            if(ep0_state==0x80) // SETUP phase4 T( \0 R% b. T0 U" m
            {
7 v' e+ [2 P! }0 S                if(!setup_packet_service())0 V0 E. i: `" _3 _' Q) P) u
                {, N6 i- G( p' c6 H% s
                    ep0_state=0;- A7 L, K6 B0 v% j5 K" X
                    // not supported
% M* p5 }7 d* v) G                }
( Q0 b" v% T* m/ K8 @" p* ]) x                // ep0_state should be set to 1 or 2, if processed
# Y* B  X3 K% c. {% v            }& w; ^8 r. T2 D6 L2 [
            else    // OUT phase# }3 X# U7 c/ T, ~8 w5 k, a
            {; |; ]* {/ g0 f, B! U7 ?
                // process data2 T1 j  W9 T! Q/ b) a
                show_LED(*(uint8_t *)(USB_PMA+128));! t0 X; `  ^( J! x+ j
                ep0_state=4;
1 b* `- M7 }- j" S# I' a. Y" v                USB_PMA[1]=0;       // Zero length DATA0
; P! X3 {+ T. X+ a4 o$ I$ T                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;: q# H( R6 v, o( K: T: l" P) _. s( A
            }
) K4 }2 [& n7 C4 X% e) M3 ?; |! U/ o' A        }
) W$ W  b2 K9 E9 \" y" y        else
) C) t( I( d1 X  b8 W8 g+ o        {
2 F4 @; ~, X$ @. ]  B* \            if(usb_address && ep0_state==0)
/ Z2 h- [/ n- x4 ~            {6 ~% @) O2 M/ v+ F; S  t
                USB->DADDR = USB_DADDR_EF|usb_address;
6 l* x: s" n7 \. l                usb_address=0;
3 N* X/ c+ ?! ?  V3 v            }. \6 H7 I$ J+ [  \
        }
6 o: R  ]6 f; _        if(row!=scan_row)   // new scan line
: o0 q& e6 p5 s1 q6 r) C; {' l* y3 q        {: C9 Q9 T6 R' |1 j# H
            if(key_state[row]!=prev_key_state[row])
4 {- v6 V$ C1 P# }* i            {
) d& B2 L0 B, D5 W+ w                uint8_t test=0x80;
  a* n" C7 T  B, S7 x                uint8_t diff=key_state[row]^prev_key_state[row];
7 c$ A' i( ]5 c# i( V  k% i                for(i=0;i<8;i++)
+ L* t: i7 G, M* r' _+ b7 C' l, z                {& r2 T1 _% U/ ]( E$ J
                    if(diff & test)' K# Z" L  k; I, u+ M# k
                        update_key_matrix(row,i,key_state[row]&test);! t8 y4 a/ N4 p8 I, ]* W) U
                    test>>=1;
! s0 r& r) l6 F                }
* s- d- v( h+ ^# o            }
2 h$ z) D; k' r; a7 L! Z0 w            row=scan_row;
  U3 W! k6 A( R, L" H0 C        }: ^- _7 a) {8 x' h  D6 r0 H
    }& A* n6 Q: ?2 f+ U8 v  B
& ]  g3 m0 D3 U3 y0 A

( G0 b6 P0 G9 Z$ i" S5 ^EP0的控制传输,把用到的请求处理一下- n; N9 b* ~. e5 w
char setup_packet_service(void)
: c* r$ t8 x2 ^5 ], n8 ]{+ t# T( m2 u* T3 M5 x" P- {
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific6 e) I* a3 [6 }
    {
0 f: |$ w5 D. s6 u% N! e        switch(ep0_std_req->bRequest)
7 E+ L# I/ t, X2 `+ S( j$ w        {* x+ Y+ N* e! X# _- a
            case REQ_GET_REPORT: break;7 Y5 P/ k- ^2 a5 H
            case REQ_GET_IDLE:
- L- y1 a. u+ G  J3 }$ [: X                USB_PMA[64]=0xfa;   // return 1 byte. n: W+ E+ M7 r9 C4 e
                USB_PMA[1]=1;
% h, H1 t6 _( I. T5 y6 M                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;5 E& Q) X/ W  N* x: e# j
                ep0_state=1;  ?, f7 O' u/ I
                return 1;
8 }* f! B/ S) e" r3 q                break;
4 P( H! K( c6 f3 b* ~            case REQ_SET_REPORT:
' M. c, R7 {9 b7 h1 H& g1 V                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
3 n# V+ H+ N& a$ L% J                ep0_state=2;
) l5 c- U5 \9 Y9 ?9 k+ H                return 1;
. I- C; q2 i) Y6 d5 K3 w8 e7 v6 }                break;4 u. Z/ J& ]# e/ m- g2 J/ b
            case REQ_SET_IDLE:
/ E8 v3 ]& |# j+ x; m, G# [7 Q                USB_PMA[1]=0;   // Zero DATA# q) t' R, x2 M7 `6 Z) f  j
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;. i3 ~8 L' F) o, b
                ep0_state=4;
- `0 O  i$ w# E: X# Y( g                return 1;  Q2 V5 P5 z0 U% V$ H
                break;. M9 f6 d; U8 h- r: o+ W* c3 h* }
        }
6 E- j6 @- M+ J, L, m        return 0;
5 D+ |0 a# V6 i  L    }# K, l% I, ~1 z1 u5 r. ~% P
    else    // standard
6 y9 T/ `0 l( F9 B/ s" V    {9 G1 B& K9 X% J& H  y
        switch(ep0_std_req->bRequest)& f9 D, }3 u7 f+ f, I  G  A
        {
$ w& ?$ `$ e% h7 s1 {+ b            case REQ_GET_DESCRIPTOR:
, M# Q5 g2 o9 u9 L7 s                return descriptor_service();) \' U" a0 H5 _( X8 S
                break;
& O4 R- x) G  n8 x            case REQ_SET_ADDRESS:
7 i  L' n* h. _# _  K+ j% ^9 A0 l                if(ep0_std_req->bmRequestType!=0x00)6 D- D/ N9 m9 l% o4 s
                    return 0;
4 P& f1 z0 X/ f' ?                usb_address=ep0_std_req->wValue;* m9 s6 r8 V- K4 _: ?7 ]3 |3 {
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;8 Y+ N8 s* P' F3 n+ ^; @7 @0 Y9 x
                USB_PMA[1]=0;       // Zero length DATA0
$ R/ R" Y2 T# J) P7 M# H) F% x                ep0_state=4;    // No Data phase( Q3 n: |9 _, x5 @1 ?1 n2 ?
                return 1;
9 m0 W; k. \8 {- C2 T3 `! X            case REQ_SET_CONFIGURATION:
0 Y* ]0 @  _7 d# _                if(ep0_std_req->bmRequestType!=0x00)
/ f2 a2 G( e5 |( ^3 x' j0 n                    return 0;& @" |% d0 f) Q+ n
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;8 f1 Y2 e1 f9 @4 Y- p
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;. o# P- i. m8 P3 O, z6 }
                USB_PMA[1]=0;   // Zero DATA( Z6 ~" I3 p) W6 D. c
                ep0_state=4;    // No DATA phase0 A  k- {' c3 o
                return 1;
0 w) f" E8 Y9 }- x) K            default: return 0;! V% D9 B1 D, L* w
        }
7 H$ S3 z! q7 U* {# I    }5 y# h  d" B8 u" Q
}& n( }) d$ Z! M/ ^  ]/ o# z9 j

3 p8 f( Y0 H6 [" C: q- j4 n8 x: @/ @1 a3 V8 W* K% r
9 _) Z: X1 N% \" [: P3 _- t
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的. M: J9 J4 X0 b# T# l
char descriptor_service(void)
4 {0 W- k# H+ [% ]4 ?8 F# e5 I{6 _" m! ?$ e( T- W! L' ?
    switch((ep0_std_req->wValue)>>8)$ Z5 U- J- k* ^2 C1 t  q+ V+ z
    {
( ]4 h" F1 Y6 n% C4 |, V        case DESC_TYPE_DEVICE:+ ]' t0 c# U) w% {
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));; f$ w; p3 V$ D: g3 p# Z
            break;
0 {7 ?9 U2 {3 p" C' `% a" I0 }        case DESC_TYPE_CONFIG:8 U+ x7 B4 ^9 b' [3 P' \9 [/ O5 F
            if(sizeof(ConfigDescData)>ep0_std_req->wLength)1 V* e1 X7 q" N) F! A4 c
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);8 ]4 q7 ~7 F% q; w! v
            else+ k# W. U% r( \7 D! ~
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));! V% b0 c$ J% k
            break;
/ w8 i- n% w% x! X3 A/ A$ K8 @        case DESC_TYPE_STRING:: U6 y; F& i# U/ M+ A9 b0 H% G+ \
            switch(ep0_std_req->wValue &0xff)
7 }  g6 N, J  s8 V  c: m            {2 D1 ?$ r$ ]6 [: ?, E$ @5 p
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));) Q5 Y4 a7 [% X
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
. M  e' c) C- d& ]4 v                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
# a+ g% M0 J' P! o8 Y: C                default: return 0;
/ e! m- ~- Z7 q) R8 g: L8 j  M            }
4 b# @; ~% I% b3 O! s! E: C" k: p            break;
, K. Z- u( P0 [+ ]9 n7 S% W: n        case REPORT_DESC_TYPE:+ [8 ~; u+ R  b! H8 D+ @5 {- U; M
            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));; O/ ]; f- J3 r8 y! e9 Z! t
        default:
- T" y2 g) K7 w            return 0;( N! r1 d9 C9 E& V8 X& p5 I' _
    }3 q$ a. W$ \, V( m- v+ J; I
}4 h; l4 W, S0 i" {5 V1 j" n: R2 i
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.. @, U# G1 R. N+ r
void TIM6_DAC_IRQHandler(void)
$ d) U. J- w: p: C{8 O: D! ?- \# e1 n0 O
    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);" E; b0 A% j% X# c

! _/ K  k9 h' d) `6 L  x7 `* `

1 u* {3 O8 n' g0 }, h9 R    TIM6->SR &= ~TIM_SR_UIF;
3 \5 Z# p( _6 J+ ~    prev_key_state[scan_row]=key_state[scan_row];( I; b: J4 K" y% h
    key_state[scan_row]= *PA_IDR;   // update key states7 c& F( ^; C! D! B& S
    switch(scan_row)
  i7 h$ M. o: P0 O) }    {' U8 N) y. S+ n# z, \
        case 13: // next row PB14
8 S9 R. L. n- f5 f) z! U% `* K                GPIOC->MODER = GPIOC_DEFAULT;
" v. \' m/ w( N  q                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;5 t; G' E2 W+ z/ S  i! H
                break;. C; N+ L$ n3 ~4 H! c$ G% Z" C
        case  0: // next row PB15
) K6 S" F) R) q                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;
4 Y9 Y* G) n* j2 F  ?                break;
  ]" ^+ G7 C; H        case  1: // next row PB3( y( E/ q8 F! F
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
. @# v& Z0 ?, \& \- S4 B                break;
& [0 \7 S+ A8 w( i7 V        case  2: // next row PB4/ |. C1 r/ s5 Q% \
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
& I  V$ s5 p' g, |                break;
% e1 |& w1 ^: v. V3 g        case  3: // next row PB53 a0 o# r8 J6 G
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;) P/ r0 m* _6 C0 I! q
                break;% Z3 g& J9 C: R. K
        case  4: // next row PB6
. i+ y4 I& e1 x  i                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;# X6 n  ]8 N: {* O
                break;7 J8 J# ~$ l1 r
        case  5: // next row PB7
1 C! a4 }- b2 S" v                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;* A+ W) V% }5 ]. `+ |
                break;5 q. ^+ U( \0 ], E% L. d
        case  6: // next row PB8
) `! {8 E7 H3 o3 g* I                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;# ?3 z3 n/ X. E6 q* l3 i( m
                break;
6 w9 G8 r: x( J        case  7: // next row PB9
& w  ^% E! S. A6 ]/ n! N. O                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;
  a" `6 |" b2 S8 N6 C                break;
. }, @% T% c+ S4 a( \        case  8: // next row PA8
; v+ c. _0 y/ P                GPIOB->MODER = GPIOB_DEFAULT;
; w' M) C5 h( N) l6 m                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;+ M, e( Z. f8 N% |; V' j4 h
                break;; [  Q' I6 V3 C& [7 m: H3 Q
        case  9: // next row PA9
" p, _" t9 u% w! k' A2 r! }                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
6 Y, ^! l9 n& m" ~  @' R6 z/ y                break;5 \! Y2 S- Y; w/ i+ f7 G+ l4 S6 F
        case 10: // next row PA10
+ ^  s8 d3 q$ U. \, i' w                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
' A4 @! Y% R( a6 Y1 ~) B: m5 w" I                break;
; n) f( |5 B: M6 x        case 11: // next row PA156 P# G& p" q# O; T2 z, A
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;# X; V: Z1 N# p5 m0 n; N8 p. D: g
                break;! q8 q- y4 {; F0 L4 e9 T$ f) o
        case 12: // next row PC13) n7 ^& @: N/ Q1 T5 j. ]9 |
                GPIOA->MODER = GPIOA_DEFAULT;
* Y% W. B: G: f% p! x+ V+ I/ ]                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
. b5 I( p' h+ Y& t2 ]0 @+ F                break;% M% c+ B5 U6 z3 H
    }
' n7 t# T4 x/ F" e! g6 R& M    if(scan_row<13)/ V, T9 g* l' q7 N
        scan_row++;
3 O, i* N$ O1 N& w- e( k& [    else
/ a' C7 B: k$ _( g# k) k        scan_row=0;5 Y; n! T- @! D+ y' E5 R0 B9 b, T0 H0 ~" r
}/ B$ C5 s. k5 `1 Q% _$ f/ n( m

4 j/ R. S1 P; d, o6 Z' p8 X' E7 J2 I1 f* \2 ?
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 8 U1 W0 T: R9 M4 w
2 ]6 o. l5 j+ n0 z0 s4 H
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
% U8 d4 |$ l' i5 c7 `
, @( n2 |+ `3 k; n+ l' W要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
0 {$ [, x( t8 }3 {6 }9 a

* l5 z" V8 \# q, x- g
+ D7 ~3 e4 T% U# ]- rconst char hid_keymap_qwerty[14][8]={
& Y$ [4 H( G: ?& p  @& H, c    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
: t5 o9 `3 z; P% ~    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
. ?- ]5 {1 \7 B# [" b8 W    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},# a3 R/ w# t1 W% r0 B8 M* c
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
7 E+ y& |3 y; G, U    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},; h: W' y$ [" ^( w& R/ O
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},* Z) t3 X, D2 }  {9 R
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
5 ?( m& y4 e; @* b" i    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},0 `  K5 v4 o1 z* b" `% l
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
, I3 k. B  O. v7 }    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
& c: B: O5 b. P+ l, }( `' V    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
6 ~  y7 r6 n: |8 @7 D$ }( n    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
1 O' o4 m9 h9 |    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},- R# V! J+ r+ y& u
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}. r: z' c) Z# A. W; d, i
};7 m! [  r6 w. P1 Y; w/ q3 @2 S: @
( B* }# `2 V4 G: Z& M& k

1 Y0 x, Y# r+ i$ Qconst char hid_keymap_dvorak[14][8]={
+ c9 V' r+ _8 Z3 r    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
* G3 `$ K; x9 X2 Z2 M7 h    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
1 c. |5 v1 @( e    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6}," b) H5 j4 G2 U+ L1 Q
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},, I, o  `0 g. u% \/ T# x4 }
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},1 ^9 \7 O7 _$ m
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},1 Q) n" \1 k( {6 r7 r' ]$ m6 [
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},6 K1 P. M5 C# ]; z7 _  m6 Q9 J' V
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},/ M5 U8 H- `0 s3 V
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},7 p0 Y+ C) w4 y1 z0 x
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
( R, A9 o6 b+ P% o  R( U; r    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
) t  [* i* v- A: n# I' l3 x2 y1 i    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
( Z- R- ]& P/ w. M) K' [    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},% I% L1 n5 b3 [
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}' _6 r% ^' Y, ]$ ?
};
5 [' e8 `( M+ a3 [+ }6 B
' l! j/ [* o& n0 I; \
; T$ o8 d3 s/ M/ G7 y( D上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
4 k7 K' ~$ t2 ^7 O9 Y
6 E+ ^2 t" _6 g( ~9 NHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

: y. F+ U4 h* q0 |* a
' X8 x* n$ a, o; L# q- b4 hvoid update_key_matrix(char row, char col, char onoff)
8 u) z& g  P% m( f8 t$ D# L' D{
+ d# d6 ?( Z, J+ g    static uint16_t hid_report[4]={0,0,0,0};3 i) y! a# w' g4 H9 r# C
    static char (*hid_keymap)[8]=hid_keymap_dvorak;
8 G6 N7 ?' J3 e+ ]- k- }
2 r# D( D! r9 F. y

( h$ v7 e4 ?, b& E; t( ~3 o; Z9 ^    unsigned char key=hid_keymap[row][col];6 ?+ h: I0 p$ a$ r- F
    unsigned char *report =(unsigned char *)hid_report;
3 ]& i  K" T/ Z5 |4 l    char i;
) V& ^: K* [6 ?. h5 W
0 \' H: D6 \5 V- B  {1 e0 ?7 q

4 \, U) b; B8 n    if(key==HK_MODE)
% Y6 I; F* x, _# h; c  b    {
5 Q" l% s8 j$ N* h8 b, R8 N        if(!onoff)
! G" g1 Z% Z7 Y        {
, D$ k) g5 }8 H7 n  j            if(hid_keymap==hid_keymap_dvorak)
5 O: V. j' k7 `            {( c- H- r$ m3 }3 y+ t" g4 _8 s2 P" B
                hid_keymap=hid_keymap_qwerty;* d7 d8 G* R  z
                GPIOB->BSRR = (1<<2);- b! f$ B% `" Z
            }
( u# P# C% v$ D' e$ h: h            else
4 Y, C5 w5 u( k            {
+ S8 Y& d; v. n                hid_keymap=hid_keymap_dvorak;
+ B9 l1 @9 ~- t                GPIOB->BRR = (1<<2);
. U4 K" W! x4 V' k  G* V            }
2 Z) J/ \+ y0 U# I" z* Q        }! c% ~4 r0 S6 u$ P9 _
        return;# n5 S- {( g. m! \: l
    }
4 x. C$ ]$ z6 \9 j' {7 ^4 t# ^1 ~! l: p6 @3 A
$ T1 E2 k2 S7 |: U: U3 L7 _! W
    if(key>=0x80)   // Alt, Ctrl, Shift- h5 m# K- ~) p3 }0 n% w1 n( {+ P
    {
; I5 _) |, D1 K+ M        uint8_t bitset = 1<<(key&7);
! J' D) b/ I# A4 K7 P6 X+ z        if(onoff)   // non-zero is key up3 M: C. m) v5 z/ `: }) Q! ?$ i  g
            report[0] &= (~bitset);! L1 Z6 }! }( `) F5 A
        else
/ |2 c* i+ W$ [& x1 I5 j            report[0] |= bitset;
8 {/ {6 e* ]5 E4 Q* {1 d7 ?: L    }
- a( e; }" _% @+ D: p" O8 V7 ?    else
+ u0 j4 T- M4 o) J( h4 {* Z5 n2 x    {
! q- }  G9 p: t+ n        if(onoff)   // non-zero is key up
/ _* k8 }# S1 ~7 m        {
" R" u! i+ ?5 P( o9 s" L- L0 l            for(i=2;i<8;i++)
8 ^  g) I1 v3 g8 |6 X3 `% Z            {
7 x$ {! M& n; v% E) l                if(report==key)# @5 t  H! Z: z6 d  u
                {# x$ T7 j8 R$ g5 P! g9 b
                    report=0;
2 G. C' z. W; y4 b) o                    break;
1 ^! `% A: v" E% n: f* u" V7 b                }" i9 E* e5 ]3 X1 i
            }
: e, V  h" T  b/ ?. C        }+ \' N: a' H$ L5 V! ?2 H
        else9 `  v3 q$ Q8 |+ D
        {
, i& M9 L1 o7 ]2 q& O' l. H            for(i=2;i<8;i++)& k/ y& [/ c3 O$ G
            {- \, W) c, J( @& q
                if(report==key)7 Y- n. k, E6 i5 F' [% i/ h3 w
                    break;
0 e4 i# P8 `  z1 a. [* f5 w2 k                if(report==0)
2 p3 I) f* X: K                {0 _" \$ B$ u/ f, `* i# ?
                    report=key;2 @9 ~+ L$ G0 m8 V( ?0 B4 G8 i. u
                    break;
$ a. ?+ i& L- O/ w" M                }3 W* `: ^8 O0 j8 \; G  {/ Y
            }, w* ?! U1 w& F* A6 j
        }" i7 p; b" h) V9 y( b8 Q7 E7 D
    }/ E" l' f: ]' [# A$ w4 E7 N& |- P
    for(i=0;i<4;i++)7 |6 a1 a6 l2 k& E& l
        USB_PMA[192+i]=hid_report;+ {* z5 q9 R5 W: ~5 A7 _2 r. h! J6 M
    USB_PMA[5]=8;   //COUNT1_TX. c" V3 D# n3 Y6 m! y, d5 P
    if(ep1_wait==0)
( A' a1 _0 L- Y/ [/ k% s- O3 Q# ~3 @    {9 p' A$ {- ^( q' L# \! j; [
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
/ k1 x1 M  x; y$ v  H        ep1_wait=1;
8 ^' Z( a) w1 H- N+ m8 [$ t    }
1 {$ K5 w% d8 O( v; Y}) V, H& K* E( P

. D6 M  N$ W8 j2 j% ?/ O# l- }/ N3 `" g5 X, W  m( A0 k) p  h
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
) Y. ?6 d& X( Y  X, ]- l keyboard.zip (8.7 KB, 下载次数: 6011) " j: @& W( N! F: B7 U5 N) C2 N6 n% k
$ C' Y2 d! l+ y; K4 [

* |+ A6 ]( b+ E0 x' e1 K; x$ N! Q, q# Q) K3 \/ _9 |$ L

- _$ N% Z+ v9 v) n/ E3 C
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。4 k( Y+ f) I  T' t% ~; x
回复

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
$ Z* J5 e6 Y- M; W1 x0 e不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48, M. p: f" p5 |/ X! g  E: r0 m- e" K; [8 V
刚开始我以为要把打字机改造成电脑键盘4 p1 I5 N4 m: b& k3 {8 [
不过楼主也很厉害!
: \/ }8 g& ?3 l* O$ H' J+ M, i
哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
6 T$ y: z3 f" h3 c, P: r/ Z
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-7-27 15:23 , Processed in 0.259544 second(s), 27 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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