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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑 0 U- z" I" E; y* ^6 F9 q7 A% p

! W1 `" i! z, |0 h) _( Ghttp://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1. m  D7 p% K  k* Z4 `& \
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。+ |" W8 q7 @2 f

0 h+ I" E  X) C* `' @3 z在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。
, u. H& }5 T" ]5 I: s" x
0 f$ U  c4 G$ T0 c/ S, r7 N/ U  ]
235140i3a36qivqzuvmt5q.jpg.thumb.jpg
: q/ g7 U8 u( q
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。
8 b- H2 o+ n1 {8 | 001734klbyoluenuwz4h4b.png.thumb.jpg
5 o3 w5 i5 w7 z3 h. a5 A4 n! {为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:- Z+ S- N" ^# v4 \% k6 U
003625r2agx2f5v922cf2f.png.thumb.jpg
7 E, k# z) q: c8 a其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.
& I$ N# P# k0 j; o6 bDvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
$ |. s+ L+ p  o7 ~
005836yvs0wvovwsssgd3o.png.thumb.jpg
3 j! z8 h% E" w8 a! z+ SDvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
0 J( h! Y/ c$ O- K" k" c1 h& P* U' K
2 n; S( a3 e/ B  \; g5 i+ ?# u& P我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
1 c8 J; B2 x8 W# l; S* F8 t" M! E
0 P$ z0 ~/ L/ [7 e" E3 h到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
. {, {& S2 U% l& P  b5 x( j5 s+ S# B' }3 s2 Q! C
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
3 Q$ A" T$ p, X2 n' a. X8 n
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
; q3 u8 G2 A& }0 C/ }! C% @  L8 j----------------------------------------------------  分割线 ----------------------------------------------------------* m5 f- K. ~& o3 K
$ I& M  ~6 \( Q/ J* M  q& A
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
) ~! D% G: e/ n$ ?! L7 ?
020011osionbunl4ui44vi.jpg.thumb.jpg / F6 W: z2 H  O: z1 Y
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。- z# E0 I* d+ |$ B
020017j8ycmnv7788bqv52.jpg.thumb.jpg 9 m0 q8 t, U" W
特写,80C49
- p  g. m( N; G2 h 021040oujzuvtut6iujtvz.jpg.thumb.jpg
7 S! M  o* m) P5 l* wLED部分,使用了一片D触发器锁存指示灯状态.
. c: O( L" z2 |6 y 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
3 U& K% o( l  a: j" K+ }2 s  a暴力破坏,将80C49拆掉2 d* H. b* j" S4 R2 }) @
021113e48qq98vyohvhzzh.jpg.thumb.jpg 0 w4 y: ]# R! \* r4 ]& v9 [
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
3 L5 n( G. I  r0 G 021125nc9az6dj33rlds2r.jpg.thumb.jpg 9 H; Y3 V! k+ q
焊好元件后的板子,准备替换80C49
, j. \% w6 W4 b# H 021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
9 ~/ b, A4 g$ c5 I用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。" i2 k" d0 S6 J5 C; C, T) R
021104shifhnrqbr3o5nlo.jpg.thumb.jpg
3 B( ^9 P- R* |% O' h! o3 I这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.( F% P! F  u/ b9 ^+ x' O: f
022003ym1p9u4ug40280uu.jpg.thumb.jpg
1 @- p& A1 l2 ~& r( D开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。# _/ `! M! Z4 }% U- `. ~
023313kt141q9qajtol7ma.jpg.thumb.jpg
' d/ |& k$ k( b: T1 I我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
& p7 M  N7 z! b+ f- f; ^6 b5 g 023322nt7l5xb3ltttkltt.jpg.thumb.jpg 0 M5 {2 A4 E* c/ X: W' E! E% Z
主键区键帽就位
8 a% b1 X$ E! f* I! _! e 023331hin88e8wkrwzwikx.jpg.thumb.jpg
9 A2 G/ J+ E; U3 }' X4 n, @3 O编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。: v8 M  q* r) ?2 A) Q
023336wjzlgopugg1jyy79.jpg.thumb.jpg
: W2 {- G- }" W; f最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。, y0 s+ ^( j, E( D$ P5 t8 _
023341sffu4j3g2323h6fl.jpg.thumb.jpg
2 Z: _5 F: H" t4 V( W8 a, C7 w

5 K0 a( `; N; {; g$ G# M----------------------------------------------------- 分割线 --------------------------------------------------1 L# r" v. f2 a& C" `$ I" A

, t4 I. i2 P+ H, I) l& Z
6 P# T- B6 A7 [
: V5 |( N3 U) m8 C9 l$ ]
- G% a% @% {  ]& f

7 i0 w+ b/ L2 Q

; l# x, _3 d$ S1 @) \' Z% X% j

7 M' f! T9 W& C% k" E+ `* ?. t) B7 Z1 b. D( z5 t7 G% f& D

$ F) g7 S6 B* m& o( v( [) Y* ]/ X! A$ L# D2 G; h: E
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。" y/ K" _3 u0 I* Z# C! Q; [
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
3 d9 W" W+ ~" _8 u7 Z; f9 I# \ 104025nzibm2rmiomhyirm.png.thumb.jpg
% `" r2 E: b/ w' i! |5 ]: T
1 A6 }3 C& \4 q2 w/ a7 L( Y, G' c* s
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)) ~, o) [7 m: i" B( h; J
105004zkrez5houvkkznko.jpg.thumb.jpg 8 O- W, [& i. Y) x( w

- ?) i' \/ J/ X6 F) ?' \扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。8 r, \7 W. W0 L+ ~

3 [$ t9 m/ Q: w: d0 r+ ]! E这是我设计的电路图:
2 C( d$ M) Y7 ? 110344ej2z2oo2rflo7oe7.png.thumb.jpg
0 T) @' i: t1 f& e
( j; o/ A9 z6 [) ]) q
PCB Layout:6 ]+ M' \- v. {+ K1 u0 }' o8 j
110847jjbjvt34vwt3v5bb.png.thumb.jpg 9 M6 q: k+ [6 V' ^/ {6 w) e
1 s7 @/ U" h* K  Z' T0 K. R# O$ V
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
  N( G- E5 p- G( K9 @: V& U- {2 \" x6 ~' x

# O8 D0 p% c( }" C2 z; B% h0 k
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑
- \9 t9 O/ M5 p. b2 C  z
( A# l8 Z8 ?5 g0 j软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
# a$ h+ N3 r6 W& D- u9 P
3 f0 c( a6 ^2 N. g9 }总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:
$ j+ ?- w7 a7 e( c" v& `& Q' y" [ 113818pmrfsb6z0byt6t06.png.thumb.jpg
3 T: M3 d, }/ e8 P
  F0 E% T8 j% D+ F  H% b
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)4 V$ P. }) E9 c1 j

6 x- U# q6 q7 U6 l, ]2 KUSB的中断ISR,bare metal哦! P0 ~8 a* B: q  |9 ~6 A2 S

( [+ [6 L; C2 r  {9 Bvoid USB_IRQHandler(void)
& {% K2 @1 ]) [4 S* l. p; q{
) A8 ]2 H4 ]9 K% K& q    if(USB->ISTR & USB_ISTR_CTR): I3 `( }7 X' s+ r9 b, q2 r
    {! R4 z2 q4 M. E4 _- h
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
2 S2 e6 [$ U; D; c        {- w7 k1 [% n2 x  B& Q: a3 S1 G% h
            switch(ep0_state)
5 e! F' [. ?; y0 X9 d$ Q/ h$ h5 O# {            {; p6 |) J( P  n" H1 l# C6 e
                case 0: ep0_state |= 0x80;% o1 P% G# M1 l, |5 ~+ ]( R
                        break;
) {- N  K# {2 u. S$ D9 y  d8 k2 [                case 1: if(USB->EP0R & USB_EP_CTR_TX)
% ]% [8 ?% Z% b                        {
; W/ e7 K2 p2 {- f                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;9 y; X  Z' B) D/ f/ {  W* X
                            ep0_state=3;: ]' c5 i4 O. F* ^2 A& g& H
                            return;
. ^  f1 \+ Q& }, Z7 }* r                        }0 D. E/ Y6 s+ i$ H7 m
                        else
. |! ~+ B% `3 ~: e% z                            ep0_state=0;
7 t% T1 g! @  M5 @  _5 `                        break;- |5 ^2 s8 P+ T* t* a% n+ w
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet' N0 w. I. U1 S2 [" x9 f
                            ep0_state |= 0x80;
& U: u; P4 @  J& F& V/ C- N                        else
9 `9 C* Y5 h+ Z                            ep0_state=0;
7 F  c$ u3 {9 H8 E' K4 x                        break;& v, N5 [& H3 v. Y" C7 ?
                case 3: ep0_state=0;
3 W1 ]2 H: e6 \4 U7 N% a: c$ h0 Y                        break;# L3 h: [' z" T  G/ z( J; k- ]
                case 4: ep0_state=0;6 m9 J0 T  S5 L8 f7 {& {: y
                        break;" H7 E; y* l% [$ V
                default:ep0_state=0;+ R1 @$ t+ q9 h+ |! _/ f
                        break;
6 H. f3 v% Y$ b) W            }
& M8 a. A8 @% J2 r            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
* _7 l: r# r+ j3 o% ]8 j            return;
6 x( n$ W) O7 A+ O/ v. O9 J* o        }& d0 [0 ]% ]+ y9 F& z! j
        else    // EP_ID can be 1
" x( W3 q) n( x, _! }- a: l        {3 g! K. S0 \& x
            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag- c5 o$ {: e; ~% H" Y& Y. Z
            ep1_wait=0;
. z& f3 [9 r3 e- f$ I% Y) d* I! d/ A            return;7 _  r' P  g7 @" N  e7 q3 o
        }, b- {6 G1 A3 ^0 D8 O/ o& }
    }
- o. W  I5 P6 }+ X3 ^    if(USB->ISTR & USB_ISTR_PMAOVR)* u" Y" J" }$ S4 m) l3 D
    {7 x2 ^$ y) {* \  h
        USB->ISTR = ~USB_ISTR_PMAOVR;
$ d* b* d1 W/ T7 N    }& r0 [$ V& a; C, d3 |( H
    if(USB->ISTR & USB_ISTR_ERR)7 O: h) K- M, l" N! N  h! f  q
    {
( U7 z6 V' o0 I" Z        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear; {5 X. {# F7 J
    }
- T3 D& U( m4 f+ G# f- s    if(USB->ISTR & USB_ISTR_WKUP)& w- r5 e/ `; @
    {7 z2 w% z/ Y! u
        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
8 s( A3 C) x! e4 G4 p# I        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear. u" c2 s; {- S
    }
% H1 I# e$ p; U' g    if(USB->ISTR & USB_ISTR_SUSP)6 l! z7 _; R( X
    {, L2 f( I" _# j; ?7 L; C2 Q) R& @
        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend' @7 n/ L- x: h. U9 m% K7 i; D" H& J8 u
        USB->CNTR |= USB_CNTR_LPMODE;   // low power8 ^# z7 n5 f, Y5 }  G  u
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear' M3 n  D6 \6 [! ]- t
    }
& L0 p5 n) Q4 s  g    if(USB->ISTR & USB_ISTR_RESET)
( Q) i' b% @* w. R    {
7 j2 K- ?5 u; L# V5 Y0 U1 w        USB->BTABLE = 0;    // buffer table at bottom of PMA7 ?" C, C# x$ C( t% a  J7 v* r
        USB_PMA[0]=128; //ADDR0_TX
2 V4 e5 b" \2 R# ]4 p. k4 v- |        USB_PMA[1]=0;   //COUNT0_TX
1 l9 V! q( N4 y# P, D        USB_PMA[2]=256; //ADDR0_RX
4 r/ Z2 O0 `+ X/ r        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes( @2 X. t% F% L8 Q# ]
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;) T- D2 n" c. z* E3 Z" Y8 S
        ep0_state=0;
0 j6 T  v, L" A1 _! ]        USB_PMA[4]=384; //ADDR1_TX
7 g3 F9 G  l# y# _        USB_PMA[5]=0;   //COUNT1_TX
( c1 y: I0 v/ w$ g, i( \6 C" P        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
! }! l. D8 K# q        ep1_wait=0;/ ?- r6 g4 P: }9 ^
        USB->DADDR = USB_DADDR_EF;      // enable function
3 k  p- L7 f( V! y2 [        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
8 S) u7 K" D/ g$ l; J$ Z$ b    }
: l- ~6 G. E( F3 j8 G1 k; Q    if(USB->ISTR & USB_ISTR_SOF)2 J+ Z* r+ N4 h' [, F
    {' I; u  X7 y% L9 _" `
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear
: _% q' U: S5 d) ~: l    }" z4 |1 J  M  w! c
}- \1 q3 i) [* z4 T0 g# \* Z

+ `3 ]  u& d* W' t, |( `
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。/ c# Z1 k0 T5 i) K8 @7 _) T' j
1 ~  `7 N* B) j; }  K
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
0 Y. l3 R- T, ]- b. l8 _% o7 {" j1 ]; [' t
    while(1)" L& c! g: ^# f1 y
    {
# a! U% P( M3 w        static char row=0;
0 f* B; }& n' P# O$ Y        __WFI();
4 r% e+ d$ i; p        if(ep0_state & 0x80)    // request data processing
* \$ Q: \4 Y6 }        {
, Q7 i( x' S3 n9 n7 D3 d* T: O: S, d            if(ep0_state==0x80) // SETUP phase
# q  q# E* d( F" ^            {
/ c& `2 ?% @% L1 j0 m0 x4 ^4 F                if(!setup_packet_service())
* B! C( z4 E8 Q$ o  e                {
1 \; o0 H% R# u6 Q                    ep0_state=0;* t! L' C/ K; U
                    // not supported
+ c7 S: H$ }  t1 s$ W                }
( V) X* i3 v: G                // ep0_state should be set to 1 or 2, if processed
  {% u6 B1 |, ~* i2 y% e; A            }4 O7 Z0 `1 L6 Z8 i& n% u( S* }3 w
            else    // OUT phase5 j( A3 Y) R" {' U+ D
            {
7 J% S$ \5 D! v9 l) s' b, C                // process data
) s- F+ I0 W( N6 D( A- Q  W+ z                show_LED(*(uint8_t *)(USB_PMA+128));
- p$ o) W) H3 u7 m  ]                ep0_state=4;/ E5 N; U) I* [- D5 H+ t
                USB_PMA[1]=0;       // Zero length DATA05 _8 l1 t- D! k  ~" V$ d
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;7 F: J5 y3 L+ @+ H, `
            }
0 @8 p. z! u1 B) w        }  f  x( z# }5 |3 ?8 f; `) \1 O2 h
        else
9 m1 i  c8 k# u7 ~% u" h        {+ r* u# |7 z' j; h! |
            if(usb_address && ep0_state==0)
9 o( Y" I- Q. x2 E' [7 |            {8 q7 ?' Y) I) @* ?- P2 y0 t$ n
                USB->DADDR = USB_DADDR_EF|usb_address;
4 {; i+ X$ j' z0 k4 b                usb_address=0;
& [& _* l/ \( R4 T$ f: u. ]            }
' R) ~( N4 A5 ~8 e4 I4 l        }# f( i- U3 X- X& K8 u$ n
        if(row!=scan_row)   // new scan line
2 S& ], W* H1 T        {! e" j2 T  i9 K$ ~, N
            if(key_state[row]!=prev_key_state[row]), ]/ _" g, |& \2 E- _
            {
* [) H7 u8 J4 O' `" W& t7 A                uint8_t test=0x80;
% u" |5 q+ j2 N& ~9 b6 w& O$ |" k                uint8_t diff=key_state[row]^prev_key_state[row];  u# V# S" O3 P
                for(i=0;i<8;i++)
7 H" G7 g- T( r' L                {
( P# n) n  B/ a                    if(diff & test): G- V& M- m- }7 }  W2 r
                        update_key_matrix(row,i,key_state[row]&test);9 S6 Q& Z9 A* c/ ]+ T1 f( G/ c
                    test>>=1;
1 g2 Z3 M6 D3 @* a& b+ ~+ r  ]2 W" h  k                }3 p4 q4 y" b* ^8 g# W" D
            }
% C' r7 P9 ^7 t            row=scan_row;1 u* i5 R3 @, d% q1 c) S
        }
% Q- U+ O7 c; f5 U# ^! C6 F; ?5 l    }# [7 B1 l! Z% m5 m) G3 ~
  f8 Z2 h+ c7 v( o: i

( [9 ?, s- s. p' f; rEP0的控制传输,把用到的请求处理一下/ l) L. f# Y: O' }' A. O5 v
char setup_packet_service(void)
8 I- o+ k  l! c5 ?{
3 ]6 y7 g* ^1 |3 s1 R& g4 f3 R    if(ep0_std_req->bmRequestType & 0x20)   // class-specific/ V9 y" y4 C1 a
    {
; z3 a- G- G3 T6 w" L; u        switch(ep0_std_req->bRequest)& {( N& g& L' w$ s; m* q# [
        {8 E: E3 c/ d4 u
            case REQ_GET_REPORT: break;9 I" y5 ~5 Q1 u. A4 f( B0 {
            case REQ_GET_IDLE:: ~5 X; z8 |; \. d. b
                USB_PMA[64]=0xfa;   // return 1 byte- E: T2 \/ G7 x5 I3 s
                USB_PMA[1]=1;, o2 g/ g' O. `) c- I! J% l
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;  R$ H: a2 G) ^' n6 J
                ep0_state=1;
% B  N" d' w! }8 s+ {. J                return 1;
6 C, O' ?/ L) H, C, s. N1 P9 \* {                break;# U( F/ [  U. }4 V0 n
            case REQ_SET_REPORT:! n# v% l2 ]! }. e# h# c
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;" @8 h& s8 C  D  a
                ep0_state=2;$ }- u( k3 h/ z3 o  O6 T
                return 1;5 f7 w' U! o$ y
                break;7 a& n& ^5 s5 s) }8 M
            case REQ_SET_IDLE:, B9 ~  m& p4 |7 V- i4 q5 s
                USB_PMA[1]=0;   // Zero DATA
# j0 \4 s- \9 @/ p  c2 l8 k                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;  U! w- S9 j" Y4 u' ?
                ep0_state=4;" L3 F% y& X6 L
                return 1;% q: J" D/ p4 ~: d3 R; i
                break;
6 P+ [( ]' ]1 z" E- _" V        }
3 F8 v8 [: z+ C2 \& M3 N8 a2 E        return 0;% |' g7 {4 Z2 t' W0 K) ?) ^- \. W
    }
) J& ~+ P/ D  S; D( J# R    else    // standard
% I! s  a+ F( i% x& N5 U    {
; V0 o3 N) m. O' d5 j        switch(ep0_std_req->bRequest)
1 O9 A7 w$ o- o0 f. u) V$ O# I        {
: H& [9 U8 _+ a! [            case REQ_GET_DESCRIPTOR:  h5 o9 T6 y4 M9 C
                return descriptor_service();" f5 h0 `* J; ^8 C
                break;
% v6 C  X. F+ X2 U$ X6 C1 z: ~2 E            case REQ_SET_ADDRESS:
1 S4 @7 h. I1 M* K- s' n, f: b" j                if(ep0_std_req->bmRequestType!=0x00)
5 }; O1 J; {8 `& P                    return 0;
# Y6 \: C9 \$ ~4 U                usb_address=ep0_std_req->wValue;
" l/ W4 y' }8 g2 F- _: p                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
* s( d, {& o$ s) ~                USB_PMA[1]=0;       // Zero length DATA0" j4 v. r0 A1 ^% N6 k
                ep0_state=4;    // No Data phase: W" ?" f" {3 c0 Y
                return 1;
* V1 J0 n( v$ C# W3 {            case REQ_SET_CONFIGURATION:; F- N9 Q- X2 Q3 ?' z
                if(ep0_std_req->bmRequestType!=0x00)
7 c/ Q8 b, J( G& }- N                    return 0;
: T+ W8 S# p" q2 P8 Q- w* V; E                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
8 t4 a( _7 m" D; v2 a4 \4 i                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
: i$ T- @8 Y) r! G0 q5 c4 \                USB_PMA[1]=0;   // Zero DATA5 g: E3 T! c/ M' [6 P4 Y1 ?" j
                ep0_state=4;    // No DATA phase) I7 G" @7 H( F/ K8 ~/ B7 h1 t
                return 1;
  M- X8 J% k- f3 o9 {# c            default: return 0;! R  Z) B+ w! O
        }
1 s# u& M+ B( t* E    }, U* J' v, J1 |6 `0 ~9 |' P, O$ a
}5 K1 ~- ~6 |; K& a8 N) q

/ _) s# e6 `: H* m$ S/ o( y4 {3 m8 V! a2 w' t+ u: Y
5 E0 h9 k" d( }, L9 M
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的9 m" K( R% T! `, x. `! w
char descriptor_service(void)
4 q8 g: K1 Q* p' g2 E, d2 N! ]- z{
" X9 `. e5 \# @* C* c+ P    switch((ep0_std_req->wValue)>>8)
# x1 `% y( U( c: G  k3 i. H0 Y    {/ y& i+ [4 Q( ~  b; A
        case DESC_TYPE_DEVICE:2 ~) U1 p7 M2 K: V) o
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));+ {' n- }0 ]( F7 X3 z7 n, s
            break;
4 a5 Q; F. S* J, P3 v6 t        case DESC_TYPE_CONFIG:( h- l' x7 Y; E: D- T; {: u
            if(sizeof(ConfigDescData)>ep0_std_req->wLength)$ p4 g% w  Z# @
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
% |2 B, r- N3 t, P            else: J# H$ D- w( J9 \
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));5 T) U% v: C1 w. x: U6 J, v5 b. s
            break;
$ h) o. L; y3 |* K2 T2 L        case DESC_TYPE_STRING:
* @7 _5 r6 m9 F            switch(ep0_std_req->wValue &0xff)
9 K. O  F( h2 d% q; M4 H: X            {5 S; k8 n$ V, t+ T+ ^
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));4 K  h' F9 i3 N. s! }* S2 z
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));7 f4 C) c) a/ D  J! G4 [, Q* ]
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
$ G7 C& W( Z4 S# i1 H                default: return 0;
1 z2 s- I4 r0 I3 ~  s  D' d            }
1 h; {  y1 N* y2 G            break;+ {/ {7 P6 @4 }. y2 ]/ P
        case REPORT_DESC_TYPE:
5 Y; X3 r+ l2 v2 c            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
8 d! h/ ]6 ^. {% D$ s9 p- {9 P, T        default:
" \0 }8 o9 x$ _% f/ B. y& q/ c            return 0;4 o$ z# n3 Q. M/ k. H+ b$ Q2 }
    }. E# A; S2 d( |% }- w- S
}& X! f6 w2 a" o* _
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
5 c6 \( T% l" n7 ?5 t+ J; o: Vvoid TIM6_DAC_IRQHandler(void)
4 a: H" n( g* W! ~! K3 {; s{
4 H, Y) e5 k3 T, j9 C$ @    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
4 E: R1 [, n. U. a5 p* w& k5 V% k
; `: V, j3 ^$ _# f6 b8 X# ?+ ]$ @
$ m) v) H8 {- j
    TIM6->SR &= ~TIM_SR_UIF;7 z4 b' ^3 d; r7 r. Y
    prev_key_state[scan_row]=key_state[scan_row];0 T3 E6 w, T4 {( E) u& ?9 B! s
    key_state[scan_row]= *PA_IDR;   // update key states
/ e( ?, G5 D% J0 h    switch(scan_row)& E. E! d) l2 P, u& K! P1 n/ n" j% ]
    {
, u+ V# ^$ o5 T        case 13: // next row PB144 U6 Y4 g8 b$ U$ d* ^
                GPIOC->MODER = GPIOC_DEFAULT;
! `4 N9 r$ @+ M" ]                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;5 t: ^/ _% ^1 E# p* ~3 z6 E
                break;
0 t% ^  Q! M8 [# @  i        case  0: // next row PB15
! F$ L* G+ E' q6 N                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;
7 e9 o2 A. _; ~7 a0 b3 t                break;
* f  m. D$ P* J2 [0 ?        case  1: // next row PB3
- }* E: ]" W: R( @4 q  z# x                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
; A- |: F  J7 }$ @. E/ D                break;" j) A  S; u+ `/ b
        case  2: // next row PB4
+ f& b# D7 C6 X1 j4 o                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
6 P9 B' D1 \8 ?* x6 x! h2 M                break;% _* M/ x2 s$ h* q7 {/ B( t  Y
        case  3: // next row PB5
. e# h% ~# z' E) Q, b                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;- O0 J0 L3 z7 y- Z; U9 U7 @
                break;
% [/ {/ z5 _7 x3 v& R1 B        case  4: // next row PB6
% W/ z# P! _) C; {3 n                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;7 X/ Q1 l  ?# b- W9 n7 C" P2 }# j) }
                break;9 S3 Y1 Y5 P/ q) X$ i
        case  5: // next row PB78 S5 z4 u# R3 u5 q5 o
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;$ D3 O: h! ]1 ~& Y& L$ v
                break;; L, G- y, u- V  ?' q9 H9 r
        case  6: // next row PB8
" Y, l" r- [; Z# Q" E* O                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
, [; O; u; x, R                break;0 c/ G, a1 m1 H+ [4 L: |1 C
        case  7: // next row PB9; b1 F' ?9 W/ `8 q# E3 V. k8 |% k; B
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;
; S6 C1 k4 {# ]7 Z7 ~' t. [                break;8 {7 _$ @2 ?( Z$ z
        case  8: // next row PA8" s0 J0 z/ z* L  v
                GPIOB->MODER = GPIOB_DEFAULT;
% g5 W, s9 N& o4 ^' K                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;
2 z  n  @+ G) }/ n) H, R                break;
  z# m5 w! X: D  K- R        case  9: // next row PA9
/ ^$ g5 a# f/ E. ?1 X% ^                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
6 E+ F  W: W1 v# `; H  a! E: H* V1 L                break;3 [; O  c4 }6 B- W+ m, C; N8 V
        case 10: // next row PA10& y0 L. K# x1 I; P! z
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
" J% \% q7 n$ ^9 L# J" P                break;: v! w+ B, ?7 x7 J+ h; G; Z) ~
        case 11: // next row PA15
, H% @; L+ L( {" E                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;& \! v& F9 z" R, Y4 K$ N9 W
                break;
3 t& i& T/ v, X$ X& L3 g% r        case 12: // next row PC133 e! H4 l5 [) m, t* W! v9 d% L5 L; C
                GPIOA->MODER = GPIOA_DEFAULT;( {9 L. v* x2 c* w; D/ s
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
  T2 B" c' p" d7 x! K                break;
5 B+ i- Y  ]+ t$ l2 e4 z4 }7 d    }
0 a( m/ r+ ]& K: s; y  T    if(scan_row<13)
8 T; M: Y+ q0 d# H+ L. ]* X' K        scan_row++;
4 e/ W$ T$ ~4 S5 @/ f! u    else
" j0 ?6 ~4 u# P8 c8 }4 _! X        scan_row=0;
' p2 g3 Z! D% m9 J}% k: I/ _% T; z. {- T
6 ~# ~. ^5 X) ~( Y

# i) @; }) B; D
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
) j7 S$ z  _9 J3 e2 b, w
, I+ Z1 F6 F* M, e扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。  J% |5 U) K/ [- W
: U% {: t) m2 Z8 t
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:

" \' |; j6 V" P4 C- B1 n2 e* d# G) S; B% b0 M, o+ Q: u

+ v7 q  H" G* h1 S6 c. O0 K$ [const char hid_keymap_qwerty[14][8]={
- G' a) h$ ]5 A: `  L% g* Z# S    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
. W" f: ~7 e1 S/ T3 _. {; s    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},6 B( s* M4 i. \9 ?7 p
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
& Z# }# o" s" [! }    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},9 `" W0 m( J! F2 y/ [
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},7 H7 Y: Q+ B0 D3 Y( Y( h+ f, j6 K
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
6 u/ b6 @) l# R/ `    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},. D3 L. n. I7 Q( [
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},. Y8 x! H9 J0 |* n  j; a
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},7 ]8 O) x+ x! t% A9 t9 k. q' c) r" [
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},' j+ y$ L3 E; ]* p
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},% w0 L( L8 ]: S5 i' Q
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},: V9 H1 i6 g1 m# `; _0 A
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},3 _+ A. u% Q0 T: p4 v, F% c
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}* i, u# s2 q0 `/ Z8 m
};
: d2 Z( Q3 i2 B, Q' [1 W1 T/ P. ^  k/ e. ^

- g9 E& a9 }1 _7 g4 ^) Iconst char hid_keymap_dvorak[14][8]={/ @. G9 ]4 F% w) b4 W% ?
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},: ^- ~- m7 R8 t5 `' j5 ], E) q- F3 F
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
* V, k9 D% B) {1 a$ _; V    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},/ _1 o0 U$ [1 G6 E0 D" \+ T  G
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
+ i( {) Q. ~; M4 H! c& N# ^3 w    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
( P" h/ S  I+ i- f+ S4 G9 t    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3}," u7 {& x! E& x0 t& a. E# |
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
4 [; v- S1 G  T2 A+ W5 Y3 ^% l    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},! ?& W3 `" `- n3 t# M% z/ s5 L
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
- S0 q9 Y' ?0 |- l% P    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},# E3 o3 x0 A* z2 H1 l5 S
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
8 [# D0 s, K) O4 a. @3 S' c    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},8 O: _! X0 M% d. S+ D
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},0 I8 w6 F. y9 C3 u% c
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}! G. x/ r! g8 O7 v2 `9 T+ i5 V
};$ e# B& P) J( d
3 ?; A2 `, b! w# I4 d4 w
3 w0 G/ ]1 {. m& k
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。1 A. \# n+ P0 R4 }3 g% j5 O
9 F7 b! F( }. |. r7 |, m* s
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

! J$ S. _7 z/ s+ @; f! z
0 F- \& t) n" ^8 D3 jvoid update_key_matrix(char row, char col, char onoff)
5 f7 X. g( `( j7 f0 q5 G{! H7 _9 S+ l% y3 V3 K
    static uint16_t hid_report[4]={0,0,0,0};
( M9 w, s, f: @/ M  ?. F    static char (*hid_keymap)[8]=hid_keymap_dvorak;  z/ H9 P. L- ?7 w! K9 ?
1 @) k! f: W2 R. u+ u4 n* h* X! ?
3 ]( h5 L: y' T* o9 y& n) s
    unsigned char key=hid_keymap[row][col];4 H/ U& ^" N9 A4 q( T4 O. T1 J- U
    unsigned char *report =(unsigned char *)hid_report;
) O0 ?' d0 C) u    char i;
1 h$ K. P# e' @9 A0 K: j: w9 ~: w& d$ R0 x# x% P
& d% M8 s; [1 a- q! W' f5 Z, K# \0 W
    if(key==HK_MODE)8 c  q3 K. D& ~/ }
    {& @* ]' a" x0 Y+ ?, F/ Q  L) [
        if(!onoff)# h' V* I: J; L4 a2 o! t9 a
        {5 @+ Q1 L/ I5 {5 ^" J
            if(hid_keymap==hid_keymap_dvorak)
' k% Z) y- K! J- ?            {: k; o( l9 e, N7 E% O; E! T* ~' }
                hid_keymap=hid_keymap_qwerty;
6 {. S! i8 Y+ O                GPIOB->BSRR = (1<<2);
1 I5 N  P7 L3 K4 v            }
- y3 \; V& n6 y0 V' d1 w            else! [7 B2 D3 e1 `
            {
# \( Z/ C/ m' R! j( }                hid_keymap=hid_keymap_dvorak;
$ M! X3 P* f/ f! z                GPIOB->BRR = (1<<2);6 e0 u- |" ]1 D  c2 X9 D8 N) }
            }5 N: f& M2 j  A$ R! p
        }- L2 S! m$ A4 z, R. Y! \
        return;( |9 H8 g- p2 U4 \2 U
    }' h( h1 [5 t* X0 F3 \
1 v* D% M3 ^) C3 @5 E: y
9 N( E8 n5 g9 q) p5 v' {) ^6 i
    if(key>=0x80)   // Alt, Ctrl, Shift% _% m! A' B, n  D  H5 v( H' s
    {
$ r# h' {! E  _- h6 F9 \% q        uint8_t bitset = 1<<(key&7);
8 l: t' V" N1 L) {        if(onoff)   // non-zero is key up
( [- k5 n) x4 g" m' _            report[0] &= (~bitset);1 d: f. H/ F4 J5 {$ c
        else
$ b3 D- y8 F1 M% b5 d# x8 H+ L            report[0] |= bitset;
) U: c; W2 M( U6 [+ w2 N+ W    }
1 \" F- @% l; k7 m0 _3 h    else
+ M) j, ^) _$ h3 G* ?    {
" A' Q* S- s* ?5 a        if(onoff)   // non-zero is key up
9 m" \* ^/ o' W. R) N8 p" c        {
* t1 y0 S' p8 {7 [( V            for(i=2;i<8;i++)
, Y; h& p# _# }            {& w' Y" b8 Q" H$ v
                if(report==key)* }! e" L2 H0 u4 A; r# C
                {" Q- t" k( O% h- i9 M; S
                    report=0;) H! Q% A5 W, Y2 S0 Y
                    break;
$ h5 d' {! A2 \! k                }: Q) P) ]) v) g& C$ h' U
            }1 {7 e  D  A' `2 x" V$ \3 E9 Y! Y
        }
/ Y2 `0 V6 M1 M! Z  |. J& H        else
2 _0 \2 s3 \0 U3 \) o" E, j        {/ j1 L9 A: n/ Z# o1 H& B2 a
            for(i=2;i<8;i++)
) I5 d6 X4 R8 X; q7 S            {
% T6 G6 h: A% S/ l2 t( s                if(report==key); G) ]9 ]& m& h
                    break;1 b8 d# y9 F* k; t! l% d
                if(report==0)
' E$ X, q! h. t7 Z                {
" x5 C8 R; L/ I' Q. o% ~  a. k. j                    report=key;
, p# {( z  I( z                    break;$ y% u8 o. H$ x( a! J
                }
% d1 H' w" s! w+ D! n6 M            }- P" f9 g3 a' v8 w  O5 m, G( g
        }5 d4 Y8 _( @* p" c" m; e
    }
, h* B2 q: K( K: u3 X. ^8 H2 Z    for(i=0;i<4;i++)
+ J. d; ^4 T, \        USB_PMA[192+i]=hid_report;* \5 M8 I( B$ i$ E5 L  g
    USB_PMA[5]=8;   //COUNT1_TX
- M; e6 f& i/ T6 F% j+ r    if(ep1_wait==0)! @" j/ a  [& G* h) I# t
    {
$ G5 g7 y. f% L2 u9 x        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
6 t% C% `( E+ T* Z9 S& K        ep1_wait=1;# H- L0 @; ?/ n) f) s
    }
0 p1 B9 W  r, g# ?5 j) b% N}
, j1 A) \% _8 S$ W* m$ }5 {6 f3 F3 C# _# q9 k! i# s$ g$ |  M

6 ]+ n$ e% G' Z8 X6 [7 y3 {完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。7 A! {% b( a! @& c
keyboard.zip (8.7 KB, 下载次数: 6701)
' X* r0 R/ J: D/ O1 N& h+ R* V; M1 H
" r2 s. K  A3 ?; a- h8 z8 v& h
8 {: j# q% B) J* ?5 X0 ]
! U( D4 B( X9 V+ _

0 t1 ^3 _" j; F# y% `+ w; I
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘  E- P! c/ w8 o8 x4 y. Y
不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:486 M5 Y+ U( k, ]  p
刚开始我以为要把打字机改造成电脑键盘9 h0 i& }9 y8 Y% s7 F* c( m
不过楼主也很厉害!
$ I# B. o6 [3 k- k8 n
哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害/ [/ ?$ g6 M7 q; ?6 e
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-10-14 00:32 , Processed in 0.186275 second(s), 27 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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