请选择 进入手机版 | 继续访问电脑版

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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑 : S" M( y% I3 _; ]: x
" r1 G9 `6 S# ?9 X* u* |  J: Y# G: b
http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D17 l: ]4 B( M% F8 S  o# M( ~
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。$ k. ]: S. J1 W; r! F# ^

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

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

. \! Y. D1 i* Z8 f1 G0 y 005836yvs0wvovwsssgd3o.png.thumb.jpg
( Q8 Z+ M! J. Y, r4 ~Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
. J/ L, {' i3 d' J; G! Z
) v0 g3 _- Q: F* v- s我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
3 w' r0 B. @0 J' r4 K  k2 M' s' `/ P* D, W
到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。9 m2 y+ [' I# Z) K, ~6 B

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

, M6 F. x9 g5 M+ Q/ C1 t' f/ E 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg & p4 x8 \3 J5 v5 d; U- E
----------------------------------------------------  分割线 ----------------------------------------------------------  q0 }$ R8 k6 M. O

! f5 {0 w- P# c7 J! B+ E先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

5 C, k4 b) J" ~# G/ R8 Q5 K 020011osionbunl4ui44vi.jpg.thumb.jpg 9 U9 Q8 a2 u; ?6 U
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
4 K  [! Q) r  Y4 ]5 O8 p$ { 020017j8ycmnv7788bqv52.jpg.thumb.jpg
3 L4 z* Y- l2 w/ C- n( {特写,80C49
8 N, B6 S" X" X  g* { 021040oujzuvtut6iujtvz.jpg.thumb.jpg 0 E- G! Y  L0 ]
LED部分,使用了一片D触发器锁存指示灯状态.
; p# t5 z$ a) ~) S: m' a 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg & Z; n) H; O' ~
暴力破坏,将80C49拆掉. q$ [) Y. ~+ z, x- G+ K- Y7 E5 B
021113e48qq98vyohvhzzh.jpg.thumb.jpg 1 q1 M/ P. W0 E( \. I, J" Q( o
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。+ j/ t1 A# P$ u- |! E
021125nc9az6dj33rlds2r.jpg.thumb.jpg
4 S' B" n. q7 X. c
焊好元件后的板子,准备替换80C49# {) f/ D/ B+ V# `0 l( o
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg % ]" {7 g1 l) j( B% @
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。
/ J  Y$ Z1 c* y: } 021104shifhnrqbr3o5nlo.jpg.thumb.jpg
$ V0 ]3 D5 {$ M这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.8 B8 ~  |+ b( w) R
022003ym1p9u4ug40280uu.jpg.thumb.jpg + s1 M  `* P! J" E( [' K8 i
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。/ P; n4 l3 d/ ~! K. D  t
023313kt141q9qajtol7ma.jpg.thumb.jpg
% ^1 p* I$ ]" \6 R8 H4 F' c* c我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
# B- n3 s0 v  W0 F4 f: |* }8 v 023322nt7l5xb3ltttkltt.jpg.thumb.jpg 7 o+ }+ D! b7 b
主键区键帽就位
3 \) b# {1 K) C6 m 023331hin88e8wkrwzwikx.jpg.thumb.jpg 2 p! d2 \) K/ V3 P$ o4 |8 @
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。4 n4 I; U0 H& n/ W! W; I
023336wjzlgopugg1jyy79.jpg.thumb.jpg
/ Y; K# V2 p0 C* W# Z% a最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。: F4 g! [/ I8 {& C( n
023341sffu4j3g2323h6fl.jpg.thumb.jpg
- q$ N5 t/ R. w9 B4 P8 G" O. n/ w+ b

' D6 |7 I( @+ V----------------------------------------------------- 分割线 --------------------------------------------------
3 t* C7 W; m, X5 o/ x

+ S7 F; Z2 o+ |" j# R

& P- J9 Z2 k9 S0 h7 |0 `  O  |5 y- X2 O5 g, n% [

9 r5 T$ u0 \' C8 Q6 h1 }7 d
. ^' i; g9 W9 {
# B7 f2 z/ k7 A2 y
* r# L' t, [$ T; x; t2 N

+ j' U" L) H) j0 d/ E# P/ i
1 O: [8 v  g, M2 n: g. \2 W1 J# e. l- f' }3 e7 e# M9 B3 {
( Y2 I! @% E# {, O
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
9 s% |: o4 i: ?- X+ H: E: d80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:' Z3 M% i4 T) f' U( i9 i
104025nzibm2rmiomhyirm.png.thumb.jpg
$ F' p. t( c$ ~8 Y/ q. E3 c! M
/ H  ^4 p0 {. n4 @
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)9 |2 H0 T+ P' W" w& [2 {
105004zkrez5houvkkznko.jpg.thumb.jpg & ^- c! c3 {$ H6 J3 z& b
5 W+ g7 k% D/ C( x
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。$ A9 h  ~+ ~  X! r* W6 p' C

7 J; Y9 u1 }$ R3 ^- _这是我设计的电路图:
( s$ b, e9 F# \, q( d 110344ej2z2oo2rflo7oe7.png.thumb.jpg # a( G! o3 n: w; L" o
) P1 T8 G8 l$ T6 n
PCB Layout:
6 |* k! s% K8 S' M* C 110847jjbjvt34vwt3v5bb.png.thumb.jpg
* j) Y. Z' Q4 O' e+ M$ f5 p

5 Q! o8 m% e! T不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
  v7 z1 V" g1 J0 w1 m8 i7 u- I7 y: q. S' F. b; h& Y

' S8 d$ j2 ?& v: J
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑
6 K- d& n/ }; \
& A+ B- e5 _6 W, E1 q# `% _软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
( A# i) o8 \9 {$ u5 u1 Q2 K
+ P: y- X1 {" b4 w; b! L总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:0 I/ P/ \( g% E- I9 w
113818pmrfsb6z0byt6t06.png.thumb.jpg & L. m0 }+ V  d9 r

) l6 ^2 f8 A& ?1 o7 Q6 i! c其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛); `2 f2 b, T7 H" B* e

, E* z/ y2 B/ q9 @& }USB的中断ISR,bare metal哦
, {$ W( {' U. h; R

/ }9 j; M2 m3 s; z* H# l6 B$ R  [void USB_IRQHandler(void)! B1 d; ?; n/ g  q% W6 I4 Q# e5 E
{
3 _8 m: N! h2 U; |( d% V( Q    if(USB->ISTR & USB_ISTR_CTR). {6 l- f0 ~! |( h  W0 W- x5 a
    {# D/ `: a) e, v9 x* l% e
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
" x' b  E5 ^/ z( W        {
0 W4 O. @4 z; E- x' S5 u2 h            switch(ep0_state)1 r* R1 D# n1 F' P. O( N" [
            {
: Y/ H  Y' [, G$ V+ H                case 0: ep0_state |= 0x80;
2 h) H/ F  }- |% K6 p" e% _                        break;6 w% ]0 z0 J5 q# C2 |. a
                case 1: if(USB->EP0R & USB_EP_CTR_TX)0 n; m; O0 g+ d" i0 O
                        {
2 }; B+ B6 u- B# a5 x: a                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;2 j9 e. c. V7 e! v7 |
                            ep0_state=3;
* N1 n/ U. T& h: S2 E                            return;
1 x( L. P! [5 r! ]: O8 k1 j                        }
* y4 \$ v+ t- }  c% I                        else7 R/ D3 W2 ]# R# W
                            ep0_state=0;
0 R+ U9 o. B8 G  z  P                        break;
1 ?6 G$ u! ]% I; T                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
: ^- |5 r  f$ r3 H. a: ]$ w; g                            ep0_state |= 0x80;, d8 C  u6 Y3 E8 n, T
                        else
2 C' L) R' k/ d3 C" ]# u                            ep0_state=0;( A" E5 z& s$ B
                        break;
0 c4 ]8 m. G8 x( Q" v/ K4 x: v                case 3: ep0_state=0;
$ `4 i  M( x1 g                        break;4 f% g+ Z: c% Z9 R
                case 4: ep0_state=0;
& }0 W( x. e+ D! j9 M                        break;9 i# G8 I! q! Q; c4 d' Q
                default:ep0_state=0;  o# ~7 K% w$ r7 ~
                        break;4 F) i7 z: h1 o( n. c9 H9 Q  ~
            }
0 z- g! h4 {; F            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
5 g! O9 N5 h  D            return;
; ~3 R& P# p# F4 g        }
7 \& r) X* A- x, U        else    // EP_ID can be 1; H6 {# P6 G, p" V. Z
        {
6 C: e: `  _* ~# f; {, a. e; ~2 n            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag2 p8 q6 F3 g# ~- y7 m0 r' t: ?# y% F
            ep1_wait=0;5 i6 B3 t. O8 F' m$ H+ `
            return;
% D) V) u  i0 ^* W; c) k        }
1 [" I* D+ D6 _  N    }+ E; \, f, _. J) r* S7 m
    if(USB->ISTR & USB_ISTR_PMAOVR)
7 m3 p2 \: H) N0 S( G    {) {4 n# y/ A  |7 `  e) H
        USB->ISTR = ~USB_ISTR_PMAOVR;' M8 H0 `* J0 w( a8 G% }
    }
2 g3 }0 L& K+ \4 V% P    if(USB->ISTR & USB_ISTR_ERR)
0 W& f# S1 ^! X$ t" [    {' f& e  D: B$ i" l8 d7 q! X/ z
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
  X! [& p+ _0 r    }' @% c: c7 |: n  D1 u
    if(USB->ISTR & USB_ISTR_WKUP)
# v6 ]. H% ?3 o' G! d8 ~    {
$ R5 ~. L+ R+ G2 i+ B) W        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
7 F" b5 N8 ^0 H6 y9 M        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear- ~+ x4 u% M* g6 }9 [1 k
    }
3 @7 j  W, t/ v" W8 c. h( m$ {5 n; m    if(USB->ISTR & USB_ISTR_SUSP)& I, b+ U3 o" E% |6 o0 @7 J
    {6 p# q: B% F' E1 |" p. Z
        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend# V# Y5 a+ `3 V9 w4 F/ @- C3 v
        USB->CNTR |= USB_CNTR_LPMODE;   // low power* R7 G0 P- W) X* C1 S, t6 w
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
/ W5 [& q% U6 k6 C- P; P    }
1 [! l& P" R* U0 z; Z    if(USB->ISTR & USB_ISTR_RESET)3 ~1 f9 n1 M; F; _
    {
+ O/ h$ g9 U/ ^        USB->BTABLE = 0;    // buffer table at bottom of PMA
6 x" N* j2 p5 s6 f! D1 U        USB_PMA[0]=128; //ADDR0_TX
5 e, t1 x8 n5 n. F# E" Z. u- p. y        USB_PMA[1]=0;   //COUNT0_TX6 Q  U8 T' M2 ?% A8 H
        USB_PMA[2]=256; //ADDR0_RX
6 A7 P" \$ g3 h& U! d) z        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes8 d5 Z1 \7 O! |- v
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;# `; j$ t- B; _6 h+ x: F
        ep0_state=0;
7 h! I/ p3 t0 C. J# W0 [3 A; v8 O        USB_PMA[4]=384; //ADDR1_TX
$ z+ z1 F5 a, V4 z  G/ p        USB_PMA[5]=0;   //COUNT1_TX
: y+ o5 `( g4 ]6 w        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type1 a/ F) l) w6 V7 _3 ^1 c% x0 W1 P
        ep1_wait=0;" w- J! V7 S  L8 q0 T! a& P' C
        USB->DADDR = USB_DADDR_EF;      // enable function
$ C+ r/ i# Z7 }3 a6 t7 X( j        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear. p$ O; u7 X, K. N1 n  O$ u) A
    }% {: }6 t9 h0 ?, ^, B, ~
    if(USB->ISTR & USB_ISTR_SOF)
" E8 {/ f9 u  r0 _. W1 H    {6 G- n/ g; ]6 _; X' L2 d8 J
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear8 F8 A$ R/ c7 Q
    }
& |2 L2 d" R7 s% w0 n2 t" F# N9 g}
/ z$ N; `  H) n' u2 m. w" {: n+ r, A5 e
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
# P, B/ N! L3 o, q2 H0 P
, S  z, z, @4 j8 d主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
; X  s* g: j$ C8 T+ Q8 _
- v/ f" e/ M5 {$ `& a3 f* n3 t
    while(1)
) d8 D$ x9 r& U* m5 l, {    {$ C( x9 c! x/ U) ~' M
        static char row=0;
' J( G& ]: N( u, A/ U  @        __WFI();8 C& ~5 y/ w! m  E  f
        if(ep0_state & 0x80)    // request data processing
% p, R# \$ s) D4 P$ h: w        {
# ^- j% \, ?, u3 E; w' [            if(ep0_state==0x80) // SETUP phase# ]; v; D; h: ^
            {3 w# T, {; Z/ P% n' ]
                if(!setup_packet_service())
, I) K/ f9 ^% k& w; F: p                {- ^0 S+ a3 k* s& K' e. l
                    ep0_state=0;* U6 B- Q. N6 `: M$ `. G# P% W
                    // not supported0 A  Y' f: W+ N) \
                }5 k8 m. W3 q6 U% ^4 n
                // ep0_state should be set to 1 or 2, if processed0 z+ Z' m6 c, @6 X3 ~0 i$ G
            }
% z2 ~( I, @6 w- x# T7 \, I            else    // OUT phase- A* W/ f/ V, T1 m8 I
            {
; f0 |) i( \8 f! P  T2 K7 `                // process data0 p# S+ a( l/ H/ o! m& L& i4 |$ o1 M( M
                show_LED(*(uint8_t *)(USB_PMA+128));0 Z+ Q# x: u" R1 h7 w* R
                ep0_state=4;" A1 u0 n$ Z, E, W2 f
                USB_PMA[1]=0;       // Zero length DATA0
+ i  e) u" N) e$ v                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;9 Z! H! w7 W0 D% @; p
            }
8 Z+ D7 c5 n$ S% M3 t* |        }9 O# A1 v( g/ A
        else  L' n, s* i! t0 i0 g: F; C2 L
        {
7 E' _' v6 _8 a- |2 M) k$ u1 M            if(usb_address && ep0_state==0)+ J: p* m3 B% r0 Q8 B
            {1 k" ^& C  i0 O
                USB->DADDR = USB_DADDR_EF|usb_address;
# ]0 J* B- E; Z# o1 S8 p- I                usb_address=0;& ]( {8 w& I# B  \1 Z
            }
6 w- V' Y* {+ t# C        }) M2 W# B" s3 L
        if(row!=scan_row)   // new scan line* ~" s( B! H$ E: L
        {9 V& ^! l9 m' N/ \
            if(key_state[row]!=prev_key_state[row])5 H. x3 |, b+ n$ g
            {# G4 }% v% Z# A. q9 {5 S3 u
                uint8_t test=0x80;# E" L* @* X7 g+ p0 c$ Z6 Y
                uint8_t diff=key_state[row]^prev_key_state[row];
  o& L: a# S) Q' }2 z                for(i=0;i<8;i++)  \4 v  ~: O4 m  @6 f) U& x
                {% n) L8 T1 s3 ]8 y
                    if(diff & test)
: {1 }( W3 m- q& z- b                        update_key_matrix(row,i,key_state[row]&test);& Q7 `4 ?7 N1 F. c  J8 R0 W* x
                    test>>=1;
& C( o$ h% U3 T) ]) i0 i& N( Q- j' A                }
7 s2 _. s% S- E: @; g  m% C. K/ x            }
, z8 s  c8 c, H+ D9 O0 o  E, \            row=scan_row;
. V! O8 `5 B) u$ b1 n        }
6 R# c8 R. T* m" U8 s4 m/ R# x    }
) p0 }1 @! o! S! Q- n# _
& g5 _4 R: n; Z5 }; c' k9 G( Y1 E# Y- [6 M6 \
EP0的控制传输,把用到的请求处理一下
% i  K5 y" n8 @7 `9 nchar setup_packet_service(void)5 I: u' C5 C* g# w
{) C6 @" |, b" X# U7 {
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
( Y/ d# J7 z: L    {
8 o* n1 B: s- S7 D. I        switch(ep0_std_req->bRequest)/ V5 G4 j* W3 h8 H( z
        {
# k0 L& }! H- a            case REQ_GET_REPORT: break;
7 }+ F; W+ M) t. @. ]1 n            case REQ_GET_IDLE:
0 G" n% w- x& t. [                USB_PMA[64]=0xfa;   // return 1 byte
8 p( B  N0 s0 d9 b9 M5 ?1 J% j                USB_PMA[1]=1;
! _# K2 [- r0 N- T                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
, b* [0 x9 J/ \8 O                ep0_state=1;, z. ], G! p& \4 ~; y: p
                return 1;
9 D4 Q4 ]  u# p* x                break;
/ Q% }" D0 |5 k0 d; Q: H  b1 R, t7 [            case REQ_SET_REPORT:( U% @" Y3 i; [& i+ X6 W! [/ |
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;6 I* J! D2 _3 h- A+ K8 R: O& G
                ep0_state=2;/ X: F1 w  m- t8 ]6 f
                return 1;5 A- i# \0 W( ~8 a  r$ R; H
                break;  j6 d: K- {2 l/ c* J
            case REQ_SET_IDLE:# P# Z. x; k- \+ }
                USB_PMA[1]=0;   // Zero DATA* s* q5 m: y/ `$ s7 O
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;5 N/ |+ _1 ^8 j8 x" L& a2 I* p
                ep0_state=4;/ q% p* z; v% V( a
                return 1;
# \' V. p' V7 r0 k) G6 ]$ ]8 Z# `                break;7 \7 o+ y/ ^8 v1 W1 L6 O
        }" q" f1 F' l2 X+ a: [( c
        return 0;/ G2 x+ C! w  X/ A9 o4 i' w
    }' t) h6 v: j, d7 k% U
    else    // standard
! V$ S' Z: ?: H! H% U' \* }2 `    {! [2 n3 c( g6 s; z7 q3 C
        switch(ep0_std_req->bRequest)2 s7 I3 [+ @3 ?5 C3 ^" M' Z+ q  N- R8 s
        {
" e! e6 w$ M- u( S2 v            case REQ_GET_DESCRIPTOR:
: c, k. ]# e5 M4 Y; U  x% [' ?4 o; r/ B  p                return descriptor_service();
- V' X$ p) h* r9 F) c6 b+ {                break;
% o) }2 p# I( l; z& P" ~5 V            case REQ_SET_ADDRESS:
) @. Y9 A& a, B& h' z7 m+ X                if(ep0_std_req->bmRequestType!=0x00)/ |+ m  E& ^" _$ O/ I
                    return 0;6 B  F. I( R3 E4 o3 G! A
                usb_address=ep0_std_req->wValue;4 I6 o. s6 V8 u( ]- |. ~# x
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;  M. S* r  a$ [& A3 h0 O
                USB_PMA[1]=0;       // Zero length DATA00 U/ x& }4 Q6 w; f/ `
                ep0_state=4;    // No Data phase* M" E6 l6 T3 E. I
                return 1;
* w2 c8 ^; t/ }6 s2 O6 m  U9 O            case REQ_SET_CONFIGURATION:' }  C/ S4 q8 G3 v0 F8 p8 z5 O
                if(ep0_std_req->bmRequestType!=0x00)+ K* Y8 K7 ^7 c0 h  h2 l- p! q( L$ G( I
                    return 0;/ g: x! j8 |6 h6 ^2 u% C% E
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;$ y1 h$ B# U  a, g% v! h& S
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
8 ~2 J3 `& Z; E7 p8 e                USB_PMA[1]=0;   // Zero DATA
4 {: i! y9 d! A7 \& m" |7 [                ep0_state=4;    // No DATA phase! h0 E. y# ]# `- f' c# U' g
                return 1;2 ?/ L- Q- _7 l
            default: return 0;
) b& ~; a$ M' X1 s        }
7 y' Q" }& Z$ |$ W+ ]    }
& a3 O; T$ }3 r1 A% `$ G}& G, Z! I+ B6 D) ~( J

% j! d& V+ i/ p' Q. v" l. z2 i' ], w1 D( ~8 n0 F/ `& M% g

- a9 Y% P( k! r; m: ?
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的8 d$ J  v+ }; r9 |2 C2 m
char descriptor_service(void)
# q  B: P. q. Z: K; C{
5 h7 c( P: f- z5 s    switch((ep0_std_req->wValue)>>8)
( ~; a( e* |; `5 b: h1 m# G    {
3 n. P+ q0 j2 l$ Q9 t4 d# G        case DESC_TYPE_DEVICE:5 y, u5 Y: g' ]% i2 d/ S
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));9 ~# R# x0 ~6 L! s
            break;
3 W/ Q/ z4 T, J& L% K- x        case DESC_TYPE_CONFIG:
- }* D3 s4 E, r: f" i% ^0 r            if(sizeof(ConfigDescData)>ep0_std_req->wLength)' z% b' b& N. M. K1 F  E$ K
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);" f9 f  [/ ^4 ]+ j" W
            else8 l. g: S* |( Y0 W
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));" _" u- A! w- i2 ~$ o: F6 [
            break;
) v9 E, S( R0 R3 t! F        case DESC_TYPE_STRING:
) s- N: S- M! ~$ \0 m            switch(ep0_std_req->wValue &0xff)
8 K. _2 |9 \: f3 [            {
# q$ w7 P' }. S, o$ R                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
# ^/ K6 ?+ s" V) f- L- S                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));/ j2 E. @; i. }4 p% J3 c0 H
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
5 h& X2 Y  u( z                default: return 0;
" t) O- N1 A  ]5 g, i) ?/ Z            }* T' i* Q# m, h0 w7 X. G1 P& o3 K/ Y* s3 M, y
            break;# {8 o9 ~+ i9 b# O  q/ O3 \
        case REPORT_DESC_TYPE:1 N4 X8 d+ {8 l
            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));# V8 Q1 d* c: I3 c0 z
        default:
. W1 k" k$ K( m& X' k0 ^  F. G: ]# c            return 0;
3 i: b' v* |: C0 A    }$ S- Z- ~# `  B' K$ x, R# c
}# k, o6 T  Q6 m2 u
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
* m5 b& `7 A, w" B+ [  Z  t* f' Mvoid TIM6_DAC_IRQHandler(void)
0 A5 D7 r$ h# q{) }" h. \; B, x. B
    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);  c# {1 S9 n/ [7 I

* I9 C2 F3 A* C' O: }* e) n

0 D9 D7 p  `0 `2 o! p4 z& ~    TIM6->SR &= ~TIM_SR_UIF;
5 q1 C% g! I* l5 E, @$ U9 ?    prev_key_state[scan_row]=key_state[scan_row];  ?  G% B' ~) D0 Q
    key_state[scan_row]= *PA_IDR;   // update key states
$ i7 r0 ]1 D; a  w    switch(scan_row)
/ N. H& Q9 B7 S3 B& |3 i4 R* W    {6 w7 F2 x5 K/ `( T8 {
        case 13: // next row PB14
4 s9 r1 Y  [" d) i                GPIOC->MODER = GPIOC_DEFAULT;
4 _+ d4 I2 [: t" l                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;7 Q: m! f% ~& G3 h- O& @) s
                break;
9 x1 H  g% \$ A& I4 S& ]        case  0: // next row PB15
7 g; o3 E, f( m$ i- x/ ^" H, Y' T                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;
9 V3 R7 Y0 z: |! _4 ~# P' K# g& W1 g$ m1 u                break;
. W/ |' C4 h- N        case  1: // next row PB3$ N9 D' V$ g) l& v& C4 ?
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
& z* V3 D3 J2 b; a! z                break;
+ \. E4 w2 c5 [5 X        case  2: // next row PB4  G/ o4 |  F6 v% W
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;5 @+ o& ^8 q3 n; S: F7 g' E& R/ {
                break;2 P( q7 G$ y: Y; n
        case  3: // next row PB55 C- D* B3 W  R. I0 e, l: C2 ]
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;4 U7 \4 @0 B( `4 A, h
                break;9 Z) @( \4 @3 X
        case  4: // next row PB6, W- N# R+ X$ d
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;! g8 y0 v/ s7 j6 T* {
                break;
! S, M" @* f0 w' o$ T; z1 {7 W        case  5: // next row PB70 b) `6 _- L4 ?$ v, P! |
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
6 U- H- z) e. [$ i: W                break;: Z2 S7 [6 e& {# p- P/ E
        case  6: // next row PB8
2 ^( A+ _& N( U( h( N4 i$ q; G- `% I                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
8 F+ }* o4 ]5 p/ g8 x4 _  C; _5 H                break;
/ W# ]' ]1 k0 k7 W0 o        case  7: // next row PB9. m$ j: h' ]; B4 p$ I+ {; O
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;' z0 C( y6 r7 F1 G4 a7 \
                break;
; V7 v! C( ?1 ]; G: o        case  8: // next row PA88 l8 E0 J4 l2 M# t% i
                GPIOB->MODER = GPIOB_DEFAULT;- B! C( B, O1 M0 {8 t
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;# d" I6 P+ Q% i, b6 t  i
                break;
3 J7 g& k/ g( s! n, ~        case  9: // next row PA9" }9 Y9 A' u" d% z
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
+ v4 f# ]- Q! V( Q* w" B                break;
3 {, _3 [  i+ |8 ^        case 10: // next row PA10& U$ o$ [4 @# L7 B: F% `
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;, C! }# X4 A2 O
                break;" i1 }# w9 y; [" V
        case 11: // next row PA159 _# L7 x% p( i  C2 J, Z
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;) z. P0 b9 K) l6 t
                break;( M8 d0 ~+ R6 m0 g5 Z
        case 12: // next row PC13
% N: V( r1 Y1 V/ Z. ?                GPIOA->MODER = GPIOA_DEFAULT;3 c4 K  B8 f8 S0 V
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
2 S% o0 u1 a( g, q                break;/ Q4 |: u+ C4 Z+ {0 W
    }
& g; s: k! W- B: P    if(scan_row<13)% L; G& [. S2 T0 n
        scan_row++;6 g& O" c# X+ ~& m8 r
    else# ~6 x" ^; g7 `% b  x/ j7 j
        scan_row=0;/ S$ S# S2 O( w$ N( r8 r
}/ o7 _- j" b  J$ k+ H4 l- R4 J

% `) Y: o. u# N; [& ?( h) B7 l/ M, Y1 ^! z! Y/ Z- K
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 " N  j, I, H& x

8 l, |9 U( H3 }" `扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。. Y7 f+ L" J# l7 R. O

6 e9 F5 I2 Q- f0 @! ^4 H- D* o8 u要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
" L  e* C4 j; d8 K7 D

  A% l/ s+ v! ~; f' S% g) r
1 x8 [0 v: q/ }1 S  i+ kconst char hid_keymap_qwerty[14][8]={
2 {) _" U8 u  }  ]2 \1 O" U6 e' c    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc}," L% P& h$ Z3 E5 M  f4 `- ~  w
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
5 Y2 `1 K; s0 j7 L7 S5 l0 f6 k, O    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},$ l* q( b$ L+ v3 P" Y4 K
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},+ r; p0 u( L7 _# g
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
4 F. L/ H6 y) \0 V6 ^' f    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},, ]8 |* a$ I, [8 s0 O
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
# \: R4 y' y. d, g- A    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},0 e) e6 v, Y/ |% c1 G' |% S, M$ L
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},; y9 s4 _* Y% ?! a, |
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
4 H) u3 }) g. H    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
2 e% k- i' m$ ^; F, M2 C! w    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},/ ^6 E( ?( y  F
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},; A9 o3 l; o, Y5 u. M0 E
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}( s+ }0 u: Q1 ?4 b3 M) r/ ~
};, a6 W! }) W0 k: _8 \

! u3 |! c0 l! w( W* w+ l4 }" s! o; w

, r  `; f% n" v8 u) p+ d/ Xconst char hid_keymap_dvorak[14][8]={
- ]3 ~2 m" [$ G% X8 u    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},& ], G1 f% A* e$ G/ k( Y
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
$ h9 J- N6 U9 @! T0 o    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
- Z- m2 D" W. @" q, {: x    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
4 ^4 i% w: L' o  P9 u    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
3 \6 b8 c4 K! s' h9 l4 f& r    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},' F/ b, k5 L$ o7 |' C& q7 S3 u4 N
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
- \! t+ t3 j- w7 C9 P$ m5 G* s    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
/ w6 }2 v/ z: `8 R3 ^    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
# M! P( T7 O$ i3 D2 J    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},6 y3 N& d3 V0 [: d
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
' X% F1 T0 T! {) `    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},; t5 @# v6 k. z/ Y7 }3 i: `6 p
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
  Q  ?: C- F5 Y* {    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
. m- M; O3 X9 p1 u% R};- q0 e, z% h! _& p  J$ h

" L% v( Y/ Y% W: V8 t3 p* M' H$ Q: Q  @9 G! B6 S( c
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
. l8 ?+ @% H) n" I7 v7 A. n+ u6 T% `: Y
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

: S2 Y- a, T" C5 @  H" F* {' g) F& R' `
void update_key_matrix(char row, char col, char onoff)- r/ L% f0 U) A
{
6 g" F2 O: O: Q7 U    static uint16_t hid_report[4]={0,0,0,0};
8 [( W8 v* A: R1 f+ k. y. l4 Q    static char (*hid_keymap)[8]=hid_keymap_dvorak;! m' X. T7 U/ C  J8 u- q: d
6 F% X: D( V! t4 b

. z8 M$ M) @0 E    unsigned char key=hid_keymap[row][col];
5 e( \7 _. _3 e+ ~+ @8 |    unsigned char *report =(unsigned char *)hid_report;
" B" ^+ S# |; U8 J* ~1 e    char i;4 a. x2 A$ d2 z3 M0 O# P
& W+ C. Q. C( j  _" f/ q' T, n

: u* x7 q& G/ @9 l2 |+ z: k+ N    if(key==HK_MODE)# o# h8 ~7 U  ^( H
    {
* x0 x: H" \8 p, m1 d        if(!onoff)+ l# X3 U) k& s+ {7 J
        {
& ?: _6 y& B) g: {) K: m5 J            if(hid_keymap==hid_keymap_dvorak)* J' ~+ l# ^' n) m: f4 b9 ]: Q
            {
& p( Q5 a6 e9 G( I1 I                hid_keymap=hid_keymap_qwerty;  v) p0 R+ x  ~  I/ x, m' X
                GPIOB->BSRR = (1<<2);- X) k% ^" @. U0 v' r2 L% Z$ d
            }
& u$ S' B  i: N8 p8 x% v            else2 U. ]# Q5 w* i  r9 I2 s
            {2 a) o/ K8 o4 F, U/ Q
                hid_keymap=hid_keymap_dvorak;
4 L1 H& n3 C0 Z. F" g# R                GPIOB->BRR = (1<<2);
6 @/ d' C' s- G3 [            }* m  C( Z0 r, s
        }
" V3 U4 ~9 G* t        return;% M/ t! ?5 Y9 {- ?  f
    }
) A2 L! ]( ~9 ^9 @5 V9 A$ C) o' ]* p

( F+ X% k; V6 t% m3 x    if(key>=0x80)   // Alt, Ctrl, Shift
) d7 Y2 J$ T3 H1 A4 `) L4 ^    {9 j: u7 V1 m6 X' {/ C% r8 ?4 N* s
        uint8_t bitset = 1<<(key&7);6 G$ m( L9 V7 A3 }2 J" [
        if(onoff)   // non-zero is key up
# q5 _) I2 R, v            report[0] &= (~bitset);" J7 @% C/ _8 _. g( q# u
        else
! c! V0 \2 b6 Y, o            report[0] |= bitset;  c+ m5 G2 L3 x$ ~) }6 t
    }1 \0 k: D& J  {
    else* ?/ x# t7 B: L2 \: `  v
    {9 `* \; G' H; w1 _# z3 k
        if(onoff)   // non-zero is key up
$ r2 |6 u9 B; w. U' v        {
9 t6 y. x" P9 i            for(i=2;i<8;i++)( w/ {0 d4 h" f2 g" I, A2 J
            {  t* N5 `: x& u. ^
                if(report==key)% l) W2 B- {' b4 F
                {
# E% k) A; F" K; o$ U, J                    report=0;
2 ?7 f0 m3 X; S) g; J' h                    break;
- P/ [1 P) o: B                }# f8 F% n+ [' C* j
            }
6 y% c0 A) C' ~( M, ?        }- ~: j3 u- s9 v/ X# X" n3 u* L
        else3 d' p5 q, T; O' c. x1 E* Z
        {
0 N3 k& J. O* ^1 |# P/ R# f            for(i=2;i<8;i++)$ q2 ~+ T, z+ K4 `  Y8 C
            {
8 \0 s' h, S% V+ E                if(report==key)4 A& C( ?8 t/ m. ]' ^6 {
                    break;2 N5 @; r: z; c( J
                if(report==0)* ^- s8 ]3 g' k5 U2 A9 [
                {7 ~& W* T$ i6 K+ i& T4 ^7 M) L
                    report=key;
# ~7 E% h. r% o! a) H                    break;9 V$ Y; n( i: B7 }$ R# B& b: u
                }) p! Y1 h" n  J4 @+ y% |' O
            }$ Q7 q* q7 B1 V6 ]0 j2 S5 |
        }
- u3 f/ H( ~6 a* ?    }
6 Q1 u) F3 [3 I1 M1 N0 \# u, U    for(i=0;i<4;i++)
+ b- i8 R+ z. H6 F$ m- j        USB_PMA[192+i]=hid_report;
* D6 W9 c! k, K0 _9 J+ h9 U9 t    USB_PMA[5]=8;   //COUNT1_TX
' I4 w9 a& k. u8 u5 t! r! i) ?    if(ep1_wait==0)
: b8 h( K- h' @0 Q3 P0 }3 n    {2 Y3 j& ~/ T) p
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
' ^. m: ]* W: l# O' P" n+ W        ep1_wait=1;
$ P8 C1 K5 e% v! P    }
/ N  ~& i! i9 r' ?3 a}. ?; g3 @9 t, c" O: e1 j& \

1 m- N9 p( ^6 x6 P( w/ R
. E# b& ^1 h: \  \完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。! o6 g: A" f2 C: `8 s
keyboard.zip (8.7 KB, 下载次数: 6420)
: n/ V; g3 ^$ E# e1 d

$ g: |9 p: ?( \% [
  y2 o* c" O( \: b# c4 N- E4 D- |- e. n$ u7 k* G

% j) Z0 F8 x  n. H: E  {
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。
+ e/ d5 h( c& e+ q7 A% c4 G# k, Q
回复

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
, t* ]% E6 ~( P2 H& P+ |不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
0 a- e) q8 l' N5 V5 [% G刚开始我以为要把打字机改造成电脑键盘
/ V3 H) }5 ]# Z8 m& I  H不过楼主也很厉害!
4 ^) f1 i5 N) h: @0 t
哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
; h$ u& v! ]! N7 b
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-2-17 03:46 , Processed in 11.688445 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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