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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
0 i! v, h" o; m
7 V( Q+ P+ ^: `7 u/ Fhttp://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1; s2 j# n5 W3 O, X- y
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。
( O+ y# ^8 _! T2 B) A
- U' O+ Y: Y7 n9 Q% W9 O6 u在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。
. R  `$ Z# Y8 i1 ?3 ^

3 t( o9 f9 |/ B* F0 V& A) M 235140i3a36qivqzuvmt5q.jpg.thumb.jpg ) f! E4 }& L# K  t+ o
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。
8 }2 @0 Q# A1 r 001734klbyoluenuwz4h4b.png.thumb.jpg
; v3 o8 s  e- x! a4 g6 {4 A0 y' @7 U/ C为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:+ I6 X+ e1 y) d6 D# K5 t: `
003625r2agx2f5v922cf2f.png.thumb.jpg 4 w& S! h3 e' R  r3 E2 {
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.) ]$ X4 _/ P) V
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
. @. P2 ~* w2 n+ V
005836yvs0wvovwsssgd3o.png.thumb.jpg 5 i8 n: l, c/ m/ Q4 }
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
, u+ K, |" R( R" v2 E, F$ d+ R- u  C+ M5 w
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
% ^' `. G$ h  T# h( o* ^' _% d+ G0 E* X* p, q* x0 Y+ M/ E' `
到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
- ?4 \4 m7 o! F8 W9 O. K$ Y3 A' o* u) R' o9 D: j
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:

7 N# S* a, y  p1 u 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
( p9 O& _1 D8 o2 y$ r  m- r----------------------------------------------------  分割线 ----------------------------------------------------------4 U' D7 A: G* w: l% K
" M' p/ v* w, m
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

4 S4 v/ O4 W; U" G8 E5 w 020011osionbunl4ui44vi.jpg.thumb.jpg - ^9 w( R6 G0 d, V1 k
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。2 B9 Z2 ^4 m" a0 s4 X" u9 w6 U
020017j8ycmnv7788bqv52.jpg.thumb.jpg / v4 C  s* @- s5 _  e4 a. l, @
特写,80C49
% p7 N) ~: W1 { 021040oujzuvtut6iujtvz.jpg.thumb.jpg $ d/ R5 m2 y' Z* H  i4 A2 o
LED部分,使用了一片D触发器锁存指示灯状态.3 t" F6 ~" |: J- U
022209ixzjfjc9crmrrqjm.jpg.thumb.jpg , o3 d: O$ W2 C$ z$ I- A' O2 p
暴力破坏,将80C49拆掉6 d, X% l! p2 a' \
021113e48qq98vyohvhzzh.jpg.thumb.jpg * X' S, Z9 L, }
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。; O: Z6 r6 E6 D/ P
021125nc9az6dj33rlds2r.jpg.thumb.jpg 2 `- T; x: `' t- Y
焊好元件后的板子,准备替换80C49( U4 q: q! t7 Z
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg 9 \6 f: w) x* z
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。5 z; t5 P; o+ r- q+ ~8 N/ s
021104shifhnrqbr3o5nlo.jpg.thumb.jpg 3 r! x, T( E7 y" q0 S1 j. d. u
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.: `1 i8 d; z# J1 x3 d+ w
022003ym1p9u4ug40280uu.jpg.thumb.jpg : Y4 D6 L" P" P6 r9 x* b* v1 d
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。
7 A& B1 V3 A/ V 023313kt141q9qajtol7ma.jpg.thumb.jpg
2 y0 g( ^9 o4 G0 W5 r, U我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。$ ]4 ?; V+ k) G; g. I( m: t) M* P- z
023322nt7l5xb3ltttkltt.jpg.thumb.jpg
8 Q4 e/ ^/ h9 i
主键区键帽就位) I9 S( X) A' s/ A+ b
023331hin88e8wkrwzwikx.jpg.thumb.jpg 7 n$ h0 d- b9 y3 y+ T' S
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
7 H. t9 @5 Z) g2 Q2 q- l 023336wjzlgopugg1jyy79.jpg.thumb.jpg
7 b$ Q1 _# @0 @0 h最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。$ Y2 s4 k7 \! p9 j( q# e  u
023341sffu4j3g2323h6fl.jpg.thumb.jpg
" A; U$ c% |" B# `4 M# I
  D; }* Z& k$ W
----------------------------------------------------- 分割线 --------------------------------------------------
, h6 _! T4 c& h$ j0 E& E# I

0 _0 r) m4 U7 B4 B% ~
& _# C9 B# o1 \' H' \2 A9 E  I
+ P7 e" i6 P3 x+ v% {# D

$ _  C# W/ R; K. u2 }" U  M5 e
0 f) T2 d, H$ J: }

. |+ S  y2 H8 ]! W' p
; x$ G! ^3 `8 U6 b* c% Z" I- }
1 e# g. H- o" i; n7 x
5 W2 d' p% e9 s# m3 O4 V; U% Q  P8 Y
- R5 [* ^3 k; X& q# Q5 |' I1 q
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。0 e  T- G7 z4 X, q9 ^, S6 N. F% ^' C! d
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:8 w% A! m3 d1 p& e
104025nzibm2rmiomhyirm.png.thumb.jpg
( i7 ?# s# X+ ~& a5 z$ ~1 `
5 w* e2 H( M& o- s
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)) W0 m) |" k0 [
105004zkrez5houvkkznko.jpg.thumb.jpg ! m1 N8 p: U4 g: M4 U' f7 h
/ p- X/ |$ }5 u6 i0 P2 |
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。6 `( Z5 W8 L5 t

  e' L' ~, t0 T% J$ C4 X这是我设计的电路图:' m9 ~) J% v! L1 X
110344ej2z2oo2rflo7oe7.png.thumb.jpg * a5 O* t. K* J5 U) H& w
# A- {9 Z) ]) O2 G' D" N
PCB Layout:
6 s9 M' a% S, k, I 110847jjbjvt34vwt3v5bb.png.thumb.jpg
1 K( i# H9 V; H5 \4 o
) m6 G: Z* Z# [4 Q4 n" A
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. % C2 F8 i& v3 U, \# ]

& g. O/ X# p; U# ]- H' X6 H
' O* J/ Z) X+ C
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 - K4 w+ _3 [7 ?

9 y, X. X$ m/ x& l* r软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。3 d% @: U7 L, g4 q* ~* _" g  ?0 Y
" Y. ]+ R8 k8 [1 n, T6 A( h
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:; ?1 w/ p% D# o. `9 n( y. b# T7 b
113818pmrfsb6z0byt6t06.png.thumb.jpg 0 f3 F, \6 f) w  z# |2 E

# `& |5 b5 U9 B其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛); n! i: b- o8 H" r8 R. Y/ M, z4 |
4 J2 y/ x* w, P" S. M
USB的中断ISR,bare metal哦
4 [, K! L: f% x/ E5 }
) p( L" A( L. X; h( V2 T' L5 \
void USB_IRQHandler(void)
* a7 ]3 T+ P" c$ M- O{
2 A2 b. U% b3 t' ]1 s7 x- n    if(USB->ISTR & USB_ISTR_CTR)
* M6 J3 c% a: \/ C1 B    {
+ J: `+ B/ N: Q3 U4 M8 ?* g8 Z        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
- F7 I) W% k( \; P! b( g8 c        {5 O3 T. F5 N, |  Z3 j  m! p& F9 u
            switch(ep0_state)
4 i7 Y4 M. p: t: u  t4 s4 G  u  V            {% q, B7 k# F$ t, B6 b5 y/ w4 q
                case 0: ep0_state |= 0x80;7 D4 s' q+ z. _
                        break;
& S5 Z& A; ]- H                case 1: if(USB->EP0R & USB_EP_CTR_TX)
; |, w& D6 v' v                        {
2 [/ C! o. e8 M6 N6 w1 k6 o1 O                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;6 @& J; r4 q) `) |1 a4 S$ r; c
                            ep0_state=3;" ^& Z/ P7 E. e/ Y7 u
                            return;9 i+ V/ `7 e0 L+ I. C
                        }
* s/ R$ z3 X8 x3 F5 M. U0 H                        else2 l- G/ o$ ~2 s, g5 W1 ]- }: s! k
                            ep0_state=0;
$ x( N6 k5 Y8 Q- E. D! ?- h  ^, v                        break;3 Z% ~& b& e0 }# U2 X
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet# F/ j" X* H  u7 N1 @7 o( L
                            ep0_state |= 0x80;
" K& F' V, H* S7 N$ B$ U1 A0 O$ z                        else
) K1 T: ~; f( L( l$ U; Y% c, W                            ep0_state=0;
1 i/ b9 Z* J7 N7 e7 V  d                        break;
. }9 U# O0 r- t9 t. u# s                case 3: ep0_state=0;
3 T5 C- d9 o& s6 T* P4 O* I                        break;, @: F& {6 b+ B3 X, l
                case 4: ep0_state=0;5 x' }. h' I6 @  R
                        break;
9 B/ \  d% d# D1 p                default:ep0_state=0;
% C4 `5 v% c/ J- F% g                        break;$ \4 x1 w& Z$ J: G( r2 Q
            }2 E9 S8 R0 U7 |8 e
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
) y9 o% I3 G& O9 M5 Z- X; C! k            return;
- r; n- J( O8 }) G        }. A# h$ C* l# Y" L
        else    // EP_ID can be 1/ }) b) K, r0 `: j) G* z
        {$ x5 Z& A. m+ c% E8 b# d  ]% T! F
            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
; @  e8 R/ h+ ]- [$ G0 B/ i) U            ep1_wait=0;
6 g9 ^! m( h( ~1 v! Y! F1 d2 z8 H9 }            return;- I1 ~% {' X# `3 Z# {
        }
/ D/ L# N8 E' i% d+ I    }
! l  p' l8 I, n: p) e7 V% U    if(USB->ISTR & USB_ISTR_PMAOVR)
* }& ?7 L' K7 t9 F    {5 O# i& p& C+ ^/ C
        USB->ISTR = ~USB_ISTR_PMAOVR;
/ x% N) S/ n9 d6 F$ Z( N    }
# K/ k4 j1 h' Y3 D% N% J    if(USB->ISTR & USB_ISTR_ERR)
( T% Y% Y) y+ S$ b3 p. |9 o! y    {" w1 Y' u5 P% s& N7 s: w
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
- m$ W$ ?; r- P/ Z& W5 _    }7 P- }7 h- O5 N: q  r- u( C& }
    if(USB->ISTR & USB_ISTR_WKUP)
8 B0 o# f7 v1 u( o    {6 F- b; o# l! F3 o$ B3 z
        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
4 u" i. Q; \) o# |. M5 E8 E        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear4 D7 t) W( w, Z' ?  v
    }
# s2 `5 _1 i0 N    if(USB->ISTR & USB_ISTR_SUSP)
/ ?! N1 R$ R/ r3 V3 S0 L# f    {
2 u3 x: E* [( }3 {% D        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
+ ^4 d5 Z- w. S* n& j6 o6 [; y        USB->CNTR |= USB_CNTR_LPMODE;   // low power# [0 l! S  {# o0 p& s) [- n% U
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear$ y/ S  g) j. h. N& \
    }0 Y( X1 C8 ~; }* D1 g, h
    if(USB->ISTR & USB_ISTR_RESET)
7 P; O$ ?$ p9 u    {
' M+ y! @5 r8 X- V$ A        USB->BTABLE = 0;    // buffer table at bottom of PMA. a" }0 \+ w& [, I8 O
        USB_PMA[0]=128; //ADDR0_TX# N, C, E" ~% Z, d3 e2 s
        USB_PMA[1]=0;   //COUNT0_TX
. o8 k- p/ ?% O" @. n* y# l        USB_PMA[2]=256; //ADDR0_RX
# w% Z+ q  c! c: e4 }1 d0 `7 T6 n; h6 f        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
  a' p; V5 c* L        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;' _: Q  ^9 k/ F2 k9 O; g# f
        ep0_state=0;
# Y) u  [) p' }: W2 |$ c        USB_PMA[4]=384; //ADDR1_TX* M) m% Q' s; i- G5 _$ a% v
        USB_PMA[5]=0;   //COUNT1_TX. d/ L3 Q0 |- e; t9 d/ L1 o
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
  @6 v# m8 X: v6 y$ K7 Q3 B# @        ep1_wait=0;
9 H6 l4 I. ^' _* g5 l: U        USB->DADDR = USB_DADDR_EF;      // enable function
# T/ {" s* ^# d: h3 z( F8 G        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
4 e! ~# u1 \& v( }' ?/ ^- v4 w    }
+ U' m% A( l4 x4 W1 Z    if(USB->ISTR & USB_ISTR_SOF)
6 Y' f& M+ p6 n" a0 V: b) l; a    {& L, z' n( a2 e
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear
) c& M; Z7 {8 `% ?5 O    }
: L1 H% j# C& H0 A8 [; V}' r8 z" u2 l9 y' H2 ~2 w& h

8 C: W) ]; b! t- Y/ G
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。; v. s4 ]+ k0 u. d8 _9 d6 r

& N; F7 Q9 b% c主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
, r* L: U8 h! w$ ~4 ^3 _% X! ]0 \* M. u: p  h
    while(1)" u- S  v6 g& U: V0 m
    {
% e, I9 f& K: j' `- M0 p0 b7 o        static char row=0;
6 I* W6 f) P6 @        __WFI();% ^# w9 I, C% o( e$ P/ q9 j. j
        if(ep0_state & 0x80)    // request data processing, W5 B( s$ E. R) c; B
        {
  H& D9 P8 K2 I# i) R6 e  N            if(ep0_state==0x80) // SETUP phase- f0 T$ }: m0 D% E) z" O: Q8 i* ?
            {
. v+ }3 q1 l1 j# @0 Q8 `                if(!setup_packet_service())) T% V6 a$ F. r
                {
* C/ r4 Y  M9 l: k  I' x; r                    ep0_state=0;, x' ^4 |6 \0 a( z9 T. O" L3 y
                    // not supported9 U$ I, @6 B) |2 T4 L$ G& ^
                }
* P  ~: a; G* b* l( s                // ep0_state should be set to 1 or 2, if processed. [$ B5 y5 _6 O: X
            }* x! e: F0 L, a5 k: I! R
            else    // OUT phase, n/ x8 H" w3 ^
            {
0 t$ i  o' V0 b% e: v; R( ]                // process data
3 }4 d: B; x3 U0 M6 q                show_LED(*(uint8_t *)(USB_PMA+128));6 i& X) z# i- Z
                ep0_state=4;
: \3 f& m- F1 k2 V$ o" F5 [9 q                USB_PMA[1]=0;       // Zero length DATA0
: ]$ L6 ]6 |( e0 O                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;$ W$ `1 h# b1 S7 z7 e: s7 J
            }5 Q# C6 W9 ^. l) V0 ^" A
        }" m$ l5 N: C5 ?6 R
        else
2 k/ V1 @( P6 a0 K& ^$ W" P        {
( R! U1 l, \$ U6 a% W1 O; P            if(usb_address && ep0_state==0)( X7 f3 N; D, A/ ]' M  D( R; f
            {4 T3 w9 {: n, }, e! u: M1 W
                USB->DADDR = USB_DADDR_EF|usb_address;. o$ |1 _# m5 l+ B& e3 I; _4 D
                usb_address=0;# Q& g4 @- Y0 o# m9 N2 r* X
            }& ?5 }' M# d. Y. I6 T4 S+ a. K
        }( K8 s: _1 _' i# A
        if(row!=scan_row)   // new scan line# o5 r8 n. m9 E
        {
) s! k& a; c& ?3 P* p& t/ b& z            if(key_state[row]!=prev_key_state[row])
; A4 ^4 |! B: t. {4 I0 v            {& G0 I; [+ \7 i/ J& Q" ^
                uint8_t test=0x80;* F  p1 k7 E4 l
                uint8_t diff=key_state[row]^prev_key_state[row];
% \$ B7 B. }/ C% q                for(i=0;i<8;i++)3 U, u7 X  ?! @0 V
                {
+ ?, S( I2 L$ n; `: R$ Q                    if(diff & test), p/ }5 m7 t$ Z3 t# q
                        update_key_matrix(row,i,key_state[row]&test);( S) P- o3 @6 r7 p. s! N  m
                    test>>=1;
1 P- a8 o& L9 U& ]( w# l                }
$ |% A2 Y& @9 z: R, x3 |. ?            }  L8 Y2 J+ X+ T. O9 h
            row=scan_row;
; R( B% l' K1 r% R3 t: t        }) Y% T) d9 Y: i
    }
/ B8 N6 G) `0 S* \  Z8 K) A* L1 D* T7 B# }3 D. X
2 x9 n) `0 v% C9 f/ ^
EP0的控制传输,把用到的请求处理一下
& W& |' R4 F! c. V; echar setup_packet_service(void)! M/ P& B! W: N
{: V; ~0 K9 i& ^. o. y. M4 ^
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific. A. g) [" [! r% ?; I; {
    {
, E8 P* V3 m" A4 D6 m' Q        switch(ep0_std_req->bRequest)' g# \& ^5 e8 k! I
        {) A8 ^9 }9 U4 Y( g3 z( k- d2 g
            case REQ_GET_REPORT: break;
, e) G5 P0 H* Z/ h7 A3 K  s2 O            case REQ_GET_IDLE:
2 f( p% N; a0 T; y. M" H. s- @                USB_PMA[64]=0xfa;   // return 1 byte1 g/ Y( ^$ d% x! c6 s
                USB_PMA[1]=1;
  O* q; ?0 U' q                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
" S) }& E% n  Y5 [! e; U                ep0_state=1;
3 B$ |7 _2 ?: i! ^% `, X" \                return 1;
9 l: u  S3 V4 z4 p                break;4 F0 \9 c" b8 O2 T* p9 H' ~6 ~% ]
            case REQ_SET_REPORT:7 x7 u9 r5 C' @2 F! x& @: U
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
& K4 ]/ R0 W: F5 F) x                ep0_state=2;
* S9 z( W/ j: @4 ]7 K- l2 S                return 1;
1 o6 G$ U% L# I. W2 o                break;
6 E5 X, D6 `  r$ V* E: N( H5 {9 h3 u            case REQ_SET_IDLE:! g+ m0 d% o; X5 e$ f+ ]
                USB_PMA[1]=0;   // Zero DATA1 \# Q! r: A* X$ w8 I' P/ F
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
( A, ?" k1 G$ b8 v                ep0_state=4;1 X( i1 f# \# |7 Z/ f/ b% E
                return 1;. Y4 ?, Y2 Y, Y$ |* q( N% t: p" a
                break;
0 G8 D# [/ J, D7 b* K. g+ `8 B        }
% L! w9 G& Y2 M2 ~; K; p8 ~8 M- D        return 0;& m$ v- _7 W8 F8 K, _5 d
    }
5 n! T8 d) Z' L" r0 c( C    else    // standard: `3 O& B7 i  b; s5 Z
    {
1 Z/ Q, u/ h1 k, g/ m        switch(ep0_std_req->bRequest)- L' t3 s5 ?7 H: x
        {
6 j* }* i% R/ [            case REQ_GET_DESCRIPTOR:
* V3 ]" [6 X/ s, A$ `  B1 l8 y                return descriptor_service();$ b8 t  H+ Z- v$ K
                break;
' `) A1 r, Y$ W" L. @% z4 J            case REQ_SET_ADDRESS:2 G- l. ]. U3 L9 k: u; l
                if(ep0_std_req->bmRequestType!=0x00)
) I5 h& [" m/ l) T' R                    return 0;( ]# Q) \' p7 H$ T
                usb_address=ep0_std_req->wValue;
6 ?1 o+ f/ [5 Z: {: ~                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;/ W# r: ?0 s, j* Z" t
                USB_PMA[1]=0;       // Zero length DATA0
: N( B$ y% J8 e                ep0_state=4;    // No Data phase
- J. x; m; U$ M* ~, m! \7 j! y- V                return 1;
) D  w$ e* T8 l9 _            case REQ_SET_CONFIGURATION:/ H+ ^4 J. \7 V) S2 j+ ?6 V
                if(ep0_std_req->bmRequestType!=0x00)1 n4 G0 v& a5 ?
                    return 0;: }  K0 }/ v8 K* W9 l6 o3 U3 A
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;+ A- k  z/ \3 |; |9 _% q' `6 r
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
2 }2 R: m% k* i                USB_PMA[1]=0;   // Zero DATA* ]' {  [7 ^) u5 S
                ep0_state=4;    // No DATA phase
8 ^! t" M! u! h6 u& B; s" F                return 1;- m' {" B: L8 A6 z9 w2 F
            default: return 0;' ^* N# ^1 e9 g6 g. \4 d4 a
        }
! ]3 |# p; I$ M2 }+ w3 p0 ~. F    }
" w: u2 }. C2 T6 U  x: s}
" x9 ^9 _$ I+ H$ p9 R& w" T$ i" h
( o; I" m7 i* _
$ K& h8 I! O' a" S  `
. g& r- f/ D2 v& A1 {
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
9 B  w) w/ u8 S8 m' x9 @3 B* ?char descriptor_service(void)
7 E0 f9 W5 Y% W( |" W0 R/ H{4 Z7 X) Q. r, {7 w7 s6 y! ~
    switch((ep0_std_req->wValue)>>8)
, c+ i7 M# j4 a- A    {, t8 }/ R/ @/ h3 w1 N
        case DESC_TYPE_DEVICE:5 O$ V6 B; p8 x+ Q+ R1 E6 V9 U
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));
+ V9 f- l' p* M7 I' ]5 J# x' d' X            break;  i$ T( t, d5 E* q
        case DESC_TYPE_CONFIG:
) M8 W: j7 ~, X7 @" C* K            if(sizeof(ConfigDescData)>ep0_std_req->wLength), F' n, Y4 Y/ n; ]3 N" f( m% r
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
7 F0 `2 k% c6 J7 e  ^            else
; l: t9 c4 y; \& @3 G" Y' @                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
- ]8 a- w$ K1 l( Y( m' s            break;
( `" i2 W! G4 a/ h9 ?        case DESC_TYPE_STRING:
4 l# J# \  W/ W6 A& n2 J            switch(ep0_std_req->wValue &0xff)  u" |; W" a1 }) M
            {
$ K) M$ n, [- n3 A1 k  R                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));4 ]( F% v8 ^9 m3 t, R2 f# D
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));, f: D9 T* g- L# u" x
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));$ X8 Q, h2 i7 Y. U8 b0 [
                default: return 0;
# |4 U! O" P& Q            }% G% I3 H4 D0 x4 ]8 B0 t
            break;( O: x& m% b6 v1 D' }
        case REPORT_DESC_TYPE:+ w( Z3 s/ r1 J4 x% p' k; _
            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));# q6 y2 B5 C6 |2 s( i8 K
        default:
% y) y4 I" g( k1 a            return 0;) M: k  O! g6 _/ Q
    }
. s0 F+ o: z- d. U/ I7 a* M}2 t6 W& V$ ?2 P4 ?# ?0 L
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
$ D% ]( C& X+ J6 M) \, A5 cvoid TIM6_DAC_IRQHandler(void)1 X6 k+ u) m3 g0 I9 s
{
: J8 M% ^/ {. s  U3 Y) A    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);. `$ o' N+ ^, [; `
6 w, S! K8 E( W+ v! I
- W1 q( V2 y* E# `0 }& P0 _
    TIM6->SR &= ~TIM_SR_UIF;: `3 O7 N$ h2 H3 K3 k1 Y* K$ q
    prev_key_state[scan_row]=key_state[scan_row];
! A9 ?6 w6 S8 ~3 K( _    key_state[scan_row]= *PA_IDR;   // update key states
% r: L2 z( E& W+ ^- s  J    switch(scan_row)
2 ~3 k. d& c6 t    {
! C6 X: m' o0 \* N: g- C        case 13: // next row PB14
7 x6 o; K3 S( E8 _0 G                GPIOC->MODER = GPIOC_DEFAULT;
0 i6 f- ?: B9 ^2 Q+ ]                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
( o* Z9 d* m4 Y& Y( y5 B                break;
! \5 |' U; F) g4 e" O6 y0 T9 Z        case  0: // next row PB15, u. x0 h4 N2 _2 }- R4 z
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;
& n! P. _7 s, c6 y% f8 e% e: r3 r2 u                break;( ~' R0 o$ Y/ M( H( X
        case  1: // next row PB32 O( i9 q+ o  |7 m# g4 V
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;3 j9 C5 m2 i+ R# Z9 i& \% P( ]
                break;; K0 r4 r5 k# l6 m3 U/ d1 F
        case  2: // next row PB4
) }  p, M! O: M6 `8 j: c% j                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
8 D- H3 V9 v0 u6 p; c                break;
# U1 b( a7 X8 P. G$ D        case  3: // next row PB5
; h! e7 i% K4 s0 j+ \# E+ t9 u                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;
5 {* e8 X$ {% {/ \8 P/ c                break;
( |2 z* Q/ o" i        case  4: // next row PB6: b/ q, q1 f, R! @7 P
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
9 y1 n5 F% g1 u/ a' d  ^( I' n$ L: v                break;9 |8 m: D" W& P' ]$ f- ?
        case  5: // next row PB71 f, p/ e# b+ J9 a3 |" V
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;) w% J* A& H$ P2 G" P
                break;
1 L; i& N+ q7 \" E1 @0 T        case  6: // next row PB8
* d: l) d) E. q! w, D. C$ X                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;8 |6 k4 |1 W" r
                break;
! b7 g; f2 T1 G0 T4 x9 D4 w        case  7: // next row PB9* s4 d* S; I% f" r) j
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;7 r$ y# a- `1 f8 L2 `
                break;1 l) _! A: \, U& e4 k4 V  Y/ a2 J
        case  8: // next row PA8% |% f! s1 u0 `+ c8 x9 z& I- Y6 W
                GPIOB->MODER = GPIOB_DEFAULT;8 P# [! e8 x+ S; _4 K. K# V
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;( H4 J" ^6 G4 Z4 e/ x; P
                break;
1 _+ l$ N2 B  n! e4 y: D        case  9: // next row PA9
* X; P$ i! w* x                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
3 z+ G- [$ N+ T4 M7 a                break;  j; M; R5 X* t/ n2 {( ?
        case 10: // next row PA10$ a4 [% t- h  f$ J0 z4 k5 F* N
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
0 r+ d6 _3 w5 J! A1 D, r                break;/ V1 `1 K% s1 V; M! ~' F7 l
        case 11: // next row PA15
# k+ \% ~; B: @" P                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
/ s$ c, A" R7 k8 o; k: H                break;& j0 Z! e  g' z' p
        case 12: // next row PC13
$ z1 T% X0 v0 s: w1 u4 p) z                GPIOA->MODER = GPIOA_DEFAULT;
4 h1 b2 q+ t& x/ Y                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
- o2 m: g1 S- i: m) U$ k( y: ?% H0 s                break;
3 Z( q8 ~$ A% h+ z! R2 F% H    }
: V' [% K5 S0 E* ~9 i1 V    if(scan_row<13)  l; b$ m: I" Q, |' k* D
        scan_row++;6 B; H- `; N7 N3 R# e+ J
    else
0 {/ f8 o7 {1 S9 n8 a  F        scan_row=0;7 U; a" _/ D) r- g
}) I3 N2 n& G3 Z; k: }
. Z4 o2 [  j& n4 ]( Q6 p7 _
+ ], M. e" a/ C9 ^$ B! `
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
1 M2 E+ ^' @/ u3 n( w% t' A
/ G0 v0 x! N4 j- H/ [' j. q扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。/ v5 `2 ~' T4 ?( i2 L
, P& |0 M' K8 R* j: Q
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:

& s! ^/ H, c3 r6 J: ~
* V  R( K3 ~7 `' N7 ]% o$ [/ z! j* R$ Y9 x$ N
const char hid_keymap_qwerty[14][8]={
" m4 k5 _4 Z% b* U$ V! [  e    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
4 U. ?7 z9 s! j9 _( M    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},& w0 o7 z; I* d# N7 y( j0 r' R- t
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},4 h2 y0 c: b) G- ]# E
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
# ^& w3 q' m+ A' g' ?/ o( c1 k$ m    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
7 r+ o  N) G  h5 e# Y    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},4 ]# r  V5 w8 ~+ W. z
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},+ _6 p2 y  T+ c) F- Z
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
' ], t7 e/ H4 \2 b' y  b    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
1 K' y" V0 M0 |$ ]" t" r  Q    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
$ o( `/ B' x" o$ s    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},& Y6 g- u' o8 B1 H* t$ b+ H! a
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},3 J% M/ ^: D  S/ g6 B  f) R
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},; N. s& z+ j3 j: M4 s
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
7 q# Z0 E% }$ G* Y# Z. [' R};
% v: d; x1 _! G- Y  ~2 x- j! w% [; @: m: M0 _0 |' {! O4 w1 |# q

# h) q' w+ h% @5 J/ tconst char hid_keymap_dvorak[14][8]={/ Z2 z. D, k  i4 k
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
$ Z; q5 z  k2 i+ q  v* |    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},/ m2 _3 W/ |8 f4 ]) v( O
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
: d! J" o  a: ]: l    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
0 O7 @" {& l% A' s! v    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},( a& H" t" i* o6 h" R% k) O; _8 I. G
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
2 E, B) l( _2 g) j    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},6 i9 m  R7 K7 h4 n
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
  p. X6 C% U1 _; Y$ Z5 W    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
. H9 T9 h+ K0 h9 G6 f    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
, N* S! P+ p6 @- N$ V5 l    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
6 `3 [6 }' h) @7 C6 E$ |    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
) t7 D" Z$ U5 x" Z  _) L    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},1 E2 m) |' b* K$ Q6 v
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
* B& ?9 ?% f" u% H, b  ~0 a" h};+ v; }5 P) W0 P1 p9 e- |3 N& B& t
0 W3 b/ p0 S, M9 g( J3 Z
2 g( Q$ z( {0 S6 a: ~2 Z. V
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。& v+ w' I. B( `/ @& n; A* U2 ], @3 B8 y

3 \. L3 K$ M1 I4 b7 XHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

9 c9 m" N$ S# O: |0 w: S; K' q# @
# U3 |* ?& R4 r9 N7 O. l1 b  ]1 S" z$ gvoid update_key_matrix(char row, char col, char onoff)
/ T1 X' O; J$ d& L: {{- ?/ ?* f' }" i9 ?5 o& {2 Z
    static uint16_t hid_report[4]={0,0,0,0};/ q+ `! M  c& ^! x" ?
    static char (*hid_keymap)[8]=hid_keymap_dvorak;
0 {8 H- N9 k3 P$ C- }2 ?4 |$ m: R$ _* ^

; B/ S/ V* d  z9 \$ {, N. I    unsigned char key=hid_keymap[row][col];8 e; h8 K6 V, L6 R
    unsigned char *report =(unsigned char *)hid_report;! U  j5 U+ r' v8 o' V- C
    char i;
; c1 H; ^8 Z1 V6 E( f$ }4 C+ a# ^" h
2 u  ^& F7 O# i5 a
    if(key==HK_MODE)' e) D# b* n5 o- e/ E5 w
    {: d0 ?* a/ Q% M6 _" P7 w
        if(!onoff)
; o) H! |9 O" ?7 \3 R        {" {1 `8 O" u7 s# i3 M
            if(hid_keymap==hid_keymap_dvorak)% S6 a! h% T" D2 B/ }- Y$ @9 J5 K0 O
            {% O( @' A! ?7 T. H2 C
                hid_keymap=hid_keymap_qwerty;$ J2 T$ O, S: [
                GPIOB->BSRR = (1<<2);
6 H% d+ ^+ @! [5 r5 y  W. C            }. {6 e( Y6 L; \# }: E9 m2 z- N2 r
            else
& |* {& f& D$ P2 w: V. j! G0 H            {
3 U3 z/ E) T% z4 b: U# P                hid_keymap=hid_keymap_dvorak;
" l# |6 o1 k0 s$ r                GPIOB->BRR = (1<<2);! k0 N/ C0 S0 n: q+ h  x$ ~- t
            }8 j+ f$ c- p5 }2 V/ ?4 {# B
        }
+ a# X$ n$ f2 q. r% H* l        return;3 A- Y% J$ e* u
    }
1 Q6 `# e+ H: {; Z+ Z: f! A; l6 _
% b9 ]$ _6 o& z8 {/ R
! W4 k+ a5 ^4 Y9 i' }
    if(key>=0x80)   // Alt, Ctrl, Shift
! o& K$ m2 i5 P# Z. m    {8 ?, G1 t& A, Z( ~$ P' g# V
        uint8_t bitset = 1<<(key&7);
" P. c" L) i& ^        if(onoff)   // non-zero is key up
) L- a8 i* L2 ?8 Y' e4 ^  F! j( _0 W            report[0] &= (~bitset);% ~( p; ~2 E+ Q8 g" q- F7 q
        else
+ W  o( i6 ?# L( A: v, ?            report[0] |= bitset;
' S8 i" G2 W0 D' x" E    }
. P4 U& E& X+ v: R( O, Q6 k- {8 p& [( J    else6 C9 g+ Y; o# p$ i8 L0 }
    {, H2 [0 j" e4 R7 P, D, }) X
        if(onoff)   // non-zero is key up$ e+ u4 y/ I2 D, B4 s- k
        {
8 e- X9 g4 g; U; b' ]            for(i=2;i<8;i++)1 M7 @& W9 U3 J2 a+ I8 x! h
            {
' H8 b* L; o9 k$ d  q, z3 i                if(report==key)- c' b! N; s% Z) r  ^& j( b! h4 t9 g
                {+ J' g- Q$ G( d7 V+ \3 v
                    report=0;; j  M4 y+ y% u0 S) B
                    break;0 g# V. A- W6 h( N+ }2 T8 k- @# g: ?
                }9 x8 ]3 a! j7 H' @
            }
  u  T' q7 y4 q! L- |5 J- F2 Z        }
3 Q6 O6 X- O% u' T8 r) J8 M. J& _        else. i- z/ D' J: @7 p  j+ V
        {
! |/ z6 Z3 F. }% c* j            for(i=2;i<8;i++)
3 N. n; v1 c3 a3 s, P. e            {
. I! {! V4 `" O                if(report==key)" [9 h% I, w1 Z  G
                    break;, h9 S2 F8 \* h3 [7 h
                if(report==0)1 m" t" \3 ]8 c) U3 D* H
                {
6 J0 W6 X2 G! c                    report=key;% H# G& W9 s3 L
                    break;
% h6 x. l+ ~* V! ]$ R                }0 I9 ]" e2 }) k5 f% m% d
            }
4 ^9 \7 S5 Q, F! u        }
  @) e% T; |. j7 U$ r! z: r    }8 b9 Y5 t2 y6 }6 O  @
    for(i=0;i<4;i++)
# d( u; I5 C) o6 v8 S        USB_PMA[192+i]=hid_report;
$ H8 L7 W  M4 V2 d6 D: X    USB_PMA[5]=8;   //COUNT1_TX
0 y% Q% n7 P& B1 M1 y" a! g    if(ep1_wait==0)
7 z% t' }0 w" o    {( M. Q7 L4 s( l) }, x
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
4 Y; G8 x1 b" S- ~, ~        ep1_wait=1;. U: A: z' f% k2 P% f
    }
& p: r; F' D* s+ }- j}* G0 W# e7 V/ g- h

: R3 X3 D. w! G1 p: R/ ?2 j
8 G& ~4 p+ A# `# n6 `; ~完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。9 N9 {: a8 b  n/ K) b. d5 Z5 e
keyboard.zip (8.7 KB, 下载次数: 6305)   n" Q" z/ G1 R, j
6 {2 a8 r# P+ [2 D5 j
3 [# R, ^: f5 z% I6 h3 C9 k
3 T; x  f+ T" q3 {" @
4 ?* D9 ^" w1 k" ?) L8 V
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
. W* y  z" G0 n5 n不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:482 I, B9 M8 E- p  _
刚开始我以为要把打字机改造成电脑键盘
4 u" @7 p. l# N) @6 o! q' c' F不过楼主也很厉害!
& W5 Y8 n6 M. v
哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害/ y& Z' g0 c  _) j) I" E$ W
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-9-18 11:09 , Processed in 0.195124 second(s), 28 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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