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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
7 m( W: J3 z) d8 @# {7 }7 q8 e- E3 L" E
http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
: }0 P, t' B. K! Y+ \8 J5 B6 S; _4 B这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。
: [5 {1 G& F0 @- C7 i* k, O, ^! G" X0 X: t3 @$ |
在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。
; _! R# Z+ ^6 j2 e3 }* Q. n
! Z! p# W/ K6 n
235140i3a36qivqzuvmt5q.jpg.thumb.jpg
# B# a5 c+ [! C2 r
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。
: D: I4 ?  \! I3 H1 Z4 b/ [0 v& J 001734klbyoluenuwz4h4b.png.thumb.jpg
- E, ?1 V2 `8 \" b为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:
' c3 C1 W# ^3 z2 V 003625r2agx2f5v922cf2f.png.thumb.jpg / ^/ h" _# q& y8 d. s
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.
$ D. k- H" Z( v9 i+ UDvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
* z( Y+ ~! k: M# C1 c8 y0 p
005836yvs0wvovwsssgd3o.png.thumb.jpg
; T* `; M" |: i. Y. x4 t- rDvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。: x+ q+ s0 s+ P( B. ^; f
9 V" R3 U: b5 x! s) w4 J7 B+ @
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。5 i  r. h( h3 t6 u* H- P

7 h. ~. E" {  [% k2 s6 R到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
7 \( q$ k( T( [- s# L! d7 x$ h3 I# w$ C& R% i& M8 U0 l* l
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:

& u5 m+ d8 q9 k2 V1 m 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
5 U- c9 i. f9 X8 j1 F7 t' k- X----------------------------------------------------  分割线 ----------------------------------------------------------( Z9 A9 X* i8 m7 f7 X9 B6 U  S

+ J" G( d; ~2 l$ K1 }先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

5 P; [& @; k6 ] 020011osionbunl4ui44vi.jpg.thumb.jpg 8 G" m+ _" U) O0 N& `' N
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
3 p8 x% Q' l4 D+ c 020017j8ycmnv7788bqv52.jpg.thumb.jpg 2 A2 U' ]/ p5 h6 c, W# K+ G8 v
特写,80C49  v! N+ Q6 V9 y! S
021040oujzuvtut6iujtvz.jpg.thumb.jpg ; T  I. X' s! `+ ^) K2 c0 D. q
LED部分,使用了一片D触发器锁存指示灯状态.& q9 V: z' W( h1 U/ l
022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
5 D0 f, t# _% ]1 J暴力破坏,将80C49拆掉
6 ?. ^4 K2 c+ B1 ]5 r( k7 X 021113e48qq98vyohvhzzh.jpg.thumb.jpg 6 L: _. `, _' ^) X, g
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
+ A3 H! ^# l' u 021125nc9az6dj33rlds2r.jpg.thumb.jpg 9 @# g% \' }" {% n* G( K/ R- M
焊好元件后的板子,准备替换80C49
, a1 |/ N" P( ?" S9 H. s 021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
4 y( w3 T: m3 W6 P" W5 c用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。
* N: F2 V; _* S) G 021104shifhnrqbr3o5nlo.jpg.thumb.jpg
% C, b5 l! L; r- i) T, a2 J这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.! x9 h: A. O( c: q7 p" `3 K; s
022003ym1p9u4ug40280uu.jpg.thumb.jpg
8 ?" I; |$ Q! J3 _' ^# t6 g; B" i+ d开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。! s' G; r$ e! K/ W2 f6 A7 X5 o
023313kt141q9qajtol7ma.jpg.thumb.jpg
4 B: [# R+ ^* v) v. z) T6 T' ^我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。; G+ Q0 ^, R4 n5 A
023322nt7l5xb3ltttkltt.jpg.thumb.jpg
+ q- E7 D- K5 H) s+ @7 F
主键区键帽就位  Y! N, @1 |  T
023331hin88e8wkrwzwikx.jpg.thumb.jpg
& ~* N5 b5 m" i& |' A; {% S编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
& K3 j- @9 v2 J 023336wjzlgopugg1jyy79.jpg.thumb.jpg 1 r6 l- ^3 C% Y( P
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。! V* U; b: k2 w- h
023341sffu4j3g2323h6fl.jpg.thumb.jpg
/ w6 k7 \& I0 Z6 [, \

6 X) k* m- f( D+ E2 g. e----------------------------------------------------- 分割线 --------------------------------------------------
' I) B) q0 _8 s
7 A% k5 m) X" g
8 t1 N- q% K2 _' Z' P2 z1 c; L
6 X" {# F% O. [5 _% W( h

% D) x2 M7 H; B# V* A  k9 p2 d( d' h9 _- z6 W

" O" K/ _4 W5 P2 v7 ]1 w
+ h7 `. A, l. l; Z3 A
% M* v" ]; V, S4 u2 l7 f" d; @7 A! P% {/ I9 x
, a/ m2 c. ~- b( i- A. ^9 ]- q8 }3 z

6 E8 i6 V. T2 N- y; y
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
1 u- M2 t3 a2 ]( l80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:3 x1 J* X  D: f1 f$ Q+ @$ v
104025nzibm2rmiomhyirm.png.thumb.jpg
6 s" u6 g9 A: Y& [1 S
, V, u) i3 \% ]: K+ X
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)+ H; E3 E- F) `) q1 |. p
105004zkrez5houvkkznko.jpg.thumb.jpg
1 U1 [6 _- C" S+ O
; K5 e/ V& M' W+ @: @8 w
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。0 L4 L7 P8 f' f

+ s6 D6 E% r9 l这是我设计的电路图:) ^2 q% l. V- u& c9 s( @* y& ~/ |
110344ej2z2oo2rflo7oe7.png.thumb.jpg
/ i9 _" o  T& p% Y
0 c7 A0 B  X- O" V3 D  |
PCB Layout:& u2 p; ~$ Q8 t# p" E
110847jjbjvt34vwt3v5bb.png.thumb.jpg 0 t+ C: i1 ~3 k

) b2 @, {7 A# U- W% D2 B' H不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
9 n3 y2 b* p0 U/ U) e& p, ]1 R2 ^- V1 e* |
% P8 L# k% A" u( ?
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 # @" A# k3 q' {/ {

1 [2 B- q- S/ c4 g& A* ^- B软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。$ @9 M, z/ r  S' [9 [: _( T

7 m1 y& k! c+ f5 V3 l* ^总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:
' W/ V$ W0 I" M! C 113818pmrfsb6z0byt6t06.png.thumb.jpg
$ F) c: P  q$ c$ I# s% z  D
  Y# x- g7 `: E2 v% ~3 `9 B
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
" j( P7 O+ k4 W% S! R4 I/ Q9 \; d" _6 c& D8 U
USB的中断ISR,bare metal哦
8 @3 @" K7 i: H% ]8 F" [* S

" d6 C" ]. B  V8 C1 H6 Yvoid USB_IRQHandler(void)
: N% \& Z6 J! p{4 @* @5 [. s; I7 u/ y6 ?
    if(USB->ISTR & USB_ISTR_CTR)
  K4 |6 F. P2 D    {. C3 h8 H5 X0 }( a5 S
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
5 a! y. W6 f7 o. E7 ~        {, ^$ \" Q+ L) Q' u
            switch(ep0_state)7 G0 y* W7 C( N  k
            {1 K8 _. Q9 B0 _; I
                case 0: ep0_state |= 0x80;
5 g! ~+ ?! L9 B, @! }) P                        break;# y) L' L" N$ q, O9 O
                case 1: if(USB->EP0R & USB_EP_CTR_TX)
" k: b( P, n3 l- L# R                        {
/ N. T+ A1 n% p2 O- ]2 C0 h- M+ |                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;" J* k% w! \) U' X( L' ]
                            ep0_state=3;
; C2 [7 I  j  z9 w' y+ f' H$ X                            return;- \# A6 z3 U" F; u& s; \
                        }. o3 N; l0 T0 i$ I* k2 o" G
                        else
: `: r9 W- s: o6 d                            ep0_state=0;
" }# ?' M/ ]$ Z* y( H/ C/ ~! k                        break;+ I! K8 C4 N2 h5 A$ G/ M
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
2 M. H. m0 C3 _0 X) V                            ep0_state |= 0x80;7 ?# I, x% M5 t. R
                        else! q, [1 U. X3 t; L7 C3 Y0 L
                            ep0_state=0;" {/ [3 ^' A/ d1 [* U4 @0 F8 W! C
                        break;
! \, C+ ?& ]2 S' L, d                case 3: ep0_state=0;
7 r' _6 n2 L! u% L; e+ l: z6 D                        break;
7 \3 ~2 X" H# @9 t0 r9 |& c  {                case 4: ep0_state=0;
* u$ j" S6 e& y                        break;
, U) |! u: ^7 @1 h                default:ep0_state=0;  s- X9 y4 ], `, ], ^
                        break;: r+ W) Y, V' z' x  p
            }, r* l- c' G: t+ r, C
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
( o* R* N5 a/ S+ r' O            return;+ |- b/ j, l5 G6 P  \
        }
; h2 O0 O! v9 t, [) l        else    // EP_ID can be 1
+ a, K! A- m; K; ]3 n; a) D( B        {/ E: G$ Q- {2 ^( l5 G
            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag$ T  b' R+ N3 z7 ]1 c
            ep1_wait=0;
0 q, E- a! y, F* c4 w" P            return;
+ x* [& ?6 ]7 q: d        }
6 {6 m3 ^8 f( [$ J/ r  j% S    }, r8 V1 C$ T; n
    if(USB->ISTR & USB_ISTR_PMAOVR)3 e8 u* A! _5 x' v' K2 V
    {# v$ Q6 |6 E/ U( [* O
        USB->ISTR = ~USB_ISTR_PMAOVR;( d" `: |) n3 Q: d% D: M
    }
# }) S' c5 @* }7 O8 s2 ^$ a    if(USB->ISTR & USB_ISTR_ERR)
6 f( J- M1 D( Z, n0 R3 e    {8 f, f8 g8 m4 b7 g/ {: B; y
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
, {/ U6 Q- W, s. m, w, T    }
% T3 l1 g% T: R; T; c& u. P$ l$ V    if(USB->ISTR & USB_ISTR_WKUP)- F" ~3 G1 K7 G- `* {
    {
, A4 F/ W7 M. d& [        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
) \2 p/ \' w0 ?8 ~, j3 O1 J        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
; h) ]  F$ `  s- s: r    }' z: r  d, b$ ~+ @0 f9 }
    if(USB->ISTR & USB_ISTR_SUSP); _/ K( o8 o6 s* X: c
    {
% z' }& ^! K( u( q- {! f9 n        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend4 N6 O; R' V7 I) G# o# Q8 v- Y
        USB->CNTR |= USB_CNTR_LPMODE;   // low power
$ S/ a) F* @! `' C* `# ~        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear% e9 m% _% E: ?  x# \: Z: _) q
    }0 o! U# h$ U; M+ A
    if(USB->ISTR & USB_ISTR_RESET)3 K7 _2 V2 C7 }1 V0 d4 s2 j
    {' }7 _; H" s/ U1 R
        USB->BTABLE = 0;    // buffer table at bottom of PMA
0 y+ h! y$ P! x        USB_PMA[0]=128; //ADDR0_TX
1 F) y& o. h1 R$ ^; Q8 b9 f& F* x        USB_PMA[1]=0;   //COUNT0_TX% }7 M8 O; n" ?! |0 H: E3 ^7 y, [. l* w  r
        USB_PMA[2]=256; //ADDR0_RX
! B1 N( g# r# }1 m5 s' F        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
6 i5 [6 y% b6 U; r        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
( ]" P1 _1 A0 g& Y5 p& `        ep0_state=0;
$ H7 ?2 Z) c. A. l0 v5 ^& `        USB_PMA[4]=384; //ADDR1_TX
- s0 u) D! \  C1 t- |! R        USB_PMA[5]=0;   //COUNT1_TX. u+ K  R5 a& |5 C
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type2 ^: R& D$ d( ~9 _, h9 C- P
        ep1_wait=0;9 C0 \2 n  k: j: e* u
        USB->DADDR = USB_DADDR_EF;      // enable function, R1 t7 B$ H$ t+ L9 Q7 _: K
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear$ [/ B, a& d/ E' C& _: E
    }
  B7 E- N) J% R- D' f    if(USB->ISTR & USB_ISTR_SOF)
* x* f5 O2 F6 m" @" |7 O8 o    {& m6 C# n% K% }
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear
9 d& I$ E3 i( B# B, E# s    }) T3 N2 [" }: t  H0 D
}
$ A, B3 Z! C7 \1 y5 Z- W' E1 c0 H, A! w" n
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
9 b* C5 o& }& q. R4 L* u7 l
9 z" K8 K. i( j9 x主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。5 N1 C) d/ N6 P0 a4 x0 Q
1 v% h8 k5 P# [0 B8 Z
    while(1)
/ q, F& I" }7 C8 S" k    {# V) t6 Y8 o1 V- A; d. P
        static char row=0;
8 I% F$ J) U( F9 z) ^( k( |        __WFI();
; v* g8 ?, e: E# {8 c        if(ep0_state & 0x80)    // request data processing! T! g. v8 q0 N# r" ]. U
        {+ R& L4 i5 w) Q5 `
            if(ep0_state==0x80) // SETUP phase
, v; \8 b' h5 H4 h- u3 B" K% I9 a            {6 f+ i# _( R5 y6 M7 @5 {9 I
                if(!setup_packet_service())
. S6 N$ n, J2 g2 P                {% I; J! ~; Y/ P. `" c
                    ep0_state=0;: P1 L" X/ t+ }
                    // not supported
0 `: F( U  L+ w/ S4 f, G) w2 {; d                }7 {5 S  }" J5 j5 [- ^
                // ep0_state should be set to 1 or 2, if processed
+ f# u; L% E1 |+ f, M            }
' V. B% P& I" Z* j& G. U7 c            else    // OUT phase& P5 \$ Z0 H8 t  {$ s
            {$ {( @* S1 |( i  K! g, j% ~
                // process data+ [5 P  ]# A0 S( O
                show_LED(*(uint8_t *)(USB_PMA+128));) h3 C' {) C/ n7 D
                ep0_state=4;
/ i/ V) O, c( O9 q: Y& _$ I                USB_PMA[1]=0;       // Zero length DATA0
8 Z3 a# \- N) g$ }& U                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
" f- d" \4 I$ m# Q  ~7 B0 u' J            }
8 H/ O1 C4 X; W' \) N& N        }6 y1 e( t. W/ v8 I- N; f/ K) ~! q# H
        else5 I* K5 _6 L5 q: I- y' P
        {( ]# {% @5 y2 P, b) [, g: g0 E: Q
            if(usb_address && ep0_state==0)1 U2 C7 A. s0 `  V) t9 O
            {* l( S9 ]) J" z# A; A! }  C0 q
                USB->DADDR = USB_DADDR_EF|usb_address;
% L( }$ j+ y# e6 \' e  l                usb_address=0;
6 n% _: X4 l" C            }5 N, {" l" q8 Y5 b- K4 e! d3 u9 q
        }+ Z6 M, {# H7 o/ b. M: A
        if(row!=scan_row)   // new scan line
+ Y! B3 e- J& k: E        {
0 u" p, x& d0 L2 l6 _$ _( B! J            if(key_state[row]!=prev_key_state[row])* _/ e4 ^. \, @1 k- i# x# W) f8 z! E
            {9 V& k3 W0 f; M% [- Q. ]& w* O
                uint8_t test=0x80;+ m: O0 J) I4 X( J3 J$ M
                uint8_t diff=key_state[row]^prev_key_state[row];
$ _, X/ G: d' {. n* v                for(i=0;i<8;i++)
0 j$ q' u1 M, r& r) k2 w                {; J2 B: L- Y) M8 k2 n: m3 I9 R
                    if(diff & test)
) r' d! {1 ~9 b4 x                        update_key_matrix(row,i,key_state[row]&test);7 ]! v0 _* a8 C
                    test>>=1;
6 @: H: v3 t" z/ @% x9 _+ e$ c8 S$ w                }3 h+ j$ j$ c8 Z# m1 d# Q, g
            }
, H) ]7 ^: r* W. q            row=scan_row;
# K- G, K& `8 n: v- P$ [        }
! a$ i+ @% O) m/ Y. z/ D! m    }3 F! F' E0 [& V: V8 c/ X8 B. L

: H5 x+ P0 N* C- u$ K7 E3 X% `$ m; Y
EP0的控制传输,把用到的请求处理一下
4 x. {: S" A' |char setup_packet_service(void)
' t2 X7 [$ k% U5 c0 Q3 E9 V{4 I7 @' Y, h- ^' K! D$ o0 n$ |7 x1 ?
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
8 W% t4 i4 O# i. v1 C& M    {
* u$ z& V6 s( P& E        switch(ep0_std_req->bRequest)
6 @% G8 k3 P8 D( y( B5 ?        {% e/ u  {* n' e( S& k& O
            case REQ_GET_REPORT: break;
9 _1 ^' l  O, [: N7 w# C) E, b- K& E  a. g            case REQ_GET_IDLE:8 ?+ [0 f& i0 e% g9 S
                USB_PMA[64]=0xfa;   // return 1 byte0 }6 E" Z3 z$ p4 N
                USB_PMA[1]=1;% p$ p$ p  H0 t$ i$ C
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
. U0 ?! @  y( I, c! C: `                ep0_state=1;
; {) x5 O# s, ]2 @: ~                return 1;( W0 b) Q' ?, m" s* {6 M% u
                break;
* t$ Q3 Y% n& L            case REQ_SET_REPORT:# X% h+ q3 g4 t. U1 v6 C. ?' P
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;, U1 P( ?8 B7 U8 l
                ep0_state=2;
7 j- D. q! O, X# i$ X. D/ P                return 1;* `$ s( C' j; m! m' I- c6 f
                break;
3 T# F+ q( t! V: Y( U$ @+ H3 D( B' Q            case REQ_SET_IDLE:
; j; }! ~& h) O* W) z9 S% r                USB_PMA[1]=0;   // Zero DATA
9 E+ d( q* {  h7 ^& U                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;9 ~9 t5 u1 o  t6 ^; o7 N: v
                ep0_state=4;
# P; E8 ^7 Q% G: L1 f                return 1;; t* x5 j5 T2 A  y3 C
                break;" P. U- v( \" ~% i" P' F$ l* t
        }
6 }% v- Q$ }* s# i) m        return 0;. `& h: t& D6 T4 B0 ~1 g" g
    }
1 W/ ?0 c4 I* W4 v; ?4 x: Z    else    // standard
2 F; r, F9 ?6 Y% w  p" `    {
/ Y% P" I$ T' l0 b        switch(ep0_std_req->bRequest)
* }  z2 `. L" ?' z: i& C& l; Y9 a& E        {
4 k# d$ P$ A5 n            case REQ_GET_DESCRIPTOR:9 G& ]6 k, H2 ^
                return descriptor_service();
* j2 U0 o* e  Z8 C                break;
% `! l' E3 ]# W0 b            case REQ_SET_ADDRESS:5 L, {5 L) r7 i( I' y) l
                if(ep0_std_req->bmRequestType!=0x00)
; z7 s8 z, w+ T3 W, N                    return 0;
( a8 {! T2 F" t. H- T! y! U                usb_address=ep0_std_req->wValue;
9 e- s" U8 x$ @% V0 Q                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
3 {% ^) c  v8 Q. J6 z8 q4 y                USB_PMA[1]=0;       // Zero length DATA01 u/ G. ^) |" h: ~3 K
                ep0_state=4;    // No Data phase
5 F6 c! Y  ^( l0 n% t                return 1;
5 d6 S- z, U+ c. q8 M' n% D' n( n            case REQ_SET_CONFIGURATION:2 E7 l% Y$ ]2 o& I7 p
                if(ep0_std_req->bmRequestType!=0x00)
3 a( E; r9 s/ ^& U% j                    return 0;6 T! l- t% J( w& q* H
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;, D! d: A. d* n0 |3 ]: U% s
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
4 c5 T; \8 z; p                USB_PMA[1]=0;   // Zero DATA3 M% j. E- n( ^- ?5 J
                ep0_state=4;    // No DATA phase
$ n- s" x; L3 G$ ~                return 1;
) l* F3 W3 {1 w: S' f            default: return 0;! T7 M( _! ]1 k' P1 K( u: V
        }
" x) R; Z, ^! L$ D, [" J    }/ _) F% W5 D6 F) j! x
}
! a% g3 i* d$ Y& e
7 Y/ p7 d1 m! u; l1 m+ `1 U8 _8 H: b, C
- V! t& K8 c! D2 r; P. z0 u* L
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
/ L$ ^, l' f4 K3 R# o' n; _char descriptor_service(void)& K& p3 C8 H& D4 u' [; f
{
' g' D5 }* C9 L0 _! E1 o' o; X    switch((ep0_std_req->wValue)>>8)/ {. S) \8 a1 ^. A5 K# ~
    {
$ U3 J& ~/ P6 D        case DESC_TYPE_DEVICE:
" }7 k! ?; L9 R. S8 X+ G            return ep0_preparedata(&DevDesc, sizeof(DevDesc));& B7 F( z1 I* g" C  }+ R$ X, `) S) R2 b
            break;( I1 m; Q+ L; Z) i" I
        case DESC_TYPE_CONFIG:
8 q) U8 q2 N. S" T            if(sizeof(ConfigDescData)>ep0_std_req->wLength)  x: U  Z: `7 i8 B' ]
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);1 l% i2 ~6 [5 i6 b
            else+ u4 ]3 ^" q- O  e
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
. g9 ]* o$ b& H- s- [            break;
7 @' S* G" K4 ^+ d# E        case DESC_TYPE_STRING:
, C* a6 [+ ~5 O# R$ C            switch(ep0_std_req->wValue &0xff)" F8 q' G8 S1 r# ?" t. Y
            {4 j* j4 |5 N) l% l0 J
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
0 c3 o: O, D6 a7 m' d/ m' [# f                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));+ |. w* ]  F* E0 Z
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));; _& y) g% A9 d8 {" D; K: G
                default: return 0;
3 G. D$ N6 ?4 W, i* b! E            }# B( \9 A' }- p# c1 R/ @* X- n
            break;
4 I! i" m/ ^8 K" t: j+ P# B+ Q( g        case REPORT_DESC_TYPE:
" c7 A, Q6 q6 T8 p            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));4 U0 g" M- u" i7 q$ N5 b4 u  U( o
        default:& z3 V  O# E$ e; a3 R
            return 0;
9 L6 y6 F4 h* w6 ]3 r) v    }
4 C: A( i5 W0 \# W& ^}
5 |$ N! Z$ M- O下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms., [) E' V: F  e
void TIM6_DAC_IRQHandler(void)& C- Y6 l" I) C8 A3 b; z! ~2 O3 o6 i
{/ ]1 i% n1 w! m
    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);' s! |/ z- K% a9 n6 G) G- R

8 |' U% W0 h5 V/ f

, X; M/ R: i0 D1 L% a    TIM6->SR &= ~TIM_SR_UIF;0 t4 S5 _4 c9 R0 K
    prev_key_state[scan_row]=key_state[scan_row];( W0 S' D; w: y1 e) v; @2 P/ `* @
    key_state[scan_row]= *PA_IDR;   // update key states
2 h7 L6 r/ ^- G8 E- A0 W* j    switch(scan_row): Z: W! k" `) t/ @/ V# b  n. |
    {$ T  H1 D! B, t7 q" Z. d
        case 13: // next row PB141 y/ b; o2 [) P$ _  o* O9 n7 w$ u
                GPIOC->MODER = GPIOC_DEFAULT;: A; {1 m: r( u0 a6 F1 Z" T
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
1 s& {% T) u2 l6 K  s                break;
- z  I: T9 X6 f2 T" B* V        case  0: // next row PB15
. X1 q1 |. b+ |) [6 O# \  p' C                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;$ g* N4 v: e6 D0 \3 L3 Z" b
                break;
. \% S% D! {- P% Q        case  1: // next row PB3
2 B& e; ]7 e5 S, J0 F8 A                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;. @# u# p, w1 |8 Y6 g1 Z
                break;
2 Z; T% ?% F* I3 E        case  2: // next row PB4
# u2 ?% _1 o* v9 d! b9 v+ v& N                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;  y& `: X* \! K4 }* p+ Y6 g+ w
                break;, D2 w7 v& E" `" f( J
        case  3: // next row PB5) A5 }% x- o0 e; i
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;' m5 {8 v7 V% V, T: R; B* D
                break;
% ^* g3 B( D' a  U& z2 ~  i# N        case  4: // next row PB6
+ X8 Y, V% t/ ~. y7 f1 \) B1 G5 J+ h                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
+ t1 F5 n) B8 C) J                break;  @# p6 G6 m  E* Z$ \7 i
        case  5: // next row PB7
$ l+ E# ?" C7 n+ k: b                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
& Q( Y  r; k5 c( ^                break;
) ^8 {. N+ a8 R1 x! {( x/ o        case  6: // next row PB8
  N$ a, p3 I" \: V; F( L                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;: g5 `3 C8 M, a( G4 Q3 n, w
                break;: Z; C, z7 _6 P
        case  7: // next row PB9
; r9 D  w; Q% Z( w4 e9 o: v                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;6 T4 Y; |* j7 [# e
                break;
$ v+ Q0 `) q, M# T$ o        case  8: // next row PA8
3 j8 i! W3 h' o9 `- d/ a. c                GPIOB->MODER = GPIOB_DEFAULT;" s1 B1 ^7 s) f; F8 [
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;/ }# O: T& ^7 W
                break;
, ]! f+ x" F4 n1 y4 l; d. l        case  9: // next row PA91 @  G) O# L6 n6 \5 r
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
( R% G) Q# ^* y4 I- }' _* g0 s                break;3 u1 @) C2 X5 O4 X5 Q) R# ?
        case 10: // next row PA10
5 q0 H4 g( W0 S5 F+ D$ c                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
5 J$ @6 e+ }' M- C/ S* o# ]                break;7 y6 X" w" L; M- i# A3 y  |
        case 11: // next row PA15; d- {5 J& b1 ~
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;/ m( i3 `' P9 W) ?& l+ }7 K
                break;3 _! @9 i7 y. S
        case 12: // next row PC13/ L0 ?+ q# N. m$ ?. E' q
                GPIOA->MODER = GPIOA_DEFAULT;6 C$ Z8 Y0 L; N
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;  \# P  ]: e7 O) n+ f6 F3 F
                break;7 b' l, H. b: p+ y4 I- ^
    }, B, d7 e% H4 L, t9 U$ I
    if(scan_row<13)7 [) ~0 O/ Q9 L6 s$ O# \: k% G7 ]9 D
        scan_row++;6 @+ j: L' U" q5 Q
    else5 p% H6 G  G" f1 i1 p7 r
        scan_row=0;
. H+ \0 J" w  V- {! d0 P, Y}
: O4 m& Q  f( y( z$ z( F3 K. ]* Y7 M' D" A) a1 V: l- Q
; e2 u7 ^! f8 I  I' w/ O' V6 K' l
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 ! O% S5 |+ b# e0 v

' Z$ E4 T% h) M扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。+ X' O; d: i, h5 i+ G" B

; ~0 N! @  p$ L8 q# F要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:

- y( e# _( L9 F  W% o
  ^9 ^6 A. v  I3 j- f
3 p  T7 E3 h! [3 s; z8 vconst char hid_keymap_qwerty[14][8]={
, g8 [6 F# q5 \7 K, b( g0 E( F    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
) M' ~6 |! K7 K8 Z* C. v) V9 C    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},4 F! w) R: \; G1 _+ b9 C
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
3 i9 {/ N/ k3 O3 C( |7 G    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
/ l8 u) Q9 x/ c& w/ f6 O/ @    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},; p$ w. f2 c5 H! H* F
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},7 G" Z- v+ d) h3 F  k4 U; ]+ y
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
' q: {0 b; L/ o* ]7 l4 @    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
' y$ m2 e; e3 C3 Y    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
, d( z% @$ N# X    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},$ M% C/ m" H2 D$ E) p) o
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},: B7 Q2 F. o5 d* C
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
' L5 i1 [, V$ f8 S* c" V1 D: k    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},  c; m6 r: b3 [% [$ I$ W
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}6 K+ s6 Y2 I; h! C& F$ W
};
# k: I! B9 q- z1 p* \
% L2 y; h' V8 S$ O# A
$ I) L/ S" G1 K
const char hid_keymap_dvorak[14][8]={( T) L* ~" }5 M* U- U
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},' j4 ^# S/ ?6 @( W; j$ X! U
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
6 Y& B5 t# i6 U/ o    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},; T' r0 X- Q& v; a; b
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
+ M" a9 Z: P' g8 B# W& _: z* ^    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
& g3 K# ]; D  T7 [4 t    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
. Y4 w( A0 @6 I+ T    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},' L' U+ z" k) [, i% j$ N' Y0 U
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},# y# [8 d: l, ~. @' {
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},9 S2 C0 P) |; c& g) m9 j: r+ m
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
0 B# {- E9 Z6 M9 `    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},4 |: j% @+ D$ i8 ]8 Q
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
4 W7 z+ e9 z! i% }  j4 n6 t    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},7 H! `0 R, z9 B; P/ F
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}8 r' x2 o. p6 Y; M# D
};1 Y: {5 @6 }& F. [/ c  W7 {$ V% y0 c
$ ^% `  u, g1 \$ C; ~

# p1 C! k# d: T% C+ t! P上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。4 j/ |! }& v7 b" t( T2 W% y

/ P. s, X4 W: x" X$ UHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
  t4 Z# u8 \+ W0 b( ?

3 Y( G- `& `$ _* z0 t* fvoid update_key_matrix(char row, char col, char onoff)9 @0 R( D, f% b1 l* B
{
" l4 S' G& \0 E: U4 F& P# n+ |    static uint16_t hid_report[4]={0,0,0,0};
8 h( t& V/ u$ ?* [6 |    static char (*hid_keymap)[8]=hid_keymap_dvorak;
  o7 U# n. Y; a2 Z# c3 i, S" I3 \& p& G2 k. P

- i8 @' T/ @# }( w    unsigned char key=hid_keymap[row][col];
$ o0 ?. q. u, |; X% j    unsigned char *report =(unsigned char *)hid_report;# u* M% r( U  I) e1 J; v
    char i;* w. _2 ^9 z5 W% G& w: m. ~- P
& \# e3 ?0 H2 `

7 C' ^% h+ C2 u: m, p8 L- ~    if(key==HK_MODE)8 d2 i+ j7 I" ]  C
    {
) P7 ^* @8 z4 Y        if(!onoff)
4 Q* N1 b6 u& X2 }! w8 N        {
3 Z$ X# O3 ^! r            if(hid_keymap==hid_keymap_dvorak); v" U/ @+ R6 _( H# E3 z1 S
            {$ Z' k4 x$ |7 p+ c9 |0 o  l
                hid_keymap=hid_keymap_qwerty;
! c! \9 Z; M+ D0 R% l1 m4 c                GPIOB->BSRR = (1<<2);
6 F+ J5 V' t2 W. }# {            }( v9 O4 g5 f, n8 z/ F
            else
- t6 x; X9 q+ e, K8 x            {1 M* A. Y1 n" D0 I8 r) M
                hid_keymap=hid_keymap_dvorak;0 ^; }/ a4 t) A9 Q2 U3 {
                GPIOB->BRR = (1<<2);, h. M$ w7 M. B, B) w/ J
            }- L* v5 ?! w; G! f: h
        }
  M* R; @3 q" O- d" t# K2 ~( @        return;
+ A& `" w0 i. I1 j6 N1 T$ F    }) W! s+ j. i2 @: {& b, n; A

; i3 s, H; \0 X) ]. o

' Y& r* |) ^3 @3 Y# l+ c( g4 @    if(key>=0x80)   // Alt, Ctrl, Shift2 r2 W/ x3 i0 g+ |
    {! g8 _4 {, f% g/ O9 F2 ~+ Q
        uint8_t bitset = 1<<(key&7);
; ]1 o. Z3 K1 T1 N; b% @5 Q        if(onoff)   // non-zero is key up; d$ Y+ {" S% [) ~" }
            report[0] &= (~bitset);
/ T, X3 e/ r5 a        else3 P- D2 |5 ?) x3 y* u
            report[0] |= bitset;
: A% X) X7 x9 D& C. |7 f    }: k  |8 v3 M5 d2 k
    else
3 i0 b& d/ J6 }4 k    {
3 w% F9 Q; S% h4 c6 {; H+ s! y" B        if(onoff)   // non-zero is key up1 D# w+ @. L2 {
        {
5 `0 Z9 U9 p' n            for(i=2;i<8;i++)* q( ~9 b7 ~, v' t% F6 C
            {1 `- l7 V2 }- f
                if(report==key)
0 Q5 n7 i& Y& r/ B9 Y                {7 T5 m4 j* U' X5 |2 s
                    report=0;
! b8 Q; @7 j& ]  y  V                    break;
2 S; a! h8 Z& S) a0 R3 u                }
3 ^, P( ^% Y) Z5 |1 ]* F            }$ ^4 r6 L8 ?3 {2 Z+ g
        }
( \/ s3 h' K, V! {2 g; W4 v        else
1 ^& W% i# r' ]1 j        {2 U0 R, v& F$ [9 z0 |4 g4 b
            for(i=2;i<8;i++)* K, U' m9 f: |4 V  Z' f$ u# t
            {- N  `4 N7 g) {+ u1 P0 [
                if(report==key)
1 Y  T3 E& }  I4 |; G, K                    break;5 a+ Q! f! h& s. F* ]' ~2 E0 ~
                if(report==0)& B1 J2 T8 h" C
                {
( I8 F. g2 }% p$ x7 n$ S) H                    report=key;' [) R# j5 E7 F( j. D1 z) O
                    break;" P5 G& ?! d- o% _; I4 \
                }
* A2 S+ P( G$ G2 h4 ~            }
* y$ u2 F' a; ^& S# J        }
/ M2 }& s, n0 F( Q: T" ~7 n    }! K# j. T$ z1 B9 d9 M, z' Z2 q' E
    for(i=0;i<4;i++)* B+ a" N0 F/ A' r5 U
        USB_PMA[192+i]=hid_report;
' v0 X$ \& u2 R# O/ i    USB_PMA[5]=8;   //COUNT1_TX3 o& I1 r5 ^: y6 D1 P9 o# T" H& D
    if(ep1_wait==0)! E! W" x0 c9 E1 O
    {
. T/ m" k9 g+ ?1 ~        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;# M& ?0 f) R' V8 a" ~
        ep1_wait=1;8 j: u$ r! n" E4 }
    }
& z4 U) W1 `. w# q% Y+ z- y}
% B' |3 M# B& r& O  [7 L5 e  m$ \  n
) s. p! b) \1 u/ e0 ]8 f- c; K& {" K
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。+ l- X  @- ?8 L% |8 {% T, ]" M9 c. n
keyboard.zip (8.7 KB, 下载次数: 6464) $ W; ?: K1 ?* q4 L4 x/ y

4 H% N5 H/ C6 q: l
3 r% ^* E. }8 u+ T" s4 W! p8 ?- P, D( z: n2 B/ t( ^' F: ~5 _5 Z. }
, m! F; x7 R$ W* u2 m6 ^
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
, y/ _/ V% d: ^2 u不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48) ^7 C3 b) u) |& B
刚开始我以为要把打字机改造成电脑键盘
5 A( H# R! I7 C% I0 j1 g; m5 d  s不过楼主也很厉害!

& [  Y; j  C/ N/ W/ k/ r9 k哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
( N: {" ?  N4 X
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-4-17 01:03 , Processed in 0.303766 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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