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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
  K' g$ H8 \/ _8 Z& W5 i7 p9 I* S) A# }% y4 T
http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
, _$ x" u  b" B# }2 ?这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。$ X. W$ F5 W3 P. |/ V

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

/ |# b4 \% y1 p2 H1 b0 O 235140i3a36qivqzuvmt5q.jpg.thumb.jpg
1 n6 N0 |( b- g6 d
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。& W- z+ r) P( C  H& L5 \& i; D+ n! g
001734klbyoluenuwz4h4b.png.thumb.jpg
& }8 w' S, a: T$ B# l为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:
& ]; z. `. m4 Y2 o  L* b$ i 003625r2agx2f5v922cf2f.png.thumb.jpg + G! D$ `3 _2 T. X9 D& l5 W
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.* C( Z3 G5 p/ ~
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:

. [5 M9 T; I, x9 A" i' L( a' d 005836yvs0wvovwsssgd3o.png.thumb.jpg 0 G' ]  i; G% H; C' M7 l  Y# _, X
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
+ B7 H8 B- K# C; o) A0 }* p) d5 G- e- y( A9 c
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
1 V3 n) C& h, w# Z( O
$ P  _1 J9 _$ [# g( [! j到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。* ~# M4 m! m0 P1 o; ?9 o

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

9 i# R" s$ D* c# O6 n& ^ 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg 8 v; p* f0 f& ^0 N
----------------------------------------------------  分割线 ----------------------------------------------------------9 d! Q6 k7 l! I. `# I- f2 W

2 I9 F' o/ _0 r0 Q$ f7 {- n先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

1 j- |; y9 P  ^5 y  _1 Y 020011osionbunl4ui44vi.jpg.thumb.jpg
  S5 o" Y$ z5 K! B0 E1 M轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
1 @: T" H9 D- }0 E 020017j8ycmnv7788bqv52.jpg.thumb.jpg
3 Y; N% @# v7 M2 x* e" z4 u- T特写,80C49
! ]) t$ m. X7 U3 m* E/ O2 X 021040oujzuvtut6iujtvz.jpg.thumb.jpg ; _& I- m7 n1 D/ i6 p
LED部分,使用了一片D触发器锁存指示灯状态.
4 f: w6 B4 d5 D2 p9 D 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg 5 _# L3 T" W) l8 [% ?: Y% z, A9 N
暴力破坏,将80C49拆掉8 B4 C. b' i/ i3 N1 q3 f6 W% C
021113e48qq98vyohvhzzh.jpg.thumb.jpg , A( W3 j+ |/ B: H
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。: g# o  V7 o7 K) ]
021125nc9az6dj33rlds2r.jpg.thumb.jpg $ A. x- b, n  z, o4 o
焊好元件后的板子,准备替换80C493 X2 W. Y1 ?# p4 H0 T+ q
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg ' c8 y' B, @2 O
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。
) K2 A: e0 p5 g6 x 021104shifhnrqbr3o5nlo.jpg.thumb.jpg
) U2 |+ o. i" {$ G这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.9 i7 Z9 c1 I0 K2 D- a
022003ym1p9u4ug40280uu.jpg.thumb.jpg ( S* T$ ?$ `) X% ]
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。! {- g9 y1 t  `. O4 b5 H6 x
023313kt141q9qajtol7ma.jpg.thumb.jpg , h4 a0 Q" o- x: q; E$ H; v$ Z- l. o4 J
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
8 V/ a) ^* o0 @9 ?( ^  G 023322nt7l5xb3ltttkltt.jpg.thumb.jpg / S; t" o8 {# \( J% [
主键区键帽就位
1 b2 g, Z+ I! ]& D 023331hin88e8wkrwzwikx.jpg.thumb.jpg
9 a5 @5 J) Q" G编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
% H7 K7 i8 K2 i 023336wjzlgopugg1jyy79.jpg.thumb.jpg
. A* a  i  r4 P6 ~" |最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。
6 Z2 H, ~# b* |( ?7 [( X, M 023341sffu4j3g2323h6fl.jpg.thumb.jpg # l( t) T" f) v

, }+ ]+ }0 ^5 E6 N/ J4 e2 w----------------------------------------------------- 分割线 --------------------------------------------------
: j9 u$ n$ ]% j% ]; m- ?4 K
3 H. Y+ d3 s+ f( R' _

) W! D( D: P" x) C5 R1 L
" Y" c0 C0 {; W% p) O- R5 v6 M! v+ l+ w! n+ Q0 e# l, V
9 @( J- q' z3 U. c, I
- w8 l6 d2 S& H7 N3 i
$ M  ~7 [  L8 z# a3 x: L4 Y7 k

% B+ f. a0 C1 B- B  F: f$ n8 {5 l; h' g1 n

3 R- s2 ^: Q5 F" N( z5 G, o# w
2 G& x0 H6 O4 y% t% r
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。/ M; P' N+ H, ], s' L4 S  S
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:1 O* h- K8 l- r6 M
104025nzibm2rmiomhyirm.png.thumb.jpg ( t) j' N3 l- _# p8 F
! Z' C- M* B- d4 O4 D! Z5 D0 E
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
3 F" O! ?0 |  I, r) t7 w5 n 105004zkrez5houvkkznko.jpg.thumb.jpg
* F: m. u( N6 f7 f3 V
/ q" S" X. |- F1 G: {. V1 P4 _) J
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。- a2 V+ d7 r1 q  i3 m7 ~
/ Y! w% w+ @$ S  b6 e" a. o
这是我设计的电路图:
: d" r9 M6 [& M7 Z. D6 o 110344ej2z2oo2rflo7oe7.png.thumb.jpg
$ j  S. x- O+ J3 F) k8 Y
2 ^" t8 Y7 v. X4 P
PCB Layout:$ K# i4 Q% `8 j4 ?  a
110847jjbjvt34vwt3v5bb.png.thumb.jpg
- @) {, g# K& i: U5 q: }* h, F
1 T+ G1 ~; [/ M4 }
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
, z7 t4 n. m6 E0 |( `. i0 j6 a- J$ y3 k  J& I1 c2 j2 C/ F  i

6 y0 `( \8 Y2 B+ P( p$ n
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑
2 F$ H  g5 L1 p" S  B) z7 |9 h5 f9 X  ^7 V
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。2 j9 u' l% h/ E* Y

" _( N& M5 o) t3 P4 B1 e4 M0 ^2 {. n0 b8 C9 r总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:3 |' _' Q' \* n4 n! m
113818pmrfsb6z0byt6t06.png.thumb.jpg
4 ?) z; f& B, F

! S" J! Q! w4 _6 n% a其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
9 Q6 v$ n7 A) N5 H7 M7 [% i2 D' K
USB的中断ISR,bare metal哦' w% C$ m+ A$ @0 k1 H7 R( [/ v

. T! d" V1 v4 L& t3 Fvoid USB_IRQHandler(void)
7 K3 I* w5 ^) m' E{, ^. _& W9 y0 B# y1 r
    if(USB->ISTR & USB_ISTR_CTR)
' f6 G4 t2 Z$ J. N    {
2 L$ S# g. j8 o6 L7 y! M2 t        if((USB->ISTR & 0x0f)==0)   // EP_ID==05 g4 U4 y1 t' ]" k
        {) b! ~% F9 ?  ]& f1 V% e+ a: {
            switch(ep0_state)8 X2 i& ?8 x& l# r1 z: ~
            {! X- |% f) a* M$ d6 f' j+ I9 z
                case 0: ep0_state |= 0x80;) j1 ^# ?2 n: b$ a) q6 f2 S2 Q
                        break;
6 G# f* [9 w, D% j% L2 k( W                case 1: if(USB->EP0R & USB_EP_CTR_TX)/ U, j8 J6 ~6 g2 e, O
                        {
' ^% T" V. E/ s" q% b/ v                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;% b- B$ D" f! l. K- ]3 ^& B
                            ep0_state=3;; u: b; k, A" ?: {9 D; s" K; L' R5 W
                            return;* u# M; J2 T8 f% |
                        }: L. e; E! b  `5 W2 q- g/ N
                        else8 q# S+ m3 [7 _/ n2 q, p
                            ep0_state=0;
9 s% E3 R8 X5 H( l2 n6 f. [% [                        break;" Z! k7 t) g$ E  J: z+ O4 f9 P5 V
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet0 w/ J: |- H4 j  K* ^, K: F9 x
                            ep0_state |= 0x80;$ l) ?. r6 ]* q
                        else
4 j5 `  a7 j! W. P! d                            ep0_state=0;+ h8 }# o! V% Z# b
                        break;/ R2 Z, o4 c% E0 \  u0 ?# B4 F$ `' i
                case 3: ep0_state=0;4 q  R/ }* ~/ v4 i2 L% ?
                        break;0 ]8 I2 G) ?2 N5 E0 X1 m
                case 4: ep0_state=0;
; A$ ?  h, x: z6 l5 x4 F                        break;
( v( \2 V( Y) O% N; D* v3 i                default:ep0_state=0;# o* X6 |1 w/ e* q" R8 F
                        break;% D+ S- D/ Q5 L  w2 L7 ?. v1 o4 `
            }
- T, U% x/ W  a7 S) f) x! ]* r+ D$ e$ W            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
4 Y4 i' M4 ^0 }) G            return;
$ J( f/ \$ D- U; k8 v, E  g! o, l3 o        }
) E6 E- x5 f0 m6 ^# a        else    // EP_ID can be 1
+ `8 a) d& ~) V# y        {
" j( d" ?& N% p/ w            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag  n" w$ D% u- i# _& ?, v0 v
            ep1_wait=0;
- k# z' \: [, k            return;/ P& Y1 U5 s1 m2 {
        }
0 y0 R4 t+ C% Q' B* X! s) p    }
* d0 ]2 K# S5 n2 a8 h& x- c    if(USB->ISTR & USB_ISTR_PMAOVR)9 l, l6 D/ u5 ~
    {
6 u' ^5 z1 m  l8 _/ O! s        USB->ISTR = ~USB_ISTR_PMAOVR;
0 X% }$ t' n  e( w    }1 ~- h) A2 {! a0 f  J& b) u% N( j6 o% K
    if(USB->ISTR & USB_ISTR_ERR)3 k' @% D! e" v8 d4 m4 W
    {
8 A% f2 ~! t/ O( O. J! D8 J3 R8 [        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear8 c% ~7 y# H, f8 A. p% F
    }
/ d, _1 {. U3 i, q  G( w) j    if(USB->ISTR & USB_ISTR_WKUP)
$ j/ V3 F. Z. x5 P) D    {
. l) p. h6 e4 ]: L* Y        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
6 x. g+ R3 s6 C$ o- J        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear9 `- Q! s, I8 ~7 k
    }
/ c' p" }4 A* C* l3 O    if(USB->ISTR & USB_ISTR_SUSP)9 R, C  |! i& O# z3 l4 d% \
    {
2 i7 U! b% Q7 P2 E1 i+ N( C        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend" L% ~: T: ?$ ^! {6 g6 a0 q' g
        USB->CNTR |= USB_CNTR_LPMODE;   // low power
8 T! Z. Z! K% w, m        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
5 X" a/ p2 b$ _' t% X8 g( x, F0 q    }0 z$ y0 L6 E+ m1 g! v$ p" P
    if(USB->ISTR & USB_ISTR_RESET)
& N1 s% w. O) ^! I    {
( @' b3 O6 ?5 |        USB->BTABLE = 0;    // buffer table at bottom of PMA
, }2 K' a8 Y5 k# |        USB_PMA[0]=128; //ADDR0_TX0 z, {& d$ F5 _$ M$ m0 }
        USB_PMA[1]=0;   //COUNT0_TX/ g  O; P5 X1 a+ @) i- A( Z6 J
        USB_PMA[2]=256; //ADDR0_RX
3 i. r  m+ P- T1 W6 d  a0 N, R        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
! ~8 `$ Y6 g( a% i        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;8 [8 ^' k: j* ]3 n
        ep0_state=0;
  R' [8 B1 d4 B; k, l        USB_PMA[4]=384; //ADDR1_TX
/ k+ f6 R# E3 e9 h. i, M: C) H        USB_PMA[5]=0;   //COUNT1_TX
9 U; {/ j  ?3 z2 C! p. m( \8 r        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
, a3 E% L9 u% U+ C0 p        ep1_wait=0;
' X& D2 W7 R8 U' {7 }        USB->DADDR = USB_DADDR_EF;      // enable function
9 E! c9 a3 m2 |0 i+ A6 t  {        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
3 T9 G  m3 x0 n, T    }8 s$ z* s( l, z. i9 O
    if(USB->ISTR & USB_ISTR_SOF). G; A6 ^7 ^# s* p" i
    {; J% `, ]' W3 M& W
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear
$ {% X! f2 v+ h! b    }
* E8 j, r! N( ?3 i2 H) f}, d+ j; j" l3 D# i
& b, I: o) @  K5 k: _$ @/ Q
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
( v! ^$ C8 p( l4 A/ R: E7 O: t, a; W- r  h
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
# m- B' A% b* m# }
' R. B2 t) n2 F
    while(1)
6 u3 v+ X2 v) z0 J    {
, y5 F8 [9 ?1 u+ b        static char row=0;
* h8 }. \" d& c        __WFI();0 V- e+ ]' j3 W2 |. g( n, ?$ F
        if(ep0_state & 0x80)    // request data processing
5 Z. ]8 f# W) B1 z# e5 Q# \" u8 |        {
: R8 o3 ]) Y; j, g2 a            if(ep0_state==0x80) // SETUP phase
' k" ?/ a% u* w: e            {2 C' u* h- \' _" Y* ]
                if(!setup_packet_service())0 q. w6 c/ K$ ]1 k9 T
                {
$ k; h2 v/ C9 F                    ep0_state=0;8 j2 J8 _: G& T/ _) B/ G) O
                    // not supported
* b$ X( V3 w4 B: m7 b! ^1 F. n                }
, d" a6 e4 ~! o7 k: Y                // ep0_state should be set to 1 or 2, if processed
/ N, d! |( z2 J, ]5 d+ n            }
% j6 }/ K8 ^9 o# x) y            else    // OUT phase
: u- y) h% m4 S: b' B            {3 P+ j- I( l( y( k/ v( F
                // process data1 l5 {' j, A0 R
                show_LED(*(uint8_t *)(USB_PMA+128));$ v- f) @% A# f: U2 j5 W0 u. H! ]
                ep0_state=4;7 a; E2 s  [& I0 d: V( |
                USB_PMA[1]=0;       // Zero length DATA00 U% V- R& H! k2 t* J% N6 `. o0 U
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
: r2 Z# z# Q+ Q! i            }
2 |# U( U" `) ]9 }* ?! A2 Z) M/ V; Z        }
: q( C! [, F7 s6 K4 l% s7 o        else
1 H5 Y# {# }5 p* ]9 B8 u9 v1 x: F4 z        {
1 d( P8 G' V. m, P% C# _8 m            if(usb_address && ep0_state==0)& k; ]: ~9 Z3 X" z" I6 u2 u2 l" H& I
            {
- k" t/ U  ?* \, _, q                USB->DADDR = USB_DADDR_EF|usb_address;
- U6 \" d! J3 W+ s                usb_address=0;
5 t+ V  B6 s0 Y; i& \5 j- U& [            }
4 J1 o  U7 T& j5 i* Y' L$ k        }
1 n3 W$ {; c: `5 G        if(row!=scan_row)   // new scan line
) v) n. O( n+ F5 ]        {. v; ]7 p9 D0 o3 M! C+ i
            if(key_state[row]!=prev_key_state[row])
/ C( K+ H* b4 G; }  C            {8 E& Y! ~; Z/ m: j& `* B8 z9 h
                uint8_t test=0x80;
) O+ V# \* ]0 i! P. @                uint8_t diff=key_state[row]^prev_key_state[row];; D9 S* J  v9 g% z% e
                for(i=0;i<8;i++)7 c( h$ S* e1 ^8 m/ E4 q9 y
                {" r& g) O1 Z0 y! u# q& R# k0 o! `" X
                    if(diff & test), {' C* K3 L/ Y3 j. s
                        update_key_matrix(row,i,key_state[row]&test);
% c1 t8 L& F8 `0 [7 z  g2 k9 i4 P                    test>>=1;' z! }3 e: ]8 E- }
                }4 y" o1 c. C! X* D/ N7 m8 ?2 K
            }
/ v/ j8 ~) K+ [& G            row=scan_row;% G7 K3 o8 _. e4 V
        }
+ \8 m& u' {' F1 i    }
2 X9 J$ |3 V* ?2 y2 y
2 Y0 @( o7 O$ y. `
3 K  T. H- b, K# G% HEP0的控制传输,把用到的请求处理一下$ b! R3 ^8 o8 e  }5 b; s
char setup_packet_service(void)6 Y" \/ ^) o! Y* p: p9 `& A
{
% k5 F' K7 E& I7 C    if(ep0_std_req->bmRequestType & 0x20)   // class-specific. G: i7 |1 s1 i( x0 U6 R0 ]9 u
    {
, J( y8 ?+ Q+ Z" I, S        switch(ep0_std_req->bRequest)
6 N, [1 E% H9 n        {7 E7 X4 u; I1 {4 ^
            case REQ_GET_REPORT: break;
7 z- c) }4 x3 l1 v            case REQ_GET_IDLE:9 B6 N! \2 R/ |3 {& u, Y
                USB_PMA[64]=0xfa;   // return 1 byte) D& i2 `9 u2 I5 l5 p
                USB_PMA[1]=1;
/ m2 W- {/ m2 i# r! |- Y                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
# f2 b: l" B& \- d                ep0_state=1;
3 X! M- ~0 \# E/ d) R9 `0 G- r  u                return 1;
' J; A# z, C4 L/ u1 @9 v                break;
3 F. h9 t8 i- Y# _            case REQ_SET_REPORT:
$ `5 h$ I4 p- A/ I; x! G% m                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;! j8 }: M$ v* }# O' o+ I# I
                ep0_state=2;4 G- {  ~/ O5 w" m
                return 1;
0 r3 A# Y! U: X4 @; y                break;5 M, d  Q- i& g( z
            case REQ_SET_IDLE:
1 t' \" q* V( d6 N1 y4 W                USB_PMA[1]=0;   // Zero DATA: l% Y- ?4 b" z+ a9 V5 ~
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
/ x8 z& K; |4 Y- N                ep0_state=4;
( n# K) M$ e3 R0 G! b% U0 i                return 1;
" I0 h. ^, q2 _                break;
* ?) M( X! b9 b  M) ]: Q        }3 a# g3 f! e. N! J
        return 0;" I9 ~; C8 J- D: |& C0 }7 x
    }
6 X5 ~$ I8 }& X6 @7 ~" Y. ^    else    // standard* Q' Q1 q' x6 w6 B- ?( a
    {
0 p6 [+ _  a" v( s( L        switch(ep0_std_req->bRequest)
& k; ?0 o$ h- L8 Z& g6 y        {2 x% U% I2 k1 q
            case REQ_GET_DESCRIPTOR:
) G4 D$ ^5 ?2 M+ k                return descriptor_service();, _. O" y/ Y% k8 r. \
                break;5 D, C6 X# W6 V" ~1 D/ }: N
            case REQ_SET_ADDRESS:
; w! h, |3 t0 U5 e( U- B                if(ep0_std_req->bmRequestType!=0x00)' g* a$ x/ \% V+ X8 b- R8 b, t
                    return 0;
$ L7 E6 a3 L* Z9 q1 |6 N4 M                usb_address=ep0_std_req->wValue;
: v- B& J6 \' @( b; L                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
/ v' V. s, I1 _6 J( z                USB_PMA[1]=0;       // Zero length DATA0, R  `9 z  ]8 t" D, v; B, d* @9 G' R8 ~
                ep0_state=4;    // No Data phase: d9 a! a! D- H, c* u
                return 1;4 O6 K- g0 i0 }
            case REQ_SET_CONFIGURATION:0 Y; W0 M3 A5 h/ y3 p/ H
                if(ep0_std_req->bmRequestType!=0x00), q8 D5 T7 G! x5 o% ]
                    return 0;. M: w5 p6 `, A0 c% a5 S
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;1 h- i) u* P! s2 R. s, |- A5 ?+ n
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;6 [- T! L; S) o+ w# S
                USB_PMA[1]=0;   // Zero DATA6 c* \$ \, C2 E" B3 {6 Z! |* e. e
                ep0_state=4;    // No DATA phase
% w" X+ Z. S. b; e. v                return 1;
  R7 ?* p0 k# d% V2 d            default: return 0;2 v. P9 _* T1 T5 w5 H! b
        }: \3 ?! @% l, l% u$ J4 S
    }& X( \/ a% ~' D5 {; M8 r6 r4 [
}
" H+ Z) i! H% m- G5 `$ s$ ]- l/ [9 a+ |& B9 f1 [$ U
9 \' f% C- V$ Y. a
8 e$ z: t  s4 S" F# I
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的3 K: d7 Y3 X; J; k+ Z  g
char descriptor_service(void)7 a& I3 M$ j! H
{$ u4 o2 Y$ f! N; N1 U
    switch((ep0_std_req->wValue)>>8), a6 i( @, `' D. @* B7 U# u
    {
+ |1 F& A7 _, t! k        case DESC_TYPE_DEVICE:
' c& S: A( Z" L, ?9 L! M+ v0 i% C            return ep0_preparedata(&DevDesc, sizeof(DevDesc));) K1 e2 E& Y5 ]2 [# N% h: R
            break;! ]  S7 l5 b5 j( O, t
        case DESC_TYPE_CONFIG:
$ U+ o* q; [( f( ]- j            if(sizeof(ConfigDescData)>ep0_std_req->wLength)
1 A" G9 u. M7 g! D- |4 A& G                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);7 H' _. U) k$ K5 K
            else8 f$ ?; m, x0 C: ^# r0 F! g) M
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));" T9 k3 \$ F+ V& M7 ]5 k
            break;" D, T/ D5 d2 N3 K6 @; r
        case DESC_TYPE_STRING:
& m9 d3 Z7 V" w5 m; e8 j2 G            switch(ep0_std_req->wValue &0xff)- E9 m: g6 M6 k- f* u
            {
9 k! O# z1 u/ `$ W- K4 x* A+ _4 I                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
" I/ t0 \) W% Q4 O                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
& @$ H) M6 ?" M                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));. o. @7 b: R) x/ j; t7 A
                default: return 0;
0 s0 ^/ ?9 A) E2 P( k            }
, ^  B  g# Z, q2 E  p3 l% N- `            break;
3 ^& k2 p) k% n        case REPORT_DESC_TYPE:
. S" }) n: T2 c( ?' [; Q            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));. r- G( H7 Z( U
        default:
; o2 l  O. A. L( ~1 @. ?& a            return 0;
: u# W2 l. K0 G    }
. `& c; h1 {- M}
# F4 _+ a* O2 n% |下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.0 y  n4 Y$ `% }; w  K" F( R4 }; u% D
void TIM6_DAC_IRQHandler(void)
; _$ i, u7 z  I& q! a" k( S6 `{9 }, P% J+ X: A9 r: N( |: @: C7 I
    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
# y& `8 T" g5 r; \7 d6 S
& K# D% [$ \0 K+ N" _

$ B) Q( {) F. f9 k  c# b    TIM6->SR &= ~TIM_SR_UIF;, t7 u. ?* C- S$ ]; }5 Y
    prev_key_state[scan_row]=key_state[scan_row];) n2 A5 W* s0 T$ S1 m3 B
    key_state[scan_row]= *PA_IDR;   // update key states. X* s: P0 ^# z7 w/ m6 B
    switch(scan_row)
+ Y1 D( L" ]3 F9 }. U- \$ d5 g    {: H4 h, W& w' C( f$ n
        case 13: // next row PB14
+ t' {# S* Y* D- s$ K4 ~                GPIOC->MODER = GPIOC_DEFAULT;
: S) h, t, g# g7 E                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;, g9 F- `( u  F
                break;+ [  p8 U# b1 ?+ H! s# M4 r) z
        case  0: // next row PB15
3 n: M; b* Q0 `* P/ _" G                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;9 W0 o) n8 d2 e' j9 N: H* f) e
                break;
9 ~  L. ]8 M( W, q" U) c        case  1: // next row PB3
0 D- A, P% `. H  t3 n1 W                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
7 |. t* W$ a# f. O                break;
0 i' _2 e( Y' e% `$ Q        case  2: // next row PB4
. w6 F* \! T1 {, E: q: b                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;# C, Z/ u2 n# l3 O, z% ?" G2 f
                break;) c% V4 k! L& C$ T
        case  3: // next row PB5
! N2 ^& X: ]6 o/ H9 X                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;* e. X) M% R2 z% O) G( m
                break;
* M/ n* I1 z$ @$ p        case  4: // next row PB6! a' C% U+ e& v, W2 J; G) {
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
4 B' P0 l" o' M1 @8 F; ~0 w                break;0 B/ [* @  a: n; x' X
        case  5: // next row PB7; B, O4 h( T1 N* ?
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;$ T9 G+ R% Z$ j: U9 T
                break;
( m9 Z; v* y* [* x, Q6 l        case  6: // next row PB8
* J) h2 u9 y" r. r9 J! s: ~; z                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;3 H" Y' o) @) ]4 e0 ^1 J4 G3 g
                break;- e- C4 O) a, G* F9 h- F! V; x1 ?
        case  7: // next row PB9
' D0 c) `6 S( f2 S                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;8 U6 O+ h/ Z: I) [4 V. Y
                break;
( T' o& B! ?9 [3 }        case  8: // next row PA8
6 J5 {' e' y: h                GPIOB->MODER = GPIOB_DEFAULT;
' k+ W- H% E2 c( L& Y7 F) i                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;
8 }9 J; n1 U/ M2 y2 U                break;% \5 h! J" m+ ^
        case  9: // next row PA9
# x9 A9 v7 K# u/ _" c/ |% W3 O# u                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;- s; z. K1 W6 b" Z4 r
                break;
8 ]7 K0 o# F% d1 ]        case 10: // next row PA10& F& n0 k3 w+ Z8 X* R8 L
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
+ a' A/ |. f6 N( k% D- }                break;% r$ ?- C/ P2 o( b8 `" k0 q
        case 11: // next row PA15: p) d$ S. `& l% A5 e2 Z1 @1 r
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;# z2 Q6 T! x, f6 S8 B
                break;
" A; v; r# ?- T( J        case 12: // next row PC13
( Y& M6 b) U2 ]! ]* Z6 P" N                GPIOA->MODER = GPIOA_DEFAULT;2 z+ \; V/ X! p" U
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
+ D+ }0 m" g9 r- i  _/ e4 D                break;; L; K  |! L  [$ }# r
    }- F1 H  ^# k% y4 H8 c. E* i
    if(scan_row<13)
2 t( s" V- Q$ f        scan_row++;5 x7 k6 Y  [7 |! K/ g
    else, {. Z  u& S! p4 }' M/ J
        scan_row=0;
4 J  X$ s* g2 B  B6 v}
8 F) @8 u% g. ~' z! y% x0 s* U0 G# L/ x- p8 F7 U7 S8 C" e

# s: J% \# w! R" d; V8 @
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
4 f# Z8 @0 v  t5 Y2 f, d0 ]0 L" H' l1 ~* h& {/ F
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。( P- h6 C5 e' F) d: B7 f* M

) q6 j, W- B4 |* n! K" Q" H- Q要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:

5 d9 T  Y1 g! A, r# w3 u3 d$ {6 q3 K

& @- u# @" }0 i+ o) R& y! @1 `$ Y1 hconst char hid_keymap_qwerty[14][8]={
5 d  d  Y% a* W: y    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
% ?. r; Q8 \9 l8 q% ^) P    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},# ~: q. ^4 T% X% P5 i( Z0 q1 ]
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},# t5 ?: |# {2 [' W# c
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
8 D' C! S0 {2 V- z    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
! b1 H6 I& z8 Z% `4 f    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
* C2 f3 A: ]! {    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
% X  Y# u* ^3 O' K+ j1 w: o1 c    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
( Y$ n0 ^$ k# [  k6 i& E) ~9 ]- J    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
; A6 }% H" n: z    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
8 `. [1 [3 \/ u    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
1 ~* ?4 P% S# `' j& S+ j% Q    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
1 m% ~9 [* D- E    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},0 H- A* L( M  ], O7 ]" G
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
& }7 N" S! r6 A- R" ]. G$ K& x};
) O  T) y3 {. \. e% t: q8 F; i
! I( `& `) D% c4 f& T
1 P8 R1 O8 y6 ~9 q2 g* B
const char hid_keymap_dvorak[14][8]={
+ X2 N* Q" M4 g" Y    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
# _/ M3 b* A, k; N    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},! s8 j# Y) h: N( w
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
+ c  y5 @+ u& S8 U! s* U# s& |! r    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},5 l* P* t; A! q3 y9 B
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},) Z1 D9 {  A! k
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
7 u2 ~+ Z7 M/ Z' G    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},3 ]9 k4 K0 ]- s% h* ^/ f
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
. v% D- ?' Z) ]5 F0 J% A% J* x  P    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},; y& x1 i3 \# g- B4 F% h
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},7 B5 N! Z+ c& r& _
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},, A5 L( {9 A/ B
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
) h- @8 [8 a# C  B, P    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
3 F6 }$ p3 M+ A: U    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
' e' e, A2 x/ ?' M/ R) R0 d};! @( z5 {# o: h: h$ Y, c+ E
' q9 c8 ~& }* x

' r- y  G: l" j上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
% G1 [# n' `; [6 @" X6 B1 u
  _/ _% n2 W* u, N" G- ~HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
3 a$ A, s3 T. k7 x' |
: r$ i% S* ?! V
void update_key_matrix(char row, char col, char onoff)
) [0 p) V1 E3 J7 t3 }; k{  o" ]' A& s) x  d. _
    static uint16_t hid_report[4]={0,0,0,0};
- v& J3 `" q( A5 N    static char (*hid_keymap)[8]=hid_keymap_dvorak;* |. E# s+ K1 k0 q, \2 t
8 r6 }2 K! t$ R

* b: P( `5 s1 t; e( n! F) X    unsigned char key=hid_keymap[row][col];: e( [' `# r9 A
    unsigned char *report =(unsigned char *)hid_report;( U4 b8 e' `; U7 |7 X, I' `
    char i;
7 y8 ?: w* F/ R2 s- K2 D1 r! |% Y! k1 W5 \( C( S; Y4 w

. t' n0 \9 Z. N2 W8 J    if(key==HK_MODE)1 W' p8 f! m& z" Y) {9 J
    {
& t2 _5 s/ k, N8 ?; p        if(!onoff)
9 I/ x8 G$ a9 ^+ }# w! e% N        {# ^% h4 i5 \" A
            if(hid_keymap==hid_keymap_dvorak)
5 f, ]# W$ ~. _7 l" X% W* h- e8 o+ U            {
: g- U) ?0 w7 E( N+ w  b4 }1 I/ B                hid_keymap=hid_keymap_qwerty;" P' M0 U4 M. y( ?! `+ K( X
                GPIOB->BSRR = (1<<2);7 B8 F& r! m" b3 `+ V# {% X8 n+ \
            }( |, E8 G! O3 j6 o
            else
/ ?& G0 C5 ?5 {& E6 M7 b. Z3 X            {
. a; v# N: L7 Q* |  D0 [                hid_keymap=hid_keymap_dvorak;2 D9 A% ~# \1 h
                GPIOB->BRR = (1<<2);7 i6 d6 o9 y+ ?1 E$ a
            }
3 W% Z9 X/ H# t2 G- R        }( i  E) [2 Y$ I' m# \0 Y3 T
        return;
# J2 n) h2 l- a+ U    }# [( x9 v8 c; H

6 i* X0 d% f1 Y3 n

% S; d9 F) b1 K. G    if(key>=0x80)   // Alt, Ctrl, Shift- C) i  z& h( h  ?4 t6 k% ]
    {
/ x/ z2 T% b; X$ ^% q- @, i( S        uint8_t bitset = 1<<(key&7);2 @5 c; ?* F/ }
        if(onoff)   // non-zero is key up5 F% Y% x% I- F/ }- N' y5 z
            report[0] &= (~bitset);) h& d# ^3 u1 U' A3 p' L. N- x
        else
7 [1 m" w" c. h2 x* ]            report[0] |= bitset;
7 {) e4 {! o- ^( w9 K$ X0 q- j    }
% E$ @8 x* s" ]7 d1 C7 }6 y    else
. Z& i* c$ b4 P( z0 `+ ?! q    {
& S1 n9 G  `6 m5 i        if(onoff)   // non-zero is key up
3 J/ }% s7 |. B0 n: w1 q        {" w$ c8 C. J! H) I1 s
            for(i=2;i<8;i++)( b0 K. W5 B# f( R; U
            {, N( P# j: q  S+ |, L: ]
                if(report==key)8 {7 z" S9 S5 e5 b4 L
                {4 d# M. ]( v" K0 q1 j* W+ G. ]
                    report=0;
) b1 j% x/ `- r( B$ O- N4 h                    break;+ E5 w5 O' w8 W. c) r
                }
: b$ T3 Y* A; W& z# a( E  M) T            }6 n* {8 i( i% @
        }0 Q/ e6 B$ Y6 e
        else( \+ S. ?0 F+ [- j% U
        {
+ @2 c  i$ K1 Z" O4 o3 f# c/ {            for(i=2;i<8;i++)
: G7 B, I$ l5 x            {# k# L4 w+ \& }# @
                if(report==key). I" J" ~$ w" ?
                    break;
& N9 y# }. L" {% W% X& @% S8 k                if(report==0)
6 n6 h* s# h. P# _( X                {/ |! m7 j3 R) ]% F$ g. q$ F
                    report=key;; I+ J$ H" ?% [& l. @( ]  B, D. ^
                    break;
+ _+ ^/ E8 E) b: T4 V4 T) A* b. E                }
& @: q! v, `5 F' i            }7 Z% B1 u, p" i& B" L% `. Q
        }7 d2 L: G& w+ H! e- O9 q
    }) o" F2 v0 ], w3 ~% B$ S
    for(i=0;i<4;i++)0 @" l( w! R+ J
        USB_PMA[192+i]=hid_report;
9 E$ y  u4 J! _8 T    USB_PMA[5]=8;   //COUNT1_TX
* [, i( E  x( W0 K2 x9 P    if(ep1_wait==0)2 o4 u( I2 v* T5 E# N+ L, l1 S
    {
8 E1 K9 u1 ^" K4 k* p0 i2 B" m        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
2 c9 K0 P4 Y& F        ep1_wait=1;# _+ E) ]9 e# S  F9 z5 G
    }2 R& l2 j1 E6 j5 A/ X
}; `4 j  X+ e6 {) u

: u% H* x! B8 v$ w+ E5 x# D0 j/ W
1 d3 q1 J0 b% R7 M2 w完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
) y$ i4 `9 T% t" o3 B- s: S keyboard.zip (8.7 KB, 下载次数: 6455)
( V$ B# I' t& }. b% \

: l. F( m/ w1 |0 F9 V
: G  Q$ D: e/ D
3 `6 F' k6 h2 U7 E  a3 l4 Z/ f. P, d0 \) m7 V" K
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘3 G7 P7 k7 T  L/ _
不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:480 K; w3 r. {9 Z) J1 h* K
刚开始我以为要把打字机改造成电脑键盘
: n% |: z: g7 M4 ?6 ?- J不过楼主也很厉害!

5 k6 e# T7 ?! {8 c哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害; i* p3 g; y2 u/ P
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-4-9 09:10 , Processed in 0.184942 second(s), 27 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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