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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
+ ~1 ^3 B& h  w# c! E6 t/ A+ u7 A9 D) o$ O+ y' T# g: {
http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1; h9 y- a! J9 r2 c+ J5 X  N
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。" b5 P# V; o2 e% t5 Q! Z
% m: `0 v- j8 B; S* f: H" \
在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。

& b" T' L; h# u3 b9 Y  g% d

/ c8 `3 e  p3 l/ [' z6 L% z) s 235140i3a36qivqzuvmt5q.jpg.thumb.jpg 7 A1 j, u# }7 u: M, I% y9 g: |
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。
5 N  k7 M, F" E- v( s3 a 001734klbyoluenuwz4h4b.png.thumb.jpg 9 X7 C5 c+ Y( }4 ^1 P1 g% h4 h
为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:+ E/ x1 v5 M' O3 C. }7 a+ x
003625r2agx2f5v922cf2f.png.thumb.jpg % P8 c6 b5 Q6 K8 |* c. b; g  Y
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.  L/ s; U: R5 d. Z" T- H3 C+ V4 q
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
8 E5 x1 I( A) r
005836yvs0wvovwsssgd3o.png.thumb.jpg
0 k6 \5 S6 O3 r) oDvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。; q0 c6 P4 J: v5 d( g+ x7 H$ N

! N& C! u6 {4 q! S, T( A我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。# r# P. a9 O: }( w6 `

0 ?8 @) }: ~" Y2 H, z- _到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。  z. j4 i2 \8 S

$ _/ @2 ]. a' O' I+ h机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
& N3 a( ?* B1 A) i
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
% r& m& p7 f5 i- g----------------------------------------------------  分割线 ----------------------------------------------------------
1 B' F7 b+ c; F4 D5 K$ J! [
# Q, ?' h; `( L先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
' t# p( N8 X$ ^+ d4 V- k2 X0 R
020011osionbunl4ui44vi.jpg.thumb.jpg ! T9 r: D* o; u2 p. }' X
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
) [/ j( t: N0 v) `. q 020017j8ycmnv7788bqv52.jpg.thumb.jpg
6 Y% O' d# t4 l( w! q特写,80C49$ a* H, o: Q( B- H) L
021040oujzuvtut6iujtvz.jpg.thumb.jpg
& j# x4 e3 L( c6 CLED部分,使用了一片D触发器锁存指示灯状态.9 z7 M7 A0 h% G0 }) u4 C" H( D
022209ixzjfjc9crmrrqjm.jpg.thumb.jpg ! |+ v- \/ ~! m: G7 g' E2 \
暴力破坏,将80C49拆掉
7 _" p0 b' I; L0 Q/ v 021113e48qq98vyohvhzzh.jpg.thumb.jpg
* }' T2 s: Z) E9 ]拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。& E8 P& R2 I) }# r0 G
021125nc9az6dj33rlds2r.jpg.thumb.jpg 2 _& Z' W3 O+ O8 T
焊好元件后的板子,准备替换80C49
# o7 q, u( n2 c% }" M$ o  j, u 021146zmmpmh4pav4o7g6a.jpg.thumb.jpg ' [: y4 Y0 l# j: ]  V( J. E! o
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。& n5 K( }5 {0 H, o& R
021104shifhnrqbr3o5nlo.jpg.thumb.jpg + @9 C/ \* }9 H
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.
- u, x% l: ?+ j6 e. a$ c& { 022003ym1p9u4ug40280uu.jpg.thumb.jpg 0 T4 q/ x0 r% M/ U1 R0 V) A* |
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。
" C4 {) J5 B6 {2 |/ _ 023313kt141q9qajtol7ma.jpg.thumb.jpg
# W) }+ ]5 N$ l' B+ s我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
. v5 p) t" s: P6 B 023322nt7l5xb3ltttkltt.jpg.thumb.jpg
5 L' u9 \' I6 B
主键区键帽就位6 f1 B6 E/ U5 ~) ?
023331hin88e8wkrwzwikx.jpg.thumb.jpg ) ^* d# o! q5 b
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。+ [6 t5 f, u( w9 ~7 m
023336wjzlgopugg1jyy79.jpg.thumb.jpg
8 E" c5 M. A" W. M最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。) E1 @* w$ R, r6 C: |; ?
023341sffu4j3g2323h6fl.jpg.thumb.jpg
7 U  s+ x6 _3 o% o
: l) t" s/ ^. P3 @5 {# X* F& `2 |
----------------------------------------------------- 分割线 --------------------------------------------------
$ Q+ Q8 g3 G( [0 p/ |9 G

% D( U4 D% _, y- v/ j3 ], U/ y
$ j2 A( f$ D" l/ i; C% Y3 g
5 c  e: ?/ r; I4 r1 o! _

$ A$ {+ C) k* v; Y9 Z) F
( c1 v& g% ^! o0 F7 w
; w" Y+ Q( m7 |( X0 }0 t. \
2 {; ^) R, C2 M

5 S6 [6 p/ ^4 g& n% Q
) Q& q4 x: C4 C& E+ R, b$ C: _2 q
2 \# _% }9 \( ~  C6 `/ h& }/ O0 x- R5 S$ a# D$ {5 X$ R9 i4 o
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
9 d9 o* L' T; _. ^& y80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
, X) k3 D: K! H* N3 Z 104025nzibm2rmiomhyirm.png.thumb.jpg
, ?! ^3 |% R7 z2 U" O
. L7 E1 x! }; F  n' A( c0 I" R
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)6 a; f  A2 i, p
105004zkrez5houvkkznko.jpg.thumb.jpg ) ?  C- ?0 z3 @9 m8 F
: g# ^# P& d1 X% z- {+ ~
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。$ v4 \9 c* l% q, M8 k9 O
5 n/ j- ?0 N4 h/ ]) @  i
这是我设计的电路图:0 K' V0 A4 g0 v0 x) }5 E( J
110344ej2z2oo2rflo7oe7.png.thumb.jpg 4 y. A0 ^, S' X3 L  Q. Q/ w5 k1 P6 l

  G+ `1 a1 @1 tPCB Layout:
& p$ ], }; p6 J3 @8 r4 D 110847jjbjvt34vwt3v5bb.png.thumb.jpg 9 L3 v5 C* K' |4 g5 Y
& E! I3 u5 q6 Q- ]( C4 u$ R3 Z  Y
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. 1 ?! [9 u% `- m- Z8 P9 ~
- \6 l7 K. w8 H" B

# s+ S+ D9 w- h, q- L
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑
$ O' P/ H9 N4 p  y9 g
: N. e9 D5 L+ j7 e, o( q软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。! p- W/ z- H, K- U6 p3 x& X3 R: m

( p& S$ T4 P; L; Y* [总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:; t5 C. O6 F  [  h  p
113818pmrfsb6z0byt6t06.png.thumb.jpg 4 x1 f& d$ v$ K& }3 X1 |
! v1 N+ e& ]. ^' ~1 W
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
! S" z! p' u- q& w+ T2 o1 I1 H2 G7 ~7 q) N* o, d5 w  c
USB的中断ISR,bare metal哦7 i* c0 E- T- @; d- n6 O  y

$ K" U; V, z9 R; N8 A5 Uvoid USB_IRQHandler(void)
+ ]- |0 r1 C6 U* U: l* O' Y. [4 [+ w{
, F- c) K; p' {8 o# r, F9 I    if(USB->ISTR & USB_ISTR_CTR)
- R# a4 W, r4 v9 V$ y2 U    {2 D8 ]. `  [: p  m3 Q5 i
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
1 \) Z( _1 _- t$ a        {8 W: e: d% }$ Q) F1 x
            switch(ep0_state)6 V6 o5 j- [( Z
            {
: E. Y. Z3 z  t; g% Y4 E8 G  B                case 0: ep0_state |= 0x80;; t2 h* t" i$ L; `, G
                        break;4 t. A2 K; g* _5 V
                case 1: if(USB->EP0R & USB_EP_CTR_TX)2 R6 S' Y  u# Z& O2 l
                        {
4 E4 i( V$ Q- R; [0 r8 y8 r                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;/ A+ V# ]' T9 m9 [3 q8 }6 T* @; \. a
                            ep0_state=3;
9 }+ v4 H4 z% Z& I. z                            return;' d: v  J0 L* i0 ]) s# `
                        }$ n3 D* G- s" K$ N7 E; q
                        else$ u/ `: S7 u  _6 t0 G( R
                            ep0_state=0;
4 t; S+ c- P8 C2 U. I' @2 d3 H                        break;
8 q$ E; i/ C) p2 m                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet) A" t: B+ p" s/ s
                            ep0_state |= 0x80;- b* u( u3 |, z* ^6 p
                        else
$ u( j. b. [0 l% p  j                            ep0_state=0;
+ G& e3 g) A) \6 g% X                        break;
8 B- m  b( ]# A4 b: m. s' _                case 3: ep0_state=0;
: E4 H6 ?: B' G0 m- b4 i+ I                        break;* Q1 ^) H( k- _' j2 i3 {
                case 4: ep0_state=0;7 h9 q$ a" ^" I' U6 f. U7 R
                        break;6 y# z( f- R6 |9 M" R
                default:ep0_state=0;
  }& a+ _. a2 ]8 m8 r: n) m; [: l                        break;
7 m5 `8 Y$ A+ E7 _) K            }
  e+ I/ p4 o4 ]% f# t            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag0 y% [; i: }% R' m( Q; M5 i
            return;
6 [  \  ]0 N2 x2 y( I% n2 J+ n        }) v3 G' a& c9 {7 ?! D
        else    // EP_ID can be 1
9 K+ W. G2 _) ]        {
# O- x! k! Z8 b& ~7 [            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag4 m& u. q3 U% F  S
            ep1_wait=0;" X* A. V& W8 U( {  v
            return;( Y) l+ J- d9 Z
        }  P1 ?# e. t+ w6 d) q  `, o
    }- K6 ^$ L+ _4 T# l- {8 B3 D+ o
    if(USB->ISTR & USB_ISTR_PMAOVR)( @4 s# ]# x' F3 f6 Y
    {+ C+ `7 j, q* k' A% U" q3 x
        USB->ISTR = ~USB_ISTR_PMAOVR;
; N7 C. ?$ Q2 Z, M# W- }8 I8 y0 m    }- Z* g' t/ x' t* t7 B. V1 N# X' j
    if(USB->ISTR & USB_ISTR_ERR)
9 M8 I) E1 d0 E1 K    {1 q0 E3 P6 k* i9 W5 l, q
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear8 O0 Z& F9 Q  k4 F( J
    }4 T! |6 r, {. w
    if(USB->ISTR & USB_ISTR_WKUP)
$ n1 m* f0 b7 z& D) [8 K0 S    {
$ X1 e8 Z4 t0 v* @6 d# R$ ?- e        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
' b; r1 b) l0 D/ P0 e        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
: w$ o! d# n0 F% u9 ~  R+ A    }2 U# l1 a8 a) f* ^) M' L
    if(USB->ISTR & USB_ISTR_SUSP)/ z$ ]# h8 `' ?" l/ Q- H) c- G) ]
    {
* C  |* o# S) q5 e4 f. y; K' C        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
& }% W! x' j( B  y( G5 @7 e* s        USB->CNTR |= USB_CNTR_LPMODE;   // low power
) W" h: }$ a! M; R6 ^8 u9 Q2 k8 ?        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
/ d) I" o8 [2 ~    }2 d8 g! ^+ ]( ?% x' w9 A
    if(USB->ISTR & USB_ISTR_RESET)
$ s% V% [8 W- x4 g0 ^& o2 ?    {5 D, A: [- J6 Z0 }
        USB->BTABLE = 0;    // buffer table at bottom of PMA
& i$ ?' Y- v2 [2 f/ v; O* O! `. R        USB_PMA[0]=128; //ADDR0_TX
& |& ?' U+ m6 d) i        USB_PMA[1]=0;   //COUNT0_TX
: S1 _1 `' y$ _7 r' {& K) K        USB_PMA[2]=256; //ADDR0_RX
) A3 E, X0 k6 Z( j9 b; Y* ~        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
8 ?. L; c) X* I+ f        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
  y; m9 l. a# }, ?7 x        ep0_state=0;
  g0 M7 ^1 L/ w) f        USB_PMA[4]=384; //ADDR1_TX
; F) U0 A4 A9 e0 E5 ?. v7 u0 V" h, q        USB_PMA[5]=0;   //COUNT1_TX
* p9 i; g( n2 W        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type/ C# f. C( E. ~0 {* }
        ep1_wait=0;
5 q  Z, ?9 v0 v8 Y0 k: l        USB->DADDR = USB_DADDR_EF;      // enable function7 N) K. J9 @( U( p! ?
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear' m% ]3 K* b; e$ v$ m
    }3 F: h. T* @0 r. K2 A6 I
    if(USB->ISTR & USB_ISTR_SOF)" W# @5 o9 ^* m: A# K/ c" u
    {
3 r. T# U8 M$ M+ D" a# X        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear
0 X& H/ k0 m/ \5 V" L9 a4 P. }    }
9 S2 q* Y& m* _}. p/ @2 D2 \# L, ^5 e, f
8 a) K  e5 ]4 Z2 _( \
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
3 y1 j' q& d2 W8 |+ ?  Y6 h. }
5 q7 A8 U' F2 d7 }7 s主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
7 a3 n, d  w" O4 G8 u- g9 h7 D: P+ A0 F5 w8 ?, d6 ]* c0 o- j
    while(1)
  t1 G% R) w& {# O1 w    {
7 I$ R; b' {$ H/ O        static char row=0;* d. ?0 L7 }: s: @$ c; e4 A
        __WFI();0 ]' n& m: E9 m; u& C5 ~
        if(ep0_state & 0x80)    // request data processing0 k% g  k; [  l2 H& Q, ]1 L  \# [
        {
" \! w0 m2 K# `; x# y            if(ep0_state==0x80) // SETUP phase. u1 `7 M' g% C0 r6 X, A5 b$ V
            {
% |7 h+ O; ]6 ~( S                if(!setup_packet_service())! W) Q# }. p" b' O  x& g/ F
                {' g4 U. S! X/ j! }" w8 u
                    ep0_state=0;: |5 I4 X" S( ~
                    // not supported
+ o1 P1 y, ^/ S7 ^/ {& g1 G                }
3 a, `2 Z9 c0 ^8 g                // ep0_state should be set to 1 or 2, if processed
+ j( Z& ?0 d# ^  I! n            }; w" n; u" e! I; o
            else    // OUT phase$ O2 t+ t0 y" k' I* T: c
            {
$ t9 D( a3 \# E0 C* d                // process data& e" J/ g5 ~1 p, Y, U  N
                show_LED(*(uint8_t *)(USB_PMA+128));) ~# s6 |$ u' e! X/ ^) E  u3 x
                ep0_state=4;
$ C( t. }4 U7 O" X! f                USB_PMA[1]=0;       // Zero length DATA0
" w' d* m" v1 W- e                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;  g# b3 n+ T5 M/ A
            }
, }1 o: T( L2 J* Z* m4 U        }
& S$ _. S1 T" |1 W. c, g        else  I2 P) q1 a9 L
        {
, r2 F9 M4 j5 ~5 M  O            if(usb_address && ep0_state==0)
+ V, o9 R2 E/ M/ \6 H" ?4 D            {
, ]5 f* e4 p9 N' j/ c/ b- w  A                USB->DADDR = USB_DADDR_EF|usb_address;
/ q' O/ ~" i" j/ m                usb_address=0;
1 y2 c) n2 S1 A3 T3 l6 [7 u. c4 C            }
; M9 f) u/ F: e5 c/ ~        }
- v6 y2 `9 S: w8 p2 ?% U        if(row!=scan_row)   // new scan line2 J; N/ W6 d, h& J5 k5 d
        {
, V0 m: A. s  f, w% `0 R/ o3 T            if(key_state[row]!=prev_key_state[row])/ Z% j  a& d, i
            {) x9 m. B1 j* a! m7 h! K' C
                uint8_t test=0x80;1 Q% M+ V# E7 _4 D2 X7 A/ e
                uint8_t diff=key_state[row]^prev_key_state[row];
- E1 [' w- J1 j                for(i=0;i<8;i++)" H  i2 o- O! b) B2 y
                {
5 G2 B( ?8 T8 W3 B- R5 e  `) `                    if(diff & test)
) |# S& u# o; W! }) U                        update_key_matrix(row,i,key_state[row]&test);7 V& i8 ^" `7 T5 o$ O+ b- p# _
                    test>>=1;
6 d+ S, ?4 S; H9 t3 m; m                }
. ^7 e# w/ [3 \            }" W0 g9 `% H& K
            row=scan_row;
& Y: A$ ^0 k3 K4 ]2 o( T3 K. s        }
  _- j* J& V1 _0 Z    }
3 p2 e9 t- k7 ]  a8 K# o" ]/ n( ?$ I7 O$ v- ?9 z1 L+ T
1 p( @% \7 ]& X& h& z  d$ O
EP0的控制传输,把用到的请求处理一下5 j" T4 P6 j- q2 w/ ^& F6 V
char setup_packet_service(void)
4 ?) I, Y. }* _0 ]5 N{
3 D+ Q2 n3 D6 o& u3 G. E    if(ep0_std_req->bmRequestType & 0x20)   // class-specific$ C$ J5 |4 k1 o, c: n
    {& o0 z+ F) ^" @& d- [! ?
        switch(ep0_std_req->bRequest)
" Y6 A7 z! T# t- |5 y" R' P& |3 r        {; j1 ^3 h8 R, G+ f9 u
            case REQ_GET_REPORT: break;- ~2 S0 P; D$ j  F9 p- Q) ~8 l
            case REQ_GET_IDLE:
9 Q8 F" o+ m- l                USB_PMA[64]=0xfa;   // return 1 byte
! {0 Y- G& P, m$ K7 \2 d* y                USB_PMA[1]=1;
  Y4 R( a5 U' D5 \, [                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;. `$ m- X/ ?% o2 {. \& H
                ep0_state=1;3 w& P6 [* ~* D1 q1 r
                return 1;  ]- H, a' R! _5 ]+ }  g  Q" d5 d
                break;0 |' o9 J1 W) e1 L: a5 J9 E( Y
            case REQ_SET_REPORT:' ?! {) _* V6 x7 u
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;/ w7 v% \7 A# w8 ^+ z+ F
                ep0_state=2;
' a5 o; w. O5 ?; _! U                return 1;9 p. ~7 B  ~' b1 P
                break;
: S$ F/ v, C7 ?9 {9 z, M: u            case REQ_SET_IDLE:( }4 m3 h3 M: x7 `' t
                USB_PMA[1]=0;   // Zero DATA
9 L( Q/ y) ^  L. b  k                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;+ k" o2 ]  H) C8 A. m6 p$ @
                ep0_state=4;* }) l( [8 Q4 C& Y+ _
                return 1;
5 `" e. p1 ]' {! |: x' W                break;( v8 r% B3 X/ O5 ]# H' k  Z
        }
/ q# k- p4 `+ _. F' r, E        return 0;/ K# q$ ^5 Z3 V( V1 S  D
    }3 m6 R& R( X9 M7 H
    else    // standard; i  n, j' y# O
    {5 B8 U9 c9 X+ H
        switch(ep0_std_req->bRequest)* }$ k3 O/ S4 I" u9 u6 C
        {4 l4 h- E. X# n! q8 G2 H% U
            case REQ_GET_DESCRIPTOR:7 @6 _- V7 ^  I; ~1 A: m/ f: H
                return descriptor_service();
- Z$ A' O7 U8 p, j/ y: H                break;
8 C1 t0 a2 m+ K; S. N            case REQ_SET_ADDRESS:) J+ F+ t: ]: c2 X0 V- m
                if(ep0_std_req->bmRequestType!=0x00)& d7 ~) V9 ]0 F2 W5 i9 g" A1 Y
                    return 0;5 p2 y5 s" P' A" }. Q4 G. E
                usb_address=ep0_std_req->wValue;
5 V+ T7 d  Y" T6 @$ M7 |! q                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
' }0 ?6 m. ?$ N9 ]: V                USB_PMA[1]=0;       // Zero length DATA0- X/ P& R" O; y: G  w
                ep0_state=4;    // No Data phase
7 ?% P5 U  V4 L( }                return 1;
. {' q% h: S( e( V! x% [! x            case REQ_SET_CONFIGURATION:
! b; }: K) X: k, J0 B                if(ep0_std_req->bmRequestType!=0x00)4 R) [! M4 j5 G/ v0 D* W# g
                    return 0;
+ @! p! h- \% T; N+ M/ B8 y1 ^* Z2 F                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
, j0 I9 ]) k3 x8 l  j- P9 ~                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
9 U" C1 v* u  \1 h: L0 A                USB_PMA[1]=0;   // Zero DATA# H3 m7 t, I1 r# S" w8 i
                ep0_state=4;    // No DATA phase
: k/ F& N; K  h- ~" l4 C( q                return 1;4 X) x7 j2 V  ^
            default: return 0;0 M2 P& r" r" b9 G
        }3 c" I  S3 y- f8 f; t7 D
    }- n, x: d4 R0 }; j4 W) i
}
9 U% U1 E* s8 C, p8 {4 W4 z- L* k* U5 T  h+ Z( m. D
4 D+ j$ |1 r5 E  N

9 I, h9 `$ f. q# }/ v7 w
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的  A3 T+ _* \  h( X6 s* F- Q' o
char descriptor_service(void): V5 {5 ~% t5 P) w. @/ i5 `
{
+ f  q' Y) n$ K4 Z1 P6 `    switch((ep0_std_req->wValue)>>8)
# c  O- u: ~' c1 a2 R8 t& Q& b    {
: X/ T/ d( L. s- ?9 }        case DESC_TYPE_DEVICE:
' U2 B$ F  f; c! v1 J            return ep0_preparedata(&DevDesc, sizeof(DevDesc));
- {7 b* [; g$ |) \) Q6 t            break;
  g% ?5 h- |+ |; x! O/ H1 p4 v        case DESC_TYPE_CONFIG:
" Z; ]3 G% B) M0 H5 A3 k4 e% B            if(sizeof(ConfigDescData)>ep0_std_req->wLength)% u7 N8 \; c; ?6 Y
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
3 k% b  _# S% l; J+ Y$ s; k  E, x            else( K, |/ v* v' E8 n
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));; X8 `( o2 P) i6 w6 A, z
            break;
! j" V1 j3 n# {        case DESC_TYPE_STRING:
- J. Q# D* B& |: x! N$ u            switch(ep0_std_req->wValue &0xff)
9 X. @2 {, W1 l, g$ H            {
8 t" G5 q6 b5 Y* c: b) t1 d                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));! ^8 e% K! i5 L/ U
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
! L) C$ X: z: o( {9 y+ E! t  e                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));+ l6 g. D- a% M/ {# P: \( d
                default: return 0;( D: M. p0 }* e1 A
            }
" r" E8 B3 {2 G8 f! ]6 n2 R- U            break;4 q# a: `1 T; m3 H  j
        case REPORT_DESC_TYPE:
+ g0 E3 E. C* L; g1 H$ P8 Y2 L            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));& S8 U5 i( f8 V
        default:+ {" M) U& G4 F5 v9 {) R
            return 0;
; J4 H& Q3 n# ^0 h5 O, E0 s7 s9 |/ X$ y    }
2 w8 _! v( H# V4 H* S, L}! @) k% y; \  x) e
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
4 a5 ~7 o2 b" j' b* F6 [void TIM6_DAC_IRQHandler(void)' g4 E# z" w: c  b$ @5 ]; b
{
, e- c0 C" O9 f! r    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);% V5 d, V" h( J7 ?- r0 W% b

' y+ F% S$ [+ _1 T3 F& d
8 E6 Q: ~! G/ [) {' S6 W
    TIM6->SR &= ~TIM_SR_UIF;$ A% v/ W2 U( \' z( z
    prev_key_state[scan_row]=key_state[scan_row];8 Z0 ^/ v+ L, y4 b( W+ _4 D# \4 D
    key_state[scan_row]= *PA_IDR;   // update key states$ U* p+ [) D2 D7 t5 \* O' C/ S
    switch(scan_row)
2 e8 k; P4 I# R6 j3 M$ U    {! n% c3 G4 C0 j5 @
        case 13: // next row PB14
  \" c1 m* K: M7 f( R                GPIOC->MODER = GPIOC_DEFAULT;
7 ]& N. A5 S) V0 x, n( Q                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
. x( ]& m; D2 J! g                break;$ u7 g" V" m. w$ c
        case  0: // next row PB15( L4 b7 i$ R# w( z9 K* ^: l4 s
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;2 O# \9 m; P& ~( N  x, ~, ]
                break;
# x+ u' J# o; y! U        case  1: // next row PB31 t( @7 B; m0 ~+ i( y9 r- U0 T
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;4 ]( P% d& ^2 w- b% {: H+ K
                break;1 j: V3 `  {8 D& H! k6 O2 j
        case  2: // next row PB43 L: r! T. J: x5 Z$ Y% h
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;. H; O# C0 i$ O0 b) K2 m
                break;
1 I% I& s, ~; b9 t0 L        case  3: // next row PB5: h$ E- t% P+ c0 A4 A
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;) V1 g) i6 v$ c" }- K
                break;
3 A5 I  g. \  \# M5 E        case  4: // next row PB6) ]9 A) Z* M, u3 O8 v8 S7 {2 g7 Z
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;" |0 e, }- r+ E5 C* c4 w, c
                break;; A$ X$ L: V0 ~
        case  5: // next row PB7
: V6 o6 E" u- G                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
. f$ N& }2 v3 t8 f! k, F                break;
5 N7 X' \4 P' m        case  6: // next row PB8* u, p% h5 t9 h5 a& A1 N& z& D4 y
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;9 f5 P: s0 k# K; F; p( C
                break;8 B% |+ G% T! `- o3 z
        case  7: // next row PB9* u1 q7 {; s9 y
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;
) r2 t/ N. ~) o( f                break;: X2 [) x% o. H( R  {; }
        case  8: // next row PA8
% n9 l! m1 h9 e                GPIOB->MODER = GPIOB_DEFAULT;
& B8 n% P2 x3 l& h  s5 Y( d! E                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;
3 w' c" H1 {$ s. y2 `                break;
6 L8 h. ]& h0 H+ c; M* W$ ^        case  9: // next row PA93 Y) x4 u3 N; Y2 v( {9 V- m
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;1 V& c$ Y3 N9 [) u
                break;
; O5 {: y: \# M0 i1 H        case 10: // next row PA10$ n; e  H( O' X5 j
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
7 s$ O  \3 v$ H. ^                break;9 C! U! A3 f8 E2 S
        case 11: // next row PA156 G  |; H) J( e
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
, V7 E# A, a' h6 [" S3 c                break;
# x' j2 v1 r' t        case 12: // next row PC13
, U  m. Z4 V6 h- p) P  W8 o                GPIOA->MODER = GPIOA_DEFAULT;
) {- V- T: V% R5 r; d# c                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
6 F# y: ?$ Y% }, I5 v                break;# T' q) i4 c: g
    }: h5 R. {2 @2 i" Y) Z; X) r
    if(scan_row<13)
" G5 F) I2 V# P3 M4 m4 ?8 L4 x        scan_row++;" b! `1 E) w* w5 W& v
    else  O5 w( e3 {3 R5 i! k* C7 I7 R
        scan_row=0;+ O1 }. w( n5 M+ E
}% _% s4 i) V; T) b
6 K2 m  P& i$ I! R1 |/ k1 i) j

" @! s$ o5 b- m. ^) w( P' o
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
- [$ u% B0 ~5 G/ B
5 `* b* g# H- r扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。. b+ P' b% S4 W
: \( I' P# P6 a6 z7 A" v
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:

# n  F! X& l* z% z6 @2 W4 }! z9 u6 z' J8 s. f$ ^  j$ \7 ^0 a- j

# L  p/ x) w( [2 }% i4 l1 tconst char hid_keymap_qwerty[14][8]={
; P  G0 X3 h8 M. F8 M! \    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
3 x+ [+ Y0 u! d( t# |    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},# T4 h8 X+ f$ e; L$ d# r
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
! G& E( n# Q3 _+ C- d9 s7 V- R/ q    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
+ U  a. a. Y2 G& h' F    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
6 \3 a5 G6 x! M2 d" D    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},6 `3 G. F; x2 m% o4 X' |
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
6 Z# g5 G3 a2 W' I& g    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},3 K( N- E+ l9 E( ]! U; t& Z' B
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},' {$ C7 r0 B! m1 N
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},0 N" Y! z7 Z4 b/ f
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
/ W( Z; l( g9 q# g3 g/ {  R    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
/ F# t9 V  [: g: i' o/ q: @( z    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},; T) w+ y- [$ C- S
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}6 z# x. B9 W" Y5 x% D
};* e0 \* g: v3 C, ?; B- c& G
: R- V. ?+ k3 r
9 j  J0 x6 m1 c6 `
const char hid_keymap_dvorak[14][8]={1 V  ]% J0 A5 [! Y9 _: ]6 F5 A
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},- [5 ~5 V5 \( W7 X; v( _
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
: L5 [5 L5 e7 w3 l7 q& n  Y    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
' T3 Q0 N' z# c* i7 S% d7 e    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},5 y3 ^; q$ W5 V3 h1 `7 N
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
5 _8 X5 ~  {* _! _3 X7 v; W; `    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},8 C' @( _- H! {2 Y" k! N9 D
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
* [6 f3 n% n3 p    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
  a  ?+ ~3 F  C  l0 x    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
* [9 n" m$ j% I7 l- U& Q8 v" Y    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},# x" r6 i  ^  C" b0 v0 T5 t; P0 l
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},. e4 i# s  k( c2 ~
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},, v% ~( L& X4 l' N! g% e1 `
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
5 r0 z# `; J3 W  d% @4 t9 |) J    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
4 p! u* J8 F& c" ]2 G7 d# R};
2 K, Z( x9 M$ T: k7 E1 X9 g
5 R. w, W, e, m8 q( c+ H% N. L4 K3 G1 M  _! X$ B/ Q, A
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。$ ?2 N! o+ B/ r; l

$ d1 m* b/ O) I' O# CHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

) y$ y( z: T7 b! g. K4 ?/ z6 {6 X; P" F" T! L5 |
void update_key_matrix(char row, char col, char onoff)
5 x7 r3 ]& L0 ]( j" |: ]& T9 ^{
: M1 d) w# i' ]: T5 n# {' E! m0 K    static uint16_t hid_report[4]={0,0,0,0};
8 y/ B1 |3 K7 S# Z+ z* i) ?    static char (*hid_keymap)[8]=hid_keymap_dvorak;5 b4 M3 b2 `' F6 f; K, u8 s* c$ y
7 _. _) F( o1 w
8 G8 h% m' @% o# P" ^
    unsigned char key=hid_keymap[row][col];( k5 X0 \/ [( x4 i' M; R
    unsigned char *report =(unsigned char *)hid_report;$ R+ @5 f# ?$ a$ \5 _. h0 g7 V
    char i;* `, _) \5 n7 ]6 H
- I1 {! q% N- T' ?/ N

6 _8 {6 }) Q* C& Z* E5 _    if(key==HK_MODE)
5 [' D5 ?$ f  R  ^$ ]% k2 u- T    {
* ]; i# N/ z3 k" v( d        if(!onoff)/ V, z0 R' r# _  @' N+ a; `
        {: i* Y0 b) n" P- E. v' a
            if(hid_keymap==hid_keymap_dvorak)( r/ p. s0 t) M: Y& n  i$ k3 T
            {! _9 `2 D! W6 i2 Z8 h$ {0 L' d
                hid_keymap=hid_keymap_qwerty;  z4 w% A9 p3 S3 |+ W; n* c
                GPIOB->BSRR = (1<<2);; u+ E: g* R& Z3 f
            }0 y1 ~* l4 G  Z6 E5 v
            else7 x9 [, c: J) v
            {
4 D: H) _# N4 B: R                hid_keymap=hid_keymap_dvorak;8 `$ j, ^1 G3 k# z( R  ^. G
                GPIOB->BRR = (1<<2);
9 W, A+ D5 z+ t            }
8 G+ S  d0 g8 ?" W8 s  G* _        }* ]) \1 J+ G, d4 q% B6 y8 m: C
        return;
3 B+ A# U, Y7 N) Y    }
1 y9 V& O$ Z# Y" q( e4 A$ H" X* s" O. @! ]- W+ x, B. i

% R& K) T  Q" Y4 s3 u% y    if(key>=0x80)   // Alt, Ctrl, Shift
3 Z$ U; x7 j& W% q+ Z5 c4 |' S    {! o' L( I" T# Y7 o! r$ K9 F+ U1 t
        uint8_t bitset = 1<<(key&7);
" e# Z; U$ R" `# T        if(onoff)   // non-zero is key up
% c2 x+ e. O- v* Y- @* E* Z. w            report[0] &= (~bitset);6 T, i6 a: o$ \3 d, q; `
        else' N/ ^1 ^7 \) M8 W
            report[0] |= bitset;7 o4 P( g* Z$ a
    }9 C/ ]6 D4 H( a' q2 e! u2 v$ R0 w& W
    else
5 k: ]) e; F# Z  A) c( H; ^# v3 Z2 l    {& P: g( i  z0 P- |2 e/ W$ i
        if(onoff)   // non-zero is key up
& t* X) q5 D4 _7 c- j( S6 a        {
# z  ?' b1 @" W0 a            for(i=2;i<8;i++)3 C- J$ ~4 x5 C; A* ^
            {& m) m# H  F6 j( y! \, t
                if(report==key)* I8 ]- C# @# }' E  j  Q
                {$ S9 {" T1 k. Z6 `: Y6 v/ M
                    report=0;
- n0 ?# c& u+ W7 I! n) R                    break;! E1 ?9 z. J# q5 u
                }
" e! O( c/ m7 ]% y, U            }, O; m2 h1 z* ~
        }. N9 [5 Z1 h) o; X  t
        else
  l- p; k7 v1 f        {
( `& g' [# O* Y$ \8 E            for(i=2;i<8;i++): Y  W) @: Q  {: n" s3 j! g* `
            {
8 K% O9 R0 K( D/ m! ?9 a! z                if(report==key)  X, A$ l8 V, h& m2 P6 v5 [- p5 Y
                    break;) [% ^- D6 T4 b! {* O3 ?  D" i: l
                if(report==0)
& W! T7 Y5 x2 R3 E                {; E9 M/ m1 w# c. L/ v
                    report=key;! K9 s( r: }4 A6 h" Z/ H
                    break;
4 X0 K% y" c+ Z& t3 N3 V$ R                }4 S7 c# G+ g& y' ^) O$ o2 K! j5 {
            }
( U- `0 `. D; K2 e9 d$ l7 l        }
" f; \' G! N( k2 r    }. M6 f2 Q$ l( M: k: H- g
    for(i=0;i<4;i++)
$ n6 A5 e7 s9 }) k9 {: C3 p, c# G% W        USB_PMA[192+i]=hid_report;
* J& K2 m7 e. _( m; L. Z+ b    USB_PMA[5]=8;   //COUNT1_TX
6 r1 j" w+ l' ~' ?" _    if(ep1_wait==0)
/ f, Z5 X0 g/ ^- _( q    {! Z+ y& t; X, {- B( x" `/ J8 w
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;0 d, u! B. P$ a, J8 g* j, }
        ep1_wait=1;: H8 O& k& X$ C4 x- r
    }0 m' i' v  x6 l0 T/ ~
}
2 T& @" q: A5 F. s+ P! C: m
6 N" Y% \8 F4 j; L6 Z4 r& u4 H6 a0 z& u% t3 T
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。; }# ]* k# X# G% S8 O
keyboard.zip (8.7 KB, 下载次数: 6697) 4 G6 d6 v1 K, I9 k2 i( @- \  i
$ T# P! V' T) w
: X0 H# c, \5 P3 L
. I1 I) O( T. n( c; S% m! i

' j" T/ |4 u- a' H( d
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘2 ~$ m! Y' }+ Z8 L; j3 B, Y7 |
不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:485 s1 t6 N* X4 S: G4 u  Z0 _
刚开始我以为要把打字机改造成电脑键盘
! A0 L" M; n5 D3 Y2 O7 K% ]不过楼主也很厉害!

4 E5 L8 E4 D8 X' o哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害5 o: M$ w' S+ A/ T
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-10-6 20:41 , Processed in 0.175169 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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