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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑 8 Y+ P0 o0 f9 q
  g) G+ n: l6 e# {0 h7 a- k2 W* T/ h
http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
8 @" Q1 q; o$ {这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。2 i1 Z9 m& l: _: `" B  l( z

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

0 h0 ^, e0 C7 ?. k' v 235140i3a36qivqzuvmt5q.jpg.thumb.jpg ' {1 s. m, O( S" n
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。/ D  t$ m6 d) _% E
001734klbyoluenuwz4h4b.png.thumb.jpg " y; i1 p5 X3 g  S. k
为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:% u- W# W  G+ g  F
003625r2agx2f5v922cf2f.png.thumb.jpg 5 ^% n1 _9 c8 G1 j2 P8 j. Y; P; F0 D
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.  Q  @4 A8 }; X) x
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
) Y7 o. ^- }" _# Z' J' p3 F2 i/ p
005836yvs0wvovwsssgd3o.png.thumb.jpg # p( r: m$ ?( s# Z3 [
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。" U6 {1 _" e4 t

4 Q: x7 l3 J; B. `; s9 ^我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
$ C  F% p3 @9 e7 P
2 |1 _% t9 b8 a0 \& c! J到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。/ H0 U! [' q6 n) U* f1 i
8 E( X0 z  Q1 j& c& Y1 u
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
4 e/ a$ c1 g' w' m- a& v( X. b! I
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg . d% P  N$ x! M. E9 g0 K9 B' m3 C
----------------------------------------------------  分割线 ----------------------------------------------------------; S# U% t4 g* |9 D$ Y

! k+ r8 c/ r4 b$ I先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
- m; K- d5 A! {* L1 P
020011osionbunl4ui44vi.jpg.thumb.jpg
" O% z6 ]; {, d( ]' {轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
1 m  ]' {! e2 v% @4 @  ~* } 020017j8ycmnv7788bqv52.jpg.thumb.jpg * P4 o1 d4 Z/ t8 f' U
特写,80C49- ]# q, A# N+ F; ^; W$ r
021040oujzuvtut6iujtvz.jpg.thumb.jpg
  `2 T2 T& ]# f+ U3 \+ hLED部分,使用了一片D触发器锁存指示灯状态./ L1 b1 {/ k5 f2 o
022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
( t1 ]' X% L& S暴力破坏,将80C49拆掉
% C+ ]: [9 Y2 {3 _3 R 021113e48qq98vyohvhzzh.jpg.thumb.jpg - n4 x: e; w7 M% c; B
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。8 I5 a3 Y! Z2 Q% i
021125nc9az6dj33rlds2r.jpg.thumb.jpg
  X  M- ]& U% a" p3 ~  f
焊好元件后的板子,准备替换80C49' q) n% a0 @% t7 m! O3 t9 V; J
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg ; {  P" K, O  p( v
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。
- p1 c+ n) z1 d8 i" F8 ^& A2 C 021104shifhnrqbr3o5nlo.jpg.thumb.jpg
, a( S9 i2 r7 s8 S; M4 j4 G这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.* B" y# D* P. _
022003ym1p9u4ug40280uu.jpg.thumb.jpg
& K9 A+ f1 Q1 e# M2 I: V开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。
7 ], V& s! c8 o! P! ?  M! L; n 023313kt141q9qajtol7ma.jpg.thumb.jpg $ _; r  k% t6 ], w# j
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。- R$ {2 T/ ?0 t' E1 e" d( H# a+ \
023322nt7l5xb3ltttkltt.jpg.thumb.jpg
# @3 `" T' k0 b
主键区键帽就位6 d0 j& _' y& O! }
023331hin88e8wkrwzwikx.jpg.thumb.jpg ) o1 [; n4 a, [/ f) _
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。5 I( a! e4 e7 e  i1 O- ^: C: k9 H: J
023336wjzlgopugg1jyy79.jpg.thumb.jpg + [4 [8 ^/ i6 V& z
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。+ A% G2 O2 b! W2 b, a: v8 h3 H, k
023341sffu4j3g2323h6fl.jpg.thumb.jpg
& k/ x; N6 \3 f( f+ S5 B. O! H+ L; w

6 M% Z0 j( m5 Z/ s9 E# t: c----------------------------------------------------- 分割线 --------------------------------------------------' k# x* a# {  k/ E5 r( z
: |( p& X/ M; r; d

( F. A1 G7 W% v% R
6 j0 H8 b+ }8 @  }
2 I+ @; C$ R+ t! `( N
) R2 i. Z) ^8 o# M; O- _: w
8 d: S0 m6 |. K- S. r( f
6 ?; }1 L, B- ^. b) E
" D2 O, ^1 e+ g0 }
7 z! I+ G! @# l' d7 ^
8 k2 O4 e. W- m+ u/ W) J- Y# b6 U

4 U$ t) ~4 q7 \- l/ D
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
8 I8 {( {% Q: [& o6 T9 E80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:8 o6 b( E% r" J/ I& [8 |
104025nzibm2rmiomhyirm.png.thumb.jpg
. ]& k6 L' h' C6 @/ l
. \  u8 V& d$ A2 R& W! g) }7 f
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)4 N: @: @9 m& Q8 N
105004zkrez5houvkkznko.jpg.thumb.jpg
8 }1 E  \$ O# q& J- u1 n8 q+ s1 y. E
, x# }8 G1 n/ `. T5 L& I$ U
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
3 k' Z& \- W  Z+ f. b2 R% W
, }9 \$ R8 x1 ^  t这是我设计的电路图:
- Q7 Y, S7 X  M1 F- m# b9 C 110344ej2z2oo2rflo7oe7.png.thumb.jpg ! }$ l* n; Y& ~' {, I9 `  N  B6 R

" l3 f" m5 u# b' T, [! p! u/ HPCB Layout:: h, J4 d9 C/ K1 k1 \
110847jjbjvt34vwt3v5bb.png.thumb.jpg # \0 G  A* a/ r5 c" R
2 B+ H. U. Q- r$ z7 v2 e- l+ l
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. 8 v) t# u" o0 i/ ]& i, `) B! c

" H( \" H2 I/ e( H/ D/ a" m, E, K
  Z2 d3 M9 w6 ?& D
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑
; h# u% l6 ]  r" J0 N( ?. u; S
, \) }9 x1 h1 N7 V3 G软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。# ?: l3 _! D% M& K7 G  Y9 r0 {+ _

. S" Q( K/ ^! }& \6 A4 p2 ]总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:
: B5 T4 Z: |' V8 x% S( S4 ] 113818pmrfsb6z0byt6t06.png.thumb.jpg   N! K. m9 ?& O: V, j

: @/ Y* ~+ S& w- Z" z2 F( h其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
, y# U2 z- u, c) ~1 N5 V! [6 W8 f" B
USB的中断ISR,bare metal哦
% _/ Y8 N/ E2 e+ s! w0 u
" [9 u- M2 x: e0 G& \; I
void USB_IRQHandler(void); I9 t' Q& U9 m* R2 t  l1 k5 ^' F
{: _3 t1 l+ O/ f( W+ C
    if(USB->ISTR & USB_ISTR_CTR)
4 ]7 O5 w6 M  U" o" t! g    {
$ G( K# u, p) ^1 @3 b, {" I        if((USB->ISTR & 0x0f)==0)   // EP_ID==0' L: J3 L9 g* i0 _
        {
" c2 r4 g: k- M! r: N' \/ R            switch(ep0_state)) L6 c7 ^2 r# Y2 j8 D
            {+ c" H5 _) c2 a- f
                case 0: ep0_state |= 0x80;
, J% ?  d1 `' Y                        break;2 t; C5 B; q4 x% c# k1 L& k4 E
                case 1: if(USB->EP0R & USB_EP_CTR_TX), C& m" m# U' G7 {! r$ B" r) H! b
                        {# v/ J0 A( g7 g% ]* Q+ ]& L$ p
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
& V2 s6 b( d( p/ c6 H                            ep0_state=3;' k% a% D* J5 A$ g* ?. r
                            return;
# ^) g' m- G+ L0 J- C$ }                        }$ S2 ?9 F1 K: X+ q1 H, Z
                        else
: _( U, b6 a8 W                            ep0_state=0;
# u2 ]2 r# E2 V0 X3 ]- O6 l2 H                        break;+ L1 ]4 Z  C, _. ^' D5 U  }
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
+ E, n. f- V. ]. T! Q+ X                            ep0_state |= 0x80;
& l. ~2 B2 o7 M                        else
2 _& Z, H, E4 t4 S                            ep0_state=0;
) v: r. z8 K. Y                        break;
3 p5 D7 e( d- z: J$ S0 v$ e6 D, l                case 3: ep0_state=0;
1 y' k' g. P- |+ i: H8 F9 a                        break;
; w7 r6 P& ]# n" x# P( n                case 4: ep0_state=0;
% D( q6 g# h! U2 T: I' n                        break;9 J, I" f# m2 _
                default:ep0_state=0;0 G9 g* E7 `% G
                        break;; m* j# Q7 Y+ f' _
            }+ J/ i# q% O) c, l$ `2 y) |  q  V% l
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
% N: C7 W# H( y# E' f5 Z' E, j            return;
: H1 ?) O4 c0 w  S  ^) E/ Z3 @# P        }
0 p' ?# \; i1 `! v        else    // EP_ID can be 11 ^8 M0 [! u5 U: M% s1 Z. r
        {
0 D% M7 _0 x0 M2 l            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
3 v5 s1 `& C6 N1 _5 c& r            ep1_wait=0;
, v# H$ L5 ]! F" a            return;, P% R% h- t2 {1 \0 S+ q# t" m
        }! C: r/ s& Z) Y$ H, j' V( e3 x
    }) L. q7 K0 h0 f5 `
    if(USB->ISTR & USB_ISTR_PMAOVR); j0 Q, C. C( Q& K" X! [
    {' ]1 Y6 D& S- j7 |# H% f& W
        USB->ISTR = ~USB_ISTR_PMAOVR;
9 ^& Y7 B/ r4 N& r; b! z2 b  C    }) K5 X, z* V) m5 A9 c
    if(USB->ISTR & USB_ISTR_ERR). g( L) ?6 }4 ]
    {! Y6 n; ^) m/ ]6 h3 v0 M
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear; z, J( G7 D9 _! [0 t) N5 k
    }
- e5 W4 T+ H+ n& Z( y/ l5 [( g    if(USB->ISTR & USB_ISTR_WKUP)) L* E8 h/ U9 k: E
    {
' o  e( a. F0 G. ]        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
/ }& e7 v& e' S3 F8 v$ z        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear- c. p. L/ k/ t7 R" \) L7 k9 r! m
    }
$ i* _* U; o% A    if(USB->ISTR & USB_ISTR_SUSP)
+ y8 H$ l8 ~! i% ~. i8 {2 F    {, Y+ ]3 L7 [0 M) k3 i: p- b
        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
: j$ |! [3 n$ N- Z. H        USB->CNTR |= USB_CNTR_LPMODE;   // low power
4 l+ n  _# B: [        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear, B) I0 E* |8 G$ |+ w; m+ a
    }
+ ^/ ]3 N# H& ~. j3 M) L9 \    if(USB->ISTR & USB_ISTR_RESET)
( N9 ]' b8 U2 h) Y2 o    {
' w. |' Z: {! `) I3 n- p) c3 |        USB->BTABLE = 0;    // buffer table at bottom of PMA% T% y0 N6 H, A) A( s7 S
        USB_PMA[0]=128; //ADDR0_TX' z8 g+ ?* K- f+ R
        USB_PMA[1]=0;   //COUNT0_TX& ^3 c3 k2 d$ H3 S# Z& s
        USB_PMA[2]=256; //ADDR0_RX
- }: D6 p4 ?3 ]( P; i2 P        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
# \9 g6 w2 L4 t        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;! E5 h5 ]/ t3 h6 s6 q" |
        ep0_state=0;5 W& P# i1 k; [
        USB_PMA[4]=384; //ADDR1_TX
9 ~, g& F6 A" y% b+ `        USB_PMA[5]=0;   //COUNT1_TX4 T  M+ d8 K, Z5 f
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
  y$ z( c- u5 a* H' Y% }        ep1_wait=0;
8 w) K8 k5 |' ]% O9 p: a        USB->DADDR = USB_DADDR_EF;      // enable function
9 u% W& ]' ], Q4 r0 s        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear. z) V% F; }3 V4 P  |
    }
7 ~: ^1 n! M( ]5 m# F    if(USB->ISTR & USB_ISTR_SOF)
% D3 g1 j5 f7 R' }9 |  R/ D    {
# P9 X, J6 j2 e8 Y) u, s: ~) B        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear( W! v7 G2 I  Q( r$ @! Q
    }5 K/ J  U/ S3 c/ y5 O$ u. ?
}
) e: `9 L; ~$ H5 Q# S6 g" l! u/ U! _  @( y9 Q0 b+ o7 x) S9 ~; l2 c
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
. h6 Q0 W; N# l" O% Q2 X# m9 l
: q, `; W0 b( j2 F  z& M; `主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
8 }" s$ I" ?* ?! ~7 ?$ {, Q& P
6 L0 z, q4 ~6 H2 `! a0 C
    while(1)
5 `/ C7 E- z* b5 T    {! {* ^$ z; z% S# O  c3 c- y
        static char row=0;
1 n6 p' J2 O/ @& L8 a& E0 N        __WFI();
2 j( v" L! G+ N- e& a4 Q$ D        if(ep0_state & 0x80)    // request data processing
8 C. @& f4 K7 G! ]: i        {" S) \$ L% ]8 M* y+ s+ \" i
            if(ep0_state==0x80) // SETUP phase  m7 O, ^5 P; B+ i1 a8 ]: M
            {+ |3 {% M' A/ x3 U+ g
                if(!setup_packet_service())
! `/ s% j, R" \- ]                {0 |1 s1 L' P9 ~! k* ], M+ e
                    ep0_state=0;: q) Z! j6 @& I. e+ G: B2 Z
                    // not supported
4 r; J5 \# a. x; r" K                }
+ r6 ]' O; U1 ^                // ep0_state should be set to 1 or 2, if processed4 ?8 s4 y3 x2 K4 c
            }( T; Z5 W7 S- |( ]
            else    // OUT phase6 u( J* h- \0 l4 A# X
            {
0 e' v  c% ]7 z8 W! ?                // process data  v$ @# c* R  w$ k" w0 A# H
                show_LED(*(uint8_t *)(USB_PMA+128));! ^- }1 Q& k& M' M; W
                ep0_state=4;
; _; ]8 k1 F" l" U2 S: x: j0 P% |3 ?; y                USB_PMA[1]=0;       // Zero length DATA0- L$ w/ D. e% }/ K
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
) @4 Q# Y# o2 n" N% l            }
7 J' T2 r/ J$ m- |! z: C. N2 N8 n        }
' x+ ]: l1 C& W6 x. f" c9 x$ d7 m        else" g6 d- V6 I% w8 T- o6 g
        {
- V2 H  M5 ]1 \& |# R            if(usb_address && ep0_state==0)0 O# X# O# ^# c. s3 K
            {) }+ k( W$ _4 z" z! Y
                USB->DADDR = USB_DADDR_EF|usb_address;+ Q8 y9 Z( [) r% F5 n3 m. G
                usb_address=0;# {1 M+ ?2 \' k; K  w) @+ y+ x( M
            }
: V- |' w+ W9 [+ V7 e        }' T. U6 [5 y, ?" `0 p) [
        if(row!=scan_row)   // new scan line
: y# m8 _7 n4 X        {+ z  l" \6 ?9 I  ]3 W* L
            if(key_state[row]!=prev_key_state[row]). e5 H  Z% n/ ~! `$ |; k
            {
6 c& F( Y6 ]: G; U1 j9 s                uint8_t test=0x80;
+ ~4 B5 }$ V, x4 s                uint8_t diff=key_state[row]^prev_key_state[row];0 O, s6 k1 A* n; f& ^
                for(i=0;i<8;i++)
4 Q# L" a0 P- N* H                {
+ X- W5 L2 L# D' a1 a) J                    if(diff & test)/ i3 i8 {% u5 [- ?7 v, U* ^- p9 h
                        update_key_matrix(row,i,key_state[row]&test);1 c- J& O. n; @& Q' _9 O1 _+ U
                    test>>=1;( C$ b  p/ G3 A4 u6 f2 Z/ L% g0 o
                }' `# @0 j' }5 @: i0 r) h
            }$ K- k' m- k; A! J. t
            row=scan_row;0 O* n- {) h4 L$ y
        }
: v: ?; K+ }1 n    }; f! w& L. |7 m& m/ M
7 M6 d2 B( M) {

/ i' \$ L) S' BEP0的控制传输,把用到的请求处理一下9 Q+ C' {5 ^/ F: O1 }4 ?
char setup_packet_service(void)) k: {; _/ k8 ^' S7 A3 Q
{
6 z* R$ Q9 n2 J8 X& U    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
  B/ d+ c2 Q2 L7 o0 Z! j# u* ]; f    {! o" \. I" q, e  f, ?# S
        switch(ep0_std_req->bRequest)% @" s/ a" P. m. [' Y; S
        {/ l  C( g5 c! s' J2 v
            case REQ_GET_REPORT: break;
: t% s( c5 {1 @            case REQ_GET_IDLE:1 Y3 `) K" |6 }2 H
                USB_PMA[64]=0xfa;   // return 1 byte' Q! F: I6 k$ w1 ~: p
                USB_PMA[1]=1;$ |( H( B' @+ K8 ~) A
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
- w* M) h  R7 \; R( O& l: @6 l9 ~7 g. q                ep0_state=1;) G4 J8 p! D; N. @
                return 1;, F- Z( |0 V" f( p* A6 p+ B3 w8 H
                break;7 G+ h3 Q/ R3 v6 `' \) ]
            case REQ_SET_REPORT:
2 [: h1 ~" \3 h" j' C( J3 L                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;) i- M/ }4 c7 E: i) C4 A; s* k
                ep0_state=2;: E6 N5 z2 Z6 S: i% n7 p$ R
                return 1;
" l4 }. ?0 O4 M5 q9 ]0 o6 ^3 C                break;
9 t$ |: A" ?( D( k7 Q3 _# S            case REQ_SET_IDLE:) H- W% o! n: y9 q2 |
                USB_PMA[1]=0;   // Zero DATA% \6 m4 c- ^# s$ _
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;) G4 \" z# D+ }- s. z
                ep0_state=4;
' a/ {, s7 x7 x7 [9 [                return 1;
8 ]/ C- J# D4 u1 }" _  D6 |                break;
& {7 q. s6 R: E# P1 R        }
# i& Q8 c2 D/ v, N/ [        return 0;
0 N0 b( ^( Q6 k$ M8 Y" T- U7 a    }; ]) b% {1 ]9 |: @) t
    else    // standard7 q. @) \9 O, A/ {9 p
    {+ ?! S8 H/ m- x7 f) d5 R( k* U
        switch(ep0_std_req->bRequest)
" k) F& |+ }# ?- ^        {$ O, B* c- a( T+ W; Q8 d  [
            case REQ_GET_DESCRIPTOR:
! ]' ?; g* I: y; E8 t' i7 f  U                return descriptor_service();' F2 g6 y# ^( Q
                break;
" ]5 c- G( c; N            case REQ_SET_ADDRESS:
, l) [3 O" Y) d# w9 v                if(ep0_std_req->bmRequestType!=0x00)
( N) m! l' z: W1 |* |/ A# G, H4 M                    return 0;
8 L3 P! Y1 r  T. c7 `  }2 v                usb_address=ep0_std_req->wValue;
. M+ X# A/ O6 ~                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;) g/ `# M% u* B; `. U3 `$ b
                USB_PMA[1]=0;       // Zero length DATA0
5 U. K/ w' n! C- j                ep0_state=4;    // No Data phase
& N8 L; Z7 k8 |  V' a8 {0 R* H                return 1;
* A& M; r7 @' l' ]. I2 k4 r            case REQ_SET_CONFIGURATION:
3 E  l1 X' a% w/ [                if(ep0_std_req->bmRequestType!=0x00)
9 c( m* ^/ B7 L( A9 r8 |0 c                    return 0;
+ D4 k/ i8 K+ R1 F/ c' b1 ^) C% u                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;& Q# q1 h" E/ y( s% _! P, e
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
7 o! A  ~. O8 u' P1 B# O: L                USB_PMA[1]=0;   // Zero DATA6 G! V& C7 \2 c& j& b
                ep0_state=4;    // No DATA phase
% X4 Y4 |* h5 S) _' {( ~6 A                return 1;
9 Z% m  @% Y' G; {1 A% B            default: return 0;
+ z% |* c3 r6 g  R$ _- Q! j/ T        }
6 K" }+ p, D- U) i2 f1 L    }
9 K# z# e  \8 Z}
8 }& D3 u& c5 d# T8 B- n2 y- K, ?5 [9 M0 s( K- v9 c
8 n) h( c  d0 J0 h, K
4 O6 F  T  y: }9 O5 \9 }
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的% E( D( k; s0 W# H
char descriptor_service(void). y/ g+ I% R; t% H; S9 G0 H
{
- N3 A/ R  @& B4 u3 V    switch((ep0_std_req->wValue)>>8)  b( T. S5 A$ G5 M# w' H5 r, s
    {
5 d% u8 N( {& x; \/ C        case DESC_TYPE_DEVICE:- [7 [' o3 s5 N
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));
0 h. Y/ j" K( c: R7 M. H3 q            break;
2 E& H* v- S: _+ W. A$ j- L' q        case DESC_TYPE_CONFIG:9 \& v( }8 t- z; E) Y! U5 m/ ]- m- h7 |
            if(sizeof(ConfigDescData)>ep0_std_req->wLength). f" b9 x! w  j5 K$ U4 Z, m. ^
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
8 Q1 `0 A, j0 B' O! b1 R0 n            else% B* _5 W0 u2 H; t4 ?6 ^
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
& f+ F0 }! ]/ g+ Y. L            break;6 ?2 u1 q) o" b& ^# @+ z4 X/ R' C
        case DESC_TYPE_STRING:
; t  q) ?- D: A5 W) u            switch(ep0_std_req->wValue &0xff)
$ h2 z* e; D, D3 i& V; X            {1 u! ~5 o( }. q. \0 b2 @
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));, z, m9 s/ w( |, u6 j& Q& p
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));7 c" _, X' g4 l) I* V' j9 ~* Z$ ?
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));7 c. i1 G1 j, y9 J' O5 Z. Q. r
                default: return 0;+ U. _( P5 L  B8 }! M" ^  b- v
            }' B( h' C$ z$ ?, z& W
            break;
* [0 i) X4 G/ K" {        case REPORT_DESC_TYPE:
% o$ O! @6 G3 {  y$ L0 g3 k" h+ Y            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
  P8 R; d; I2 B0 I5 S7 ^) j        default:
( U) [. J1 `$ h            return 0;& e. I- \' i+ P) F
    }
; o; E6 [. P* m; O2 f}* @& {4 p* Q; E" z" G1 d: P
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.& j, _0 z' g7 S: C: p/ x9 [
void TIM6_DAC_IRQHandler(void)
3 ?( W( F- x8 ^7 h- x8 V- i* V{  Q7 U( }& i( s9 z
    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
0 m* E7 q# ?" j/ `/ G& E! r3 m3 y$ D0 K% N) z( f+ l- {

" q+ \! q6 W9 z4 \5 w" ~. l    TIM6->SR &= ~TIM_SR_UIF;$ V3 D2 t. K0 A# X5 l
    prev_key_state[scan_row]=key_state[scan_row];
2 m- D, _' f1 ~$ N4 j0 V    key_state[scan_row]= *PA_IDR;   // update key states9 T$ ]/ T# [& W3 _, m( m6 N& W
    switch(scan_row)
4 W% A: m- k6 i& N8 @, g    {. j  ~6 i, ?. d6 f
        case 13: // next row PB147 j) h" Z. @2 W" `3 J
                GPIOC->MODER = GPIOC_DEFAULT;
3 P2 y0 _, l( f/ x                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
1 u( E! q0 Q( i* O7 m                break;
  @$ @( [0 ~2 G        case  0: // next row PB15
+ o  i6 Z" T1 H4 W                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;
3 J0 v2 ]' E0 r: f                break;/ D' N& p0 A& L, |& n/ Y# G) w- s
        case  1: // next row PB30 g( C" m3 U9 ]
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;( \2 Y4 t4 c; `0 V6 I. D
                break;5 \3 [+ x) E1 h4 \4 O
        case  2: // next row PB4. D" w0 Y- l8 q  k% g  M
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;, ?3 g1 N% N- }# d0 O" X2 ~& t2 E
                break;+ a0 ?% f' u# }7 V% L. U
        case  3: // next row PB5
8 X$ r- I# ]9 j  N- j, [* J- b4 _$ S: L                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;
  W7 ^* D0 c" p7 o8 A                break;3 G( c6 z3 H+ x5 v+ r+ r" y  s
        case  4: // next row PB66 s7 C6 m: N+ ?0 f
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
) p8 {) k3 O- G) f  h! J                break;
8 s% P& O% n; K+ Z' Z0 u        case  5: // next row PB7; y7 T1 U  R; p7 v& O. r
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
% K5 E7 l# M6 D; P, `$ n( Y                break;. S- ^9 A3 x- N$ }8 |( p
        case  6: // next row PB8
4 n+ O* {' e; s2 E' a                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
1 ?$ ]' i6 [' z" i                break;! v9 {( \" n7 l  J* V, R9 W3 f
        case  7: // next row PB9/ `  `5 z" y0 U: d8 R! Q) [8 Q0 a
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;3 s7 Z6 e) x8 X) A0 N
                break;
, N  g, ]+ w' e8 P; w' `        case  8: // next row PA8) E' H3 ]0 h1 Z4 Q- S8 m
                GPIOB->MODER = GPIOB_DEFAULT;
% i% F6 A* g6 U; D% M3 M                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;. N! \/ _& Y$ G6 S
                break;
( ]5 k- f% A8 |        case  9: // next row PA9
$ i; E$ Z& [4 F! }                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
5 L, L# b+ Q; f/ S1 W& ~% }/ {  ~                break;
6 O2 l6 y# a8 p) ]( V9 L        case 10: // next row PA10, R* w! M; z9 h
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;/ h4 }; l) b: c$ r& j$ B0 v
                break;
6 e; o# A7 K, E* }& p        case 11: // next row PA157 i- h. S1 L' T5 U
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
! W$ d+ u) w) K" ?6 N. a2 B+ l                break;
5 D( Z' B/ P% q+ A        case 12: // next row PC13
0 q) Q' Y* H6 o                GPIOA->MODER = GPIOA_DEFAULT;
: Y/ l* r8 X% |6 B  P$ b( C3 O8 E6 |                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;% D! @2 l! M2 X. l
                break;1 u- g" G+ D) j6 ]  y
    }3 ?; R/ Y9 m+ e
    if(scan_row<13)8 u8 M  |( v. i# B2 }
        scan_row++;
8 u  g/ H& l4 ~& j7 s    else
0 u. O" X* q2 ]) b        scan_row=0;
8 n" o7 c$ g. \3 I- S% F}5 F: k3 d# Q$ k# x+ l8 B
: U/ y0 e/ t& `

1 m% A, D# S7 M$ X
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
9 U% L1 t7 e' Z5 |: h4 p
' d0 c* N: s3 S# }. ^扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
% k# l$ Z  i, w5 T" ]
1 p9 F& A; N. }6 z3 [2 H要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
# m- E) I* L! {

/ S1 Z% P! o; Z% t
5 Q: g! w4 @$ @9 rconst char hid_keymap_qwerty[14][8]={1 r3 v. b9 x! d
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
  C' P9 T% x5 M& C5 q1 b    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},8 Z* H- Q+ J* w" u8 y  ^
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
5 r5 w9 r9 y& Y- c    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},# o  c5 C$ [0 M4 b6 Z( N
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},% r2 ~: z- e9 F# ]( Z# B5 T
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},+ I7 ?% E8 Y4 h) O
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},1 Z! ]; u# e, D# L
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},( I2 j3 y0 K* C# h6 U
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
6 A  w( b& Q( E1 }    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
7 g9 s" R" v; i. c1 @# ~7 a    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
3 V, }! |- d. t! n0 V! J    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
2 I8 q9 H# J$ B) F, O4 g8 ?' W) I8 V    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},& u7 o$ V! e5 n2 d5 q4 I
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}8 }9 o& U. u* N  w: z
};
+ a$ k7 P" W% y! r) {7 i* z
. s* W( L1 N5 o8 D8 o6 y8 n

% ~: c) {6 @( [const char hid_keymap_dvorak[14][8]={. X: g) S. V) B4 g! b$ k  \3 ]
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
) ^) r4 p; Z, N. V    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
* G) O& X1 I/ J; t    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
1 {* l  C9 W2 I    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
5 Z. n3 v$ V  q! P- T% b( B  Y    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},5 D) z7 A2 z; S# v
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},4 D: `/ Z: K! u) x6 K7 }& U7 n
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},/ I2 P1 E; x/ `8 T: X' u
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
$ K: O  ^  t* m: p    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},1 [7 w2 o5 j$ u8 r2 \
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},9 `+ {1 i9 Y1 l6 t8 e( k( v
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
: f. z$ Q8 c5 ?( o0 ?5 w0 `3 V# r    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8}," `6 l/ M" p8 ]7 j) _
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
8 h& A3 ]5 ?/ w* u. w2 d    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
. v( j9 C! o# Y, b6 a0 h) v" v& x};
# {7 _% F5 j7 X% i4 T/ @5 \! X# C% i3 g( x3 k
2 c  d1 T% h) f$ n; \
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。' c2 K. \7 m/ T

1 ?# z+ o) N0 [  m+ e6 T( A5 pHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

, ~; N! I: i1 O, d  U/ s! g, K% v/ O3 f- Q8 u7 H; c1 k3 Q2 u
void update_key_matrix(char row, char col, char onoff)) [6 Y' Y. H* F3 N$ @# q
{  f; }9 X+ |) ~. l& U3 _# u6 u
    static uint16_t hid_report[4]={0,0,0,0};9 z* Y7 {/ k4 ^
    static char (*hid_keymap)[8]=hid_keymap_dvorak;) Y0 ^5 \2 A4 w( n. B4 H  C

7 g  l' n  o$ ?/ B& x. v) b: u1 D
1 Y6 `. z  m" R! b1 x) p: g" [3 W* h
    unsigned char key=hid_keymap[row][col];
2 F8 i2 f# v0 w: a# y! x    unsigned char *report =(unsigned char *)hid_report;
- d; p5 a! p) }) q+ p4 t; |    char i;  A8 @: k& c2 H' @. r/ `

6 O& @, Q- @* b) ]3 f
4 M4 F6 F/ N# ?3 }: D+ R! b7 {0 y
    if(key==HK_MODE)$ ?7 d0 R; p. M$ }) Z! V3 {
    {6 i) E- Q; t% d" S  S' T4 B) r
        if(!onoff)0 G1 R( _$ Q7 f& L  Z% ~
        {1 [, N: C) C/ S: q9 Y8 r5 t4 s
            if(hid_keymap==hid_keymap_dvorak)* N7 h8 W7 y0 K- r) i+ h2 o
            {' M, ^7 z) m  b8 v
                hid_keymap=hid_keymap_qwerty;
0 I5 Z* G, o* L; \( i9 ~( c$ n0 k                GPIOB->BSRR = (1<<2);" b0 r0 A1 f/ J$ h+ J# @, {
            }
' f: h  ?  e/ ?# ~8 G            else  x) p/ M7 B$ f
            {/ t7 X, Y) O+ T1 m
                hid_keymap=hid_keymap_dvorak;
4 C/ B8 [1 I- c, f6 e                GPIOB->BRR = (1<<2);6 \- j6 T+ C( a" f" F5 f
            }9 W/ H7 p! h) ]4 y* @5 a
        }
8 s7 [  R; p6 Q) k        return;$ y6 z" m/ i! q
    }
* Q+ e4 c5 k6 w
5 B7 M! g; ?: j5 k
; l/ e& ~4 r1 o2 `. M9 p
    if(key>=0x80)   // Alt, Ctrl, Shift* r$ H* p* N$ i; D
    {
3 q( C8 |& D% d" Z" q        uint8_t bitset = 1<<(key&7);
8 a# _6 N% x. ]/ h  I        if(onoff)   // non-zero is key up
  {9 R& T: ?6 O: I' D; O/ o* K            report[0] &= (~bitset);
6 Y; q: R& q/ m- [* G' \3 B. d# I        else5 k% A' m! Z" ~- s/ G" Y
            report[0] |= bitset;
+ P& {! O( d* n" Y$ ^1 u: B9 H" f    }
% n& K7 b" a# K) i5 {$ K0 Y7 o) E* |2 x    else6 N( k: A' ?: S; Y7 X3 K; J
    {) d2 S; f! [* ?: Q
        if(onoff)   // non-zero is key up' `* Z) w( j8 i  A1 @7 Z5 t& S
        {6 a+ D8 F" k4 p$ i8 Y( C
            for(i=2;i<8;i++)
+ o$ {- ~7 B. i' y' g1 f            {
" n; a2 ?4 w) K5 Y/ ^: c) `                if(report==key)
- p* F5 x$ o! l/ ?8 K( x1 P/ w                {3 y5 B( W: p5 T6 w+ f
                    report=0;6 F6 x% {0 y- s# ?6 l$ l
                    break;
% u" H( I3 m2 B' M' B& ~                }
: Q- [, J, q2 n, o4 d            }# Y: X% |8 n8 L6 A+ v. W6 Z$ b
        }* y: t9 s/ k! r  [' |2 s
        else2 o2 i! A' h% E+ K4 }. f. D0 u3 s: z
        {7 [# z0 K% S6 L- j
            for(i=2;i<8;i++)
/ q$ s8 d, ~( n! b/ F            {7 t/ r3 q7 d: ~" {" T
                if(report==key)
# y. d9 k- {3 H7 G) f" c8 x7 I                    break;0 ~9 w3 D1 E- ~' O
                if(report==0)
2 {, I8 E* \1 T& ?0 l% ^4 O                {* e: |3 L6 S. M* n8 k* Q
                    report=key;* @- c$ I1 A0 N. Z2 c
                    break;
& g0 U9 K# N- ]4 O                }
4 o" @. E: }* O4 h            }
$ \+ c* r5 }9 J# [- ?: v        }1 }/ N7 B- j0 K* j- ~
    }' _, B0 _, ^3 Q4 g' g
    for(i=0;i<4;i++)9 s( ~# s5 p5 J0 v; ~- T7 t
        USB_PMA[192+i]=hid_report;0 D* V2 R/ h* x  \* I$ X
    USB_PMA[5]=8;   //COUNT1_TX( W0 k8 ^. d- Y1 m6 g4 u" P1 r
    if(ep1_wait==0)
* Z: o; C  m' h% T    {
1 y5 H; d  [) s7 ?) G        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
& H, L4 F) ?( [$ i8 D        ep1_wait=1;
: U  h7 ?/ x4 h( q) j$ d+ o$ L    }
4 w  K+ J4 ]; G  K( g}
& B4 B: t+ Z% O% \% k6 k; o, |% o
8 o' N. E4 R: f  r; Q$ _
& J1 ~$ Q: G4 o1 w完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
4 {' w- X+ X1 \7 {' e) T keyboard.zip (8.7 KB, 下载次数: 6207)
, E3 w4 h5 U4 q1 `
, `5 G7 `, ^, J% L+ a' P+ s

$ ]& ~% e0 z2 ]$ L: K# Q. D  x+ f; ?9 Q+ _' B$ a3 k; `2 v! r; B
! _& n& {/ h! f; ^* F" H6 U+ W7 g
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘6 c( b7 P8 Y( U! b/ V/ V9 g6 [
不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
6 a3 D4 n4 N1 ~0 a' Y刚开始我以为要把打字机改造成电脑键盘! z6 r2 r' {) N$ j7 m( S
不过楼主也很厉害!

# @: @/ }- C1 x; }  o  q: J2 L哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害" D( j* w6 d  i  K
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-7-10 07:41 , Processed in 0.272120 second(s), 28 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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