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

MiniDSO产品技术交流  迷你示波器-袖珍示波器-示波器探头-

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

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

[复制链接]

23

主题

62

帖子

274

积分

vip

Rank: 9Rank: 9Rank: 9

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

/ w3 i* ^+ o5 A- l8 d
/ U/ {+ A, B; B+ T: m
235140i3a36qivqzuvmt5q.jpg.thumb.jpg 7 g3 W* a. i6 ^5 Y& u
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。
8 L5 i1 B% n. T. |; [2 P( m, e 001734klbyoluenuwz4h4b.png.thumb.jpg
8 f  d& G: W# a4 w为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:
7 ~6 c- z3 [9 }' _& w 003625r2agx2f5v922cf2f.png.thumb.jpg
2 L' f/ u/ M/ V其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.
3 e3 c/ E8 |) O3 c" SDvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:

" C* n1 @. B2 i" _ 005836yvs0wvovwsssgd3o.png.thumb.jpg 5 P/ E* _; |3 s6 Q' }$ |
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
. i$ z1 g1 k" I" C3 Z* A& h, Z, s1 g
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
: W* [+ _+ K) b' @5 L
! l7 t' M9 ^9 b到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
: F, U8 c6 k6 h$ D* {- f. Z3 ~( w, v' q
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:

' s8 l# b* |3 c+ C 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg 2 x% M. Q( C9 u, Z) Q7 |' a
----------------------------------------------------  分割线 ----------------------------------------------------------
) B7 {# z6 k5 }6 N8 \1 H" I6 I; t0 g
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
9 R, s9 q" q" X; p' d$ p3 `* F
020011osionbunl4ui44vi.jpg.thumb.jpg
$ V! v) m0 [7 _2 @3 V! {轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
8 Y; D- R" {4 Y 020017j8ycmnv7788bqv52.jpg.thumb.jpg ' f. G% C  @  r. ]' O+ R1 q
特写,80C49
. L! ^2 l7 L, E8 D 021040oujzuvtut6iujtvz.jpg.thumb.jpg $ N+ j4 H: b+ H8 s0 f+ |
LED部分,使用了一片D触发器锁存指示灯状态.
- T3 |6 g* O4 Y4 L9 ~! z! `! I 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg 8 |; j) t# z/ Z
暴力破坏,将80C49拆掉
3 K/ m# v) [9 Q0 @& c+ p 021113e48qq98vyohvhzzh.jpg.thumb.jpg
& c+ L5 [8 z& h% }* u拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
. G+ l* k7 t* a1 e 021125nc9az6dj33rlds2r.jpg.thumb.jpg ( Q( q: l5 k- J# c
焊好元件后的板子,准备替换80C499 ?7 p) o; L9 Z) {$ U1 s
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
8 c" ~! s6 c! @( U9 r5 i用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。
( f1 K1 M% e4 e' R8 K 021104shifhnrqbr3o5nlo.jpg.thumb.jpg 2 K+ O# p' Q& C9 y0 I
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.
* @7 q5 T# C) D+ w% `9 C 022003ym1p9u4ug40280uu.jpg.thumb.jpg : e; P6 S* b8 S& d, j4 z- E
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。
5 |. u) r1 V) ?: w5 h& s 023313kt141q9qajtol7ma.jpg.thumb.jpg
, g4 u+ f# r1 T  O我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
! \4 I3 C  M9 x7 ^ 023322nt7l5xb3ltttkltt.jpg.thumb.jpg
# E5 h' I5 Z8 @( p2 P
主键区键帽就位
8 ~; q' {  k9 @ 023331hin88e8wkrwzwikx.jpg.thumb.jpg
( K' x2 L1 O, o5 m8 |) y& n/ @编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。+ q( R; Y; V. Q6 E
023336wjzlgopugg1jyy79.jpg.thumb.jpg & D3 A( J: N3 {' s+ g- @
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。* H6 W! y( `+ O3 t8 n% z# N5 o
023341sffu4j3g2323h6fl.jpg.thumb.jpg - ~6 o' ^# ]+ ]" ?

& Y. w" o- H* i* o! [6 }* v* |8 L----------------------------------------------------- 分割线 --------------------------------------------------
/ l+ t/ @' d4 }( T$ `+ ?
7 S8 R' T  ]/ R7 D, Z0 ^3 g. @

2 u( |  C/ q  {' d2 v/ V( ~
/ s2 L2 l% n4 Y/ V1 J, J& ?  B9 B5 @( @) X3 _

/ @5 `( _" E" C% F2 X: t1 O1 p

) S! L0 E4 [1 t% r# [! g% r. U$ [8 j$ H, N) g" j- _( e- S7 ]7 Z
0 N2 R9 {1 q: e' O) F( Y

/ Q8 F1 q0 \  a9 F
2 I! A% c* w& Z$ D) E8 Z( G) w  a* F( I0 F6 }$ h
回复

使用道具 举报

23

主题

62

帖子

274

积分

vip

Rank: 9Rank: 9Rank: 9

积分
274
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。- i/ w; S: a2 G$ V( N, U
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:% n2 |, c) C/ V% [- ~! p  q
104025nzibm2rmiomhyirm.png.thumb.jpg
* |8 h7 n( @3 N4 d6 L
5 Z1 M/ a: {6 a' i: P9 m. X4 I
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
( d4 Q( V1 \# _% F/ N9 \ 105004zkrez5houvkkznko.jpg.thumb.jpg
) d8 m6 Q; u) N0 \. `0 B) A

* }% b. X" d! J% M* @3 j: m扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
. t9 B. i+ U4 `
  i9 n# }( _7 M, O  a5 S这是我设计的电路图:  `8 T7 i/ _$ r$ e3 X
110344ej2z2oo2rflo7oe7.png.thumb.jpg
: T8 V( @& x" Z+ `8 X7 [' S
- G- x: c  r1 ]3 h. o
PCB Layout:
. P4 p5 A8 A! e- [* z5 s: E% a" U 110847jjbjvt34vwt3v5bb.png.thumb.jpg
2 n6 P+ O* j9 E) t* g8 t' |$ d

4 O; p2 G# t5 b不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. + a5 W; q# V4 A; \$ \

  W, a; ^# f, K/ Y  C4 s7 g# Q+ S

/ Z  Y, C6 h% f- U, W- E! ^
回复 支持 反对

使用道具 举报

23

主题

62

帖子

274

积分

vip

Rank: 9Rank: 9Rank: 9

积分
274
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 9 m+ \/ F7 p. v1 E5 A

4 |) i# P: M$ F, u. w8 ?软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。/ C. G3 y, V; X  Z* O7 |5 U
( v2 q; C# I$ ~1 {2 T$ W
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:3 R  I8 E0 D5 t, ]3 K. x
113818pmrfsb6z0byt6t06.png.thumb.jpg
  E+ X9 @1 j! K
: `2 j% D; V7 L7 s& |6 n5 z" }
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛). J' v( F4 K- E7 ?
8 D. A" V/ }! N# A& x5 R/ D
USB的中断ISR,bare metal哦6 J5 a( o& _7 ~) H8 P1 o4 p* g
: z4 ]' U" N0 V7 [' |
void USB_IRQHandler(void). V% Q- o% l8 p! n$ N
{
, A) m) A: M5 m/ g% l. V    if(USB->ISTR & USB_ISTR_CTR)
2 P! N1 }( \0 L6 ]    {6 M% `* Q1 U1 C2 L% d4 p% [
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
0 Y: B5 a! j7 @7 Y! s        {
& |! P0 q4 v/ K  y$ J9 |/ j% Y            switch(ep0_state)
; f2 R) r' a9 @, `            {8 N) }4 _, U. R7 ?! H& g: R
                case 0: ep0_state |= 0x80;7 A- K, N2 ]3 f3 r' `& m/ i% o
                        break;( A) H+ l6 T, f  f
                case 1: if(USB->EP0R & USB_EP_CTR_TX)
& k( v9 v9 O+ E  R! S1 k                        {
5 M3 O0 g$ ?8 z$ C                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;1 d- a* G: r7 |1 j
                            ep0_state=3;
/ x& d* Q+ x1 p                            return;. e2 n- x1 J) z  }/ ^5 e! Z5 n% L
                        }: i$ j7 L9 W- ^
                        else% {3 A  v& q  J  `: e
                            ep0_state=0;7 z2 G5 x- K% ?: o+ Q( H
                        break;: U% x6 h3 }$ |6 x% C) C' Y4 y
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet  ^+ W! |. p( b+ Q/ m5 p8 J; v
                            ep0_state |= 0x80;
" o3 O+ L& Y' u( y7 x9 Z                        else& \$ Z( V* a+ Z1 r, a
                            ep0_state=0;
9 U4 K( F8 T7 ~                        break;' b8 }7 o, C  l+ }8 x0 Z( j# j  G
                case 3: ep0_state=0;
! k* w6 j. `  m3 Z                        break;7 F  ~& ?, f; U/ O: {
                case 4: ep0_state=0;
; b) w3 I; N) [                        break;
8 b3 X0 o" f& O                default:ep0_state=0;- u$ O2 s' N6 S
                        break;0 g* H5 r$ T: H/ p$ l# @# q
            }- k$ H3 L5 q; [4 F3 P% k
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
8 _6 A  }/ y5 v9 Y" C            return;
) Z- K8 G( @; g- z& f9 z) l8 t        }
' p0 q5 z: b0 L' s        else    // EP_ID can be 10 ^0 h: R* o9 {4 X+ |
        {
3 h& |, Z; g( q            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag8 w7 R$ ]% M; G* J, F1 I& c$ Y+ L
            ep1_wait=0;  K4 L, @$ |: Z/ z+ s+ X) p
            return;
( Y/ u7 q$ \1 s* L4 @        }
2 }+ [7 o# Q" p( E- J( Z8 [    }
* g; W- X- q6 s/ }  x; d0 p/ y  S    if(USB->ISTR & USB_ISTR_PMAOVR)
, [6 O- u4 h) T' c    {9 i% }1 \7 ]( t2 s, C7 G5 n
        USB->ISTR = ~USB_ISTR_PMAOVR;# s* B3 s# ~6 O% y) J0 i$ e
    }- A( n  j( G. z7 ^" ?! A
    if(USB->ISTR & USB_ISTR_ERR)
7 O9 h  I+ ~3 z( v    {" R# |9 m% N! a& `$ f+ H! u! f+ q7 N
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
: E3 I$ N+ E: ?9 M+ w8 M. H    }  c+ }9 l- `! k0 s( X0 n7 |$ T
    if(USB->ISTR & USB_ISTR_WKUP)! a" f( k1 u2 W* V0 H$ N
    {
: m) Z7 A$ `0 Q5 B  b6 c        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
+ J3 K9 \9 A) ^3 s/ t& D8 b        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
1 Z& h7 b# o- v) K- s3 \    }+ x9 c' O# H6 a0 J
    if(USB->ISTR & USB_ISTR_SUSP)3 v8 c% q8 [. V( l( t" `1 O
    {
. v8 t3 z; Y* _% \1 |! G        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend& M; R* `2 i) X. E, P
        USB->CNTR |= USB_CNTR_LPMODE;   // low power
+ N6 l% X) n" `' I# w8 }        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
: u% H. O( z6 @    }
. J+ _0 _6 M) @& ?    if(USB->ISTR & USB_ISTR_RESET)
- p3 Y; l- j/ o" Z& C9 t0 h    {
; {) N" H4 W& @9 V9 e        USB->BTABLE = 0;    // buffer table at bottom of PMA5 G* j  Z! C$ v6 k2 b
        USB_PMA[0]=128; //ADDR0_TX
' q, K3 t, \- h/ T) b        USB_PMA[1]=0;   //COUNT0_TX
( }' z( p' @( V2 A3 j, N6 l        USB_PMA[2]=256; //ADDR0_RX$ W3 B: y2 |, W" G
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes8 o3 t0 \+ c! B! e
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
# R9 d9 c3 Q7 O* x6 m: U        ep0_state=0;
  k: e4 i! M/ r  e( x  s3 v) d+ {) c        USB_PMA[4]=384; //ADDR1_TX
0 q  s4 j4 O+ W* a6 U' q        USB_PMA[5]=0;   //COUNT1_TX2 O% A2 h" M, S5 y6 @
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type9 r* T$ v. e+ }( c# f0 z
        ep1_wait=0;
; O- D3 v: i7 U( Y; n        USB->DADDR = USB_DADDR_EF;      // enable function
. I3 ]& I( ^) m/ u% V+ v* B        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear5 N* S  f9 {* Y( m! g! j
    }6 {. o6 D0 r' i
    if(USB->ISTR & USB_ISTR_SOF)3 C0 E. v" u/ ?: X; H/ i; \0 O! M
    {
( Z5 _2 b5 s. B! v9 [$ ~        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear, R3 P2 _' w. t1 B
    }: S8 z& X8 D/ r! a
}
: l, g5 c" h. [9 ?4 b3 V1 i6 i- m
$ K8 B' H3 d# V+ ^
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。9 S7 n' Q  {5 B5 w* V7 w" W7 q' q

3 b3 }' [1 J4 k  _; ?主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
$ x* K# @; i' E7 r. P
8 e) i1 r* @2 ]- o
    while(1)
" p4 R: e+ H* C. l5 H* {    {
7 V, j% m: j) S$ k        static char row=0;
% _1 b5 j! _) N% e* U        __WFI();" ]6 r) p8 _& A3 E5 a5 L( M' ^- T7 ^
        if(ep0_state & 0x80)    // request data processing
; {9 E4 V: |) J" J; [& w2 Q* v        {
& l5 M( M$ ~! F! e* Q8 L! f5 p            if(ep0_state==0x80) // SETUP phase
2 R1 R( h$ ^' o; }! O            {' w+ T. @/ [5 _, g
                if(!setup_packet_service())
9 D# ^: r6 V4 M3 j                {: h$ E/ R. v; n" ~. Y
                    ep0_state=0;( i2 t8 K  @" a
                    // not supported
6 q+ F9 E% ?: a# W  @( T                }0 t" l5 ]. w3 S& {% S6 u
                // ep0_state should be set to 1 or 2, if processed1 t% W: l. K; V/ B1 R
            }* e+ r9 k* m0 @: p  i) y
            else    // OUT phase5 [$ w8 F5 w# X, M" U2 t8 Z7 x, A" W8 B
            {
1 o* H7 r( Y$ w/ i) J/ v) c                // process data
9 W: s3 L- `2 A, I8 a                show_LED(*(uint8_t *)(USB_PMA+128));$ N7 I% d$ l2 q1 W8 [
                ep0_state=4;3 h3 A1 Q% ^; _% |# V' v1 w& F
                USB_PMA[1]=0;       // Zero length DATA0
# e  L$ C' |- [( R9 s& ?" a; i                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
: I1 \. y) Q) p            }  `3 G. U: e, X! ]% `4 Z
        }) p6 f' T  g/ |2 n
        else
) P( P, c- q. J  |3 k: p) j$ p        {
6 M; I  f' b# d) B- ~6 m            if(usb_address && ep0_state==0)# H, ~# t! P# N4 @
            {. p! ]) X7 `' U2 W5 _' l/ l8 h+ F* i0 m
                USB->DADDR = USB_DADDR_EF|usb_address;+ z- O5 C3 \6 }& f
                usb_address=0;
0 g# g- A8 l6 }7 w            }
: l- F' G& Q4 T5 B& k        }- Q7 {0 ]# t- R, Y
        if(row!=scan_row)   // new scan line
0 d# o- O4 N: U; M        {# @, R7 u2 H3 j9 V
            if(key_state[row]!=prev_key_state[row])
8 [$ n9 O, s. R8 m4 h" K            {
9 H- a* |, T# W                uint8_t test=0x80;6 h0 h6 n6 S/ ], a6 M; }
                uint8_t diff=key_state[row]^prev_key_state[row];
' ?& N/ L$ [% d1 @                for(i=0;i<8;i++)' [* y6 u# G9 F. f
                {
' [9 O5 A9 [0 ^/ `! h                    if(diff & test)+ f" e: V$ @) |6 D! |
                        update_key_matrix(row,i,key_state[row]&test);* \0 s4 j2 T! M
                    test>>=1;0 t$ h; z0 ?3 [2 ]
                }+ P! Q: c2 {7 S/ q
            }" v6 ^+ H- }3 j% R. x
            row=scan_row;8 ?+ }' P* b( R2 |$ ~# [7 S
        }
1 k, v1 w% n+ t7 _- F  Q  n. C    }
3 ?5 F" E3 ~$ G5 k; Z1 q
! ^$ m) Q! ?4 d7 C7 T# \/ A
, D. N5 w: s1 f( n2 _EP0的控制传输,把用到的请求处理一下
% a+ u  r& O$ h) R* B$ _$ J' Gchar setup_packet_service(void)
  {: R4 @7 o) F' |2 N2 Y: @7 e{5 N2 e/ M/ ~& z: a) o
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
7 {! O3 U4 p, ^% I$ M    {* i  ~( P* Q9 v. c. i
        switch(ep0_std_req->bRequest)
) A2 O, A8 ^3 B6 n( S" l9 R, G        {9 v. V% |& H1 }  ]  S4 M8 [# i
            case REQ_GET_REPORT: break;, G1 c. F2 S: V/ x; G% e
            case REQ_GET_IDLE:4 c, \# y: R7 v
                USB_PMA[64]=0xfa;   // return 1 byte
' d0 s% T: s+ ]7 l# v' e7 }  c                USB_PMA[1]=1;4 S& `( T7 x: B; T* Q
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
/ Y% T" I) Z: K5 |+ s                ep0_state=1;
9 `8 _- w4 _# F7 D5 y( o+ T7 o3 Q                return 1;
6 P8 x+ K6 Y  x5 E' H# t. X. m                break;
5 g7 m' c  s6 G            case REQ_SET_REPORT:/ a: r* J9 i. Y& N  _8 ]5 Q
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;+ f  i/ R; L# C* M7 ]
                ep0_state=2;+ E7 M, p1 R0 V/ T' H* ^3 x5 M4 ?4 a, X
                return 1;
# p& ?6 c8 m. d4 d                break;
0 w) `5 d3 `6 b7 O6 `. x            case REQ_SET_IDLE:
/ D7 a  R9 J- ]+ G6 ^                USB_PMA[1]=0;   // Zero DATA. F( Q1 N* G3 B# i6 n
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
/ F- K6 ^9 `6 S  k                ep0_state=4;
+ \: W0 A- m8 o/ s/ u                return 1;
8 E0 f3 k3 w6 v9 ?9 \, b- W& W                break;7 X. F7 j( H" n- j
        }+ t& [2 x* y" ?
        return 0;
* i* ]7 w+ K  b3 A5 {    }
0 Z" z9 ?: W! X! V9 E    else    // standard2 E% b6 J( C6 K
    {, S6 d& S6 O4 z% _& |
        switch(ep0_std_req->bRequest)
9 a' x7 A9 P; M9 u        {7 Q! t1 O# a5 c6 G$ J
            case REQ_GET_DESCRIPTOR:' q/ |/ h9 b; ?% i* B
                return descriptor_service();& |$ {6 F0 x* v* l
                break;
; {6 M& I5 g2 ^9 ?9 R9 h            case REQ_SET_ADDRESS:- W: z8 s! \* `- h3 e  z
                if(ep0_std_req->bmRequestType!=0x00)
- B% ]+ B7 @" [  U) K' q; G  V3 n                    return 0;% P* J9 m9 A# i2 \, _2 `% N, I
                usb_address=ep0_std_req->wValue;* Q8 T9 m$ n$ h6 ^, @4 ^# T
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
, E5 F- b& {) l% W; H* ?                USB_PMA[1]=0;       // Zero length DATA0
3 _* @( `% Z, U, Y/ }& F6 i                ep0_state=4;    // No Data phase
4 Q6 N. W# B4 Q5 N) _- j# o                return 1;
9 m* O0 t4 k* [3 ?. j1 o$ y0 w            case REQ_SET_CONFIGURATION:
: i# x. r9 N, L8 c) n                if(ep0_std_req->bmRequestType!=0x00)+ w3 s& |. I0 _# f
                    return 0;
4 a6 @3 n1 b$ c9 J- {& K5 Y                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;2 z4 b# u: Q8 K0 F) y/ V) p. o5 B
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
# G+ v. r, W' S. J                USB_PMA[1]=0;   // Zero DATA
/ o" Q: X, H' K2 A                ep0_state=4;    // No DATA phase, N7 x0 a: @. s/ `! z2 q* s
                return 1;
5 a: F& E% y3 c5 J! [6 M            default: return 0;* c' Z( w& k3 L( C  C. @: l1 F
        }
8 ]$ M2 j# S) W. {4 O: p4 d; [    }. @  o0 M4 S! s8 V3 T
}
6 C( ?" @- C5 u. P( _8 a0 j! x+ c. N. r$ i0 }- g5 e
! G& I- j, @: F" M) ~. U
% C1 v3 \+ I9 o5 A! b) d
回复 支持 反对

使用道具 举报

23

主题

62

帖子

274

积分

vip

Rank: 9Rank: 9Rank: 9

积分
274
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的/ ]& y' V4 f+ q. w  L9 W
char descriptor_service(void)
' q( W* q. \' d{
# _# J8 u$ G+ M8 S: X    switch((ep0_std_req->wValue)>>8)4 ^. g* b" L; l7 E) g' R, J
    {6 p2 l% y. I" }2 P
        case DESC_TYPE_DEVICE:2 @9 B8 |( e2 @2 }5 c8 r: h/ c1 l
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));
. [% g) k* t8 ^            break;% t% i1 \8 J, S( i) }/ Q
        case DESC_TYPE_CONFIG:- |5 v' v+ H+ f  S
            if(sizeof(ConfigDescData)>ep0_std_req->wLength)' P7 m, B( _, [; j9 J. m$ a' i
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);  d0 @1 I5 s& o$ n  [7 q: v
            else
" _! t& K5 v9 y4 p- m1 D                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));4 `4 T& A: m' [' a9 M" B) M. }
            break;
( n7 `7 z- t: v6 j; N; u; x# E        case DESC_TYPE_STRING:! Y" `5 Z- C: ?$ k- y5 [, @, H' j
            switch(ep0_std_req->wValue &0xff)
1 n6 U) \- M2 q  z4 s4 E            {4 k. X9 U+ y& u
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));/ t" g( H/ ^5 y# v2 d( N: d
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));8 r: V9 U% v4 h9 [: j5 B) n" ~
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
3 a2 d& k9 W8 A1 n& c3 J                default: return 0;
* ]% O# b, B  X8 F4 K+ p            }- ~, }. L, N& z& [1 g, O
            break;
5 F/ D1 Q/ U7 F  i  A0 B        case REPORT_DESC_TYPE:
! k! M- d! H9 r  n* e! H7 U            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));) e% w" v0 U! I/ s7 t+ b5 B' S
        default:; w: S" |6 G3 b" k) d: o4 o9 }
            return 0;: l/ c- ?+ N( [2 |. F8 r& M8 B
    }
5 z- S/ Z: f% E6 w( k. B}
* z3 I6 z2 ?/ w下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.; L* l7 B; T: ^! \% e- f
void TIM6_DAC_IRQHandler(void)
' ]3 C" T; s# B% ~" t{
7 T2 O9 _  p# \    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);/ r% |: i& z! g, P& W9 H
  Q2 B2 R% r0 N2 y. O% U# q2 W
$ s% W4 v- {6 L: T: `1 ^
    TIM6->SR &= ~TIM_SR_UIF;9 n  E& x) Y- f1 e- @4 P- l1 ^- P
    prev_key_state[scan_row]=key_state[scan_row];# B; r- I9 ?: j; a; l
    key_state[scan_row]= *PA_IDR;   // update key states
1 t. T- g; C6 j/ P% f    switch(scan_row)6 u' f2 K" K9 E' D% V; F9 l
    {+ q( r* N. q0 W" D/ L3 j4 p
        case 13: // next row PB14; w7 d8 C3 ]" {' L. g3 Z
                GPIOC->MODER = GPIOC_DEFAULT;& n; g2 r, }( ^' r, P* U
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;3 _3 ^; |* y  q7 P0 Z
                break;( [  A! j3 L6 p  x! j
        case  0: // next row PB157 b& D- s4 b( ~: {
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;
0 L3 G5 D/ x- T6 g- T+ \# p/ C' l* b                break;
3 x# n" J+ R6 h! R        case  1: // next row PB34 [. ~4 B7 U& b) O: j! G7 C
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
6 J6 V# M% C' i! ]- u. a                break;
5 _$ p3 f& d2 Z4 k3 z        case  2: // next row PB40 i/ ~: e2 o7 J7 @3 d
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
$ f' U; M" ^  U* E* z                break;: P" f" Y8 W5 y: u+ _' q
        case  3: // next row PB5
& {  S" G" I, @! j+ p0 {                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;
9 f% y* R7 h' u# R6 P' Z8 k                break;
( X( }7 L: r7 Z, a7 i+ ~        case  4: // next row PB6
1 I) o2 h4 x+ p7 q                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
4 s* p; j# q( P                break;% h9 f  O" m* K# G9 c$ R% H
        case  5: // next row PB7
) E8 H, J- U8 q, j                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;/ y9 Z' W6 q1 p% q
                break;2 H) j2 ^* y9 `% k( O; V
        case  6: // next row PB8
& S/ O& L2 x( V$ N8 E: n# l                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;& D! o$ ^5 g( _5 V8 _( v, {; K- p6 k
                break;/ m  q) a# B- w) b' d' E  S
        case  7: // next row PB92 Y1 b& W# z9 \3 u3 @4 n/ N
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;
) G) Y# T" v, Q; ~1 `/ X                break;
6 }+ M; P4 D- s  l3 `8 i  _) |        case  8: // next row PA80 v8 g0 @# Z4 H) z2 d* Y
                GPIOB->MODER = GPIOB_DEFAULT;* S  ]' N. `% |' T  M1 a
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;- t  s) b! Q- S" T
                break;
: g9 q9 J, r* l) W6 _) u        case  9: // next row PA9
/ @& O7 W8 [8 o: Y, W+ f: u                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
$ C/ E5 G8 J! R8 M# G  T  Y                break;
: W9 i5 N6 |6 p6 K6 j- K! v6 t" y        case 10: // next row PA10( i$ B& A- s6 O3 D
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;5 z1 i' |4 `: e# ]( G
                break;2 j6 \5 v1 T, s$ M; s. ^) J; U
        case 11: // next row PA15" S; n) G0 z9 P% l. C9 P) z/ S1 a
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;5 M+ i9 K5 y! S: @8 L$ G
                break;
) a6 ?/ x. F# N. E        case 12: // next row PC13
8 m% m- z. Q0 Q* ^1 x$ |                GPIOA->MODER = GPIOA_DEFAULT;
$ |) B6 j" R$ n5 N; Z) J                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
  i1 K% W$ J! X+ x& Q                break;( H. r. _: Q/ q/ n4 J
    }
, ^, M; W) z5 @. L/ V% N$ B    if(scan_row<13)
' Y( q9 x6 v# b/ M  a5 I: H' P        scan_row++;
! p" T" b; _  Y0 h4 Z% H, H    else, N. P& f0 [1 J2 ^4 k* W
        scan_row=0;
4 Q6 r7 v9 W- a}
* I, e) a; j) c& j. B
6 e! ~. R9 c: c; {* ]8 I0 N: C0 p: u, y$ S5 j( s8 K
回复 支持 反对

使用道具 举报

23

主题

62

帖子

274

积分

vip

Rank: 9Rank: 9Rank: 9

积分
274
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 , G" F; l3 ?2 Q/ m& m7 ]

& U, u# P; _6 D3 m  i. ]扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。4 d3 ]6 W$ P1 u/ M0 X. Y
7 g' J; f/ D5 T
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
1 |/ W# b" B4 n" W

& Z. C& t! G' V( x- j7 c# b8 Y( N8 d2 }$ g
const char hid_keymap_qwerty[14][8]={: A1 i2 A& r2 Z; S/ D9 d6 q6 |" w. {
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},; }; e* p( o: _% t6 g" E3 D
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
$ @* ?# W3 u+ ?. G    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
2 p% N$ G3 |' T. w$ a% Q  J    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
& F' @9 t2 H6 h+ a    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},& c; N7 X! ^0 w9 {
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},! }& V+ X) s5 b# _* Y$ \
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
6 G1 n* b  \! p  S: m9 {- w7 V+ Y    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
! ]$ p3 r1 l: w' v: ?0 I. z1 R/ n    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
6 F( O- K- W' i' p  Z    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},$ {6 X- Y4 w8 L! g% \: w. y
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},0 N9 `% ?2 z& Y* z. z
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
7 ^0 t# W7 o& J  t* e    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
* X5 x* l# x; q- P! A3 f8 T' A    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}$ H) ~: ^9 ]% p" J
};
' X$ r6 d- g+ {
" @5 j0 f3 r  n& _8 m# \; ?9 o- ?

! w) p& R: }8 X+ Gconst char hid_keymap_dvorak[14][8]={
( t7 k8 v3 u2 J+ X, f9 g2 ?    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
; H/ B/ `8 l3 R5 F. Y# V    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
2 D& w  Y7 h. P( Q  a  E; c* M1 u    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
- q4 Y, J% a! ~8 T/ |( Z  y6 @    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},1 m# k: o- {& e8 \' u1 L4 A" x
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
( S1 H6 ]4 n% p0 W; L4 y( }8 Y) _2 z    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},$ d8 s# l$ E& y, H5 _) k6 H
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
! P( V6 }4 P) H3 P  k    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
# V& o2 J5 o: U    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},  ]/ |8 o# O3 d! p' T
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},: v# Z. [" h+ P
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
3 l- P3 H& i0 O. S3 B# Q! M    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
( E5 s7 m0 w+ L' Y, ?+ @: B6 M    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
4 ^2 B; N* y, s4 ~    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
' a7 T, i+ u* e6 b0 x};
* J0 `9 t* R! V9 K0 U- C# G7 `* z# \

/ u, M/ J" t% p/ q上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
3 g2 S6 G: {: q5 {: G) D/ R6 m: L, Y9 \
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

4 K! F2 Z1 N: U5 a* G. W4 b
4 ], y0 b/ H3 H. Q$ Uvoid update_key_matrix(char row, char col, char onoff)
2 p, O8 y, F8 b+ Q{! f2 i2 M; b% W; ~4 A
    static uint16_t hid_report[4]={0,0,0,0};- {, v5 j+ K! c* t- G
    static char (*hid_keymap)[8]=hid_keymap_dvorak;
$ v7 t* }, `& u/ q
8 R) r( C. l( O1 X3 j) N# \
4 j; e, P! v8 O' m7 N. J0 [
    unsigned char key=hid_keymap[row][col];
, y# h. t, G* s    unsigned char *report =(unsigned char *)hid_report;
# _, ?0 ^$ k$ a! r    char i;
7 V. y' |9 t. Z7 ?5 y' J0 {6 j6 Z

, [: f0 H$ k/ a8 Z7 X& ^4 Q8 D1 @* Q    if(key==HK_MODE)% j2 E2 F' o  |- O) p( {
    {
4 }2 i" e# l- {  e+ s        if(!onoff)7 Y. l& q( B% B  S6 x
        {6 c5 i3 O; _5 |) R2 U' `
            if(hid_keymap==hid_keymap_dvorak)( q0 n7 r- W7 ?2 H& v+ S- j# M0 M9 g
            {
: j* d# t% j! a4 l5 g* u+ _+ A                hid_keymap=hid_keymap_qwerty;: X/ }1 a9 T( o( t$ A
                GPIOB->BSRR = (1<<2);, L& v3 I- v% z* r
            }
) J) J# x; Y* ^8 I5 n9 X% P4 \) \8 A1 V            else/ @5 L3 E# Z6 L
            {
7 y+ u; j1 j/ Y  r                hid_keymap=hid_keymap_dvorak;
3 J! t: p* ~) G; N3 O8 c- Z                GPIOB->BRR = (1<<2);
% o' V/ K! e! ~' u; p            }
9 }3 A- f# f* ?$ q. ?% n        }
' x9 R) }. s- Z3 _, O4 v) x        return;& p0 `# I. z% T
    }! G  d2 x$ D. \; G
8 G# e7 g, P! f3 W  M: Z/ `
7 z" D6 |/ I5 f9 g
    if(key>=0x80)   // Alt, Ctrl, Shift
+ _  y" i$ b7 @$ x    {) M& u7 h# S2 I& l  U
        uint8_t bitset = 1<<(key&7);; T& U8 m2 x0 [  \
        if(onoff)   // non-zero is key up
0 z7 {' ^; j$ v/ p! D* l) C- Y6 x            report[0] &= (~bitset);+ y# {+ b* }$ `
        else
6 _0 K9 Q/ q( P6 L            report[0] |= bitset;
7 _( Q" [% N  I+ Z+ Y% K    }7 X! \# s2 D9 s4 E" y
    else
! w6 R0 @$ W- F+ ^    {
& D( r) }0 q0 x! N4 ]) @( @! S) i        if(onoff)   // non-zero is key up
, i3 B6 P; d2 [' N        {8 [( T+ Z9 s9 ^' e
            for(i=2;i<8;i++)& h/ n. t8 g& V- i& X
            {. C( L$ X8 t7 n% @4 K$ D; i5 Q
                if(report==key)& I+ y/ ]4 z% D' l+ L4 ^
                {! X8 I" g) R5 n$ b" l" \6 Z
                    report=0;& \2 I6 |% _1 K1 k1 }  S
                    break;8 l3 z" j' H. P% `
                }& [% b( e4 q4 `
            }
* u3 u! [& Z: o" O% Q9 G        }/ v7 l/ V1 I5 ^* j: b
        else& m. E0 u0 ^! ?' N8 ]6 b) E
        {0 e" `( m5 |9 O# D
            for(i=2;i<8;i++)
$ U/ v6 a0 u1 @( f& H" g, R$ }7 \( {% n            {
& e! P" q, _  j; {3 U7 \                if(report==key), A- o3 _5 M7 p% v
                    break;1 v* f  U0 L. h0 _4 ^% j0 {
                if(report==0)7 O9 _, T/ T' H/ n
                {% }9 c5 v& E2 I/ C2 r
                    report=key;
: T, K- O7 z9 Y0 N6 h                    break;
: s8 f! d/ `& Z: h: q& u) b$ Z                }
( m& T! \" r. s            }: E4 f& }5 q- B* u. Q
        }
2 R3 ]3 x0 m1 ]4 A% g) s/ r. B    }
1 |! C1 X% A) G8 t5 u/ E* ?    for(i=0;i<4;i++)6 a0 i# s8 l" A( R
        USB_PMA[192+i]=hid_report;3 M) V8 d+ A" i  f" {
    USB_PMA[5]=8;   //COUNT1_TX; D  U* O* O( Z! w& }+ K
    if(ep1_wait==0)
3 b9 J; `2 T6 d8 l' I0 D! J+ D/ ?4 ~  f    {
% J& C$ ?8 o: L' O% S# ]        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;5 S- ~7 _$ Y, x( S2 t1 I
        ep1_wait=1;' J  d+ P+ W2 V" c- l6 Y& y% _
    }
. ?" q: H$ O- o}
( \( B9 y1 r6 j& {
) K" E$ S9 @( O$ P7 M  r$ T. ~
$ I" @& }9 \% c- m: f完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。$ k# a: b0 V2 ]
keyboard.zip (8.7 KB, 下载次数: 2214)
回复 支持 反对

使用道具 举报

23

主题

62

帖子

274

积分

vip

Rank: 9Rank: 9Rank: 9

积分
274
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。2 Y. ?: j7 @8 i% F8 p8 H  I
回复 支持 反对

使用道具 举报

8

主题

148

帖子

206

积分

中级会员

Rank: 3Rank: 3

积分
206

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
2 j0 k5 V/ C' Q3 z) e不过楼主也很厉害!
回复 支持 反对

使用道具 举报

23

主题

62

帖子

274

积分

vip

Rank: 9Rank: 9Rank: 9

积分
274
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48$ B4 S/ F" k! V& x) Q# J9 K
刚开始我以为要把打字机改造成电脑键盘
+ [' o1 |7 H# V% t5 a; z不过楼主也很厉害!
3 z9 ]. d( o4 I$ l+ b
哈哈  是有点像奥
回复 支持 反对

使用道具 举报

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

本版积分规则

Archiver|手机版|小黑屋|MiniDSO产品技术交流 迷你示波器-袖珍示波器-示波器探头- ( 粤ICP备13039857号 )

GMT+8, 2017-5-23 18:42 , Processed in 0.171697 second(s), 28 queries .

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

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