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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
; Z& Y' N( q4 K- C, ~% |* {; R9 g3 D  q0 r; B( Y
http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D14 X+ B7 I& `# @) {3 u4 K: y& F2 u
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。
; _& K; W6 f7 P
% v& ~7 w3 Z. W8 ]7 f& F0 P7 L在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。

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

  r0 }: c+ W0 u* C! m, o2 ` 005836yvs0wvovwsssgd3o.png.thumb.jpg ) ?0 j9 _* ?0 F# Z. X$ V  S
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。# z9 r' p$ z  F; N5 N( _

6 D. W& S. t1 D: a我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
$ p/ ^4 L8 i, G5 H0 A( r  Y* m9 r2 |6 F9 x) S/ d
到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。3 [2 K7 S6 S8 W8 D
$ B3 k1 k) k$ _: |. O& f
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
+ I2 V  V# L. Y
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg . n" a$ }- j9 J, _5 I
----------------------------------------------------  分割线 ----------------------------------------------------------% S% @$ s6 L* U3 y! }
7 D" J" K) N$ H: O
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

8 t- S2 b; p8 _ 020011osionbunl4ui44vi.jpg.thumb.jpg ( V8 l4 U% I* P9 Z
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。/ s5 w0 f# U8 \5 m& l$ S$ x
020017j8ycmnv7788bqv52.jpg.thumb.jpg $ @  K9 c7 h/ L5 Z( ?1 ^( ^
特写,80C49
; t: M7 k: e( V 021040oujzuvtut6iujtvz.jpg.thumb.jpg , [% {" p. \+ E  V5 I% j( i
LED部分,使用了一片D触发器锁存指示灯状态.
% C" `& E" D4 V. P$ V4 J- C 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
2 C% N$ T$ r. h+ ?+ Z1 x  Z暴力破坏,将80C49拆掉
9 T! J" b/ R: j9 r( j 021113e48qq98vyohvhzzh.jpg.thumb.jpg
3 M; ?$ n, Q2 d- L, l' l拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
5 |, w( o, }+ Y% f6 z! C7 a7 B 021125nc9az6dj33rlds2r.jpg.thumb.jpg 4 M. @. y$ ]# g( Z
焊好元件后的板子,准备替换80C49
' R+ y) r3 f1 {4 l. u. t 021146zmmpmh4pav4o7g6a.jpg.thumb.jpg   S) a5 ]7 V6 I- q
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。
* m. C: P6 r1 Y, n2 I 021104shifhnrqbr3o5nlo.jpg.thumb.jpg
4 @7 S; S( h7 G8 }# [5 z; u5 a这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.9 I8 d  R* G0 q/ n1 F1 r' U
022003ym1p9u4ug40280uu.jpg.thumb.jpg
2 Q0 O: x% R2 q3 n& h6 M开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。8 v! f5 `5 H5 f7 J
023313kt141q9qajtol7ma.jpg.thumb.jpg 7 p1 n1 Y% ]' ]$ B$ c7 e9 e
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
$ z* @7 E1 a/ I* T 023322nt7l5xb3ltttkltt.jpg.thumb.jpg
: p. ^. k& r- [, ?( X" w' H4 c2 n+ V
主键区键帽就位
- E: ^3 I9 W. o6 U' B 023331hin88e8wkrwzwikx.jpg.thumb.jpg & o" P" E/ t7 @% @6 e% ?
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
: ]: b- v8 E; m3 n* ~8 g  K 023336wjzlgopugg1jyy79.jpg.thumb.jpg
: \3 J5 `( K3 r$ Q9 I$ x4 A最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。/ T6 U" r6 Q. w) V: g: ^. C- K( q
023341sffu4j3g2323h6fl.jpg.thumb.jpg ; X: S8 S3 e8 ^* g1 I+ R
8 M3 \4 A: F' m$ X* m
----------------------------------------------------- 分割线 --------------------------------------------------
% {+ u1 |& n) {2 b" ]7 @* ~
7 J- {; D( j' Q
' {) J: W1 V% X/ H4 L, d& e) k

2 M% _3 y8 M/ @5 I! e' R
2 l2 S+ Q  D1 \/ Q2 G! A, [$ C
5 ?" V- v- a" S0 _9 p

. y0 ?7 l1 s$ h& ^
0 f! r& Q' H; ^. ~; X( _% O
8 r& @% S: Z* m* j3 p  ^9 z# }& q* }# K2 W
. K1 I5 G: x5 n

4 ?- O, x1 x- W% G+ A+ q1 e: f. T+ ?
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。/ R0 A( b7 `+ p1 X4 a; ^  C; F
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:2 ^* j& n# A2 \4 f2 G# O& _/ z
104025nzibm2rmiomhyirm.png.thumb.jpg % r0 j- {; ]" g6 q) F
& v6 |, b2 M1 [& \1 r
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)+ |. [- e* g+ s* n" C
105004zkrez5houvkkznko.jpg.thumb.jpg
+ H4 z/ g- C. y' Y' v+ Y& U
# R4 g* z. h, I& P
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。& O: V& E( p4 Z% V: y$ C8 D  I, I* Y
+ n- }: t. E; v2 X
这是我设计的电路图:
) |1 R: v8 T( u6 b& P' a 110344ej2z2oo2rflo7oe7.png.thumb.jpg $ e& {, p: ?1 K& @, {% J

/ @& i( a* d8 e0 R+ FPCB Layout:! N; T% D* r  a) f( m- I
110847jjbjvt34vwt3v5bb.png.thumb.jpg 7 h+ z" U) h0 Q' W& U1 \

1 d. U9 F$ X) W& L! _; P不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. 2 Y& f4 h5 G. a$ }: P
# E4 V5 o( T0 s1 T. i
" M" u, I1 B0 B( T
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑
# V8 _5 r4 x. r# w9 p1 N
( _5 Y0 n+ M1 K+ Y: a软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
$ g5 \' D5 i, ?. `9 ?& o; ~: O# O# C; H4 {& V# R, L& J* K
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:
7 V2 C( u: X: H* Z, `8 t+ u. T 113818pmrfsb6z0byt6t06.png.thumb.jpg 5 l) Z& `0 _1 K# ]$ c. Z
/ j& w" _. n, s2 J% X( @
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)8 h# B4 ?2 U% s2 n5 c0 m$ @, i
, C: J8 x" D5 c% v
USB的中断ISR,bare metal哦
$ @' t, T! B# }% J$ n
6 S2 w, B( C4 H2 k9 d6 n
void USB_IRQHandler(void)
. g5 E$ T7 d) J8 N+ i. B{
1 S. d" @  {+ C( u2 c    if(USB->ISTR & USB_ISTR_CTR)
2 y2 o* N& F8 d; l* W    {+ h* O/ q7 c# v" x  O
        if((USB->ISTR & 0x0f)==0)   // EP_ID==09 W6 z& v/ |! q: k
        {
2 @( K; l! j1 s. v            switch(ep0_state)
# ^& Q$ W% v9 U3 {  h* D7 t/ Q$ R  k# ?            {. X" c$ |) g6 j. `; H8 x
                case 0: ep0_state |= 0x80;: o: n5 h8 z/ y; \; T9 Z* {
                        break;
5 c  L3 S' E* D; g                case 1: if(USB->EP0R & USB_EP_CTR_TX). y( ]4 v% s) `6 d
                        {% }. {6 ?9 L1 I( O4 z& z
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
- W( p4 P$ k; Z                            ep0_state=3;2 w) j$ f. z( }2 E$ [" _
                            return;: m2 Z2 ^% o7 H$ V. S
                        }
6 k. Q0 {9 Z$ b: O5 P1 N3 j! q                        else2 P9 G- Y+ \5 v) A  w) i0 k
                            ep0_state=0;6 A! }, D* t* F: p2 h7 o
                        break;& j$ \% E  ]  A, [# E2 u. o3 @
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
+ z1 P7 l8 G" `- s9 R9 w, Z5 J                            ep0_state |= 0x80;! w0 t, K1 ~" m7 M
                        else* p9 D5 V" t# M/ m
                            ep0_state=0;
1 s! o% r5 ^( B+ L3 g/ F: ~% ~, y                        break;
: A- V. w$ N' H3 H/ Q                case 3: ep0_state=0;3 M2 U3 ^4 b9 D4 @, M" G
                        break;: Y" @+ D* s/ y4 V0 w) O& d
                case 4: ep0_state=0;
2 I+ |* E! G, e# S' f. m0 F! m                        break;5 n3 H5 G+ u- k( @. z
                default:ep0_state=0;
+ F1 P% m: m. h( g3 @/ H                        break;& i6 E* N8 O8 y2 S! D9 n
            }
. O+ s6 S+ W& ^6 ]% O+ r            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag& Q, M/ F7 Q/ ~) q6 S9 T8 A
            return;5 T5 ]: ?0 C* e. K
        }# Q9 S. r0 n8 X/ i) r# e: p
        else    // EP_ID can be 1: w6 h1 i3 ^1 N) ~
        {6 p: N8 G) N5 Q- A8 m
            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
0 y( }5 y$ v: g! J: h            ep1_wait=0;0 A: R+ [3 o) P/ ~6 u0 d! Q. w6 m! y
            return;
  z4 K$ q. j1 D* }3 V4 o0 W        }
4 X% [- a0 O7 r$ {/ @" t    }. m& Z0 w0 S! }, N6 X* i
    if(USB->ISTR & USB_ISTR_PMAOVR)4 J  }- _% T0 Q8 P5 r
    {( S: e- h! M! H
        USB->ISTR = ~USB_ISTR_PMAOVR;
9 c$ F  u% W2 g0 d5 z7 w1 D+ w    }6 ]8 W- b9 R* r% ]9 w; D* f
    if(USB->ISTR & USB_ISTR_ERR)
$ C. p: w* P* }& o& h" \3 l, ?    {
' Z' O" n2 R+ }6 [  w        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
  [3 A7 g; F% f, n    }% g: H: w( c4 l- D* w8 s
    if(USB->ISTR & USB_ISTR_WKUP)
* l# B' w% }  V9 [# K7 D" b    {+ O7 b9 w7 B' T) {
        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend0 X& k; d* K- q6 {3 [
        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear# Q, ~. M- A/ L4 c9 Z
    }6 Y9 U, Q0 Y5 L+ G/ ]0 V7 A
    if(USB->ISTR & USB_ISTR_SUSP)
# b6 Z# U5 d) ~! e. ~+ C    {; g- c# @/ C+ N: x3 @" o0 _  l$ p
        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
1 }( v3 b5 ~) ~4 [2 P5 x        USB->CNTR |= USB_CNTR_LPMODE;   // low power
: e- q2 q4 X& F) ]        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear" {' i0 Q/ B8 K7 s
    }
9 B8 }7 J) U# Q. n    if(USB->ISTR & USB_ISTR_RESET)
! X9 {; G; Y) P0 ?0 O! t2 p4 Y    {
: ~9 g) k6 w$ y: J# k/ u        USB->BTABLE = 0;    // buffer table at bottom of PMA4 T% U$ X% N7 [, n
        USB_PMA[0]=128; //ADDR0_TX( y2 \/ j# B' s1 M. L8 w' F
        USB_PMA[1]=0;   //COUNT0_TX! V2 p" u8 O/ a
        USB_PMA[2]=256; //ADDR0_RX/ m4 I' J; p' E1 A! g
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes) f6 m) L: n- i( j! d# r
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;  h# K# b2 Q+ V$ Y
        ep0_state=0;
  j9 i" }2 T8 J5 y) C        USB_PMA[4]=384; //ADDR1_TX! h/ N3 k" T- B7 F
        USB_PMA[5]=0;   //COUNT1_TX$ g2 ]6 v4 R1 E" Z- S1 y; l
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
3 q3 i, J9 ^, R+ t" \) E: j# v        ep1_wait=0;& j- S- e$ F; ], N5 T3 C
        USB->DADDR = USB_DADDR_EF;      // enable function
6 X8 L# g5 _! G' G        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear9 K( @& o2 A- L# _! y
    }2 z6 @2 |) F6 Z  w! V5 |5 ^. l
    if(USB->ISTR & USB_ISTR_SOF)
  g  t4 G& @7 O1 _7 [    {' d7 l4 y7 J# N1 I! }) m
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear% P7 Z  p& [/ s! H2 k
    }
1 j4 O- S5 I; ?; E& W}( V. M1 f0 \: g/ z  _6 c5 r! W0 R
; `" n( d" w$ Y2 ~: V
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。1 q& P9 @. S. r1 W# G

% ~% E+ H3 `/ X1 G- S4 L7 Y主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
1 e# [6 W) L4 U3 \  K1 P  d+ e3 u0 w7 b% l6 R2 G+ w
    while(1)
$ C0 r7 Y' z3 J. \# P2 A! Y    {
8 L% i. G0 ]3 Y) F$ H" P        static char row=0;1 ?8 y$ \+ d* X! O
        __WFI();3 x# D: k; I+ N5 ]
        if(ep0_state & 0x80)    // request data processing/ e; v$ G1 F7 d- d
        {
6 N; V/ [8 a6 H* U            if(ep0_state==0x80) // SETUP phase
+ W2 s& P( s0 O- v8 P            {
& L& ~: Q- g1 F) U% u                if(!setup_packet_service())* g7 {! b" x( _  A9 U3 X& R" v
                {
$ S" V0 a9 R$ g( v                    ep0_state=0;6 l( r& G% x% `  V3 r, d& ]& z
                    // not supported0 S8 e% y2 K1 U5 o+ [2 s, {
                }, h/ B( c3 B& i
                // ep0_state should be set to 1 or 2, if processed5 f, p1 z: w: ^$ ~
            }; P5 f0 E" i% ?% L  \& \
            else    // OUT phase' `' `+ U" Q. e5 Z2 z) K7 P( f7 h
            {
6 a* H- F! ?2 p                // process data
2 [$ g/ ~9 q- r" R- \  q                show_LED(*(uint8_t *)(USB_PMA+128));
. n( @5 a$ H* R7 |$ o3 Z                ep0_state=4;
5 O  M; f6 Z. F6 E! B                USB_PMA[1]=0;       // Zero length DATA0
/ ~# }' b  C, C- T9 w) s2 J  J" `                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
" b9 {# D% K5 M- S- w% W: k            }7 r8 I9 h* `, G( r+ E6 ]
        }
, m, r3 A6 O/ c% e7 d8 H+ j        else
  P# k% k2 a7 o% h        {
3 I1 Z2 Z4 Q8 \+ f- m            if(usb_address && ep0_state==0)
4 P0 w, K- C5 K# F- D7 ?4 ?            {4 ~! ]( K  @* ]  C% y. ^
                USB->DADDR = USB_DADDR_EF|usb_address;
* N+ w: z3 {0 V4 `6 x                usb_address=0;
5 a* e% s& f9 S* b            }
: @2 ]0 f* Y: Y+ S3 i3 K        }7 f" H& ~% G3 v( T0 {+ p5 r% q
        if(row!=scan_row)   // new scan line
. j8 R/ s0 h/ P. `: U        {9 _7 M) G; h2 l; D
            if(key_state[row]!=prev_key_state[row])
! m) w' q- G" u; {5 y  i" R            {
9 l* ^; [+ H4 C) \# D3 l                uint8_t test=0x80;7 ^. P" i0 v% b" d" }# K% K  j
                uint8_t diff=key_state[row]^prev_key_state[row];
  s: _* W! c* k& Y4 r) E- c4 ?                for(i=0;i<8;i++)( P4 U) P; l2 y2 _7 Y+ m- x9 {
                {7 ^% C/ t+ W0 v: K: Q, d; T
                    if(diff & test)) i; B# {- ]  j6 J! W
                        update_key_matrix(row,i,key_state[row]&test);
7 A, @. S5 t% T$ i5 S                    test>>=1;9 e# a" d. d' O- f3 n6 T
                }; m* g' Q& ^* {' S% \0 o2 F! ]
            }' |+ E; U; r1 Z
            row=scan_row;
, n3 o" l. k' J+ n& ]) s7 @        }
$ s2 D; S* W9 {! L, q4 W    }" X! m- J) Y& q* G2 B9 R$ o9 d

. N& S4 D2 z' t& t  k2 E1 A
1 h" R2 K: K" ]: NEP0的控制传输,把用到的请求处理一下
! D4 \' @; e% Wchar setup_packet_service(void)! {8 l, A8 f0 U+ n' @2 c
{" M) Z1 r* s0 ?- D; a
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
+ }' n2 W( l2 ]8 v2 k9 m, e    {6 n# B  B- \5 ^5 P5 }
        switch(ep0_std_req->bRequest)- Q% M9 ^2 X9 k: ^
        {' _" ^; Z4 e  B  C8 O
            case REQ_GET_REPORT: break;3 s! n: |6 k0 Z* ~8 t
            case REQ_GET_IDLE:
$ K. i, k6 n  `) B- M: r                USB_PMA[64]=0xfa;   // return 1 byte
, Q3 y) U/ }% p( d3 W9 }8 t                USB_PMA[1]=1;
( V- q+ A9 B5 e6 e& N3 C: ]                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;4 s! q/ b+ n6 u  v9 k& r3 T* S
                ep0_state=1;
- e, F0 s  d- ~6 H1 a- [) E  m- ~                return 1;
( }8 Q3 {3 }! m5 a& C0 p! h                break;. ~0 h- J9 H/ H. M
            case REQ_SET_REPORT:2 Q* \$ R$ ?: Q/ |, n1 N3 P( a1 {
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;, m* M. t( x' Z+ D& h
                ep0_state=2;
% S9 ^; v- @5 B) d' l& w5 u                return 1;
! N7 n3 U/ T# b9 _/ J7 m                break;- ~4 f8 p  w$ v) S, u
            case REQ_SET_IDLE:
0 `4 V! V$ {9 m" v- N1 [                USB_PMA[1]=0;   // Zero DATA
) c1 z3 D/ h, R: h) F; D                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;" X( u' o) t6 `3 E4 ~
                ep0_state=4;% ~( E1 F- ^& o0 o- r
                return 1;
  q5 S) s0 l" A3 {+ M8 z$ E) x                break;
  Y+ G: w* `1 X1 `3 g9 h        }  v- p( M( O' w' }$ g
        return 0;7 ^# |) q: n. W' g2 W! y
    }
- {& e, v0 Q) {9 A    else    // standard
: {9 X4 J0 X. p) \' z' T& h) j3 E    {- _. I7 B. j* |5 S! B
        switch(ep0_std_req->bRequest)+ i) w2 X$ L9 I0 K$ i, u8 y
        {# O- F* R  q9 ~' E: U" P$ L1 i
            case REQ_GET_DESCRIPTOR:! b. h8 i- P# L
                return descriptor_service();" O- b- A' G; W2 Z
                break;
; @3 D! g* b, ?  o4 [- c7 p            case REQ_SET_ADDRESS:
/ a* w1 S) T( R5 ]                if(ep0_std_req->bmRequestType!=0x00)" K5 B, I6 |, [1 Z
                    return 0;' q/ T6 Q4 I+ m' E' `, d
                usb_address=ep0_std_req->wValue;
: S5 H" [! S! d9 |8 w                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;" h/ L. _4 E9 O6 h6 d7 V+ K
                USB_PMA[1]=0;       // Zero length DATA0
0 ]8 ~: z- h1 D3 T2 ]; W/ q                ep0_state=4;    // No Data phase# j) T  w+ C+ F5 N4 T+ Q* w
                return 1;
3 g6 u2 j* j& J4 u' a            case REQ_SET_CONFIGURATION:7 {1 z: R. K% C/ d- [  E" x
                if(ep0_std_req->bmRequestType!=0x00)
3 ?7 y  b* D$ Y* @: g; l                    return 0;& R1 Q) E5 |6 V, w% _' k3 n
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
$ i1 D) d8 ~2 d/ W$ n7 w7 J9 w                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;6 H8 n% {% i0 Z
                USB_PMA[1]=0;   // Zero DATA; h3 S& R! u: t$ N: O: f
                ep0_state=4;    // No DATA phase
. H+ G# K9 \, d7 ^& M. }                return 1;- B" t+ v1 C( e1 F3 B
            default: return 0;
" R/ e  Y; _! }* P5 e        }
, D5 D4 h  O& }    }; u3 f( @1 Z+ Q" x# g
}
, j- _/ `% @. O* R8 e  D
: K! k8 F* F% z8 m# H: F9 [9 |6 R1 X/ F' X  i( [
* m" ]1 h" ^+ S/ A6 Y1 p3 q# _$ ?) e
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
8 Y* i% i! F! }/ e8 `/ Kchar descriptor_service(void)+ ]3 N4 z! e* |/ P
{
! l- i* V9 h. g    switch((ep0_std_req->wValue)>>8)
& D, |" V0 z- b2 }: x    {5 E4 V5 s) f; Q" `1 ^+ p' Z; q: I
        case DESC_TYPE_DEVICE:; R& ]' n$ y; q! c7 w0 j4 h
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));
; N' H5 U$ n% y6 O            break;" A9 X$ V* h7 R: D
        case DESC_TYPE_CONFIG:
! u" L: w5 Q! {. C            if(sizeof(ConfigDescData)>ep0_std_req->wLength)
) V1 p. P: p7 K, C3 {+ ?                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
, z$ N, T+ `9 z; q) s            else. q. }" p6 s8 W( r' C! I
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
4 j) X, V% |, ?) n            break;4 a8 }. R+ u, O& u2 H; T' U. t
        case DESC_TYPE_STRING:
: L' r7 g% W( g2 \- f            switch(ep0_std_req->wValue &0xff)
) F4 `2 y# F- C1 ]            {  O4 _' S1 T  N0 ~( ~
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));: @% y) H  C9 p: N
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
6 c$ P5 r3 Q( S* o) i4 k                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
8 f( U% ^& z5 X. |                default: return 0;& W( \! e' ~! p8 s
            }
+ M1 b$ c( t: ^$ F" \3 y            break;' t2 Y' p( v! n. u7 J' ^' v
        case REPORT_DESC_TYPE:0 _  x+ J3 _9 G5 C: [
            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
) W" i' _3 [+ U/ J        default:
' q' ^) v6 _! v4 z, T            return 0;4 X) W; |; T* i) c7 ]" B  a
    }2 }! f6 i9 _1 ?
}* |8 g$ e6 z5 ]+ g8 c
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
1 n* q7 Z5 g8 o9 Gvoid TIM6_DAC_IRQHandler(void)- ~4 J( I1 g' Y- S1 @3 x
{, A+ w' W. `( C. n' U- G/ R" ^+ o- m
    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
' h2 M9 c/ w1 x* h8 r6 `: q4 u' h  ^/ f3 }3 c8 E6 b5 J5 z
3 S8 `; f/ ~2 a* y) F' {
    TIM6->SR &= ~TIM_SR_UIF;
1 \  i" R. G& i- t# p3 Z, W- S    prev_key_state[scan_row]=key_state[scan_row];0 z- b2 Q$ O7 w& z8 |
    key_state[scan_row]= *PA_IDR;   // update key states
2 C# u- O$ a1 Z0 y- \) j* b    switch(scan_row)3 K' I0 P) [: ?9 V, t$ ^
    {! B# e- O) C, D& \  |9 ~0 s" N6 X
        case 13: // next row PB144 @2 I9 b8 l1 M8 o( N! R7 p3 W
                GPIOC->MODER = GPIOC_DEFAULT;; v5 i. D/ @! o  x; V
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
! D7 h$ w, i8 V, b( E                break;
6 y( N  R* `; D. k1 m5 B" ~, S$ _( I        case  0: // next row PB15# C/ `( _# ?3 W8 E# W
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;0 V! G+ l8 H# D7 C. F9 m
                break;! Y& \$ C: X9 i' ~& V0 ]1 a  p
        case  1: // next row PB3; W; ^1 n, w7 h0 N
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;4 T" S$ d) k: I4 O2 `
                break;* \" G3 ]: O% R! N8 s
        case  2: // next row PB4
' G4 [1 C" ?( j                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
1 B- R& V8 z* O                break;# U( s$ Q5 K6 b% t7 V  a
        case  3: // next row PB5
6 y. @9 L, Z3 w# ~                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;8 u1 q2 D+ d% }% [1 [; E
                break;
' W/ c6 l4 W5 }1 q$ M8 x0 L2 z        case  4: // next row PB6+ z, Q. F# x$ B+ p4 @0 P
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
' e' z# ^$ d; t+ d8 M: N7 P3 e4 s* b1 j                break;  ^2 x% [- P* [6 i
        case  5: // next row PB7
: v# `" ^. R( g                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
" C* J% Q9 Q7 \3 j6 L                break;. E5 p6 B8 g# g. `" B
        case  6: // next row PB8: k( A+ Y( D, c: k1 B% K8 d
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;1 j# g3 B. R- A1 v  {' C0 _
                break;3 q! X- `; I$ y7 T
        case  7: // next row PB9
8 T' H* d' G* N  j                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;) A  e: I$ t; ], l9 i: i
                break;/ c4 w: ^) w! X7 W
        case  8: // next row PA8
7 `# E% `; [- d5 B. V4 m                GPIOB->MODER = GPIOB_DEFAULT;* J; H4 }2 @1 E0 ]. V9 ?
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;
  e5 B+ K1 H% E4 @. @& s                break;4 \* Y2 }; z  L% j9 m% C. g  H
        case  9: // next row PA94 @* P' f/ K$ W( x+ k3 i
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
& }$ w. W. C3 `, `0 O* r/ ], \                break;
# M5 c/ C3 p* \, D+ w, N% ^6 Z        case 10: // next row PA10
7 v# U0 w  A% e/ o                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;, q0 X! ?: g' s) E/ B
                break;
: m& t3 g+ o/ X0 K9 B; N        case 11: // next row PA159 r% z' P$ d& @. {( |; T8 M2 a& k  o
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;' D9 l* f+ g& Z) ]& b' u8 _
                break;. Y' W" Q2 O" {! Z
        case 12: // next row PC13
3 L+ ]# P2 i8 f# a* ~$ ]7 N                GPIOA->MODER = GPIOA_DEFAULT;5 E4 z- P% q" F. C
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;" d9 }! N  D; y0 \
                break;" K: N) J/ }% u: c
    }
5 R( D" l& Q$ a( B' }9 W    if(scan_row<13)# a) P2 g1 U. h8 G* v5 A
        scan_row++;
; L  V8 W5 O9 }5 x7 w% O9 a: w    else/ V4 C' f! N7 ?8 a
        scan_row=0;
* E+ g/ J8 ]( P: ?: j}
. N: S4 e* V) C; E2 A, ?+ J* ?
! l; X2 r5 r1 ?+ n0 j2 e, N8 t) p( J, e2 n0 j
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 - R1 u# O4 s) `/ x, D) n$ Z

7 b  V& E% G. A) P7 E7 I3 c扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
9 F' @9 s/ q2 P: L, D3 D( q3 A2 a7 B6 T
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
* _0 S* Y: {8 R: K
) Q8 t  s8 B* A* ]8 I
- y0 l7 W* e" Z% p9 X- L" l
const char hid_keymap_qwerty[14][8]={: ]) n& l" d( Y9 ^& @, k1 x
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},, m2 k' f% X$ L6 _
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
3 E( u) ?9 m6 q* _( h' _    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},9 v- X: x" |7 D0 s) o' g" ^- T
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
& H: n& U' A) [$ C# k/ W/ H) V    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
5 g5 O' D2 u9 t$ b/ a0 f. _    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},7 D6 K6 E* J! ?' y
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
: v9 c+ E0 m4 C4 p" s) n6 `) |, G    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
' x3 X# f/ s( |    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},  d5 t  z2 _- i4 S
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},# B7 b% S2 b) G1 v
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},7 a4 k  y7 p( z- @0 Z( g
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
; b! E$ n3 Q7 s1 Q$ l; A    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},0 O; k! O9 o5 t% m4 y
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
$ o5 K$ C2 G4 W; e, M3 j* P6 W};" s2 {) [% @. E) g
) o6 ]9 C  ]' a8 X  n  z0 t2 D. ^
: ?/ e8 z( {+ F$ V
const char hid_keymap_dvorak[14][8]={
' c7 t' Z6 u5 A  f: D    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},3 n; x9 s! T- p* V2 _
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
  R* M) |* t1 N( L% R: e5 `& d; d) z    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
& U4 E6 k: s: c$ D" l. K1 [# [    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},  T. a8 @- i) ]: Q
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},# B, S0 ~$ |% ]# w: }
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},. Y. l3 x: P) Y# m. A' z9 j
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},) C% ~3 A0 l# d: y7 C! y
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
4 d! U  `! P' ?; {* T$ r7 W    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},0 R% z" y5 j# H( W2 L' z' w
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
* T) h6 [( B) _9 |; _, j    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
  l4 `5 d0 Y) V: W    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},) T2 Z% M* ~* T. W3 x, a) W
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},  i$ P$ K; g- f$ ]0 A
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
3 v5 f# V' L4 v) d0 h0 S3 z3 i};
- a; R& D# N* c, p. i
( {3 q. ?% j* K+ `+ s& q
9 `- V4 c2 r/ z& f! }上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。) }- \2 ?3 o3 t5 ]8 G: a- K
! |6 P+ `: u1 h" g1 Q4 R
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

; }7 m; T6 K" _% w
2 |9 R8 e$ M$ X: B3 [' R( M' Kvoid update_key_matrix(char row, char col, char onoff)
) f2 i, K% G2 |+ {, P( }# J# `" K{
/ M5 H, n! w9 T* H3 ?0 F    static uint16_t hid_report[4]={0,0,0,0};
; j) e) J0 o( t* y2 h$ h    static char (*hid_keymap)[8]=hid_keymap_dvorak;4 k- ?  I+ v3 O3 U1 z* }, d& C

( v) R3 l, b0 t1 M6 F3 F
; j) ?% o7 Z1 z3 A* N/ X6 ^; m
    unsigned char key=hid_keymap[row][col];$ \! B# {+ |8 B" G6 ~
    unsigned char *report =(unsigned char *)hid_report;
3 a- T' l" o* z, F* p  z5 ?8 I    char i;5 e( A( l3 D" A1 T- q6 V1 g. P
  S1 J2 m6 x* M7 e

$ T$ e8 K9 T2 B3 E    if(key==HK_MODE)  `- u! v" {& Y/ G: B0 g
    {
3 E  U) x( a2 V* F0 g5 z$ I! ^        if(!onoff); `' T0 ]0 S( t3 C( Z
        {
" h) E" e/ x6 `. P            if(hid_keymap==hid_keymap_dvorak)  u3 x! r9 Y3 L  W* }  }
            {
6 b5 b5 L# I  m0 |* h% ?$ X8 ?+ D. s                hid_keymap=hid_keymap_qwerty;
, T$ E/ ^; D; T* v                GPIOB->BSRR = (1<<2);
" _( @; e6 a4 C8 m- }- o& i            }/ h" J. P) H  z( K* A* J  O
            else0 G" n( F6 v9 \
            {
4 ]+ r* U5 Y9 p                hid_keymap=hid_keymap_dvorak;2 N7 L" y6 Y7 |* l% `
                GPIOB->BRR = (1<<2);1 v9 g( j5 V; x* h5 v, E7 @% C
            }
' v/ P5 g6 W' ^3 R* Z        }  J) N. _0 H* n3 S$ \
        return;
/ b* b4 H3 F9 c, D/ P! H! M    }8 N% @3 c% L. s# b- A& d

" V$ A9 v5 _( Y% G

" F  S( g, ^+ J  D    if(key>=0x80)   // Alt, Ctrl, Shift& K; d) W7 K2 x. c; v* x9 V& A
    {* W+ F, V. z9 G! ~/ e
        uint8_t bitset = 1<<(key&7);
9 [. q4 }+ \" f1 @: f        if(onoff)   // non-zero is key up6 s# g$ _! V8 n9 G
            report[0] &= (~bitset);" H# _) s& B! J' S4 `
        else
) [# ~) N' {; K) F            report[0] |= bitset;- r- L3 j8 e& g; z8 k" d- I1 t
    }
" C) |% h4 h& j8 o- F2 m+ w: g, N    else! Y% R& N1 O' W5 Z4 E7 t! c
    {
) @! q) |/ H& e  g+ }        if(onoff)   // non-zero is key up) v$ v/ u2 X% d1 A6 C1 Z8 U
        {
* B0 p& i$ w  _& P! l            for(i=2;i<8;i++)9 ~7 t; r; b- \3 D4 I% E9 T
            {( _0 K9 F( {8 e/ G8 i- u
                if(report==key)
; @2 ]9 s! B( E1 g                {9 J* o9 p6 ~& F/ s6 }5 t7 H
                    report=0;
, U, a  t# ~- f% W& D                    break;- X2 |! r3 M7 m0 w
                }
% \; h" j9 J7 b% y# h4 _            }+ a: _1 d; K9 X
        }/ N$ k+ q7 v, |$ ]8 Y! D  H
        else
1 c  v: M( F, n        {2 T1 Q7 Y7 o% q5 B0 V
            for(i=2;i<8;i++)2 Q3 t, B" K$ r9 l( n
            {
. A4 [/ v3 w+ q, \                if(report==key)6 R3 _" Z& J+ b2 m6 ^8 S% I" V
                    break;
# x8 @4 l' Y: h                if(report==0)
7 Y# r+ C+ b8 g0 }. ^0 n                {
0 y- `7 d+ a0 A                    report=key;
, D; \$ j+ m: I  j                    break;
$ f2 {6 r/ n; Z( g$ q                }  f2 R$ p, m& y
            }4 P2 I/ {. e& I
        }
# f/ E. }0 S' \! i    }
( v7 b6 i4 V8 i& `$ _3 t    for(i=0;i<4;i++)0 s% q- p. l& _- ]* R
        USB_PMA[192+i]=hid_report;
! c' Z  ~+ w# u" n0 F" ~    USB_PMA[5]=8;   //COUNT1_TX' i, `4 w# J$ O0 x
    if(ep1_wait==0). B6 P. m$ p9 X5 P& f
    {" l: W7 p1 q: B( X5 s" G
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;, I0 e% m5 D$ T( ^6 p- @' ?
        ep1_wait=1;% _- a4 l; r% }& A1 |$ V
    }5 {+ B5 a; Y" t: r# M5 C
}, W9 q: y& b+ \1 E
: |: X( c! H3 l! c% w' s

/ {) e/ J9 @3 t8 q完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
9 N1 P  k$ n. g! d4 Y keyboard.zip (8.7 KB, 下载次数: 6353)
( c7 @- m" ?' I0 G9 p- f7 s; O$ M
% W3 x& n2 `: H' v$ b
! C3 m' g9 D& m1 ~6 W. z# d  R. m+ n

% [9 t- G8 b1 t) n
0 ~0 O& \6 m7 ?/ _: f# Z5 V
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

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

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
! A: P. h7 x3 ~9 K5 R; ~刚开始我以为要把打字机改造成电脑键盘. _7 k6 C7 N( Y: F4 g& M5 s
不过楼主也很厉害!

" y& g5 O  t. W9 Q- X哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
( P, {& O, r- G9 B. ]( ^3 v
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-11-17 23:12 , Processed in 0.175305 second(s), 27 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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