找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
楼主: miaozhuang

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑 $ q4 c0 O# ~# r

& c+ I2 c" d6 H2 x' j" v6 N( fhttp://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1+ _, j* r% e' ?
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。
4 p1 }6 L* _% X  f( [! f$ k
4 e$ k$ h# J) e0 Q在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。

/ f# j/ @) a1 |) Y! v' b6 |  _

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

% Z1 a3 _, I0 ]3 n% E- P0 V; F 005836yvs0wvovwsssgd3o.png.thumb.jpg
% k; _( _' l2 ZDvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
2 M2 S5 l" v0 a0 {+ w
4 T9 o/ W+ r, P8 I# f: x9 ^我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
  R* u* f. G5 ~: `5 B& R, n) m2 D/ k- u- h* k
到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
$ B$ L- i' ~' a- I9 U2 f8 P* C! t5 B0 S7 P
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
5 q# n% |# l0 z3 Z, y' _
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg 6 h4 m. f& c( ~$ z& Z% M( X! A$ U# |
----------------------------------------------------  分割线 ----------------------------------------------------------
1 I9 u' H, m" i" `' O; v6 D* @8 p# i/ t+ c6 m3 `' L
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
9 G6 q0 k8 X; f
020011osionbunl4ui44vi.jpg.thumb.jpg
2 u" m# c$ k/ P  Y' j轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
, h; v! X; C9 _/ D; [ 020017j8ycmnv7788bqv52.jpg.thumb.jpg
; R# P8 b4 y# u. n  r特写,80C49
0 q2 k' j2 Z) a 021040oujzuvtut6iujtvz.jpg.thumb.jpg 2 m  Y1 i( B4 i  e4 q
LED部分,使用了一片D触发器锁存指示灯状态./ j0 b3 `3 b1 \
022209ixzjfjc9crmrrqjm.jpg.thumb.jpg , Y2 [% ~( K( R) F; y$ E% ^8 @8 g
暴力破坏,将80C49拆掉
. ]; e' x0 t/ f* ]4 X" k 021113e48qq98vyohvhzzh.jpg.thumb.jpg ! w7 x) w1 i/ c6 K7 d2 t
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。6 D# w, N4 [$ K
021125nc9az6dj33rlds2r.jpg.thumb.jpg
* O# i5 H  F: b! E+ e) D! K
焊好元件后的板子,准备替换80C493 T; o% W9 h, |4 Q: Z3 V9 j
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
) P' g4 x  C9 V2 X( h用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。3 X( H" Q4 T" i/ E$ l- |
021104shifhnrqbr3o5nlo.jpg.thumb.jpg 7 K+ h, k7 D" p; u! p
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB./ O1 i4 Z2 p6 B! t6 d: x
022003ym1p9u4ug40280uu.jpg.thumb.jpg
1 w, Y; \- j) A3 K5 Q, n开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。$ \5 M9 q" e0 {, U! P
023313kt141q9qajtol7ma.jpg.thumb.jpg - I) v$ Y5 I" R" {, h7 O3 L0 `
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
# n2 f; k; T) ~ 023322nt7l5xb3ltttkltt.jpg.thumb.jpg
9 c4 y9 G1 b6 w
主键区键帽就位
. L' j9 N# U! W3 E 023331hin88e8wkrwzwikx.jpg.thumb.jpg
' W, {6 W1 F0 X. y3 ?/ a5 I编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
0 ?$ s+ f9 Y. {3 j: m 023336wjzlgopugg1jyy79.jpg.thumb.jpg ( L- W- G" y( Z
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。; `/ X% z! x" X, A6 r+ F- h
023341sffu4j3g2323h6fl.jpg.thumb.jpg
. |+ L+ X, |5 H; ], }  r( e
' r( E3 g  s+ `9 u8 ^
----------------------------------------------------- 分割线 --------------------------------------------------
& f" e) i/ N9 R- p! q4 u3 }4 d
$ H; _5 P2 b, x9 F$ U: S
& {: R( {6 j' u. O- ?
% k5 G1 U) |, R* |( X+ E/ d
6 E) H/ ~( B& B' M: _$ C! Q+ k
2 L2 u  M; R9 m3 I
' C& s5 O$ r' p- R8 K$ u, a$ M( O

' t8 d- O' I! Y% k0 U, C  i$ {1 f" M6 Z( E0 j( f
9 [; V; }0 H2 e% E

) e3 }; |/ Q) q. O+ ^8 K) R! A: U7 ]$ Z4 C
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。8 B5 x5 t1 e2 J; y. O& R
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
) B3 `2 v7 ]: h0 ]1 u 104025nzibm2rmiomhyirm.png.thumb.jpg
1 [+ G8 |! [2 d9 ]. T
. Q  P+ ^+ [" G$ K' F  ]" ?( h
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)7 `% _& I( N6 ?# }- S$ k) f" }
105004zkrez5houvkkznko.jpg.thumb.jpg , ~2 t1 r9 S# c* S" v' A

: R' s4 \% X) F' F扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
- m1 G  h0 [7 n) U3 h/ z
+ m( O3 M1 ]% s8 [9 A这是我设计的电路图:
2 b# z3 S2 j/ D9 ]3 G 110344ej2z2oo2rflo7oe7.png.thumb.jpg
! ^- n6 K5 c9 t: k
9 Y2 a$ x% U" l& A7 O8 D
PCB Layout:" N4 W( c3 Z( J% y( E. u
110847jjbjvt34vwt3v5bb.png.thumb.jpg
+ c" s. K$ N& S
3 I4 ?5 J& p% a( s+ n/ f) q  t
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. + {6 `3 S2 {" F9 @, ?, x" Z
+ [3 M. o) b' t# {& L
9 t- u: X# R$ ~* E- {+ A
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 6 Q6 `  ~$ Q( e8 F( Z$ o; t
+ b( r0 Y9 R2 n% l, E2 f. I' d
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
! D+ O) i0 g9 U3 ^1 b2 L( L) I- @! ^
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:
& H9 [8 S# l$ w 113818pmrfsb6z0byt6t06.png.thumb.jpg
& a3 B( r* q* P. n
) p! i2 f" ~0 r  N) k
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)4 d$ V1 X+ K: M7 j; i
# N- w( [$ [* |) u$ t+ K* [
USB的中断ISR,bare metal哦: \9 E. B- K' U7 _

/ g! J5 R/ N6 Bvoid USB_IRQHandler(void)' w' e! ^& l5 G* X! E
{8 x% b* D3 M( H* H; C! c( E# l
    if(USB->ISTR & USB_ISTR_CTR)2 y6 J3 \1 s! @; s6 v5 n  B- p" Q2 ^2 z
    {
& }& }' R6 B% j. h  p5 q        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
6 K  i4 Y; N7 f  L, L4 k        {
! q% C5 j# p% _            switch(ep0_state), r, F+ B2 F  l+ {: _, W
            {
0 c5 [8 Z% ?" t6 X. {' E5 ?                case 0: ep0_state |= 0x80;8 N  P5 V) \% h# _5 n
                        break;4 M- K! H. ?0 |
                case 1: if(USB->EP0R & USB_EP_CTR_TX)
! m# V+ A3 P" j3 s6 \4 Y                        {5 y9 d+ f4 j  b/ k7 S. e1 d- d
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;1 l# s. g: z9 Z0 |. R0 l) y- @
                            ep0_state=3;, J5 b6 P  l5 u, I9 T* ]
                            return;
" n- v6 v" A7 V3 g* H+ `2 M                        }
* s  f' L7 `4 H2 r                        else  v( h$ {" ?" a! v5 b
                            ep0_state=0;" E9 F) g6 n) I; X% W0 Q
                        break;
- R; `! f* l& u" ]% ?% w7 f; o; r                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet2 N& n# t7 F+ p- j6 ~, `! h
                            ep0_state |= 0x80;
/ K! H9 m, E! [# R                        else
/ j" q8 o1 H) J" Z+ C. q                            ep0_state=0;; t- g, x6 H, g: k. ~
                        break;
$ M( _& |2 I; Z) Q# C3 s                case 3: ep0_state=0;
% o+ Q+ k; ?& `: S* V9 N0 [2 [% c- g3 N                        break;
7 U: I! T. [" M) V& z                case 4: ep0_state=0;$ W5 I! o( Q/ ~1 l3 N  _% |( ~( z
                        break;
/ a, R8 `: f/ r; x7 Y" ?% p0 \                default:ep0_state=0;% J" k8 X1 {& ~1 c" {
                        break;
6 |) E: n% h8 B4 |% M* S* w  [            }
6 v. j8 j5 P) E( i; `8 N            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
/ ]% ^; B( m* {' O( t- x            return;
1 ~$ S! l- \( y3 ?, Z6 m        }8 T/ r3 ?$ F* @
        else    // EP_ID can be 1
1 E) ^: r$ B( k) j        {  z% D1 U  E* Z  i* m8 P6 |& B
            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
; ~- j9 q! J. U* m; c            ep1_wait=0;  u- T- Y0 T3 s& ]9 V" K; {
            return;
7 V/ i  i8 }, G! _% _- o        }
' |% A+ q5 J& F6 G! T- _    }
5 m5 m! ]9 \6 F& c' Y. G/ r5 o: }    if(USB->ISTR & USB_ISTR_PMAOVR)
0 ~& M/ z4 T4 M! q# k. w/ z) Q5 v    {5 q! H. C. M+ B
        USB->ISTR = ~USB_ISTR_PMAOVR;# h1 G. M5 S- [( D! [1 D$ L
    }
) f% j- r% R$ d# i( r; ?  f    if(USB->ISTR & USB_ISTR_ERR)
: u! @' |! {6 i: I4 S    {* T" ?. U! D3 s2 j
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
6 r, M: v% F) n3 h    }
  F1 Y: \& X" {, a, c; ]    if(USB->ISTR & USB_ISTR_WKUP): ]( L. U" \# b0 @  a0 g4 m
    {
/ t- S+ a7 i, j        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
) t" l$ J" ^1 a. `- h+ p" }        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
- S9 D* z! T7 [    }
- n+ J; f7 t8 K: p$ S    if(USB->ISTR & USB_ISTR_SUSP)
/ K) |- ?+ t+ @  [! A    {
$ U8 o2 Y7 c1 G  N        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
  b% Z) f7 p  l7 v9 X& _        USB->CNTR |= USB_CNTR_LPMODE;   // low power
$ m( d4 [5 `6 w& H/ k) u# {        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear$ i4 c" q6 C& w; y4 H) p
    }/ j) H( V9 A& m  x- w. H4 s" Y
    if(USB->ISTR & USB_ISTR_RESET)
) ^5 S# L; Q7 K+ U/ M* q    {
% N3 _' x4 Y) `5 r$ e5 w% t        USB->BTABLE = 0;    // buffer table at bottom of PMA
7 ^, S- C; f% ]' |2 x        USB_PMA[0]=128; //ADDR0_TX. Q2 V2 T6 d/ {
        USB_PMA[1]=0;   //COUNT0_TX/ A. Z6 D5 G! f- }- Y" Z
        USB_PMA[2]=256; //ADDR0_RX
; r  j) ~- m* v( n; D, X  n        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
* v$ P$ Y( F+ X- l9 N        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;0 v3 G9 [: b. w1 E* q
        ep0_state=0;" l* B3 w% J; w8 @) U
        USB_PMA[4]=384; //ADDR1_TX
2 b- ?6 k  R& G        USB_PMA[5]=0;   //COUNT1_TX
- T: d+ h- z! e3 _! L& W        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type1 C# L) u2 r3 m# f7 z
        ep1_wait=0;
7 ]" z7 K, |' i+ l4 e# l" B; f        USB->DADDR = USB_DADDR_EF;      // enable function
+ x& p4 ]! Q$ w! \7 r        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
. t( c. E* t, {2 W, p    }
/ Z3 t$ ?- V0 |7 r8 ?" X    if(USB->ISTR & USB_ISTR_SOF)! C' q: v: D2 z
    {' Z" \0 ?" F' F
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear* p3 s9 {% ]" B
    }
/ D- Z: L. @) o. x$ v}
: N% ?5 m( M8 \/ J- u9 Q5 L# v. V; w. Q: a& m/ b. h5 y
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。3 Y/ Q( {" r( W. Y- C- y+ |/ Q  K$ d
; i: y" w) m5 W- l* a& _
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。6 @$ |  T7 U0 ^+ g
3 |: j2 ?1 Y1 J( n: G
    while(1)
( d$ b0 D& t2 y* W; a3 d. E  w: [    {( m/ w# H5 p. `9 t; g
        static char row=0;0 C% F( I; }8 v1 Y& Z
        __WFI();5 W$ r5 r7 B3 w  _# N
        if(ep0_state & 0x80)    // request data processing$ k4 e8 W1 {' V5 g" d; M7 t
        {/ I; k  V8 M6 {+ k3 x3 A, M
            if(ep0_state==0x80) // SETUP phase2 |5 H6 m! t5 O" k; n# U  A& `- ]
            {
4 g* t+ B( {/ V7 j8 B                if(!setup_packet_service())
  D3 {# b( `" q2 Y& T) d7 ]7 o                {
! g" |3 H8 q+ F2 m2 R0 Q$ p. E                    ep0_state=0;
2 E4 t& P. ?$ s7 A' J                    // not supported# D# W4 h0 }0 b3 o0 Y; u4 K
                }1 j/ C" \+ h- f) m
                // ep0_state should be set to 1 or 2, if processed
% F- z* B) H4 d4 C) n9 f$ d4 F            }
" {% Y0 E/ J& d3 ~            else    // OUT phase
! w: n4 Z) [) W3 f% Y1 o' g            {1 D6 N0 D9 t7 g: j! `
                // process data
% H% M. }7 N; W  C9 b. K2 B4 B                show_LED(*(uint8_t *)(USB_PMA+128));
9 g4 o1 A' _2 u4 _5 i! [' m# k" M                ep0_state=4;* @' y5 U! F+ p' z3 m' c, h, x
                USB_PMA[1]=0;       // Zero length DATA0
! Z- u/ G% t1 d# p" Q7 F& X                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;; O: ]) e! O! ^! P! c% v) b
            }+ I0 m$ y% g5 T3 L+ b" C
        }1 x; ~0 H8 k8 G
        else
$ c& {5 A' B- B! i        {
+ h& b3 i! U1 C            if(usb_address && ep0_state==0)
- b6 Q2 ^7 ^4 u% a% ], r            {& o9 p% q$ m$ M/ z2 }/ h
                USB->DADDR = USB_DADDR_EF|usb_address;. q$ k' z* |* I( E& ~: `$ y& u
                usb_address=0;4 s" a  l& G) b: _4 B  t
            }
9 w( g/ a2 U, T9 f$ |* s: T        }9 Q! S8 `0 S4 K# f/ W/ N
        if(row!=scan_row)   // new scan line3 u, ^. t- L! Z. a4 v. S# d1 h, T3 f4 o
        {0 ^2 f* D4 M! o7 t
            if(key_state[row]!=prev_key_state[row])
2 G0 B, g% ^* P1 R; r1 q            {  H" ^8 w6 T1 `3 ?+ P) D& \3 Q
                uint8_t test=0x80;4 F2 ~1 z2 B2 B' C7 J
                uint8_t diff=key_state[row]^prev_key_state[row];0 Y: U/ R! E( C7 ~& N
                for(i=0;i<8;i++)
# k+ F2 D0 |% x9 w, p: ^9 o                {: Q4 s; y2 \$ [0 s
                    if(diff & test)
: t+ O: q, G, |                        update_key_matrix(row,i,key_state[row]&test);6 ]4 e8 n. v* e5 h2 t+ L% |6 c6 S1 s
                    test>>=1;% B$ }$ M" m: m4 E5 b7 Q/ P
                }: i# e  A- Z9 B
            }
. h+ b$ u3 r( N0 J3 B! b0 o2 J            row=scan_row;+ D- B0 p0 f  X' ^
        }6 l3 r1 @& e, f- i7 [) j
    }; q. y9 e6 @6 F
* j& L' ^  H+ |

1 t9 ?2 }  n& SEP0的控制传输,把用到的请求处理一下* j# C5 {4 n( u# n0 Q& F
char setup_packet_service(void)
8 H+ K/ S4 W6 c, \4 r7 g1 u{5 I2 A  z1 t) o( i+ ?9 }
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific  L4 S' V2 k+ P0 Q
    {3 v  b( f: `' i, c6 |
        switch(ep0_std_req->bRequest)
9 w% ^. n1 b$ \) M        {
9 `: N# Z2 j8 C& F9 F' W) S- g            case REQ_GET_REPORT: break;
% N, ]3 p* X5 g1 r) s4 B; w$ y- l9 y            case REQ_GET_IDLE:3 V- r( Y4 h% {$ L2 p  r. |
                USB_PMA[64]=0xfa;   // return 1 byte* u& p& Z. H+ p4 B) r
                USB_PMA[1]=1;% N4 H" l) W4 M. v# T0 ^% m1 v5 w. r
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;# H  z$ L' @+ R
                ep0_state=1;, m6 L, v% i2 f( r6 k6 y* C
                return 1;
; a: d: H# O3 x8 C5 x; X                break;4 g2 D) V6 a% l# u6 a+ O
            case REQ_SET_REPORT:$ _* }1 N9 `: m
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;3 G" y* w% e& O" {1 p; v
                ep0_state=2;, C, |3 z4 r. D% [* `
                return 1;1 p" g% @# ]% D
                break;( [: f1 |2 X; e! Z# B
            case REQ_SET_IDLE:
+ _( B" H* S) [- O2 O" K                USB_PMA[1]=0;   // Zero DATA0 A3 C4 g6 ]- g! K1 ?
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;( Y6 t1 R% M' ~+ s  h% t
                ep0_state=4;9 W( n: `. }9 O+ _
                return 1;) a' c9 Q* e% K
                break;
# |' \/ `+ h; A1 @+ O# g        }
; i  y3 v/ Y" ^; g+ h$ x        return 0;
; Z! V) d6 c9 @2 Q2 o    }, j" ^4 E5 s+ b! F. ~' P# ^9 d
    else    // standard3 D' a, W% u$ S
    {
% o! P/ D. W- r! v        switch(ep0_std_req->bRequest)
9 ?& b9 p( O# A4 T& ?, ^/ I" w        {; n! j5 Y- `% q" X  t
            case REQ_GET_DESCRIPTOR:1 C3 x2 z- N% Q% z
                return descriptor_service();. t# k+ ^% \$ o. W( [/ b5 \& ]
                break;: h5 F' P% k" i. S: E  p  t9 N
            case REQ_SET_ADDRESS:9 H: g% q+ t1 j! z
                if(ep0_std_req->bmRequestType!=0x00)) Z7 i7 d* I2 z1 ]% x
                    return 0;/ [% B; g, N1 e; I  ~* D% f$ Q. X4 x
                usb_address=ep0_std_req->wValue;4 Z* _1 B4 ]1 X+ _7 a
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
; T1 d& n  j: `) ?4 x                USB_PMA[1]=0;       // Zero length DATA0/ j1 N1 ~& Y; X  F2 G% a2 s
                ep0_state=4;    // No Data phase/ M- ~* [$ X: J9 m" e- i' D
                return 1;
1 q% B; g+ K7 G) ^  R+ q: H, N4 ]            case REQ_SET_CONFIGURATION:
8 \" c5 T- c) ?, N& \. _2 u                if(ep0_std_req->bmRequestType!=0x00)
8 y9 p& q: a' G! ~. ]  j. a                    return 0;; d$ F$ V" I8 o; O; n
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
$ E! e' E5 T% [' C: @; k                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
( l9 k: Q7 Y3 ^$ p# o                USB_PMA[1]=0;   // Zero DATA8 ^. R; @' a$ z+ \0 _
                ep0_state=4;    // No DATA phase  t6 H, }( J( b
                return 1;
& [$ b- D" z, \- ~$ I* x            default: return 0;
4 A7 x0 @  b" b, ]# `, M        }
  D( ^$ O; U2 y- k0 ^    }* H6 d( H$ b3 K4 b* q, @4 n
}# `: n( _* E  i$ P1 @. ~7 h: k- t! f
: F1 S2 ?1 D2 p0 a8 b4 Z8 Z
: d( C; ?+ Z1 L

1 e2 V9 g. x' J' y
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
9 e! U* q. e5 ^" cchar descriptor_service(void)# U! D  I6 i/ J$ Z9 @
{" ?: Z" H. E5 v& h
    switch((ep0_std_req->wValue)>>8); h# ]9 `* Y. U8 j
    {( X6 S1 r6 |3 [- I
        case DESC_TYPE_DEVICE:
3 ]3 q" Y. G  }) n0 I, ]            return ep0_preparedata(&DevDesc, sizeof(DevDesc));
/ M! c  O) Z2 g2 v            break;
  z0 ~) d0 n) Z        case DESC_TYPE_CONFIG:
, x- `  X& h. ?- n! }! h2 {; i            if(sizeof(ConfigDescData)>ep0_std_req->wLength)
" N9 `' c$ k  ~4 h4 P8 o                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);( M. L2 T! v+ E- B4 i! o/ x
            else+ b) k- I& U* s
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
8 A9 ?% c+ I! d# w3 `  k            break;+ G8 Q( c) Y! t' s
        case DESC_TYPE_STRING:9 {# t  e% g$ F$ E1 Z7 ^$ T$ j
            switch(ep0_std_req->wValue &0xff)4 d0 b& x- S8 s) n
            {
9 o" x- P* ?, \5 O( y3 Q                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
2 I3 l; ^) d9 d! `5 r. y# w# o                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));( w2 E' z4 L, _
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
$ o) b0 S8 I: W& J# g8 R                default: return 0;4 |  T* Q' i# O2 Y5 d
            }. H: i) |( ]2 G2 w1 N
            break;# c# z! c' a4 P+ y
        case REPORT_DESC_TYPE:/ _0 m0 g6 N! |  r
            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));* q' e3 k  t6 {
        default:
) k' R. l  L4 v+ T, ]8 D6 w            return 0;$ T6 T6 q; @$ V; Z
    }
, p* v' e. V* C  J$ `2 @$ y  D( D}' s  `* `) j6 M  f% ?% k
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
/ G( E) O3 k7 ^3 cvoid TIM6_DAC_IRQHandler(void)
7 w% y: i, P7 J/ k{
# f0 r# r, R" }( i( U: u    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
- |8 z$ C) @( {% q. }: E0 T9 p# k# J& x3 w9 v2 ]$ ^$ K
$ _7 Q4 l% A* v" ~( `0 [! m
    TIM6->SR &= ~TIM_SR_UIF;2 k! P$ t0 o$ Q3 p: W" X0 p) ]1 q
    prev_key_state[scan_row]=key_state[scan_row];: J6 b& ~" g% y, W1 y7 r& z( F8 l5 J
    key_state[scan_row]= *PA_IDR;   // update key states
  H) D, N3 m+ }    switch(scan_row)( l: L, U7 p% ~- `. U! Y
    {
  I% N" u$ K3 q9 z9 ^' b5 x        case 13: // next row PB14
. A) M' a& h  B6 i, G" I                GPIOC->MODER = GPIOC_DEFAULT;* b* I! _" Z* L9 Y7 r1 T) W4 L( g# z
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
  h  H1 i6 o& F2 u# o; L- t                break;
; _9 r! ~" f, G2 h  T        case  0: // next row PB15
. f* L3 }& a, n# F- X4 S                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;
! y( ^: S5 e% x$ L' \                break;
1 q  D; Q7 N' ?  g# n9 O- T; y        case  1: // next row PB3" ?. n! o0 R/ R- j
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
& a* Q9 v! R* [                break;
; z, t" A; ~6 ]# Q, i- g        case  2: // next row PB4' W; V5 [) P- ^! B! `8 E
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
, X7 o' Y9 }  g  [  m                break;4 U1 B" [4 \( t
        case  3: // next row PB5
0 E9 ]2 E' C1 b& [' ?9 E                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;  J, o& O5 W( A+ |# F" d, W2 P2 Y
                break;
0 g8 m" x& b, M( }; I: u) w2 e        case  4: // next row PB6
, G. P3 e) ~& J' N1 t                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
' f; G2 D- C2 N) P! `) V: d$ Q' @2 y* E  W                break;* w" T- s, H: a5 O( n; i4 |1 Z
        case  5: // next row PB7- @" _0 C: l6 g) K1 h) t
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
6 u, @1 D- p& Z% Q( Y  l- |( r                break;3 W/ R6 D" X/ f' ]8 ]# H1 \
        case  6: // next row PB8
& ~6 v6 L3 K3 u                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;$ y& f; t, e, r- t- Y* ?+ M) }
                break;- l# @& \1 x) R- p. u
        case  7: // next row PB9
: T2 o5 z$ _# |4 F. _                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;
6 J7 s8 P9 f9 P. y8 S                break;
/ i7 D, G/ v: n8 h6 R        case  8: // next row PA8
3 a8 g6 W1 A; S3 c; @) d8 o5 c& W                GPIOB->MODER = GPIOB_DEFAULT;
0 O0 {- S& a" r5 J  H/ O                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;7 i6 W1 `' r1 q/ |7 Y4 r
                break;5 h3 N6 J) t, x) [9 e
        case  9: // next row PA99 B  n: c' m: W/ p& c& l, k
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;" [; f) ]" T. H" Q
                break;
8 @9 O. q7 ^; X" W, B        case 10: // next row PA10; b  z; s- C4 e5 I( r
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;: R8 i6 j6 _* C* t
                break;$ I. M" L( r8 F' e& O5 H$ H& [
        case 11: // next row PA15
. k& i5 {, ?0 c, ?. R1 H* p                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
: u" B9 S9 M+ U" x: @                break;
2 [  X+ I, S% v3 _  B        case 12: // next row PC13  R& J2 Y! y$ U5 \, h1 [
                GPIOA->MODER = GPIOA_DEFAULT;& ?& b" U/ o% m" k0 R5 p
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
: s0 S( I0 D8 \- I- ^% M                break;
, p5 U7 S8 ~7 Z7 b    }& B/ k/ U" P5 n' {+ R6 q( i6 H6 e8 E
    if(scan_row<13)
; }7 @# i# ~' D0 _        scan_row++;
  I1 ]4 Q! f+ A5 y    else, a# O! O/ _0 V) R# ]
        scan_row=0;
! v! e  P8 C5 w" ~( E3 b}
: O7 Y. @( g2 |4 P  _9 t9 }8 j! s% r/ a  j6 S0 X. c4 _6 ]
( L# m0 a) f2 ~0 i0 m6 n& Z: F
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 # R4 m4 {# ^3 V
! M$ z  A, ]& o; u% m
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。) b9 W( A* ]# ~
# e2 \" _2 M5 T8 S( e5 j- ^
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
* E; `" C% Q- l. T

3 \5 j1 M: j. E7 z: d; k; d+ x
' N4 @5 {  a. z6 Oconst char hid_keymap_qwerty[14][8]={  F$ `; S+ c7 ?; w" s$ ^3 f
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},, C4 I3 [# R  P/ V" q2 |. T% ~/ X3 N
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},# Y3 h# @+ A& W5 E9 O& R
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
. K, e: Q; y# H* }    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
& O. w" Q4 P4 \% X    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
. }7 m9 `4 K' ~) Z+ K    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
4 @3 T% l+ E* N2 t6 @; i) L# X    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
; q: |/ ]# m' I3 M- ]8 E    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
6 |- H! V$ Y. M1 f1 e    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},( ]' M$ J" y% c- }/ F' s
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},5 ~5 o  g' g* G4 _
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},- t- r  M4 j% F' {  H5 ~
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
4 k4 e8 B& p, O! b5 z    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
$ m1 s8 a* B# x4 J* ?    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}/ \0 `- t0 ^, _( v
};: a2 Z6 W' ]& Q0 Z: b' S* g

. s$ g5 Y4 @4 G, H7 G& ~
) d9 h! M' Q# D- u. k8 _& @
const char hid_keymap_dvorak[14][8]={. a* t% u' m- k
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},3 ~* Z! C; Z1 {; S
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},& d4 d6 c4 j, b' Z! }, P
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
; G6 P" e( M' U$ h' y: m    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
8 A# Y  H. ]" u+ w    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},) j/ q+ ?! ^! `$ P) |5 |
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
8 I3 ?5 c/ h! W* h' Q3 S    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},3 G8 R$ V9 U" j' Y# M; G, b
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
) z" c, I2 r: ^4 S! C    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
# K: k0 M! s$ r7 V8 P- F! L6 `    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},) E  h! e2 x5 V7 n4 P. O
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
3 P! v! V# t/ B4 b# ]& g1 g    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},4 L. `- L+ Y: K7 i2 s
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
4 m2 s6 |% {4 }* f# ?# ?! T- U+ |    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
, S% o; y0 I  @# Z. C" A6 y};. X+ |9 a1 R5 z3 E/ J
2 R. @4 x7 `/ Y# |$ o
, W0 G3 Y4 K4 |! Y
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
5 K8 ~5 u9 \) W1 b+ ^3 U& ?0 I/ L2 _! O9 P$ t1 f
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
/ Y6 @/ s6 O9 O5 ^3 B  n8 _# L% E* H. t

9 V- ]: L* e; d7 p5 Kvoid update_key_matrix(char row, char col, char onoff): u& \, i0 g7 y0 a( y; \
{
- N  {3 M% F3 c& t3 f1 q    static uint16_t hid_report[4]={0,0,0,0};
6 A- a7 ~& O, D. {; P) s    static char (*hid_keymap)[8]=hid_keymap_dvorak;
# H0 n3 `9 a4 V" y/ f+ ~: L6 R) L- a9 M
" F. Y4 V: D" L8 w
    unsigned char key=hid_keymap[row][col];
5 z1 j4 J0 @$ X7 s2 E% E! w    unsigned char *report =(unsigned char *)hid_report;( ]1 s# J; a* _# G4 B8 p
    char i;
8 W( g4 T! G0 b+ n" T
/ y2 E& `4 S' w& s! A7 ~; U

5 J( i& g* |1 B; l5 A  ^% T    if(key==HK_MODE)
6 a; k- M# t4 w! V0 y% N& k    {) p) J  q1 q5 V0 g) R6 ?
        if(!onoff)5 n  c; \0 Z) w1 ^+ a4 ]' a
        {' J- ?8 N6 I9 R2 ?8 d
            if(hid_keymap==hid_keymap_dvorak)1 \5 z0 f. y9 W9 }" {6 ]" ]
            {. R0 @( g# r% Q" m' A+ B$ u9 [- n- T, J
                hid_keymap=hid_keymap_qwerty;( x# f1 t5 b2 o' x  S7 A
                GPIOB->BSRR = (1<<2);
9 t/ U' C; z( C# c3 L" |            }+ |4 w! {# l: f# U1 A) h  I6 P4 G+ `
            else
. F" Q% Z( {, A  I, j            {: x/ }1 @( J' F
                hid_keymap=hid_keymap_dvorak;7 V) @1 J" |9 z0 g, P( K6 N
                GPIOB->BRR = (1<<2);
6 S6 q0 U- H2 _$ v% l/ k/ I4 V% G            }
; N2 A9 _( Z( v9 N        }
# }- ~5 S+ v# @! F0 U        return;3 a- U) F/ _% u' d$ E' X! d
    }
6 z. @8 n+ G, \8 X' d! D
6 M. ~7 H" S0 R9 g
8 y5 U1 i( ^3 V$ M6 p
    if(key>=0x80)   // Alt, Ctrl, Shift
' Q2 B' x1 U5 h    {4 {% b/ h/ b6 u1 v. I7 O7 e1 \1 j' l
        uint8_t bitset = 1<<(key&7);+ C. I$ {5 [3 X. `
        if(onoff)   // non-zero is key up2 Q# i' c/ t" ?# {" Y* z2 r+ \6 s
            report[0] &= (~bitset);
/ C$ ]) O# x& e0 r' S& ~" O4 u7 p        else
  B# n5 S6 }# |1 E& r( K            report[0] |= bitset;
( I/ q' z8 A! o    }
9 ?3 z& P3 Y& k$ v+ p+ D7 n8 u    else
  D6 \( e* s1 _8 Q: H    {
5 S1 {. |9 {# ~) x- x        if(onoff)   // non-zero is key up6 M& E( e( X" ~6 l6 N  v& R
        {
4 T+ I8 [' V" L/ w            for(i=2;i<8;i++)$ K+ d: ]; k$ n' Q# v6 C; J+ U
            {3 z( o/ b7 ~, I! J  x7 @
                if(report==key)0 Y7 D- V$ w0 N% ]" m( X2 F6 }
                {2 z8 E. e( E: V; T6 ^3 Y
                    report=0;
5 }3 w9 u0 D7 _2 {! Q& I) {8 H                    break;6 C/ X2 y  q# Y; I! c
                }1 C7 S  d  c4 ^7 r5 o5 r" U  O2 e. H
            }
% P1 ?- E/ J9 w# H        }
' b1 q. y. B' B1 E( c& R3 Q% w6 V/ \. n        else
4 a& N5 f  U/ ?3 `        {
: N+ Y6 T* G& ~; q. W0 v% b            for(i=2;i<8;i++)! S; ?( n0 C3 r( u
            {
! H  b3 u6 C* ]2 P. I+ ?- K- h                if(report==key)
$ p" l' m4 c; Y6 W& p                    break;
; q3 {& s; w" [2 I3 g                if(report==0)" `  m2 g/ M2 a1 u7 z/ q9 n
                {, t; Y" {" [$ v: b
                    report=key;. z8 b/ p" ]5 ^- ]3 }
                    break;
. z0 V3 U+ \  B2 a% E                }
; Q( c5 H# a3 Z; z; s2 v0 E            }! I, c. _6 w- i! |& X- f
        }/ V. d+ C  Q  a! W1 ]3 t
    }4 A( x' S* J8 H( Y( D" ~2 ~) P0 {
    for(i=0;i<4;i++)
+ n/ K% S: j* j        USB_PMA[192+i]=hid_report;
# w$ e: A! v: U) K* i    USB_PMA[5]=8;   //COUNT1_TX) J" ?, `2 G1 I8 ?
    if(ep1_wait==0)7 V" A# _6 V. `8 T7 y
    {
$ N" K0 C* i' w, o# U        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;/ _# s/ W; {. f1 T& S- b  r" X( a7 t
        ep1_wait=1;
6 h3 \- w# @+ x+ l: u( d    }
* {+ i7 C5 x! |5 e}
: U; [7 e4 B# |+ v$ ?
+ M* ]) q* a1 o' {* e, ?
. q/ D/ A$ k: m5 _- K7 K. H. ]完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。9 S8 V0 N- D5 Z8 G1 ]5 Z# z5 E, {8 U
keyboard.zip (8.7 KB, 下载次数: 6484) 2 X& H' b0 Y3 X! h9 N& R& r
  e3 i: u! O  @3 G2 q5 t5 S4 z2 s( I

( a9 M( W# [' p! G$ E. M+ D
$ f6 b' D8 N+ F
  k& K; i/ J4 F2 f. V2 \
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
0 k- W% r( n- `5 p% y' H) U不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
* t0 ?; f6 d; y, M; a+ C# Q刚开始我以为要把打字机改造成电脑键盘+ {" E  }9 L  w: Z6 N% g
不过楼主也很厉害!

$ |; t9 V, R6 v3 u1 T' E哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
' o0 J. l% S% e( X( R
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-5-19 03:58 , Processed in 0.163178 second(s), 24 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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