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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑 % f; N4 L% [4 X( w

( H1 }( D) Q; \# _( ~http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D17 q4 G8 }- Q$ s9 J
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。7 z5 `, i' O* V/ `8 B

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

3 v' l( j2 p6 g  D: \
: ], U* L7 b3 U+ S
235140i3a36qivqzuvmt5q.jpg.thumb.jpg . o; [& E7 H+ ^# V8 f' X
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。
, l; r. ~1 Q8 \  q( \0 W 001734klbyoluenuwz4h4b.png.thumb.jpg : C, \( Q0 m# }% L" Z* i" t  @
为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:. @/ S9 Q& w$ Q% d7 e" M% O$ t; f
003625r2agx2f5v922cf2f.png.thumb.jpg
  |' u( ]; U; C% g$ P其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.
# J6 b( D4 H7 gDvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
8 i- t' I+ Z/ a7 E& A
005836yvs0wvovwsssgd3o.png.thumb.jpg 6 H' `! m+ m) b, ]& x
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。6 `) [+ a) H) B) s0 b
( M! ^' J6 V. d( P* W  u: y
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
$ @& S8 r# q/ X8 J! I: R, S8 A8 Y+ i5 G; g/ m1 q' w
到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
! ^& N. J/ I7 n3 I; Y2 d1 m0 |; u  ^5 @9 v: W1 T4 q1 c+ x
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
" Y, }' ]! `/ D6 ]) y( S
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
9 S5 \/ a, Q) u+ g8 X----------------------------------------------------  分割线 ----------------------------------------------------------
6 {$ N& D2 \" n0 J0 K9 F' J  M5 Y7 q& v/ r5 y
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

( ^: R& `  o; \0 S 020011osionbunl4ui44vi.jpg.thumb.jpg 1 m& G- M% }; G7 N
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。8 t# r3 P/ ^( z+ v6 Q  v
020017j8ycmnv7788bqv52.jpg.thumb.jpg   |7 z! I3 j; T1 ^, D9 E
特写,80C49
- t' F, G  m4 z. u& G: x 021040oujzuvtut6iujtvz.jpg.thumb.jpg 5 y1 x5 z8 e- b8 S7 v$ i
LED部分,使用了一片D触发器锁存指示灯状态.4 ~: h# F4 l7 {' J" O
022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
+ P8 n! l$ i2 D) k1 F暴力破坏,将80C49拆掉7 c6 F1 |3 \% t6 `% y2 W
021113e48qq98vyohvhzzh.jpg.thumb.jpg $ f# u2 N8 H/ i
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
& W4 ?- E" Y6 B0 \ 021125nc9az6dj33rlds2r.jpg.thumb.jpg
8 ]# E' N6 q+ e, d7 K7 B
焊好元件后的板子,准备替换80C492 w! R( n4 e, Z
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
/ _7 @3 q6 B# C1 @7 K; X用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。1 z! B! o: _1 h3 [( l3 `$ ?
021104shifhnrqbr3o5nlo.jpg.thumb.jpg . q! J" {+ c0 q3 Y! K
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.% E( u: z% N" W& r, V
022003ym1p9u4ug40280uu.jpg.thumb.jpg
9 X, L% ^9 N2 K4 R" {+ b, ^# H3 \开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。
- l# M8 i, Q: }7 ~) J1 g# u' k( B) K 023313kt141q9qajtol7ma.jpg.thumb.jpg
+ ]2 F# z$ j2 Z8 Y我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。3 u; K% z. s0 K' N: k
023322nt7l5xb3ltttkltt.jpg.thumb.jpg
- v$ T6 m/ j7 b# C7 K8 j
主键区键帽就位
) K" M& L/ G+ ] 023331hin88e8wkrwzwikx.jpg.thumb.jpg
" L0 X1 E  a( T; F( }编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
3 i4 h% [) g: B0 f$ y  ` 023336wjzlgopugg1jyy79.jpg.thumb.jpg
5 g5 t, D2 e. w5 G) R5 u- G6 N+ U最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。# M$ Q- h( t: L9 `6 {1 m5 j  W" u
023341sffu4j3g2323h6fl.jpg.thumb.jpg ! a( p" m7 R7 i
; }' \$ O, {$ L# J7 U! n# v
----------------------------------------------------- 分割线 --------------------------------------------------7 `4 @" e+ R6 [0 R
+ c5 {5 |! b3 C) e& f  g( \% `

8 l9 F( X- y' `) L' a
0 i* M! I+ k$ \' s
! i8 ~2 P/ O3 y# V5 T( g' j
, y9 k. s4 f* _4 K; H
. C; j% j' P7 B6 `" J8 v( ~+ v
) N3 \3 t0 g! P% @% Y3 W  Y

- ~, h4 I, X% t$ O; T  e7 k4 t" M+ W# s9 r

7 k$ J- _) [7 h$ e7 x, }) E& T# v- h) m3 D
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
; E6 H) y4 i$ S+ g* W# N80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
( F2 z5 K- f' N/ Y0 A2 n4 r 104025nzibm2rmiomhyirm.png.thumb.jpg : H; O" E# Y! C. [, S& C
* Z5 I1 b4 M4 v* Y# w
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)- Q' F; q  G8 i  D" o- C
105004zkrez5houvkkznko.jpg.thumb.jpg
( r# L7 Y0 \: w

5 h$ k/ R2 E: N- ]扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
" L4 p, G6 k- A4 _# s* ^1 B, E% ]! k: ^+ O
这是我设计的电路图:
2 C; {* b; n4 f+ m* ~ 110344ej2z2oo2rflo7oe7.png.thumb.jpg ' l  s4 H: I2 u( H3 j/ A
' [3 T( }9 p$ \6 i; M4 c
PCB Layout:
- V4 ^$ v- ?2 L! f 110847jjbjvt34vwt3v5bb.png.thumb.jpg
) ]0 ?- V# s' U* d, }

$ W4 s# `: ?/ e. e. c- C不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. $ x6 c) G+ u; _( L  O, K+ u6 y( v3 n

! d0 `/ G' W, J$ j" b( B

' \- o) T2 t7 R* X+ G
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 ' H$ V$ ^, H$ ^& M5 ?

1 D( m. O- k- ?8 a, Z/ X& n软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。' \1 w1 L  ^+ s/ Z

. p$ E6 U4 s; ^( q8 _总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:9 d2 J9 ]( D$ d( c  ]
113818pmrfsb6z0byt6t06.png.thumb.jpg
8 W  I6 J" h' v1 K" S
3 M2 i/ ~+ Y$ R9 C" k7 L+ k
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)5 `3 K; V- y5 T3 _

; o& E" P7 k5 g+ G9 Y+ ~USB的中断ISR,bare metal哦
- R5 D6 ]% }( M! T8 R
' D- u7 @! l/ ?" m) G! _2 r
void USB_IRQHandler(void)5 z$ a  K, ]: f
{+ |# V8 e; l* g8 \2 R$ O! J' v- _
    if(USB->ISTR & USB_ISTR_CTR)
& |: \* E/ ^$ |' U0 ]# E, x0 L* t" d    {
7 a: I6 d+ ?. U0 \1 o        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
$ Y! c" l  S: M  I! O        {0 ^7 E% k7 F9 o' k& `' K
            switch(ep0_state)- f1 p8 t+ `& }# l' `
            {+ a- P- Q, Y; K$ N  ^* y
                case 0: ep0_state |= 0x80;
* U# J; D8 R, ^8 W7 y& n2 z7 R# v! {! H                        break;
' ~. s' T( Z3 `                case 1: if(USB->EP0R & USB_EP_CTR_TX)
: J: H" Y+ [8 {4 O3 G                        {
& }$ X; @. n* m5 x                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;. c. e: I& A0 k7 w9 k' B* w  c
                            ep0_state=3;
; P& [/ a0 H7 l+ X                            return;
. H' |  |  R+ O3 L& ?0 y                        }
3 b+ H/ ^/ _# c2 r  g                        else
# d) `) ]/ o" k# B) Y7 {2 y) ~; P                            ep0_state=0;
  U0 e, I  c' L2 v* T/ P' `  i                        break;
2 {5 S: j. c8 r$ x5 x                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet/ V) J3 x) i  N  h5 c
                            ep0_state |= 0x80;
/ C0 G$ h, B  z& }- n/ N7 x4 ]                        else5 Q6 \" j/ n' V4 n
                            ep0_state=0;
, L4 j8 O: x+ I5 \  d                        break;
& L  }2 V% w' f* V3 D. ?. H1 y2 t                case 3: ep0_state=0;
% k" |. d1 C9 Z' P! |; P                        break;
3 {  U5 n6 }; d( j/ f( [                case 4: ep0_state=0;8 Q: H3 Q7 J5 M8 s( v4 ]! r. v
                        break;
8 |) n* k' e8 R, e# Y6 c                default:ep0_state=0;
3 w; L. C. u/ p2 w. t) Z& ^                        break;# Q1 {% U+ w1 Z* k
            }$ |4 ~+ E: i7 q( N# w! c$ W( y2 l
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag" E' T" E4 c. k# b# v) i6 i
            return;
8 S% W. ~' Y' b+ v$ n4 h        }
. L/ C5 W* w2 j" X  N) U2 L        else    // EP_ID can be 1
! i* r7 N! U8 k$ g/ R3 d        {
* l  q8 l, w( b            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag4 @3 K( i- d7 i* Z0 Z- f8 ^
            ep1_wait=0;2 t! G7 f) @9 x) x& N
            return;" r, r3 O# b- k
        }
$ P+ z: \8 t2 O    }
& A5 t, D& B! I. z! o! x    if(USB->ISTR & USB_ISTR_PMAOVR)' _. f2 b# Q; |& {1 {! j3 D
    {3 t1 R) h. P" e3 J% q
        USB->ISTR = ~USB_ISTR_PMAOVR;
% E& L6 s$ o3 \( G: h/ H0 G    }
5 L# k( o- F! O    if(USB->ISTR & USB_ISTR_ERR)
7 \! Y, ]. y0 i" x    {
2 ~+ z8 r! P+ F4 E4 d( ]0 C+ k        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear% r2 p( h) O2 H: w8 {* F
    }7 y6 [! V% j) P) D+ O, o' P8 L
    if(USB->ISTR & USB_ISTR_WKUP)
& j  |: d1 c1 K  q! L! o. ]0 j    {# j1 w# Z! J, @  O6 x
        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend6 O. Q& m0 p( U4 B
        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
( W8 W* n3 H) Q/ P% ~: r  m    }6 s; y( Q! I0 v5 q- t2 Q
    if(USB->ISTR & USB_ISTR_SUSP)! R" u1 R- P& h9 e/ A
    {, s9 k1 X5 B3 U, l
        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
' y9 H( O) V$ a4 e. k        USB->CNTR |= USB_CNTR_LPMODE;   // low power9 S! r1 y6 p, s2 y% F2 |
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
0 z& J/ S5 p, d9 I% x2 a    }+ T2 Y" t5 [- @" I( G- u
    if(USB->ISTR & USB_ISTR_RESET)
3 B1 E# ?  x( |0 e! v/ L    {/ m/ g+ y- W/ b+ c, p8 A
        USB->BTABLE = 0;    // buffer table at bottom of PMA5 d$ j" \. X& M: j) \0 r
        USB_PMA[0]=128; //ADDR0_TX
; s. x/ p, g. P        USB_PMA[1]=0;   //COUNT0_TX& y5 P; N4 S" N6 j
        USB_PMA[2]=256; //ADDR0_RX& x+ C2 V, _$ `- C) h
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
8 \# y* G1 A! y( ^3 S        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
( r3 E, S& Q( n        ep0_state=0;
- U8 J, I9 ?5 G        USB_PMA[4]=384; //ADDR1_TX. w4 T5 u0 m( z$ i. _! f
        USB_PMA[5]=0;   //COUNT1_TX5 G* E! f+ T6 L* A' G0 b
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type9 J5 h) Z* Z. y3 X3 m" F
        ep1_wait=0;! w" Q5 S+ E) l7 z8 X
        USB->DADDR = USB_DADDR_EF;      // enable function
  F6 }0 ]7 a4 B6 b" P        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
/ V1 e9 j# ]/ @$ Z3 M    }
+ }2 u3 z; _- @" M6 f( j    if(USB->ISTR & USB_ISTR_SOF)7 t. }2 z$ a% p
    {
; P" s  \: R) i0 y& e        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear6 x1 r8 |1 J. v9 @9 F0 O( G
    }
) i- `* A4 b  E& T" [3 x& U: ?}% D0 ~3 F$ l; e- ^7 w8 U
9 {& U/ [3 A9 T, ~( P& l0 p
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。4 v+ m2 X5 a9 t* S% e7 }

0 D( r5 K" i) Q/ N主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
8 a  [/ I" J# l! D* T6 Q: o$ X! f* f( Z6 d4 @# y" n
    while(1)5 q  a3 X! G$ w: }+ D4 k
    {7 F( o; D% R7 `$ E9 t
        static char row=0;
. P) W4 n1 K: A5 A        __WFI();$ ^6 K+ M" j. I8 \, O7 C
        if(ep0_state & 0x80)    // request data processing2 U' K( v& I1 a
        {) [6 n# v! ^$ @  c" J
            if(ep0_state==0x80) // SETUP phase+ M* m! F  F$ J8 ~
            {0 J* l5 _( j9 X/ N2 L& G+ \
                if(!setup_packet_service())
. ]2 x7 i( c! r                {/ o  J) F8 P; t' U
                    ep0_state=0;8 p- H" h+ H8 n  Q, k" F2 ^
                    // not supported
3 S6 H5 f/ w- I5 @2 C; R$ ?1 M                }5 [" h  l, ?/ I2 Y9 J. d% A0 S6 f
                // ep0_state should be set to 1 or 2, if processed4 x) V1 t0 ?  m' R
            }" ]8 e9 O0 m( f- B6 M
            else    // OUT phase
6 l# T) J6 b  |( `! Q1 \            {
* t( a. x0 j% x% R4 G; e8 ^                // process data6 f# q& E" Q9 ], F, v1 ^4 d
                show_LED(*(uint8_t *)(USB_PMA+128));
1 B8 t+ i  V5 \+ Q- K                ep0_state=4;
* D; n2 ^" `% r  ]$ a                USB_PMA[1]=0;       // Zero length DATA03 G3 u$ h. o% Y2 [: v* W
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
& I6 b# E- ?( B9 x2 t0 Q  p& Q            }
1 S% f/ ?1 v& a! p/ @        }, [- _$ H+ G) r: q, v# A8 p. j
        else
+ F6 y) Q! o7 S        {
3 S; J, h) n% S8 m            if(usb_address && ep0_state==0)- P. O/ f4 D9 P; }
            {9 Y4 T2 H+ [4 P
                USB->DADDR = USB_DADDR_EF|usb_address;9 S5 q: c3 e. n% J! p
                usb_address=0;! \8 }7 d% ^8 y) p( A8 `
            }, Z( W& L! V  `2 r
        }
+ e# P: n2 n' m/ a0 {& T7 w5 W        if(row!=scan_row)   // new scan line, Y  K$ H& X7 Z
        {
" Z2 Y1 p( F+ M. E* B; k  B            if(key_state[row]!=prev_key_state[row])/ j$ n4 ~3 Y  ?- X5 Q  S
            {0 o8 ^1 h4 L+ u* Z! }2 M
                uint8_t test=0x80;% e2 q+ d% \  N, ]7 g
                uint8_t diff=key_state[row]^prev_key_state[row];: K( D; Z% K( e7 h% N" ~
                for(i=0;i<8;i++); J) A! \5 Q- [9 L0 `: ^+ ]
                {. R. g/ t1 H3 ?
                    if(diff & test)
, Z  s; \" g  u2 c8 E  s                        update_key_matrix(row,i,key_state[row]&test);( ?2 _2 y) X+ p- F) u
                    test>>=1;" W7 A2 y7 f. z. p- T9 N1 N
                }
& S, O* M9 l" N! I            }
3 a6 f$ p  i# S% P3 V/ T  R            row=scan_row;
3 ~& |" J% [$ b0 B. G5 R! X        }8 H# A0 Z% |0 }
    }- q7 A; J3 {4 R6 V$ U( A
; I' M: i" G6 n) C: E

4 b& Z2 i8 ~3 a: yEP0的控制传输,把用到的请求处理一下# h# a- D3 f/ c# b. v
char setup_packet_service(void)# P) y( \- i; ?1 c: T7 r' w
{4 E$ I  l! m: a
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific* O* v) ~6 V. F  k0 K  X! ?
    {) B0 T/ u; ^8 \
        switch(ep0_std_req->bRequest)
' {" B( [, A& s8 d9 ?! k! v& `        {
1 J3 d9 H3 U  i/ J& ], b' g; F) V            case REQ_GET_REPORT: break;% X8 S1 B% J- Z5 c/ n( Z3 P
            case REQ_GET_IDLE:1 N4 p. H3 X3 F( r% K6 ]- K) Z
                USB_PMA[64]=0xfa;   // return 1 byte
7 B5 m6 l8 X7 X4 \# k$ F: N/ {) |                USB_PMA[1]=1;
* J9 ?4 I, P. m5 }+ q% {, d                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;7 z3 z+ ?1 h5 a/ e  F- V, X
                ep0_state=1;
( e: D  I7 U6 W" F- p- n6 T. Y                return 1;
% H/ v; h! k4 u4 t. G7 F/ i3 k2 ^                break;5 G4 j1 i7 I# Y
            case REQ_SET_REPORT:
3 ?1 m) K0 @0 k( x  I( u7 X                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
) v- f% d8 L% y' N0 @% Z, c3 v. V                ep0_state=2;0 P/ U% h  g' j5 K
                return 1;
& ?/ b9 m' t+ D1 v                break;
2 G( `8 _7 V6 ^6 h1 E' e, e            case REQ_SET_IDLE:: O9 T6 m( U3 _; K: B/ n
                USB_PMA[1]=0;   // Zero DATA
9 T* v6 b& Q9 S, `                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;. \+ l& z1 P; Z+ z/ `! }# I
                ep0_state=4;4 D: T3 F8 e4 ]3 D6 K
                return 1;
* |% m! @- Q; I9 F+ P  A0 l5 j                break;& [4 e: D( B0 G' ^" V; |. E
        }0 `& v0 E# e* B/ M
        return 0;8 s) G+ D5 b+ s& n8 E
    }! Y* U8 _) M" g, r
    else    // standard
- G6 |, u  i& l3 J0 \6 s6 M    {
# m. p  V6 ^" U# }9 ^1 G- K8 j        switch(ep0_std_req->bRequest)
+ e) }$ ?0 e/ M4 U" A1 m( R        {
$ c$ u* {, x/ K7 J            case REQ_GET_DESCRIPTOR:7 ^" l. Q- ]6 [; [3 C
                return descriptor_service();+ G& o9 i$ J7 u9 B4 u* B- @# }4 m! u
                break;
+ j: \; C+ L' E( {! l% _) ]            case REQ_SET_ADDRESS:7 X0 ~4 t- E" j) L9 S! j( H
                if(ep0_std_req->bmRequestType!=0x00)
" ]) _* K9 V( F* u+ k. P                    return 0;
, Z" ]( Q) G& H0 o: |                usb_address=ep0_std_req->wValue;0 {/ X4 g% \: b2 g/ X
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;2 N, p. e& _$ c$ V8 A6 r
                USB_PMA[1]=0;       // Zero length DATA0
) G: M+ I" ~& u3 F; ?                ep0_state=4;    // No Data phase
$ {) d; Z- t: R& r) p5 n2 p                return 1;
- S& V  l% |7 g            case REQ_SET_CONFIGURATION:# _5 q6 A( R* \  X0 F
                if(ep0_std_req->bmRequestType!=0x00)" w! ]. `' n4 ^) K* v
                    return 0;, b" Q5 H9 i) j  G8 o) }5 S/ V9 y$ X7 z
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
) t9 H' g4 A4 h                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;' D1 ^% T$ G- E( D! H  `' H
                USB_PMA[1]=0;   // Zero DATA, T9 }( i. z' h7 v- |6 K2 O
                ep0_state=4;    // No DATA phase: F" x  y( U" p7 }4 f1 B
                return 1;$ Q9 N$ T4 I2 `' O( z0 H" b2 l
            default: return 0;/ S% x' y: y6 ~+ S+ w
        }+ j# ]0 {4 w9 ^' Q* E( P
    }% ^3 m( e0 C: `& ^! b3 p
}
' P) H( s5 a8 r; Z7 q7 o/ Q  a

! Q) ^: k8 q% ]' H2 |1 E5 l' ~2 M/ O5 I2 [
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的" f" _$ D- s1 Q9 v; w4 W. d- q
char descriptor_service(void)
4 T5 }! F8 U% K* h{9 Z6 J) s5 w0 [! ^  ?( C. U( a2 d. L
    switch((ep0_std_req->wValue)>>8)
( [; ^& g9 |, q8 ]$ P    {; R0 }' Z: l2 G' M% w
        case DESC_TYPE_DEVICE:& H# K, ^, l. u, z
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));) U$ Q0 R! S8 A
            break;
' a' ^- m, B) p( v, O, `% Y+ [7 B9 z        case DESC_TYPE_CONFIG:
9 c, X# z# D( q            if(sizeof(ConfigDescData)>ep0_std_req->wLength)
3 n* b- N' Y. `                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
/ B* L3 n4 j+ Z! K9 T, q            else
7 ^0 o) E) f$ F) V; @" z+ z                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
# B3 ]* w7 F- X" w  w4 }) ]% {            break;3 K1 C, k. |5 l2 l& o4 N
        case DESC_TYPE_STRING:
, _3 B9 g9 o  T  G0 X. u9 T5 k            switch(ep0_std_req->wValue &0xff)
* X3 Q/ \9 d. e# J            {
( [* Z* J) L; u8 c                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));; G0 \# t0 U8 Q+ X9 q
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
4 _3 e- y) V, P' V; e/ R1 G                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));) o7 s6 K2 `( n
                default: return 0;
/ P& _, w& k* \9 ?- X# _            }: o3 s# y% l7 p3 w2 U! R3 F: O
            break;
, s" A: }  R) O, b) q( A; k        case REPORT_DESC_TYPE:1 Q) @5 W3 H5 v4 c
            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
8 [* S+ F* G9 U, J+ J+ ]- @        default:6 A5 L8 J6 U& e5 c
            return 0;' b; _; e! Y: d' w/ V4 H% }5 I: P. @
    }
( k% V3 N" [; v/ ^! S- X}+ R% r3 d- p1 c9 i- E! i+ U
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
; \/ @% {, F" H, e/ v8 L0 evoid TIM6_DAC_IRQHandler(void)
$ s2 }* b" `  K{
# N& P+ g; R. k$ c2 w7 L$ ?0 v    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);+ O. T. f3 a) o3 }! [! O

/ P) l1 B- s! m! o
7 f$ G0 Z# P  I
    TIM6->SR &= ~TIM_SR_UIF;; C( w; R, Z/ Q9 g6 X; z4 P
    prev_key_state[scan_row]=key_state[scan_row];& T* |& z5 E3 l! l" [: ]: Z5 y
    key_state[scan_row]= *PA_IDR;   // update key states
" U0 Y/ E! n1 ]1 x  o& l% t/ Q4 D    switch(scan_row)$ V" z) A; E9 Z5 `7 r
    {
) z; y) Y6 h; Z# G1 Y: _6 ]- b1 d        case 13: // next row PB14! \* A8 k$ a9 U) c3 U8 Q
                GPIOC->MODER = GPIOC_DEFAULT;9 X3 [: o. T2 v
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
& L- g5 Z' P# ~                break;
( D# Z8 _9 y! _1 R; z, j' i        case  0: // next row PB15* T9 E: W* G2 a9 C0 J0 B
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;: G8 n5 [+ \1 W6 w6 F1 Y( A
                break;
0 z6 Q2 Y( T8 b. w3 s: D        case  1: // next row PB3
2 \6 D; r0 W  S- \- B. w2 A; `                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
2 b1 W# e+ i, F/ s7 F% E% `                break;9 p, u% `0 r& t& ?
        case  2: // next row PB4
* [) m7 V9 i: r, ?" [* _/ _                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;3 Y; {( M: B2 p8 c; o) }  \4 b
                break;; C' K( |. }) z4 `
        case  3: // next row PB5
5 N3 A1 V" n: x6 o4 c. o  ?# c                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;
# ]# f3 g" @8 _4 s# P7 a$ V. L                break;$ K" o0 I1 o3 W% U) m$ x2 q7 s
        case  4: // next row PB6, y& Q2 a* a+ K( `8 k9 }% n# G8 t: p
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
2 f) s+ Q( b' l+ x( Z. n# {                break;
- o! [- D# }; O& M, X4 k        case  5: // next row PB7
/ f2 \! h8 O, q7 ^- T                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;( ]" i: {# B% N( @
                break;: h' O2 t6 n- Q% `" R
        case  6: // next row PB8
* C/ H; ^3 d7 w4 ^! C                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
2 b+ D1 ?9 `' d$ T; z                break;) W* x3 L% U5 B9 L
        case  7: // next row PB9
  N9 h# u- t7 J                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;/ I, X2 X6 h" g. t1 u; z& C4 l
                break;
3 z3 m. }7 i. V5 `4 l        case  8: // next row PA8
8 l  ]9 f; B9 d9 O. _                GPIOB->MODER = GPIOB_DEFAULT;; W( G7 @, s0 R3 _1 V
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;# x' W$ C) c/ W% }6 |' X* y
                break;$ d# C+ y4 j6 I( m( c# M! I3 [8 F
        case  9: // next row PA91 h/ V# I6 I9 n$ @6 A
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
7 \) h2 i# b6 m7 T# {" Q                break;
* M6 S; F9 y. [: E# a! y# h        case 10: // next row PA10
8 S, ]! \  p0 o& G) t, l$ B                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;/ N' Q9 U5 Q1 U5 _
                break;
9 @1 K0 G. H0 _: X8 d) X        case 11: // next row PA15
6 ?+ I* B5 v, b* l1 |5 a8 f                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;/ h" m* g! W! ~' A
                break;
. s5 l6 H5 B9 v& f( K$ R6 v        case 12: // next row PC13
; P0 ?$ Y% O' V1 i                GPIOA->MODER = GPIOA_DEFAULT;3 ~1 v% v, x+ F1 E/ ^( U% t# e
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;0 ]! x8 O5 _3 x0 S, G5 J
                break;
" U  T2 V9 T8 }5 ]    }
& Q; m; x& E; ]) d4 ~    if(scan_row<13)
' b1 n4 i, k( T: w0 h+ r& J        scan_row++;6 I1 V3 M$ z9 S( N9 m' F( Z0 |
    else
, B6 F8 V3 p: ]6 s8 K$ i2 o4 G        scan_row=0;& @8 U% u4 I$ I+ _# ~  }+ O2 x
}
$ a0 _8 [$ w% ^0 q- i* ^- \# P
; g5 N7 D2 \* t* ?* e* q8 L' F9 N; V5 M1 ?3 ~% V+ T+ X/ v. n
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 3 e$ j9 B6 M5 u% J

1 Z2 \# Y5 h6 N  ~扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
1 I+ H* b( R! I/ a; `9 Q: ?1 D- P- j" ^' Z' V$ u. X( G
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:

3 ^/ U0 }2 Y& ~+ {8 Z* r9 M/ w, D$ m3 a- ]$ s8 h% a

1 s  ?! W# e! L; p; X% Gconst char hid_keymap_qwerty[14][8]={; b3 h, |- T: `) E4 v# I- r
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},) C9 y5 T; q7 z  k( f
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
% x( D# N' R! L! G% y' \6 a. E    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},& |. e$ i2 q; M2 Z9 X' ^/ A
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
3 Y' n$ }; x# l$ A    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},% d  c& n% Y! r0 q
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
1 K! c1 m8 k5 O9 b" d8 b    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},: a0 T1 ?2 Z1 d- M( v$ W0 T
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
6 J. Z& A7 Z, [6 w3 o/ s- }    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},; h: c+ h9 ?( h3 B# u9 t
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},+ p; R2 C5 c% Y" \' e
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},0 D+ X$ N7 X& t
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},3 [# z& A% e& V, }7 z5 [, ?0 a
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},7 R' @( d- c8 Y: \" \2 J* M
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}. |- x; r$ F9 j# \! j3 t
};
3 x: F! f: G% ^+ \  I& T  \4 j: Z2 k: K2 ?1 R4 W& \1 w
2 [* h* i; j& k9 z; o1 h1 Q3 j
const char hid_keymap_dvorak[14][8]={
* v) W, o6 S6 R& Y# ?" N    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
0 i, |; K% R( G* A3 A4 {) x" X    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
/ W- e' e1 `- Y6 J    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
9 `' G4 h  h, D3 p0 b- }! y  e    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
" V: s  v" j' n" q5 D    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
5 P" f* N4 ?& k* Y    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
8 R3 U* Y6 h2 @    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},. p1 U; S. A; s5 u
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},/ z8 M* B$ i1 R4 o/ @* K% D3 j6 f
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
, K+ d" |0 j  ]/ D+ i4 X    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
' S6 h7 _( d' q4 p" Y! x0 j6 p    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},1 p! w' F2 R& S9 }% _9 G, h
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},2 X2 M7 g8 W& r' A; i. ?# I3 P: J
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
( k5 P) }, S: `* G    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}! [3 |. }, s4 C  m
};4 ^& P7 {+ d/ d
* Z; _# `" X5 j/ X
6 d' R+ }  ~0 P) H/ Y- m. [
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
7 f2 u- m  d$ w) I6 P  p/ L% c. s( L
& l' ~7 l0 O  H8 yHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

8 G* P4 [2 F' S0 j9 i6 Z0 T( K0 [
! L8 |1 u4 J+ X6 y! C$ O9 I- Fvoid update_key_matrix(char row, char col, char onoff)
2 d1 q% K% a6 j, R; D{
1 E# }! i: _* {# K2 K( `    static uint16_t hid_report[4]={0,0,0,0};. e/ _$ k2 C3 I/ b* e
    static char (*hid_keymap)[8]=hid_keymap_dvorak;/ O6 |1 C0 T9 q0 d6 X; R1 ]
' X& c5 b; G" ]) P; `

+ q" r! _! S# x$ Z# I3 _; H0 W    unsigned char key=hid_keymap[row][col];: S! s, I" g: |
    unsigned char *report =(unsigned char *)hid_report;
* l/ g$ n* Y: E5 J( S    char i;
2 Y) ]1 q$ x' U4 [1 s( A1 G0 k+ s5 ?
% \1 m  m2 h1 H  @; H* y

9 h* o* \. q- B* x8 t3 t0 f    if(key==HK_MODE)6 |9 A: ^0 S$ [& T- Q) {* R
    {
4 O% K# b) M5 r0 {* ]8 Y        if(!onoff)
* o9 L$ F- L5 R" N$ [& y        {
+ b4 r4 H7 C8 P            if(hid_keymap==hid_keymap_dvorak)
% x) w3 Q7 D9 E* V; f: ]: c            {
$ y4 N" o; V3 e: }* L4 |3 \                hid_keymap=hid_keymap_qwerty;
2 P+ i+ v7 a% J. h1 W                GPIOB->BSRR = (1<<2);
+ V4 \3 C' R/ P3 d# e5 b            }
, d3 X5 D# A, T9 ]( a            else
. p! }5 X& B: p1 E3 h            {
# o5 X3 n5 }2 a$ \                hid_keymap=hid_keymap_dvorak;) q" A+ `9 ?3 m+ n' u. j( B# _
                GPIOB->BRR = (1<<2);
" @2 J( w: g/ x3 e            }
* [$ C6 W3 h( d) [  |        }% Z  Y- i4 W: g& a& c
        return;
2 K0 ]/ U: }9 }2 G$ A; ]    }
; V0 \. m; b, U" D& i- S: q; z. I" w. F! e* @

" a% X2 j+ F, s    if(key>=0x80)   // Alt, Ctrl, Shift
8 C( k* z0 S  y) {" J; s- f    {+ X* ~: w9 X# Y* z) }# l: }
        uint8_t bitset = 1<<(key&7);. a- p" Q* d5 z& T" U
        if(onoff)   // non-zero is key up. T+ ^! \( d0 u9 P
            report[0] &= (~bitset);1 o) P1 U; Q3 [1 t  u
        else
" [6 ?0 k) F9 O- S2 o            report[0] |= bitset;# H* }  p3 a0 C* T
    }
3 K" ?/ z9 C5 q    else
. F; d% m/ }0 @. w6 s    {
( p3 Q/ Y' m% K        if(onoff)   // non-zero is key up6 n  {/ t# F$ \
        {
" l6 H/ R: |; S0 O& X+ t$ n  \            for(i=2;i<8;i++)# o6 d6 a! J1 g; Y5 F' ?- _& \
            {& H4 i# @( ^0 ]; M* s" Q9 A
                if(report==key)
7 S1 P; K( c. n                {
1 Y6 M) f- \1 C                    report=0;
+ H, _; U$ J0 O5 O8 Q" U                    break;! Q+ ^$ a9 u8 [
                }8 A8 I4 x8 g) E7 X( \3 s
            }3 @- W4 _* g9 J8 ~( u
        }
: f8 S" [% u8 E5 p* f( H- W        else' P5 z: L; \; f2 i
        {# r( q# u; k& F2 \. j
            for(i=2;i<8;i++)( m* A; b$ s* ~9 g6 L( I
            {
, V$ O9 h/ a# F" g4 L8 ^8 I                if(report==key)
6 e4 B# D; x+ d* o- E                    break;
( F" z8 A% b. x' O" V                if(report==0)3 J* J+ G2 X$ P4 t- Z: m
                {' o9 D  U6 [! Z; F' h6 F
                    report=key;
) R+ o6 o& }* D4 {2 T  [                    break;3 U+ H; A6 q$ B
                }
4 s/ \% n8 o6 u2 y4 K& V            }
  ~/ \" o7 y- E% t        }
8 a3 O/ z& L5 z3 m5 l: Z    }
5 |1 s: T, [/ h: X7 @' X/ m    for(i=0;i<4;i++)6 C2 J9 N. \6 b: q. P
        USB_PMA[192+i]=hid_report;
/ ?) \" s# E5 u/ z  R+ S# d/ P    USB_PMA[5]=8;   //COUNT1_TX
7 Z6 ]7 A3 @' J: q1 g    if(ep1_wait==0), |# H. I, s! r& Z! z) \
    {* r9 G- ]- n, C% n: ~& t+ `
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;5 X: ]% p, t8 A- O" j
        ep1_wait=1;
& U( W, Q1 d  x8 ^" S: k1 {    }
; w9 j8 l4 z9 X7 u- z  Z" V}
2 _* F$ M' v! o$ y4 [
- g0 T* j" b; U( ?7 n7 L% X- D: T+ G# ?
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。$ e, \( w  _/ y
keyboard.zip (8.7 KB, 下载次数: 6370)
6 p' b% s7 ^! [, n' h

  }7 u, y  |8 `+ _4 T, k5 Y4 K: ~2 O2 N2 Z

- s2 f& \& V  c4 G6 w  e- N) A" [3 |4 O
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘) T( m4 m: [; I# u0 B7 w- m5 k. I5 P
不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
. K( k0 [+ ^; X1 K6 ]' `刚开始我以为要把打字机改造成电脑键盘
& [8 A. v, e- F* K不过楼主也很厉害!

% Z/ K* J1 R3 T& y1 n5 W' V$ M哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害* r* Q: m- K& L2 a1 e
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-12-15 04:12 , Processed in 0.262151 second(s), 28 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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