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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
$ E: |3 Q8 C% I3 k# y9 L
) u: A! w5 }( g- |http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1; G* w6 J) P% `$ Q4 l! r5 p- E
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。; x4 s# d7 r( C9 |7 W, E: e
" g5 g7 [+ Q( d' j2 p
在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。

) L, W7 `* k" C; Z
$ u" j# T% |& `+ j  P4 M
235140i3a36qivqzuvmt5q.jpg.thumb.jpg ! ~2 P. i5 }8 V* l9 J! a+ I
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。' ]8 K) q3 O2 k4 c; `
001734klbyoluenuwz4h4b.png.thumb.jpg $ P* h$ @9 R% P$ F
为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:, z! X4 a0 m$ F3 v7 b/ I/ `! d
003625r2agx2f5v922cf2f.png.thumb.jpg
1 r( a% {' D+ G: `: n5 v其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.1 ?+ i4 F4 ]/ O
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
5 c3 f/ n: p7 n" {
005836yvs0wvovwsssgd3o.png.thumb.jpg
# o% D4 D1 m" G0 l" S9 s( U2 rDvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
0 x! E5 e2 D( [8 l/ A) d
5 \6 o  W! j& z/ P" p, i我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。  q8 q3 l% `* t2 l
( c* c9 H+ K7 O6 b/ N
到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。) O- {9 \' k: I1 c0 {

  x8 @% l9 c; T: }机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
1 K2 k/ b5 J# t
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
' ?1 [  n1 \: f$ b----------------------------------------------------  分割线 ----------------------------------------------------------7 O' N" V" C; V& ^
) }0 c) p/ e0 a# s
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

* z# L6 q* l* v/ W! W 020011osionbunl4ui44vi.jpg.thumb.jpg ; M! p7 G3 T9 G/ T0 @, n. C
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
5 ]: O7 q8 w% y% H 020017j8ycmnv7788bqv52.jpg.thumb.jpg
* g% }) \) N/ U# [3 K5 s4 `特写,80C49
+ S8 h/ V$ b1 I' f 021040oujzuvtut6iujtvz.jpg.thumb.jpg
$ y8 l6 Y2 u+ `5 v# |LED部分,使用了一片D触发器锁存指示灯状态.
6 c8 G% i8 V' J. b/ E 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
  i3 m; j3 P- f# ^) p8 v; x* W/ U暴力破坏,将80C49拆掉5 }  `; v6 B6 O+ l" l
021113e48qq98vyohvhzzh.jpg.thumb.jpg + H( |0 w: ~* w! l" @
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。: y2 n( G, P. A0 F5 a
021125nc9az6dj33rlds2r.jpg.thumb.jpg 0 ^! a/ E8 o+ g4 o2 A# ]/ \: I* j
焊好元件后的板子,准备替换80C49
- _# y  X. F+ F1 P1 B4 W 021146zmmpmh4pav4o7g6a.jpg.thumb.jpg / M3 Y( y: `, g: a
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。) M" L% ^+ D/ x% }$ |
021104shifhnrqbr3o5nlo.jpg.thumb.jpg + q! Z- ^! c! ~  ~& }5 I2 U$ Y
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.# d& U( X1 m, K4 Y5 E0 d; X7 o
022003ym1p9u4ug40280uu.jpg.thumb.jpg $ C/ P& c1 j0 V% z  ]- d
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。" J/ i0 p: D; F
023313kt141q9qajtol7ma.jpg.thumb.jpg . o% ^/ |" K" r  m1 }
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
3 o  T* S& I. f' D5 g, s' V# H 023322nt7l5xb3ltttkltt.jpg.thumb.jpg : p8 ^2 c, n7 c' A2 P- X; \, Z$ |( K! i' J
主键区键帽就位
# r6 h* |8 }1 } 023331hin88e8wkrwzwikx.jpg.thumb.jpg
2 \7 T# z+ \* c编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
: j9 o" }  E4 `/ `0 P 023336wjzlgopugg1jyy79.jpg.thumb.jpg
7 r( b8 P) |$ W- r最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。
" e4 A- p# e0 S8 W0 v 023341sffu4j3g2323h6fl.jpg.thumb.jpg 0 q* J# E3 [- T  C- ]
$ n- d5 X& G: `
----------------------------------------------------- 分割线 --------------------------------------------------
- e9 m5 C* Z2 f# c
' d1 h& y+ b: N7 K8 g) a* ]
. m6 `7 B" K% E( O
0 a0 ?2 m5 q6 h6 S4 g

' B  q. b4 }. \# Y) K9 [3 j7 ~( G9 @9 s; A7 Y* u  ]. R

5 J! h* C5 Z0 A6 O: K1 H" b+ Z: G( l" A% W' s

# l, K) o/ W% k3 S" i$ K
- z  K9 Q; T; d9 W8 K2 M# P# A- }4 B# k4 V( G+ _' Z: P7 x6 E, Z

5 l% I) n/ _# F+ y/ i2 F3 l
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。# t4 P  {4 {' Q7 x9 |# O
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
  V% L: e) _6 F) M3 p& g 104025nzibm2rmiomhyirm.png.thumb.jpg 0 d8 e& y( u" E) t9 w% }/ o

6 Q- U7 P0 V9 _1 V: u其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
: a, z& f+ r6 L0 K. U$ G" q 105004zkrez5houvkkznko.jpg.thumb.jpg
+ \3 M! n& `1 X7 x

4 m, F* B2 e7 X" ?5 N9 z扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。# F% A: R4 d% J# E: \
# {. d' ]% P) \2 _! K8 p
这是我设计的电路图:
9 Q7 S% j7 o; [" Q: L 110344ej2z2oo2rflo7oe7.png.thumb.jpg
7 ]% j" n' v* B  O9 l; x# V- f

, T' z# ~8 U1 o+ ]' O) |PCB Layout:
6 x% {' G1 @# R; U9 a 110847jjbjvt34vwt3v5bb.png.thumb.jpg
% P2 c( z# |7 R' ]" E5 ?4 @

" C' o: l- v+ o, i! R不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. 2 |2 A! e; P. g8 W/ ?" L
9 Y4 m( ~' p# q. X1 ^6 g

$ P$ Y/ I  R9 Y
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 0 N8 O3 q# A; Y7 g( R) k  H
& F9 {7 Y- H0 Y1 I7 M8 K
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。9 N2 G# t! h+ `' f3 o2 D( ?2 s
4 k, \/ P7 M) h; ^) }
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:/ {/ M. X+ L3 ?- x
113818pmrfsb6z0byt6t06.png.thumb.jpg
9 O) p, a; ?# u5 \# f

4 ^- [, }, y2 a( P5 V% k$ K# }其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
% m+ F. b- |$ _8 B
! g7 B. I4 @! Z1 Z9 G" aUSB的中断ISR,bare metal哦
1 `% b/ h* N  b9 L2 ?
1 S3 n- Q( i3 D
void USB_IRQHandler(void)
% R1 G3 X4 V. C{
/ C, ~3 L0 _- l$ g/ r: g6 g- c    if(USB->ISTR & USB_ISTR_CTR)( N% N, s! p  k3 h! Y2 Y* c
    {
! \6 r$ s' l. h" w& ~0 M5 S        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
, k6 r: M" s& V( |8 m" T  {        {* i% _1 Z& d9 K" u% [
            switch(ep0_state), d* ^9 @1 n, }1 D( `6 G/ j
            {
3 W3 y! j0 ~! F  C                case 0: ep0_state |= 0x80;" n) [5 X& g& p: F3 t, v- c! c4 v
                        break;
' ?0 j  v& P7 w0 J                case 1: if(USB->EP0R & USB_EP_CTR_TX)( X$ f1 e6 s& W2 Z
                        {; p; o5 t3 c- |9 D
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
( |* M" e& J( ]9 [' [                            ep0_state=3;
8 I% M. f% j. K1 [0 o/ A                            return;; N$ h6 r& t2 o/ W
                        }" t! Y: D  S2 \6 L% |
                        else
9 g- C/ X  B% O6 C, Y0 o6 X0 J                            ep0_state=0;
- W- R, _' E. D) R' D! p                        break;: }# L" z  x% {
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
0 b8 U6 r1 ]! x7 V9 E. ^                            ep0_state |= 0x80;: ]* t, B& i4 C. x" Y5 A$ F
                        else6 ~8 e% T3 w& u# w& Q
                            ep0_state=0;
9 g) g0 E% Q% g  n+ {3 j                        break;4 d) {6 j5 L7 t4 c
                case 3: ep0_state=0;+ c, j$ w  K4 p5 k9 D9 ]
                        break;
2 ]# x' F* f5 r5 F- {2 j* C! n) x  ?                case 4: ep0_state=0;
+ b% e9 Q: O4 f% \" I                        break;' i& O$ ?* w" ?) X
                default:ep0_state=0;
- a! Z. |: D* e, w. I2 B( D+ l, Y                        break;; o" a9 Y$ P* x: c9 W/ D' Q
            }. _9 S% D* x, B; J% @2 [9 t
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag9 V0 ^; M* j$ V  T2 M
            return;
, I$ m1 ?3 Z: S        }4 N4 y# d+ K/ F' `/ J
        else    // EP_ID can be 1, n4 y- {+ Y) M: s- f, K
        {
+ Y5 \: X4 D& W; ~. B. t3 Z            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag: ?- `2 `, o* l: C
            ep1_wait=0;
! ^! T1 A+ n3 Z            return;
: `, d/ k1 K. I4 `4 J2 E        }
  _- ~) x; _' H! L4 [    }1 }$ F. s' \. A$ b! E% F, m
    if(USB->ISTR & USB_ISTR_PMAOVR)
! S# L3 M7 |; O2 G" z' i& t    {" `/ o3 G; Q- F2 q6 ]! W% C9 o  O0 P2 ]" Q
        USB->ISTR = ~USB_ISTR_PMAOVR;6 k$ U4 ]% E( b
    }
% Q( f; j7 L) u6 H9 [    if(USB->ISTR & USB_ISTR_ERR)" ^7 I8 s8 e9 @7 P! J; D0 J. g) q
    {
) c, ^& G1 B& O& U; V        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear$ C# |9 |# }+ F9 v% N/ }
    }6 b, A8 u5 A, f, v$ `7 m" |
    if(USB->ISTR & USB_ISTR_WKUP)
5 C3 C0 r' ]( s/ N4 F    {
0 M/ l6 }  {% X6 r5 v        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
. Q; [3 W, \( c) [2 l% U        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
, h  v4 m3 C  K7 Y, i8 [    }
$ `/ c8 k: ]+ H- a    if(USB->ISTR & USB_ISTR_SUSP)( f1 |* |0 H5 S1 |( j* R$ q# J
    {
$ l# g- r$ n/ Q3 r' k        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
) f! d6 Y  s2 y; [        USB->CNTR |= USB_CNTR_LPMODE;   // low power% e+ _6 u( R2 I* M& E: {2 q! [" \, L, W
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
/ ]9 \9 B. t7 Y2 Y% b    }7 K* j- c/ V" W9 X) `0 V6 J
    if(USB->ISTR & USB_ISTR_RESET)
- }! S" Y1 e9 p/ A0 J! ]0 Q    {4 W$ `8 g, h* c+ G& |
        USB->BTABLE = 0;    // buffer table at bottom of PMA
2 t$ G' x4 p+ v; s- z" i# d        USB_PMA[0]=128; //ADDR0_TX1 t" b# m  E. ?$ A
        USB_PMA[1]=0;   //COUNT0_TX
' o* K6 s4 R$ W  D        USB_PMA[2]=256; //ADDR0_RX
0 V$ u# J( r/ R        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
" S' f' Y; S8 l/ J0 |        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;' j* @. F+ i$ v) ~% s, A
        ep0_state=0;
2 K" ], e, r' |  U: n        USB_PMA[4]=384; //ADDR1_TX- [+ [! U# |( \
        USB_PMA[5]=0;   //COUNT1_TX
* R& Y8 R2 t# ~- f& g        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type2 g# m. s2 f+ X5 @
        ep1_wait=0;
6 u, T: F) U( p. [" V        USB->DADDR = USB_DADDR_EF;      // enable function
" }& |) ^9 q9 z3 s7 C2 Q& z: d7 `& Y        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
1 E9 v! I6 u6 Z. p' ~    }
* {; a. h/ }2 u    if(USB->ISTR & USB_ISTR_SOF)
5 ~- J. {) O, j3 X    {! u$ _" V! l1 p& x$ w4 B
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear
4 a' {6 i5 Z: T; n    }3 E4 I6 ^# H* R1 b
}6 ?4 K* e4 h0 n% S1 |) e
/ N6 A- W; r/ S) m; C) _
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
9 V- H6 T0 h! r: q' s% E& @: a7 X+ S$ V
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。! K2 D( c0 F0 n( n  I

0 ?9 o! m" D; Y2 Q# P/ [
    while(1)4 M. s. ~, C, q0 O0 i: r
    {
# V- {+ ~# X3 T% C8 |6 `. H8 K        static char row=0;+ N6 ^- R& L0 M
        __WFI();
5 _% F) ~# [$ r* |        if(ep0_state & 0x80)    // request data processing9 q6 h1 |: O6 f+ T, G* o- C
        {- X6 K# `5 y  W9 O, |
            if(ep0_state==0x80) // SETUP phase
: M' M% g0 C, ]# Z& U$ z            {
! Y4 q$ ?3 ^8 h- G7 \5 {9 t                if(!setup_packet_service())
& \5 Q( S$ r! `: x6 \, n' B3 |7 Y1 Q1 Q. H                {, H- _" J2 o+ A% V8 N. ~
                    ep0_state=0;2 `+ x/ N  f3 g- M# e( Q% h
                    // not supported4 R! ?, S3 P) }" [7 Z" J6 Z
                }
2 B' N0 ?8 s0 v  v                // ep0_state should be set to 1 or 2, if processed
; I) [' p% h/ P/ P2 q6 ?            }
: M4 p1 ]' H) V( ?            else    // OUT phase
6 b4 _; n' Q" x" b( Y6 r) y5 W; s, H            {
/ x: D9 K  N# P" _+ r8 G) H5 g                // process data1 i& s) `& j7 l0 G1 E- K
                show_LED(*(uint8_t *)(USB_PMA+128));
3 \) _( y, k4 W1 N+ t% ~( `* \, l                ep0_state=4;
) B. c% E% `, s+ X- }                USB_PMA[1]=0;       // Zero length DATA0, a' i0 C; P% n" t: X* i
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
9 _3 u/ l4 C, j$ [            }* Z, P+ R& d- G
        }
, z# `+ @) O0 b" M: |- ^! X        else
7 T+ I; M0 z' W/ j        {
. O/ e# ^" r! r9 v            if(usb_address && ep0_state==0)
; b' [5 Y- I( q            {; F: F  b" i% X( ^8 H4 n0 R
                USB->DADDR = USB_DADDR_EF|usb_address;5 m4 w# L/ g- P! |# ^
                usb_address=0;
/ |' b) w' M+ Q            }
: R/ F1 R  s5 ]. c2 q# x        }
7 }$ u0 |( A6 m: u        if(row!=scan_row)   // new scan line" }" x* W; N: t: c4 H' d
        {
- u7 o5 V" ]  G! R5 O# @            if(key_state[row]!=prev_key_state[row])% C9 D* T+ \( K. g  ?, ~1 s
            {
/ L7 H9 l4 r: ]* I2 B                uint8_t test=0x80;8 N5 j3 h! _4 n2 c  R1 w' q
                uint8_t diff=key_state[row]^prev_key_state[row];
" |, p9 x- j/ I' o) @9 i- ?                for(i=0;i<8;i++)
; _) [. f. |$ d$ I& A: k                {; G6 @4 K" q$ x, g
                    if(diff & test)9 B9 @" v* h; t4 Z# M2 }/ n5 s
                        update_key_matrix(row,i,key_state[row]&test);
4 {1 A5 k0 Y7 y: U                    test>>=1;
8 i3 v3 C/ A3 t( a: r9 D' @' M                }+ w7 v; \' T9 N. v) P; T
            }: F8 B& d/ M/ t! p4 m
            row=scan_row;" X: i1 K" o2 _
        }
& J* i" @2 L" X: s4 x5 T: K    }; C. ~+ B* Z( F5 g2 c
3 \3 @" H- ]6 ~' D0 r7 B& u( W6 A$ {
% J* a  {% J0 U7 j0 y5 u; m/ W6 d
EP0的控制传输,把用到的请求处理一下8 ?& h, ~" l* [2 z
char setup_packet_service(void)9 Q$ N  z' P; y
{4 e. b7 M. t1 B# K
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific. e/ X+ n. Q; u8 P
    {: W6 j2 ?- m* \; q. h/ i
        switch(ep0_std_req->bRequest)! i/ i+ I/ J# s- v, y  `
        {- _5 W0 b2 v( L8 b+ v" U$ K3 j* J
            case REQ_GET_REPORT: break;6 B2 R  v$ U3 S3 b& O& `, U
            case REQ_GET_IDLE:
8 V9 a6 n7 A9 U# m  S1 w; R                USB_PMA[64]=0xfa;   // return 1 byte, ~% c2 O" A" T$ v- ~( @
                USB_PMA[1]=1;
1 J* }2 F* L/ \, O3 B& ?                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
# e6 y0 t3 s8 {                ep0_state=1;# i5 O) F6 Q6 n
                return 1;
8 y% P- Y5 B. b; q' D                break;
( k$ b0 c! [/ C3 @, C$ q5 G% R) t            case REQ_SET_REPORT:
9 j' f" C: r5 s# r7 q5 N% |                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;4 h1 O2 X2 P) w
                ep0_state=2;; v7 _1 o5 |4 e4 x- a5 j9 V
                return 1;
& {- T9 `0 p" x  F) D                break;! U5 v, a) [1 G4 B$ M0 g
            case REQ_SET_IDLE:+ q/ d6 e* K+ }
                USB_PMA[1]=0;   // Zero DATA9 N# U+ G' Y1 K" C) o
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;, j% N% m7 o% x. g( z9 F7 F
                ep0_state=4;
$ f3 {9 i# K3 y0 n* j5 D. N                return 1;
, j( H3 e! W  x, P! }* I3 f2 L" q                break;" _8 k1 x( X4 v8 m9 `/ q8 f
        }
6 p8 g. q+ A2 D* z        return 0;
! m1 z' A- S" c    }
, d! m" U6 H5 @3 t    else    // standard
# V$ x5 n3 }+ |- o) J    {; h; s9 x- f" I+ S$ D3 r" X
        switch(ep0_std_req->bRequest)
0 |- ^6 J  E* k* R) N, G        {6 G, W. x+ S0 J) t& P; J0 U0 O' J) _
            case REQ_GET_DESCRIPTOR:
, d- o" ^1 c  Q- N                return descriptor_service();+ G" v6 ?, R0 b; v7 E" O1 w6 b
                break;2 O: u4 r  `$ E# ?+ T
            case REQ_SET_ADDRESS:
5 g) y/ |+ F( i) q6 ]1 T, d4 ~                if(ep0_std_req->bmRequestType!=0x00)" p1 ~1 |4 M. B% p0 s
                    return 0;
. H$ r8 l6 U1 x, m3 |' Z                usb_address=ep0_std_req->wValue;
2 s+ `- G0 ]4 r: }                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
1 @# ^( l: e' G8 \+ p# Q& s                USB_PMA[1]=0;       // Zero length DATA0+ y; W# s  {- A' r5 J" z# T
                ep0_state=4;    // No Data phase
& t6 ^, r/ U3 C! n9 D$ Z                return 1;
2 s9 R) O! r2 t) B( t  t            case REQ_SET_CONFIGURATION:
( K/ ~# {9 C+ P. b8 `) [                if(ep0_std_req->bmRequestType!=0x00)' F2 N6 a, E9 a
                    return 0;2 ^# [- A8 m; Y  e3 M
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
2 `- r; p2 }: V' w2 h                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;0 D$ R8 C/ D; f% @) p# W! r2 ]
                USB_PMA[1]=0;   // Zero DATA
) Q- s( @- M% A$ r7 v                ep0_state=4;    // No DATA phase, ?' F& W4 R+ M. ~1 S2 M) w
                return 1;
/ q7 ?# b) Z, G            default: return 0;5 \  Q! R( H4 e" u
        }
! R+ M  [$ f* B8 e+ ~& ?    }
) R- X3 m! l6 g5 U; t9 S; E}/ X; g. @$ X2 c/ Q) Q
( ~$ M0 u) E' E' }! K
1 |5 l9 S3 u+ o+ ?6 q- o$ ?9 x

. N3 x% H! M: {7 o) V
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
7 X" f" m: q- M( ]7 Q) Z' Gchar descriptor_service(void)
# E" l, X( p0 d5 E/ ]% B( |& }{
) `  V! K' D, C    switch((ep0_std_req->wValue)>>8)' o9 I1 F. s: e0 R; n. E
    {  l% D1 D4 S' E; s: x5 ^
        case DESC_TYPE_DEVICE:
+ _0 n, E9 D9 P  B6 Z            return ep0_preparedata(&DevDesc, sizeof(DevDesc));
6 c% a1 f2 a0 |- b# v3 I            break;4 a' K! s$ H- v: Y
        case DESC_TYPE_CONFIG:
. b$ I( p& @3 L' J/ ]7 r. a6 p            if(sizeof(ConfigDescData)>ep0_std_req->wLength), k) [' `1 H: A! s  p; K
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
. e' ]  N/ i' B/ g            else& u- J' ]) C' Z: M
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
% {6 t- x3 v6 {9 {            break;
# [; r( J' u3 z0 R/ }8 f        case DESC_TYPE_STRING:
+ V6 a% V. r* G7 T- e6 o0 p            switch(ep0_std_req->wValue &0xff). V* a( D- I6 i9 C# t
            {9 ?3 o; m. ]5 p) o; K# y5 }
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
" X8 Q5 R/ ^% {+ e; ~# y                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
0 c$ |' \2 K; K$ F9 I                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
; s7 `& |- E. ]* Z, H' _4 h                default: return 0;
6 v8 Y4 }# J  |$ n            }# f1 [+ {2 v8 Q8 p7 W" G* b
            break;
5 ~6 R/ R& R+ m  \" w        case REPORT_DESC_TYPE:) [/ h# J8 }- j" {4 Q- J" d0 i
            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
) p4 n# A" Y: M1 _9 e        default:
# }% _& h. }2 a( A. [1 g2 p            return 0;' f- H& h( G& v0 H9 S
    }8 t: K8 q6 K5 G
}" \& C/ P$ ^( n+ G
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.; O2 m9 S, _& ]  }/ ?- W/ c5 k
void TIM6_DAC_IRQHandler(void), w# ~% I: i3 n
{) O' l- f; B" R1 o
    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
7 q- e& q% T6 D* O
! x$ g5 a# |% W& L: _3 V
' f9 F" |4 u4 \5 [- C9 U
    TIM6->SR &= ~TIM_SR_UIF;
- W# p; U/ h- ^4 U- M7 ?    prev_key_state[scan_row]=key_state[scan_row];  I6 m* t$ E' \9 }9 x
    key_state[scan_row]= *PA_IDR;   // update key states
% l' u& u  [: ]2 I    switch(scan_row)
. r1 {! `- _1 x4 {1 A; k/ ]* C    {
; n2 E6 k) V8 b        case 13: // next row PB14
# Q3 p, q9 f* ?9 ~4 m6 N7 ]                GPIOC->MODER = GPIOC_DEFAULT;9 T  @% l- a$ G: d
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;0 U7 s4 E" Q3 Y% k  y+ D
                break;0 M3 k" F# A$ Z4 N
        case  0: // next row PB15) Z6 b2 z* [' E: k$ ~: w
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;/ X# Q9 H' j+ ?. }7 Q: ]
                break;% s$ _' R8 y8 z; k( ], e2 N
        case  1: // next row PB3
( l& f" U3 E6 O+ |" P                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
5 ?2 j0 E9 u% n" [                break;5 ?( n+ C" q( H9 \( v
        case  2: // next row PB4
7 q1 m8 g9 y* V/ @                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
" t8 ?( O( q& L9 h% s$ _0 t' U                break;; a! x5 {: D" r7 h7 D$ H
        case  3: // next row PB5( a9 F( U7 l/ r" U" b
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;
. u! m  D( B! S& B8 s" h                break;3 R3 Z/ k/ ?' a0 L( g) E5 O; @
        case  4: // next row PB6
6 d( f- Z: V; J6 M$ M                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
- v1 H- j4 ?# |' {1 C                break;" `, u+ p/ c5 y. J( T5 p
        case  5: // next row PB7" M  r+ |- A) Z& t. x9 W) b
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
' C8 k# [8 d9 N" M) \                break;8 G0 K9 h1 u# L% `, S+ n3 O5 o
        case  6: // next row PB8
: z# k: |" J' l; I# ^9 x                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
! |: @- S0 _2 j# }  D0 K6 _5 s                break;
6 X4 t, s" K+ J' \' X% V        case  7: // next row PB9# d  P! O# f. a- d) q/ S
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;1 A& T+ [* [# f) ]
                break;
6 L& ?# o( }. L* o+ }( f* O+ ~* w        case  8: // next row PA8
. |3 R6 j6 `9 L- y7 y, b4 j. L2 S                GPIOB->MODER = GPIOB_DEFAULT;) p- D, x3 L0 o. b$ I
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;# g% l, C; G! Y% q
                break;' d; g" K, s. x7 {
        case  9: // next row PA9
) e& t+ p1 Y- g6 w3 S1 `                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;3 }, ^+ E$ p3 j. S$ `! o6 r- \" w
                break;
$ B+ i! {2 ]5 |' u; P        case 10: // next row PA108 o1 `' e0 t( c+ S- u- f
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;1 _1 Q) h; V3 L6 ^. S
                break;
0 j6 h. Z3 Q' y        case 11: // next row PA15
) b1 n  K) [7 [* X- z7 R- J* Q; D5 Y                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
: d3 m$ G" K8 W4 i/ ]                break;9 r$ e( S5 ~0 B* G# h# x
        case 12: // next row PC13
9 |' u1 Z$ R7 l                GPIOA->MODER = GPIOA_DEFAULT;$ y; K& F2 f" x) Q% R+ h+ ^& ^
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;" t/ l- x' _' h3 E) r! b
                break;/ W& z5 G2 k* v  t) s
    }% c/ }+ ~* e* O4 ^% d( D
    if(scan_row<13)
' C+ f8 U3 i$ \/ z7 |        scan_row++;
, G0 m! t- ]  p8 y7 t' s    else( n1 B+ h' G/ |% {
        scan_row=0;
0 e+ U2 ^) t) K2 ~8 H9 u}
( v. h2 b* W, m' @% b
( d7 I2 x% Q6 ~8 E+ X1 }
7 ?5 H/ P: Q! o& C8 G
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 ( [% V1 d1 e* i6 w1 @- r- g
, {+ \9 R/ L( o1 M1 a) v% _
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
6 q1 z9 l2 \6 X* @" B  d$ M/ N' C5 o$ j; }: N; [2 p
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
7 Q% m* |! a# m) y, X( H% p5 X

1 F$ T0 n/ l' Q5 `) i  W" c' c+ f/ v0 w0 t
const char hid_keymap_qwerty[14][8]={
% l/ j# p6 w- n0 `6 p6 K9 [    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
6 X# X9 l& E. y. Y8 T, M    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},9 L" k( R, i4 N) f& U
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},( T8 O$ S) `. l% b4 N" [
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
$ J2 t2 k" [( X4 t- ?! X, K    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},6 u' I+ @/ g( p2 i7 g. v3 g/ H5 |* i
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},/ |+ ~1 E7 U# D4 P0 n
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
: C$ p: v+ ^4 |8 V0 o) e    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
: ^* r( F( T* g6 h4 A' D& K$ |6 ~, }    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},$ J# n3 F# e/ c
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},* a; z" ?' c# [) F: T
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},% |* `' @4 t( H. l+ x% p6 i- w) V$ H
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},' m& Q4 k! p0 H% r' E- _+ S, Z
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},6 O' F7 Q8 p3 _( T0 \9 x$ ^7 b
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
5 e7 k" \1 p0 U/ D};2 i" D6 U+ Q% S& o( x3 g2 [$ ?% \
, Z1 o' j! t( ^3 Q! M
# A) v, D( p! x+ i
const char hid_keymap_dvorak[14][8]={/ h. t& |. p/ m$ B
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},# u) i7 c: J) f- B+ A8 _+ [2 q5 l
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
5 v, P4 S% \) z: C6 N    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},2 `5 v6 l# \2 @4 @; r" ?- k; I
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
' M; h" V2 K, A& J: w# G" `# ]) P    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
# v: b3 N* g* Z5 s5 H! W) _    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
2 I% |) D% g, n    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
, k3 O% [4 i8 B" ]; |    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
0 _8 E; [1 u3 s0 G1 F; T( O    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
2 q/ e$ X& X" J4 B: n  d! W0 l    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},( i+ m0 b3 u3 |( ^: b- ?* t2 L
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},* z! O! s4 x4 \, o/ \% D& x, d
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},$ {6 P$ v0 \7 H+ P- p
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},, ]1 {8 c# V5 W$ J
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}% f) s3 r9 w$ P; f3 Z. H! X& L
};% K- ^: Z. q, x3 k" Z" D
, g# x) @* z0 s" x  c' [
4 e! N& {9 u1 J; q: n. ~6 I$ l
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
# K( n# N+ r# i
7 o0 j$ M3 K# s+ K( [- qHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

( [4 O  C5 Y% N" P5 |" y' [
9 H* P8 T, _4 g3 N; \+ L, }void update_key_matrix(char row, char col, char onoff)
7 |/ M+ e, M9 s3 z5 d2 s7 {{* _% i* B  W- m
    static uint16_t hid_report[4]={0,0,0,0};  T/ F5 E# F7 M  M7 d) D/ k
    static char (*hid_keymap)[8]=hid_keymap_dvorak;( W" k7 P  u( _/ ~) r

# L6 ~# O) o. k* ~" Q7 H% j" k

8 U" i- T5 d+ j    unsigned char key=hid_keymap[row][col];. {" R5 q- b5 M
    unsigned char *report =(unsigned char *)hid_report;# q3 l) `" E& D9 G, V5 Y2 p: {
    char i;" c9 y, m' [3 n0 [1 r' e
3 s$ B) l" `# |1 m2 Z: O% V$ }9 N
: x+ M) F# Z* Z+ x
    if(key==HK_MODE)
; o2 S1 _6 x* z1 c- o    {
# D/ L7 ]4 K0 t" |$ e- g        if(!onoff), P( v( y0 F; ^
        {/ Z8 Z' m- F3 ?" W
            if(hid_keymap==hid_keymap_dvorak)" p) `; ^( Z6 g: l  V
            {
8 J, H: G9 j' F. j4 u                hid_keymap=hid_keymap_qwerty;5 P4 a" f' D+ \4 D
                GPIOB->BSRR = (1<<2);
- @% f0 G2 V0 k1 n: }            }  g: Q; Q) y) ~, ^$ B( m
            else
: _  C8 M( I7 \' q4 o            {: j5 D! L; I3 }% r9 E; ?
                hid_keymap=hid_keymap_dvorak;
( k3 ^3 ]) h) ^" n! D9 [3 ~7 [                GPIOB->BRR = (1<<2);
8 c" e" E0 [/ p9 y$ L            }
* s3 H. ^, ~! q- g! d) B        }
4 j$ \3 J) K) F- D' S! q        return;
% K' D  B$ t( ?8 v* s! G    }% r7 {! A! H# \
  e$ X) M5 A/ Q4 {  ~) w3 ~5 ~& k
$ z. Z3 ]" H( H" h; |
    if(key>=0x80)   // Alt, Ctrl, Shift
7 a2 V7 V' v9 t  `* A& q    {
: @' ?  V6 k7 ]3 {0 s2 M3 C        uint8_t bitset = 1<<(key&7);2 G4 V5 b5 k4 z# x! ?
        if(onoff)   // non-zero is key up( l0 }( t" _* _6 p
            report[0] &= (~bitset);
8 B* O, b" s, i& [        else
: Z0 g* q  Z0 w3 h9 i1 \  B9 \            report[0] |= bitset;$ t9 H  U8 P6 `6 G
    }
. q) r% c7 y1 r1 J3 i% i    else
% v, z( h3 d. j    {
& Z. R4 @3 l( c" l+ T$ A9 T7 M# ]/ k        if(onoff)   // non-zero is key up, _/ ?4 u  l4 t, q5 }# T2 K
        {' `* ^( m# J7 C: N9 K' f* D' z
            for(i=2;i<8;i++)$ M7 l5 [1 z$ B1 j, X" }
            {
' w% [7 g5 A' e  k- a                if(report==key)+ M. V1 c( h2 u4 o5 T$ J: F
                {
* L9 ?( S& ?4 V+ s3 u2 `( ]                    report=0;! e% y  f7 w4 @
                    break;% F) \: j  w5 z. O$ |$ E( n
                }
4 i4 C$ E' h9 m5 w9 M8 f. d            }' G- l7 M0 k0 H% c8 X; B! E1 n! z6 T
        }
9 _( |; I) |) Z& E- E        else: U+ n1 B, S: B) ]
        {! V7 d1 l3 y' i- M
            for(i=2;i<8;i++)
. X* b9 ]9 w  q  p1 q5 a            {
( O: p% e  y9 O6 x" ~: p                if(report==key)
) v  b4 v0 {5 H' n2 _# m2 {$ u                    break;7 v' G9 l  P. r5 k" z* p1 f
                if(report==0)
2 L  J% q6 c& p6 r4 E1 b3 J                {
' Q6 j+ ~9 E3 C! r: R                    report=key;8 q5 ~% J- \4 D) N! c2 w0 A
                    break;; t; L+ V2 K1 O- [; u* B: X3 m5 q
                }: h* L! m' p( X. b
            }
" a0 A7 p% ~; K0 q/ D6 C        }4 n2 @, U- F- |
    }
& Z2 V; w3 W# M5 C$ M# Y8 Q    for(i=0;i<4;i++)
* O, @4 U8 I" F+ \        USB_PMA[192+i]=hid_report;
  }# ?0 d- `, i6 Q, e( C    USB_PMA[5]=8;   //COUNT1_TX3 c/ f% e& b4 q$ Y6 a! x0 j7 U
    if(ep1_wait==0): g3 v0 A/ p! O: I
    {: z: c9 c4 j  Q  J# l4 D- @( Y
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;' n7 P9 n, g* o  Q
        ep1_wait=1;
. U2 u. b: I6 y. C* W' d    }8 c0 J/ s8 L; j
}
$ Q2 O, {* E' R+ @+ H0 I' d* L; M2 t6 _% l# `, u  u! a* S, M( ]' G
" S) {- l6 g* `7 T- ~7 H3 P
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
0 G. D- Y* n/ t0 h/ c$ M  c keyboard.zip (8.7 KB, 下载次数: 6036)
: [' Y9 M8 j* Z; A
; {5 ?8 i' z+ {6 h4 S' H

! F0 y8 N+ T' `6 N+ G' I( H3 s8 D2 J0 A' U6 f

3 Y6 j: {* v" o* K' C# J9 W5 I
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘, o8 V$ f( F3 y) e7 s7 o3 K
不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
- H' Y0 }6 L) B. \刚开始我以为要把打字机改造成电脑键盘
7 q0 a& Z3 D  D' Z6 l; X不过楼主也很厉害!
3 z8 {3 [3 W" J, |
哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害2 {/ K- g  A. y7 T* u
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-9-8 09:03 , Processed in 0.189720 second(s), 27 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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