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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑 / f! T, s1 u; y' n

! o9 m* B7 G8 w+ x: o* d0 z$ J  Z( ?http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1; c7 Q: \# @" e
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。* i' {. S2 L  k/ i4 S

0 h# z% k5 F2 @1 z! g% h在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。
: @. G5 q& V8 a3 S% t$ q
! N. @) X5 i0 I6 j
235140i3a36qivqzuvmt5q.jpg.thumb.jpg   _& ]* o, V0 h+ {# J8 ]
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。+ p  c0 G2 G. ]) B3 j# \# o2 E
001734klbyoluenuwz4h4b.png.thumb.jpg ( q& h9 s& E; X
为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:
6 l! C4 d# B. @  Z9 S" ]$ N2 ?/ Y 003625r2agx2f5v922cf2f.png.thumb.jpg
. j4 b4 Y! d% A6 {0 k其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.
* {8 o8 _/ @8 {( G3 ~5 G5 {1 JDvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
1 d0 p4 V* B. W( ~" f( x) t" V
005836yvs0wvovwsssgd3o.png.thumb.jpg ! a: n8 e# `( ^* `' I
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
( h3 x& n. j9 @( M% N6 j. S* @% I
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
9 H- V0 _* S  J
& T7 f  b$ ^, o3 P* }; E8 S& B到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。8 T3 `! U0 N$ N( h: E/ R/ o

, n* |/ K0 m( M2 A' b机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
2 A7 J; @8 V9 Z/ j1 l7 [: S
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg + F3 M1 x; N& @( H' h3 X* v
----------------------------------------------------  分割线 ----------------------------------------------------------
) Y  U) Z% V  Y5 }& q: b
3 r( P* @; {0 U/ y& Z: G' V先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
' p% k6 L: c' p7 D8 J: F$ x5 Z
020011osionbunl4ui44vi.jpg.thumb.jpg - y" g; u$ K1 t$ g0 p8 o* C' ^
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
4 p+ c; `* j4 b! f8 f- L5 G( M 020017j8ycmnv7788bqv52.jpg.thumb.jpg
6 T* M3 @3 s+ G0 f3 ^特写,80C49
+ P5 X$ t, B; u  O 021040oujzuvtut6iujtvz.jpg.thumb.jpg & R) N2 M+ \8 Z
LED部分,使用了一片D触发器锁存指示灯状态.
- k- A. b( M! n% j, @1 f 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
5 y0 M8 m/ k1 Y暴力破坏,将80C49拆掉
) {# L; U: a  r. I, z% h3 z# { 021113e48qq98vyohvhzzh.jpg.thumb.jpg 3 ?( m" {5 k/ W5 C
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
& F6 d. Y3 R4 t  h& i; X" ] 021125nc9az6dj33rlds2r.jpg.thumb.jpg 9 y! q1 O7 p- |3 T* B
焊好元件后的板子,准备替换80C49
; z9 [2 u; u0 B8 P 021146zmmpmh4pav4o7g6a.jpg.thumb.jpg . H) q) z' }) N0 i! e
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。
6 z8 J1 b" R- l 021104shifhnrqbr3o5nlo.jpg.thumb.jpg
8 w, P, Y8 `+ J* m, O这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.
2 x. |. x. x1 f, u% w 022003ym1p9u4ug40280uu.jpg.thumb.jpg ' d; p$ m3 [( w+ ~* n" I& @
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。; _( {1 i4 U$ e7 E3 e- O5 C
023313kt141q9qajtol7ma.jpg.thumb.jpg % v. P$ w9 I4 |' j2 R( r' l0 ?
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
: T/ I$ }' b2 ]% j0 o 023322nt7l5xb3ltttkltt.jpg.thumb.jpg
- m( U2 I3 h& C: p. o" w& P
主键区键帽就位! a/ G; q% c8 a  P
023331hin88e8wkrwzwikx.jpg.thumb.jpg : v, l3 m( R& j) N  P$ {# u- ^3 L
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
( n9 `. v8 Z4 r  c; }2 k. H 023336wjzlgopugg1jyy79.jpg.thumb.jpg & r3 f+ P# A7 h! d/ d4 L& m
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。
+ D: {7 `6 Z0 N$ N+ C5 x! I; I- f 023341sffu4j3g2323h6fl.jpg.thumb.jpg . O0 Q& D. R9 S# w/ J" W
, `: e- l! x9 |' ?4 ^
----------------------------------------------------- 分割线 --------------------------------------------------
( r' H$ d. {% @6 ?% J/ [7 p% D

0 p8 Q, l3 p8 W5 g( ]
3 I9 v& T( p# ]) f) o7 W5 y

6 [* Z" _% ]' ?3 @: |7 R: T  c8 ^; q9 d% \# l; a& T2 [0 u

- Z2 Y# c! ~1 ^2 J1 j0 l
* M& G. D, b; A$ M+ l2 m+ l* _; ~

7 x, I" n# D( \) Q. w3 c; |4 N! E5 j! C8 [5 D4 x7 }5 u2 U! ]/ u
# F7 [& e3 {! {( J
! W, \4 W( s3 ~! [8 \

# F+ N  H4 B& s
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。% B( i' X7 I6 U6 M, V% c1 {# ~8 V+ o& V
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
8 p. R$ ~8 I3 p3 r* D 104025nzibm2rmiomhyirm.png.thumb.jpg ) c4 y4 b8 R" `6 ^2 \
2 t( p. N% u5 @$ Y0 `
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)- l( f' P% d  |5 c  f, n5 t' O: C
105004zkrez5houvkkznko.jpg.thumb.jpg
3 x7 M/ ~+ A5 G7 O# s# d. k* W
/ C3 S7 V+ @% k; P
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
* j6 H; e) B/ B; B3 v( X6 ~  P5 G2 k- o; a  _
这是我设计的电路图:# x9 p" n, g  D) G  S1 U
110344ej2z2oo2rflo7oe7.png.thumb.jpg + S- ^  p  D( v9 N- A; Q" {
5 R$ Z1 ?# H+ f1 E5 q9 J
PCB Layout:$ `5 o$ U. ^+ D; Y, T7 R8 y8 z
110847jjbjvt34vwt3v5bb.png.thumb.jpg
" z6 X; q+ B6 x* i
# f: r; O- b" R2 A" }, a; W: L3 ~# K
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. 2 k1 S9 y2 ^2 x6 T& B7 C/ m
8 l5 i" H: ?. ^6 x# `5 ]
+ }1 p% w* \1 {% j* h0 D; U, B# N
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑
6 s/ n/ ]# e6 T" p2 Z4 ]" B5 o# z: Z
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
: T$ E* x* Z1 M
3 y* b& _( o" i, v7 l4 E总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:
% J- z& S  _( j1 a  i8 N, o 113818pmrfsb6z0byt6t06.png.thumb.jpg * a' y8 ?/ O0 w

  S1 |. P) b/ `, T其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)& h- x$ _1 {0 A, h# w; r

. |% s3 ^, g7 o+ u) |USB的中断ISR,bare metal哦, v/ X! g8 M' l0 z0 O% E

3 p* q& k( _( h6 ^9 D5 @void USB_IRQHandler(void)
8 x! H  q; {9 w1 P/ F' N3 {{
% S0 y! n) d$ C. H( R& X# g    if(USB->ISTR & USB_ISTR_CTR), Y" z) J) `7 B+ a' C  O& y3 X4 i
    {4 Y% O6 @1 x% f
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
/ v- x$ B  j* _( b- L        {: h) T' ?/ @& e+ ?  {9 g, Q
            switch(ep0_state)" ?% C  ?, Z8 I2 d& k5 b
            {0 E( N* T, D3 N$ D
                case 0: ep0_state |= 0x80;
: i9 W9 s) I# R. e                        break;: Z# H3 G8 j' H( C1 _( T4 z) S
                case 1: if(USB->EP0R & USB_EP_CTR_TX)4 G4 ]. |; Z1 m# m
                        {
- P7 l) O' X1 j                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
1 B7 |2 ]; X- x                            ep0_state=3;
0 K4 w, j6 v/ H                            return;2 e) L4 _/ q8 S# |
                        }
4 Y; [$ d6 u: C/ z- J: y                        else
% }) Y& D7 }  q% M, m( d+ c                            ep0_state=0;
, D% }) e5 V( o9 ]                        break;
1 g0 \; z$ f9 d+ G) ]+ P                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet3 Q: |& R1 i8 N
                            ep0_state |= 0x80;2 `) j2 O) F* k
                        else6 d2 v' D( Y: P, M
                            ep0_state=0;
1 o7 s4 D# B  s* D& _0 P; N3 }                        break;
% e! A! f6 ?0 `* d1 X8 M0 a/ S                case 3: ep0_state=0;) E6 M9 U8 @. S" h
                        break;) e' A, g3 |  t* T: w6 }# b! e4 p+ }6 l
                case 4: ep0_state=0;! \, R/ v- }5 A/ h" w4 H, I! ]) B
                        break;
+ b, _! d7 H# a8 U3 k                default:ep0_state=0;' S2 {( g& d& I
                        break;9 u) w/ y; w# y4 L' }
            }* `0 d# O/ A) d) n3 c
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
' a- L7 s4 E3 P$ c9 {5 Y            return;
/ c; v  t' f) _6 V0 S) V- ^        }3 `$ S+ m: Z+ L6 ^! E) Q5 E
        else    // EP_ID can be 1
" }/ d3 `3 X' X8 H, f  w        {
! \# x! t5 ~7 `5 r7 Z0 ?7 F            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
# o8 V7 ^: J* N( s5 h            ep1_wait=0;( @7 R% z6 n% Q7 k% D; o7 A1 K) ~
            return;
" K; i" }  t. |: f2 ^; g        }& H4 l" ~3 F& x. w- l
    }% R1 T3 C# {3 K) j9 r# _
    if(USB->ISTR & USB_ISTR_PMAOVR)" r( _4 `# u( D' U- v
    {
; ^* k1 m2 C0 I        USB->ISTR = ~USB_ISTR_PMAOVR;
( X% y1 F4 P$ I- @- S    }& w4 N! O1 a% w
    if(USB->ISTR & USB_ISTR_ERR)! n$ Q- Q3 Z8 E9 `1 I% R4 Y; A3 D
    {1 E! i4 V. U! ]. N$ }; u- d6 m7 _
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
0 B+ [6 _) ?5 u* \: X3 I9 n    }' Y! H5 Y5 d9 O' ?1 w
    if(USB->ISTR & USB_ISTR_WKUP)5 E' d4 x: Z6 y! R  V3 y! u
    {
; R: z+ C6 i6 ]8 n; W' e4 [        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
$ }4 n' l0 P7 _5 \        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear9 T# u+ Y- ~) V* ~0 @4 z
    }
* i; w- N. B# Z. O/ o    if(USB->ISTR & USB_ISTR_SUSP)
$ y( h' a" z( ?( ^& ~( ]  N9 d' d    {
( S1 D( p* M6 g% {' f/ Q        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend/ @: S6 W& p$ I& W- Q6 x; k
        USB->CNTR |= USB_CNTR_LPMODE;   // low power/ P, {0 M" R5 a, e0 y
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
8 {1 H" [5 d. b    }
4 B- ~* ~( @: Y, t' ~( o, R4 H$ k' k    if(USB->ISTR & USB_ISTR_RESET)/ F+ M  `0 W* @; C) X/ k; l
    {
% b8 U1 a) r" H7 [" R- n: ^        USB->BTABLE = 0;    // buffer table at bottom of PMA8 c" B9 a1 {! s
        USB_PMA[0]=128; //ADDR0_TX
3 j- _% I' E# _& w5 V, O9 a        USB_PMA[1]=0;   //COUNT0_TX
& ?: S" \# d- J+ H' w  M        USB_PMA[2]=256; //ADDR0_RX
8 |' h& c' H) k  d        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes) E) |% _2 V  L$ w, E
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;. G% G3 }# O; @( v/ W. l
        ep0_state=0;
4 M. c. y( I  y) o0 p- [        USB_PMA[4]=384; //ADDR1_TX0 m8 R- @3 e- \. S4 K: J5 m  e
        USB_PMA[5]=0;   //COUNT1_TX1 |; }2 }, a3 X' X7 A8 L( l
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type3 d& U- `, F5 l/ z. }
        ep1_wait=0;
  n; c% J% ^; d6 n        USB->DADDR = USB_DADDR_EF;      // enable function" D( @- u' V" [& J) Q, G
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
' N" ^. _: Y0 s& \) r    }' J' t: e8 B. ?- ?5 i9 @4 e
    if(USB->ISTR & USB_ISTR_SOF)3 N2 J# t( ~; ]. T& E5 U+ ?9 F$ `1 @
    {. \+ V3 l7 Y6 j- }  Q
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear
6 @; ~0 P" x+ k6 q: D    }
5 b4 c6 v& t6 _" J$ ]; j  h}) e, g' l) |) O: }9 J) N
; v' F! G0 H3 Y$ R
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
9 g- g9 p7 ^" _3 W& P5 V$ r+ F* H: K2 a# P( u9 u* p
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。+ U% k  o9 ?& Y2 u4 D

" Y% e( J" [- H6 P9 j. H2 ]* g  Y& s
    while(1)
% I1 g# z( V8 {- |& E3 Y8 Q    {
. y, n9 U+ Y: M0 r: U        static char row=0;
) C9 d: X% }9 K1 A3 U' _        __WFI();
* l+ r) X' m1 g/ v# ~/ ?9 Y        if(ep0_state & 0x80)    // request data processing% F6 ?4 d3 ^8 b3 Y
        {$ k3 A% T8 N8 n9 K- E2 }, y+ q- N
            if(ep0_state==0x80) // SETUP phase
8 ?' W1 E  G: p# E. Q& h) i            {+ B) G8 O: s' s
                if(!setup_packet_service())5 K, P: j& m. K# @2 [& K
                {
+ }# `8 }. w1 \6 P$ q                    ep0_state=0;
/ i+ y% H3 }& s                    // not supported
) O( W& q5 Q: ^+ G/ l( _* {* M( w                }
- z; h& O8 s+ u, ~  w                // ep0_state should be set to 1 or 2, if processed6 Q$ T: x' \5 r$ I- u9 J. u
            }
3 ^0 ~6 E9 d9 d8 I' ?1 O            else    // OUT phase8 {# \2 J* K4 g" b
            {' n! E' @& q1 k+ c
                // process data+ y) ^4 `8 X. [# g! E
                show_LED(*(uint8_t *)(USB_PMA+128));9 x& l% X/ H1 @, h8 ~* F
                ep0_state=4;
0 E" I% y0 ?" V# G                USB_PMA[1]=0;       // Zero length DATA09 Z5 u/ V- p% b* _6 u
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
. R' V+ c- M0 F4 v# ~* U4 m            }3 s, F1 F5 h( w. y
        }" m* U$ S' q  W6 N
        else
4 ^! h2 I! l" k; \+ {3 |6 F3 l        {
* E7 h+ p5 f+ J            if(usb_address && ep0_state==0); S& ?* F0 y- C% W
            {# }- C: w* x6 E1 V& _1 p. T
                USB->DADDR = USB_DADDR_EF|usb_address;
5 B0 j- }1 y$ L- f) E- w6 A, O                usb_address=0;/ `( T. k, |& c" Z- `
            }. v( E8 D4 h, i# y
        }
; a3 \5 T; i3 J# k# o3 S! j9 O        if(row!=scan_row)   // new scan line
5 B( R- P0 z5 M1 E        {1 ~3 \' p6 V: }* b
            if(key_state[row]!=prev_key_state[row])
  u8 C% x- u; Q$ K4 T: Q4 U4 Z3 i            {
5 I+ K3 v0 E. W- Z/ S8 H* |- Z                uint8_t test=0x80;5 [6 T/ `$ a% [" N) }9 W: S
                uint8_t diff=key_state[row]^prev_key_state[row];3 a' J, v/ |: F) x
                for(i=0;i<8;i++)
4 `- {0 v8 M/ N# s1 M, ?2 q                {
8 j# |9 l; S: ?* `7 A2 t                    if(diff & test)# O7 g% c& w3 o, ?; b2 s5 W
                        update_key_matrix(row,i,key_state[row]&test);
. L+ t$ s6 f2 u* `  U# n                    test>>=1;
& V# j1 u9 @1 W+ o                }
0 L) [! p/ n2 I, g, z            }# `6 h" \, w) b2 i$ P+ R: n9 t0 Z! Q! m
            row=scan_row;
+ y8 H) m! f1 m( o; I8 ^5 K        }" k( L. w# d& H  r# @4 B: G6 ]
    }+ _! e  t9 E% C2 u4 R
4 W1 J! t. U' C, G

+ a) d, z# O+ k6 _3 ?% h' |/ cEP0的控制传输,把用到的请求处理一下: S4 K& l+ f8 r0 s0 F% i
char setup_packet_service(void)
  p& o/ i, C1 ]9 V' }{
! A8 ?/ V7 a7 d4 X, y8 |! o" o    if(ep0_std_req->bmRequestType & 0x20)   // class-specific5 f# O# Z; u' ^5 m) _0 A
    {/ ^& o( [8 E8 K2 w# T& c
        switch(ep0_std_req->bRequest)
' g3 S2 {2 r$ w. h; N        {9 u& b7 Z* M: I) u
            case REQ_GET_REPORT: break;
; E/ F& V- f8 V+ c. e2 b            case REQ_GET_IDLE:  f6 n: s3 h. V! Z
                USB_PMA[64]=0xfa;   // return 1 byte
0 I+ h7 n3 ^  K% O                USB_PMA[1]=1;9 B  l0 b! h4 k+ H/ s
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
) q* Y# W9 ~/ t- g9 \                ep0_state=1;
9 a, F1 J  |/ E( Q, e$ Q3 N                return 1;
% l) y8 ~7 D, ]& a; y# D1 z& e; [                break;
$ E6 A8 S4 @6 I4 R            case REQ_SET_REPORT:
- ?8 \  D& ?$ _                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;/ i8 S8 X3 {0 u# W
                ep0_state=2;! m6 G& U' j( W+ E9 X
                return 1;
: F% ~0 ~; _) v- j5 H3 c, Y1 O& D( `                break;4 z0 Y* p; O' d* `8 _# v
            case REQ_SET_IDLE:
; B" H/ X2 v9 j2 g                USB_PMA[1]=0;   // Zero DATA' @5 @3 t, s( ^; ]0 \. t
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
7 m) A$ K& |$ U; A5 q7 {+ A% q                ep0_state=4;
: s9 q6 J9 q4 b! \2 t  {% T/ Y3 Q                return 1;
$ q0 e: I# [% ?0 a, e) z6 O. s                break;6 \, D& b* K. W" s- K/ N' u) d" T0 V
        }+ I6 L% O0 w: g  g: z8 ~7 E5 d* ?  f
        return 0;, J  _1 Z- D7 A0 ~# y
    }8 o% l& H6 b) M$ e9 I0 I
    else    // standard
2 e! C$ l0 N$ f% x+ ^8 \6 \    {
' S6 \6 V* o0 t7 `        switch(ep0_std_req->bRequest)$ `  h: i5 t5 F4 O% N8 N5 Y) |) U
        {& b# n0 }- l* Q% U$ I8 ]( z% l
            case REQ_GET_DESCRIPTOR:
2 H: H3 X0 f1 w                return descriptor_service();
: D  x& _- p, ]( s; u, ^8 \                break;4 x! \& k$ M! a3 O4 z+ k' D
            case REQ_SET_ADDRESS:: o+ ~5 `& C1 t
                if(ep0_std_req->bmRequestType!=0x00)4 n- X, E8 B+ K( \: B9 \+ Z
                    return 0;
$ a* V( z8 H; [% N+ f: o                usb_address=ep0_std_req->wValue;- j8 L5 M2 B! \* o) I; @# n$ S3 A) V
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;9 p" c" L" M2 k7 ~' v6 o! h
                USB_PMA[1]=0;       // Zero length DATA0' _) e0 R4 ~' D4 J9 T
                ep0_state=4;    // No Data phase& v( {! }4 K6 i5 p7 C, T
                return 1;2 I$ R- T2 i3 \! S
            case REQ_SET_CONFIGURATION:
8 t( n  _9 N/ [% G# b/ B6 J! \                if(ep0_std_req->bmRequestType!=0x00)
- J: {2 A* |& [0 J9 Z4 p                    return 0;* C! m' e- J* v
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
7 X& v: e0 T% p) c" w" Y9 d                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;# D2 R) r$ L1 v
                USB_PMA[1]=0;   // Zero DATA
) Z' U: `4 I& }5 L$ f                ep0_state=4;    // No DATA phase
8 }- w/ k) a. M" g                return 1;
! a2 Z% {3 ~, X            default: return 0;
# L& }6 t* v; x4 x  A2 ]! T, k$ y$ m        }6 ~- R$ W8 h" ?: p( x
    }
. g* {* Q* h& m$ S( {}3 K0 Y0 R) d7 }

7 q' Q: ^" H, B. N. z6 h3 i* h& G) s
9 F6 Q. L# X9 R% E7 C% Q
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
% b4 V6 r% Y& vchar descriptor_service(void)
. C- A' C) K1 i: F' ?7 Y$ y! B{
; D# B+ e( _! G4 V; D2 A    switch((ep0_std_req->wValue)>>8)6 h4 W1 E0 K: K
    {  r4 u. y6 B* ]2 G0 x
        case DESC_TYPE_DEVICE:
2 Z( N7 k- Q- \" u  G            return ep0_preparedata(&DevDesc, sizeof(DevDesc));/ l3 q6 \; P( f& Y# r
            break;) n  P* Q  ?' w. _3 O
        case DESC_TYPE_CONFIG:
: r  ^& j5 d" X: W  M9 C- @0 ?            if(sizeof(ConfigDescData)>ep0_std_req->wLength)- x: \5 r* p  ^/ W. @; p3 V6 C7 ?3 P
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);: ]! U6 ]0 z3 x' U  g
            else% h2 p- r) k  y4 u: A
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));' D0 B; }! Z8 D/ L3 {
            break;
. O" M! Z4 P" J' [9 p$ O7 c  j        case DESC_TYPE_STRING:7 e4 d8 Y* y5 B" f7 u# E
            switch(ep0_std_req->wValue &0xff)" K$ J8 Y3 D/ T. [" T
            {
7 d- }+ s* Z! p& Y& K; l                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
9 j; k1 K3 |2 b0 \! b                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));  x8 B" A. w* v9 {6 v( C/ O/ G
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));5 w- o  P/ k3 P6 b
                default: return 0;1 J5 {6 R7 V- e5 j6 i3 q
            }  t' e+ o& W" [. j0 U7 u  s& `# K5 `
            break;
/ M) b( ]6 O. X9 r        case REPORT_DESC_TYPE:
& K, l5 K; h- v  f0 r, n            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
( @$ F# E* a7 Y+ D! V, A: A7 m! j  z        default:5 M6 Q/ s6 }* z6 s
            return 0;
' |/ Q" |' K4 g! s( G/ E    }
7 G! h+ A2 z( Z}# E3 V: l5 V2 u$ i8 O4 Q  o
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.# E& B' r5 Z: R' f3 w9 X
void TIM6_DAC_IRQHandler(void)# ]8 G: z) [$ i) n5 I
{
8 t& E2 h& O' X, {    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);1 `/ @- a! ]) B/ k- g" e$ `! e
, N, E( S: G: [% H5 I0 b
9 o+ o! |  Z$ p
    TIM6->SR &= ~TIM_SR_UIF;
' ~. U0 l! v0 k7 l, m8 d6 Y: n    prev_key_state[scan_row]=key_state[scan_row];6 t' p8 b9 b. M3 `) A, ~( E! A
    key_state[scan_row]= *PA_IDR;   // update key states* k' ?' s7 O+ e! `  [3 ]" m
    switch(scan_row)
0 T# ]" ^: k( c: t) v: a- n    {) p7 w" F- V- S# j1 I2 C
        case 13: // next row PB14' D: p) J+ z+ `! ?" V
                GPIOC->MODER = GPIOC_DEFAULT;
7 x" E# |7 k" K) T                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
( G6 ~: S3 u+ {                break;
) r8 j6 l; M8 m        case  0: // next row PB156 K% I: A) p7 U3 B0 t
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;# y' z" m# ^  E3 P6 ~) j* X. P% m
                break;
% o/ ^/ a2 G! C) w# C        case  1: // next row PB3
% ]& e. ~2 N* a6 b% V                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
$ F+ \7 ~$ Z, H" ^4 m7 j                break;
* H$ C# N  g4 d        case  2: // next row PB4: Q* i) }/ B" ]( K, L6 s% n) }7 H. x
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;. |% Y( r% A2 @
                break;
0 O8 f  J3 V" {: V; R( ~        case  3: // next row PB5
2 O5 z1 I: C& \: b  y; _- X                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;. n5 q$ [- k- m( I' `! h
                break;
9 j8 `! I7 p$ ]; h' o( _. \        case  4: // next row PB65 o0 l* o1 \/ O- e6 u
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;, ?  x# {( [  x9 L
                break;
1 [* J! u7 M0 G1 H7 r$ {        case  5: // next row PB71 G/ l& s+ e' u5 ~6 Y& `
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
( H! _/ b) k, d+ A                break;
& E$ V; ~! V7 [2 q' A        case  6: // next row PB8
( {7 }4 K& M$ h3 w! M; ~$ p5 `' r                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;, Y* T  W$ `3 j- c3 J/ o2 Q
                break;
9 T' {& |6 T" D5 Z        case  7: // next row PB91 E7 b9 p4 N* ?3 K7 S5 Q6 I7 u
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;
* I* R$ c: A* \. L4 l+ J2 ]6 H4 q" U                break;
: t! v/ z0 Y8 N2 i5 _* j0 X8 ^        case  8: // next row PA82 A0 C* l( z9 y- Q
                GPIOB->MODER = GPIOB_DEFAULT;
1 E* Q3 M; p) }7 [                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;# L$ p: s3 D) i) l4 d8 H
                break;0 B% L: L) @. r1 a$ c. K
        case  9: // next row PA9
0 p9 H8 W8 {1 X                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
6 F! _$ e8 D" D3 \                break;
/ _/ q) K, g* W1 a) F  [, ?        case 10: // next row PA10/ l0 U* p" t6 {" c
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;( s3 A  r, G' d# z% S% j8 s0 U
                break;
, Q& X9 l$ v7 l8 h7 H4 \: ~        case 11: // next row PA152 p! E" o' E' p# F; C# [: Y
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;; x' G$ L7 w: i+ t* l
                break;
6 O$ i2 T1 Y% x  g& B        case 12: // next row PC13
" J6 C0 a0 v1 d) `0 q" o5 y                GPIOA->MODER = GPIOA_DEFAULT;- R# L; I2 U9 O5 t: c: B
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;. p! I5 \& E: y( @7 Z6 p) B
                break;3 r. z% T0 t: Q6 {
    }7 E# |7 ~# c" K( ]" l
    if(scan_row<13)+ [/ r. V6 m4 K+ U' ?, `; `
        scan_row++;& }7 x. D* n0 [9 P+ |& x7 F
    else
' e5 ~# `: D: X) W/ D7 r        scan_row=0;$ D7 V' i7 g$ [! @
}$ `) L! D  j, ^, L* ]
% M0 n% o+ f6 i- y* G6 j

* @; ]7 }. N$ r
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 ! }1 d# J; N$ g

3 Q7 m+ }# y3 P* U) T" F  h扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。9 f$ j  S. c) P: Q$ I) h2 w
+ \( r% @( f+ o2 Y
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:

# j# r! w" b/ p$ L. V" X& J0 p2 g# s& T# K$ {/ |
+ t% V" P  i4 _0 H- M$ m; P
const char hid_keymap_qwerty[14][8]={
/ Q$ P* Q( y6 M5 E3 r$ c" e    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},7 @9 x1 v$ u; @6 ?* i
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
6 I# p' i8 H% G1 Q    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},# A# G: J9 B1 v8 l
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
) j" ?" p' R5 b    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},, b; P. i) V- N+ e, j1 n% d6 i
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
& {; V% O- `5 v$ `# n: A    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
- K/ P5 O. [: M+ X! N& n    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
* p7 S. i. X1 R8 s    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
# Q# _# j" N! D, m    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
' g3 ?% A8 b* t% j. f0 u    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
  U! [( s7 {2 T0 U5 s5 i    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
' E1 F: R; O, C6 n    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},+ j) t/ v4 K) ]5 C8 `0 \: ^
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}7 Y3 |3 N+ \. Z. B3 P7 z( {. V
};+ ?/ N- G) F; v; u

) q) u; x+ B( d
! e4 |  P& P8 d* k: }4 W1 Q! o2 G) u
const char hid_keymap_dvorak[14][8]={: p& v. t- V7 q2 V
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
$ y3 N( J7 p# T0 X    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},% }+ r5 z' l7 [
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
6 H/ }# ]& j  ?5 ?, v8 ~; y8 |% F    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},, `+ K; ?; J" \) G6 o0 O, P. A7 ]
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
. S' A* u+ e* c! t% G6 ~& g5 b    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
, R! }5 s  M  X' L  w    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
. E6 H, w1 z+ q7 D) ?    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
0 }: S; X$ ]4 o3 z9 R; Z' g8 f    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
" f" W; D: V% J    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},3 C7 b3 K  `) D! ?
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},% u/ S+ {3 v& |; x0 W0 e0 v2 \
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},6 y$ T0 }: S3 c
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
! m4 u. V% O- B% I: @    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}3 Q5 c. F2 H' J
};& h7 H$ e9 a) S. b7 m5 j5 l

. _6 q: d* C' b2 z! {( J- E
! H# U- i  t; w2 r" u+ N, r9 ~上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
% s5 f9 X* ~/ j6 E3 i! H' O' w2 G8 d* Z7 h1 n9 v& W5 `
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

8 E  D; M/ m! O, M
/ k5 y; H! A! kvoid update_key_matrix(char row, char col, char onoff)
" F$ ~6 v# G$ L. h. y2 m; ]5 _{
, F9 U4 a0 [" R    static uint16_t hid_report[4]={0,0,0,0};- B. v6 a* [  E4 g8 Z
    static char (*hid_keymap)[8]=hid_keymap_dvorak;
( n- G7 D' b" r1 }5 T" M( M" G
6 `$ {8 w. Z: H7 h! R

% x! _) r; G: t- r: ~) w    unsigned char key=hid_keymap[row][col];
; @- V' k1 L% ^! M    unsigned char *report =(unsigned char *)hid_report;  u: C7 v% O: {8 t
    char i;0 `- x; M3 f5 r9 i; h% \1 @, k; a

& X; o& c# n, m" K- {: b! G3 [

# a) I+ `/ d, {( h4 W5 O    if(key==HK_MODE)
" k2 y' U* x) Z/ \    {; m2 [6 c& z) r* b& q9 ?
        if(!onoff)
4 G: \) V3 T" U" q* F' V* I        {, t' V, R" t& t4 ~1 G
            if(hid_keymap==hid_keymap_dvorak)& r3 x/ m/ L  U8 a- J% l' L1 e# C
            {
4 }: E; n/ A' l1 E                hid_keymap=hid_keymap_qwerty;; x  _+ Y* y' e& j
                GPIOB->BSRR = (1<<2);
8 f9 O4 K7 k0 b  U5 B            }4 s$ j0 r: s1 k2 c6 ~
            else
- f9 j2 b4 j0 c3 Y9 k            {* ]9 _2 ^9 Y" b4 f0 a9 }) w& |
                hid_keymap=hid_keymap_dvorak;
- k. f) |7 R' G3 Z0 n                GPIOB->BRR = (1<<2);
( d. O- W8 ]: S9 P/ ]3 M: Z            }
3 y2 M/ T: N* i        }
. G" M! ]8 d( w  n$ @. e" U7 f# ?        return;$ I9 b8 a0 Y% H, u. a; L* s- T$ U
    }
1 o1 }& k, t1 w7 p8 {2 v
: B6 r$ u2 S1 W* H8 K2 B6 ]6 |
, @  ^$ {* X* i2 c7 H5 \+ i. I
    if(key>=0x80)   // Alt, Ctrl, Shift; [9 M6 w& L: w+ U( n" |! v
    {
% X  r8 c5 n0 B( @# {+ N        uint8_t bitset = 1<<(key&7);
# k* {( V! l/ H        if(onoff)   // non-zero is key up1 W9 m3 ~  v2 j+ E+ W  `5 y
            report[0] &= (~bitset);
) O0 E3 I- c! q: k' e" \        else* E  h  @7 L! y8 i% p
            report[0] |= bitset;
" Z, U  @. G5 k: H* ]    }$ k5 T, L! p! {) G6 L3 F
    else
# H. R3 Y7 x$ O4 u* y& g    {5 C9 w2 k7 J& f2 e0 s* q
        if(onoff)   // non-zero is key up! D# v/ ~8 V& M
        {3 B/ o1 j' q5 P6 ?/ c$ L! Z
            for(i=2;i<8;i++)
- \$ g) d  z9 c            {
4 Y6 }1 x$ Q6 G) v0 p) y& f                if(report==key)
, [5 K# |0 B' i# p! ^& K                {# ~+ S  v' [% o7 t3 V
                    report=0;) |+ O+ t& V* m, ^: ~/ o3 G1 C
                    break;0 n' m0 H1 U! t8 {4 T' Q5 A8 O
                }
" b' g. \) X5 u9 y% G1 F            }. i  }4 q. X/ L& |
        }1 r, x  _" X  b+ p. B$ i
        else
1 P8 i0 D1 n& m3 W  {7 J/ M" V        {
! V- ?: G7 l5 m            for(i=2;i<8;i++)
: g+ g3 k. Q" C7 \& H; H            {
8 e2 n5 F* v. w3 J7 x2 S                if(report==key)$ a' ^8 `0 e4 L
                    break;
: D+ f1 E% L' j2 Y                if(report==0)
3 A) Q. D1 b* |$ k$ O" }  {                {
/ U* B1 S" B( f# M/ Q                    report=key;
% e6 F; `# ?6 b8 e6 F9 `* B                    break;
  L3 O. i% o! C( c9 O- i                }
+ h0 V7 \/ k) ?, o$ t            }# t, B0 v: K2 r& Z4 U
        }3 i8 X( i# M0 A$ m4 }! p
    }" U8 O2 D& d/ ^
    for(i=0;i<4;i++)
) p" D0 V# _9 @3 F* p        USB_PMA[192+i]=hid_report;  t8 V, q7 ]8 S0 U
    USB_PMA[5]=8;   //COUNT1_TX! t1 n7 [1 m/ D
    if(ep1_wait==0)9 d' T" G. P+ N7 \
    {
5 B, h  F( B) R6 k1 m2 t        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;  x0 h4 V+ i+ x+ J1 @* @$ p
        ep1_wait=1;4 P( t+ R7 {. p; l* X7 N
    }! T  p% S! R/ I0 t4 ?% _$ w
}
8 I% ~" V2 t9 Z( q" J8 f- D
  n* R- }4 E4 n$ Y9 U9 I3 C2 e3 _, u: ~: L. c( m( e
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。& E8 x- {0 O" O
keyboard.zip (8.7 KB, 下载次数: 6580) , s0 g$ b: w/ _4 V: t
: u- S0 B$ K; R- o8 M

% r8 W/ I( g+ F* X2 Y  F8 B& b) Q& L2 D
! {3 ^5 ^6 X$ [2 [
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘/ R1 e$ O: ]* f8 ^* M1 N2 T
不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
1 e; x" D' |# z8 a' A) S刚开始我以为要把打字机改造成电脑键盘4 o+ P+ V& u: D& x
不过楼主也很厉害!
6 B0 M) X( i5 h% N
哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
2 g" ~) R6 ^2 d$ L8 p
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-6-27 23:50 , Processed in 0.385220 second(s), 27 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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