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

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

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

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

[复制链接]

24

主题

63

帖子

286

积分

vip

Rank: 9Rank: 9Rank: 9

积分
286
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑 0 `: O" s8 U, F" E% K6 B

/ W+ H2 u1 }, V$ i$ Phttp://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
1 X' G* U6 Z7 ~7 U这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。
1 d: t6 P# F; A. s& C3 d
' w; N6 w' P% c5 R, c, T在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。

' b; S9 p6 ~+ M
( A1 Y, K+ O) }3 i: `: V% L% X# B
235140i3a36qivqzuvmt5q.jpg.thumb.jpg ) P; N4 ?" l3 B* U# r
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。
) u0 A/ h3 A: G, M" p# w 001734klbyoluenuwz4h4b.png.thumb.jpg ; j; Y! w+ R4 A
为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:2 B6 r" V7 }1 ]0 p! N  w
003625r2agx2f5v922cf2f.png.thumb.jpg
! Q2 G, h! o, `  y: Z- N) O1 R其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.1 M  U3 m  A, B* X
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
* _( e* a$ d: E" }/ i* M) `
005836yvs0wvovwsssgd3o.png.thumb.jpg
; @2 ?3 [' J) k% Q3 P0 I' s' B! BDvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。- e( n9 W$ Y1 l  l) b7 J) A. }

0 Z7 h' Y0 X/ R4 T$ a我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
2 v- y- G# u! g: H/ E" k' Q
& z' [1 s7 f' V1 E$ g* S& g到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。0 J' W6 ~! `" ^5 A  k  E
: H, K0 R' l' V' @3 I9 m
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
$ V4 q# \/ G' y+ b
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
5 @9 ]( v" T* ]" G/ ]7 V2 @1 d) a----------------------------------------------------  分割线 ----------------------------------------------------------% c! O3 m! ]0 Y& {) q  N' B  M

$ p! l9 |& q" N5 t先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

( o+ N/ G2 j: D# ~- K 020011osionbunl4ui44vi.jpg.thumb.jpg
& q9 ^1 C0 D  F( b0 |! ~$ `; e# t; T  T轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
  D% z' D! V8 L# ]3 {6 l$ g4 j 020017j8ycmnv7788bqv52.jpg.thumb.jpg 2 e! T  @7 q4 F5 w" e1 r7 [
特写,80C49
4 n9 ~+ W3 A0 \1 b+ n& Z 021040oujzuvtut6iujtvz.jpg.thumb.jpg
6 l3 p, [, O+ F7 N' }4 ]# p0 _2 KLED部分,使用了一片D触发器锁存指示灯状态.
5 D0 x! n8 o! ?* n% r1 x" K) m 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
  [7 h4 I3 e7 W  n) r暴力破坏,将80C49拆掉
6 D' l3 r2 D8 q" w. [ 021113e48qq98vyohvhzzh.jpg.thumb.jpg
5 h& d3 J0 @) S' o拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
5 G& D4 W9 l# I 021125nc9az6dj33rlds2r.jpg.thumb.jpg
: y. V6 U) h+ j; r- n# E- y
焊好元件后的板子,准备替换80C49
! e1 ?; k: j5 }+ W, O! S 021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
( u% [( _2 R& ?7 g  ?3 g/ l4 V用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。
' C. q3 Y/ B8 W1 r 021104shifhnrqbr3o5nlo.jpg.thumb.jpg 5 I9 @( x" _" d
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB./ X$ W* f4 F3 \- X0 n2 D! i
022003ym1p9u4ug40280uu.jpg.thumb.jpg
" g! J2 o0 M, g开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。" c0 Y! d% S* z
023313kt141q9qajtol7ma.jpg.thumb.jpg
: d2 M& K9 N4 y0 }! Y. q2 `我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
4 C6 i2 y0 |$ _' k 023322nt7l5xb3ltttkltt.jpg.thumb.jpg
5 {+ ?2 x) h# K- w- z# J
主键区键帽就位: P( |2 m0 T3 N* }# d: i
023331hin88e8wkrwzwikx.jpg.thumb.jpg
. f& i" ]" m  C6 N) E- Q编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。9 W, ]. }0 H2 m3 W/ M
023336wjzlgopugg1jyy79.jpg.thumb.jpg % w$ b; R* @6 L0 @
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。+ \1 q7 `0 G( [2 W4 m. m
023341sffu4j3g2323h6fl.jpg.thumb.jpg
5 z9 O: x( b, ~9 O  T; r. k2 F5 h

2 V( p1 F3 H* K! A4 Y# |----------------------------------------------------- 分割线 --------------------------------------------------) |$ c9 \) t/ ]6 P; _: Y' `" R
1 S9 M7 @6 L7 r9 H
* Y9 D5 o+ S# x, g) V8 `* e6 K

6 T. p7 C+ T6 N' s# e8 @5 ?" h( I8 B/ V+ g# {7 Y

7 L5 M/ q/ ~+ r6 f

' N# ~( `+ o7 C: a+ E
, o, T1 D  `/ A, g& H+ y# D. \3 B$ H8 \# N1 r/ ]5 S$ P

7 P8 x2 y  }. O/ v" z: l( {; S. r) K
) |8 V4 P) v2 I- \8 M/ |4 h6 q' ]  [
回复

使用道具 举报

24

主题

63

帖子

286

积分

vip

Rank: 9Rank: 9Rank: 9

积分
286
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
! x1 x4 m* O' L  t80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
1 P4 F* f" C8 b/ Q$ Y8 \# I 104025nzibm2rmiomhyirm.png.thumb.jpg
- p5 e  L) _( Z) V
+ W) R4 K1 ?2 w$ \' h
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
8 M$ T0 J4 v! b* j! A) Y 105004zkrez5houvkkznko.jpg.thumb.jpg
2 c  I5 J% a8 k; {/ H

( L; s' I9 P% B2 u5 u( t扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。$ x9 `# b5 R6 {6 F" |" R0 H

7 X7 r0 G5 D% F* d1 u% o这是我设计的电路图:
. q* P3 Y9 n! h% s# \7 {% [ 110344ej2z2oo2rflo7oe7.png.thumb.jpg + h3 B6 ~- \, T$ r+ Y

; r; U9 A+ _3 R9 F& @6 a- j) wPCB Layout:4 f8 `3 v: x, G9 P; ]  T
110847jjbjvt34vwt3v5bb.png.thumb.jpg
% Y# A/ c# S/ v1 d) b. u. o

  B, Y  c/ d3 S  ~不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
) ?" C7 C/ K$ ?2 B; J
+ f/ e; s# o( M. X/ ~/ K- m

8 J+ o7 z3 e8 W, A# r; I  Y
回复 支持 反对

使用道具 举报

24

主题

63

帖子

286

积分

vip

Rank: 9Rank: 9Rank: 9

积分
286
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 2 o2 m/ W" G8 L# ^* s) `

& |- x! G: H) Z* P& f软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。* W$ E) e& i7 h- \% P& v8 ~
! q$ N: O0 q0 q% Q4 T0 b
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:
2 b; M* m0 g5 ]( M 113818pmrfsb6z0byt6t06.png.thumb.jpg 4 j1 F" y$ N8 g$ c( [4 c2 {
5 u# K2 Q# u+ i" F! _  j% A! B& O
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)& ?( |+ h) E. C2 O
/ u# T+ C4 [# z" L% U3 Z! M
USB的中断ISR,bare metal哦% S0 K0 q+ |% ?
; O" D* q; \5 w: R. P
void USB_IRQHandler(void)2 r) h3 D- V& H; ?6 R" t& G# J2 T
{% @9 O$ i& j# Y$ h( \
    if(USB->ISTR & USB_ISTR_CTR)
& \) W8 K6 ]. X, M8 V: H. Q    {
# [2 o) ^& z  W. `8 A0 I        if((USB->ISTR & 0x0f)==0)   // EP_ID==08 Q* y- X8 L$ V; ]% m3 D8 l* ?+ G( S
        {
0 x: `+ {! D; P7 c6 Q. {            switch(ep0_state)
. n+ k9 ]2 b* Q  Q. ]# Q8 y* ?            {& x" u7 `+ t% F# l
                case 0: ep0_state |= 0x80;
% h* ]3 P& W9 ?5 `                        break;
( V& Q/ f# x& e+ T5 ]                case 1: if(USB->EP0R & USB_EP_CTR_TX)
; o1 k1 w0 I, K                        {
: |' b: K* q2 i. S4 G; d" ?                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
1 K; [: I: j  A' y                            ep0_state=3;/ m. y% }' V$ m; o2 U( Z
                            return;
8 i$ z: j0 @- {  P4 |                        }
' S7 b' M  u3 W/ I                        else
3 [  M/ G; s. S7 ^8 p                            ep0_state=0;
! g5 C/ Z0 ]' v7 T8 e3 }$ W                        break;
; d1 W! T5 d! H% V- x) M# v                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet  b- P7 s( F# o# N
                            ep0_state |= 0x80;
9 A  {4 Z1 q- a( d                        else& |2 M) w& b, g, G6 v0 _0 z
                            ep0_state=0;0 Q  s, B: b& S5 w+ w% }
                        break;
: {0 u! \. e4 ?                case 3: ep0_state=0;; u6 ^6 q3 L9 _2 [; S- j- j7 F
                        break;
) t( M5 B8 i% h1 K8 S                case 4: ep0_state=0;: P) W# p( a  e
                        break;) Z! H5 F" U0 P: Q$ z
                default:ep0_state=0;
8 P) t9 ?7 `/ P: H                        break;( g3 n# y* \/ Y7 |5 ]
            }6 e1 Z& @' W2 R$ x
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
6 @: U2 }9 N3 }8 A            return;+ F: ^4 k7 P9 ]7 d& x- J+ `! l+ V/ u) v
        }
7 S% L- H, Q/ y+ Z. B        else    // EP_ID can be 1
0 j- |8 W! ], ^6 {        {5 t! e& I9 d7 J$ _
            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
/ z7 t! n" Y& Q6 K3 x# x* e3 B8 P% _            ep1_wait=0;
, y9 ]' ~' {/ q/ g5 V7 [5 l& \            return;
+ y6 Q- q: {  Q        }
) X8 K5 ^7 w# ]% y2 S    }
9 L  Z" c, d- `& F* k  K  A  ]    if(USB->ISTR & USB_ISTR_PMAOVR)$ ~3 f0 ], o: e  O/ [: a  f
    {" s/ x% G1 J3 H2 y0 L' W
        USB->ISTR = ~USB_ISTR_PMAOVR;
! `! P: Z- O4 X* r, F5 k1 ~$ t9 `    }1 b2 O) o! r# X/ r% c# J# u, |
    if(USB->ISTR & USB_ISTR_ERR)9 n5 y- l* n% b  t% W
    {
' J. R& ~3 |3 V$ z        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
& g3 h3 E1 y* r' y4 n    }9 P. p9 h4 ^3 v/ D$ O# X- P4 ^
    if(USB->ISTR & USB_ISTR_WKUP)7 S8 g, P. C: e- {$ ~& u
    {
" z# z; U4 F9 P& r+ S# q7 u! h        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend3 b! X. q% e% s/ Z0 x* r7 g
        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
& j. k. b# e3 o3 Q    }+ Y; O6 ^' ]+ v6 I+ @7 E0 m
    if(USB->ISTR & USB_ISTR_SUSP)
* }% k- y" p7 r4 x; a; X7 e    {$ T: k+ X; N7 M$ v
        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
( U: f; K& ^! c/ r" B3 [& O9 o2 j        USB->CNTR |= USB_CNTR_LPMODE;   // low power
6 \# X# o) D; d+ R7 `( l, L; g+ C        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
- i2 v5 P' S  x/ f& ]* |' m6 [# K# q    }
4 Z: ?8 x2 o1 l0 D/ v- ]    if(USB->ISTR & USB_ISTR_RESET)" j. _. m0 ^  X: B
    {
* ~. u) R4 y% I& p7 Q; C  w* K        USB->BTABLE = 0;    // buffer table at bottom of PMA/ Y+ k' R5 Z( f5 R
        USB_PMA[0]=128; //ADDR0_TX* S  K0 ]' k3 |6 y& i
        USB_PMA[1]=0;   //COUNT0_TX
, ]7 A$ y: f* [- s        USB_PMA[2]=256; //ADDR0_RX
/ }. a0 V. X$ J* ]* e        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes# u/ ~# v- w$ n) v) {
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;# N' _& m* Q; H: a( D# c" l
        ep0_state=0;
( O" g. v' C/ [( h        USB_PMA[4]=384; //ADDR1_TX
" m3 W% ]# Y8 E. }' Q9 }2 O0 B        USB_PMA[5]=0;   //COUNT1_TX
' L% l2 c2 k! p: ?6 A        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
* w) ?8 N, m! E+ O( l        ep1_wait=0;. }. o. r- b0 x) Z" ]- K
        USB->DADDR = USB_DADDR_EF;      // enable function
# \8 h+ L8 A5 [( b8 t        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear* @2 i% Y1 s7 I5 i
    }
4 }, Y  d1 d; X( a    if(USB->ISTR & USB_ISTR_SOF)
4 a4 ?- [5 b! t1 p3 @6 G3 [    {1 @8 w( ~6 v* t) J2 V/ a/ ]
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear
9 J" N( q, c& P" C5 }3 D    }
1 m5 @5 P. x5 Z$ J1 ]}
3 B) t7 U  f1 h# H* ^' m" O3 @
/ B0 l  _  r, z) P6 `1 H) g) Y
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。/ T1 \. ^8 ?1 s/ J7 J

7 O2 O$ w$ `* I% R1 V$ H主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
- O) q! U! j3 k( {6 ]) E: U' o% U3 P- h3 E7 g1 R
    while(1)
8 f+ Y  S, a; }- g, }3 q7 o    {: P$ h* c. {6 B; s, O
        static char row=0;! l% n6 g) G8 C9 j8 }5 B
        __WFI();
: U9 b3 _7 x- J$ ?1 h        if(ep0_state & 0x80)    // request data processing& B/ P* K( L) U- @0 A$ \7 x
        {5 L) I% b9 |3 T9 g
            if(ep0_state==0x80) // SETUP phase
0 u, d$ ~( V9 ~7 q            {& G, q5 e. i4 x" N8 U
                if(!setup_packet_service())
6 Q) [( \. o) }) R: v. N: U; X                {
& r. z( Q9 F" p9 T+ u, I3 C" X' P                    ep0_state=0;
3 d) N# Z' b4 k' @6 R                    // not supported
# [: e/ k; k! {& [: a                }' K& v, m" H6 A. A1 {
                // ep0_state should be set to 1 or 2, if processed3 l' C: m- S" i- [
            }" q% s- \- K. N0 J5 {/ @# l
            else    // OUT phase
  O4 U& Q# k/ W: D% ~            {: F& {5 E/ d2 ?9 ?% m
                // process data
3 x& h! h) b: ~+ p0 Y                show_LED(*(uint8_t *)(USB_PMA+128));7 K8 w8 g( x& a5 ?! ]3 y
                ep0_state=4;) M5 h4 F% ~7 n
                USB_PMA[1]=0;       // Zero length DATA03 Y+ I: r2 i* |% v$ J
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;* N0 j$ ]" `$ g( z$ E) Y
            }2 _3 b, f# U0 _6 e" R( K
        }
$ _+ g1 E+ b* Q  e0 G        else! J1 n3 \; p; z8 [2 K$ ~
        {
+ u! u: p& v) R: j# J% S/ @+ [, v, V            if(usb_address && ep0_state==0)9 _; e2 R- r; `0 I8 F
            {
8 w* S( q2 \9 p, y                USB->DADDR = USB_DADDR_EF|usb_address;9 F  z1 c. T; }1 v" m
                usb_address=0;
" b( c2 z8 I, @" s8 J            }
/ T0 o3 P2 Z3 V3 I        }
% |5 {/ r+ l: z: M; [: t+ ^$ V6 y        if(row!=scan_row)   // new scan line
0 s( @3 ]5 }) W9 X. U6 n# {: g! M, P        {
( L# x# d( ?) }! y* N            if(key_state[row]!=prev_key_state[row])
( {! C: j* R# q+ m# f$ W: t            {
1 g- `/ f* S5 B" S  I                uint8_t test=0x80;
& {; s% ~' H+ T% H+ f- k! @0 A                uint8_t diff=key_state[row]^prev_key_state[row];9 I  P5 v* y3 m0 ]
                for(i=0;i<8;i++)
/ }1 h7 i2 N1 A: c8 C4 v                {
/ f, h5 F! U4 {7 S2 }$ e                    if(diff & test)9 x" J; ?  p4 I8 E/ {1 M8 J) y7 w/ S
                        update_key_matrix(row,i,key_state[row]&test);
- o9 ?3 F2 g  }; F# I                    test>>=1;
( r+ _- t7 F8 E                }
8 O3 r+ |/ \8 H# z" g            }  e5 }# w: ~8 M) V3 ~
            row=scan_row;# h- D! p2 v4 Z% N7 F9 ]
        }1 l  S0 S: h: j; k* E
    }( @) H$ a( S; `# u6 r- I) }0 M

7 f; J9 E  O# \  @+ p9 G& J
; w; f2 s# T& p1 KEP0的控制传输,把用到的请求处理一下
8 i3 U+ F. E, I- w# u! @4 t/ pchar setup_packet_service(void)+ |9 U: o" a2 v8 S5 N) `
{
: Q+ R" v2 A" ?    if(ep0_std_req->bmRequestType & 0x20)   // class-specific9 B5 p+ k$ {$ M6 w$ ~3 R" F
    {- X' i: N# P. o1 w$ {  A6 l# B6 J
        switch(ep0_std_req->bRequest)! \/ W; o" a9 Q' A2 Z8 D& v
        {, X& A) j- L. x& S8 b) S
            case REQ_GET_REPORT: break;
, X$ a4 p, X# u& j( l$ [4 G            case REQ_GET_IDLE:8 X6 b5 A. k. f# B
                USB_PMA[64]=0xfa;   // return 1 byte8 g) B" z& v6 k* A2 G6 R
                USB_PMA[1]=1;- t, B0 p6 q% G+ {, d8 i% u! @
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;' w- M; u/ z5 I
                ep0_state=1;
' ~4 |; X; j: m( X                return 1;
! V+ |! O- ]4 _; }* {" M                break;
: g5 p' q( |/ v. X" L5 W$ R            case REQ_SET_REPORT:
" k, b9 Y7 m4 \1 f                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
9 t: x  Y' Z: U( c) }                ep0_state=2;; n" w$ P( ~. d! K2 N3 W) F
                return 1;
* t; U  e4 ]; `, y                break;6 [7 H) ]9 J7 u% e9 k, j1 @9 H
            case REQ_SET_IDLE:: S6 N" \  D8 ?. W5 |
                USB_PMA[1]=0;   // Zero DATA( c8 h' W- J4 o+ ^7 c4 j' L
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;( s! o1 g- t$ n  e/ x+ _
                ep0_state=4;
* U' o% \% v" F8 X5 J$ g                return 1;' R7 |% T: M% K' D; b$ a! a4 F
                break;6 L, U2 k4 F* `7 [8 s/ k& v
        }% L! z. q# |, l0 a  E4 J
        return 0;5 q" ~+ ]8 ?. m* h2 k* x
    }$ s) o/ g% O$ P# [$ [
    else    // standard+ @- p3 n. b# r2 r6 U* v
    {
& I/ S' d6 ^4 |$ `        switch(ep0_std_req->bRequest), _2 q& s+ Q- f, g+ F' v
        {
: b8 Q2 G  v3 [; y9 O            case REQ_GET_DESCRIPTOR:6 A5 W7 W; I' X- o
                return descriptor_service();- D% [( O* `/ S0 V* Q% p. {2 ?
                break;
6 `& M( ^2 B5 ]7 J# w) n            case REQ_SET_ADDRESS:( J& u: @0 {8 i: ]
                if(ep0_std_req->bmRequestType!=0x00)
- ^* \3 ], k+ J5 {                    return 0;  J  M$ \' A8 U2 m; m- L" t
                usb_address=ep0_std_req->wValue;. ^1 _4 ~4 ~' K* c& r  H) L2 p
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;7 [3 u" A) g1 p6 g( r% F6 b
                USB_PMA[1]=0;       // Zero length DATA0# \  G$ x  P  M8 [+ o; Z7 X
                ep0_state=4;    // No Data phase: d; |  R2 k3 b7 ]# F) U
                return 1;5 k  C9 [. F/ T* O* w4 T
            case REQ_SET_CONFIGURATION:
9 v5 j4 O1 K0 ?& c- {                if(ep0_std_req->bmRequestType!=0x00)& ~% `: `- L5 S  r& X% R8 P( c
                    return 0;
, y: S. d3 I  }+ @: M/ \& P                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;/ ~% \" I: ~* f% E( a
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;+ K$ M. ?/ V: G% x* O8 c; O
                USB_PMA[1]=0;   // Zero DATA
' r8 S6 L* G5 A; u4 J' ^7 o; Q5 V                ep0_state=4;    // No DATA phase7 c6 ~) F; _2 B6 ]& h; }3 v* s/ g) }
                return 1;3 y6 r' n/ W% u; w
            default: return 0;
- h/ j" U; f# f1 Z$ V        }
  m6 p- T' d3 B! k3 ~# R7 _    }
# q" F+ s2 Y( g6 ]}# P3 x* A7 B1 S. }9 O/ k

6 `  Y% l& A& p
1 a. ^/ e! \( C8 r& @  ], y
8 {/ i5 _' n, u: r9 G
回复 支持 反对

使用道具 举报

24

主题

63

帖子

286

积分

vip

Rank: 9Rank: 9Rank: 9

积分
286
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
/ q- W# a! x- ?4 {; Rchar descriptor_service(void)- S7 y; f5 w) \" J6 e/ E/ R4 ^# g
{+ u- h9 a' n& W" m
    switch((ep0_std_req->wValue)>>8)8 R) `3 f. |( Q$ X
    {
) ^# V* y, J: R, e: m+ h) n, x6 \3 y" q        case DESC_TYPE_DEVICE:% `& G; O( ]3 ?4 j, Y
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));
- {, o; F: _. X5 n0 [            break;3 w3 N% c) I9 p' w2 O2 H+ I
        case DESC_TYPE_CONFIG:6 ?' B. w; V+ Q
            if(sizeof(ConfigDescData)>ep0_std_req->wLength)( \( b6 L* k0 ^- p  E! `
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
! t6 S0 Q+ W& R) `            else0 A( x& z" S& N! N- f
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
/ D- u$ F3 |& O0 e' |- K, L            break;+ o/ k8 ~. V- y+ S
        case DESC_TYPE_STRING:# ~! D& \5 G6 s; y$ D+ o, a7 i( R- K
            switch(ep0_std_req->wValue &0xff)# s( a0 l8 r) p* O& o+ t
            {, }$ a; X3 l7 ~; i
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));0 n' D% I2 W& w" g" A0 N$ l
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));! @; Y& q: i$ T, W! F) T2 S7 K
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
7 W4 y3 W( V1 S  a/ I8 i$ {7 A                default: return 0;  t. K( n5 y4 i3 w3 c5 D
            }
# r3 ?, m, e4 J* F* H            break;
& E( {. C# D, h  }* ?5 \        case REPORT_DESC_TYPE:
/ [$ p, j# E( z/ n            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
) J  x, j8 q, G/ Q  K/ j        default:' r" c: G  ^1 @2 M
            return 0;! a$ x/ t4 m4 D8 w- f; D
    }
) @( I' H7 K1 x7 ?, e) r( A}
! d! M: z# o/ w7 R+ @下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.. H+ J6 ?: H4 n6 k
void TIM6_DAC_IRQHandler(void); W- k( t9 v7 [/ @8 q: J. y
{5 P$ A( \$ p' B+ t( C* p
    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
3 i+ T/ w' b( B) X( B  o+ W# D- k1 ^

' S' C. j9 a- A. \" D# m, Z    TIM6->SR &= ~TIM_SR_UIF;
- [# f! \7 Q& Z; \7 c0 w7 j0 ~4 p( j    prev_key_state[scan_row]=key_state[scan_row];. t7 f; N. i& v5 i' Z; B+ m
    key_state[scan_row]= *PA_IDR;   // update key states
6 v5 \8 I1 R9 {+ p) v) H    switch(scan_row)
# D, |) M4 `/ \: }6 E6 W* Y/ R! Z7 w* q, Y    {
9 ~. O7 h& f6 }  w" s" M2 V9 F        case 13: // next row PB14
% N0 F: d3 B, t5 r; t                GPIOC->MODER = GPIOC_DEFAULT;
3 `" Y& K7 f+ K                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;; ~0 }/ G$ }! r+ b: E
                break;
& E7 \& D; N2 J$ a9 Q- T        case  0: // next row PB15
  H2 T9 M) J4 L# _9 D' D) C                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;
6 Y% e: s/ M3 k5 j0 H4 q% L                break;
0 A* d; S, O' T( W7 r: j/ h, a& G        case  1: // next row PB3
) J( R2 R4 |( ?                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;* ~, j" \: [, t* O
                break;
$ K# v7 e& o5 {5 r8 C: ?% v1 `        case  2: // next row PB4
5 a9 U9 m  b4 a8 E% K. A6 T                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;) K) G3 _( O- k. c' J: Y
                break;
4 `, S: l3 w/ m2 ?1 }& X9 ~        case  3: // next row PB5
4 ^; E9 `4 @6 ?. H$ B                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;2 P0 S) y0 `! U& ~9 K% j* M0 ^
                break;! Y) z9 e3 u6 @4 [9 s
        case  4: // next row PB6
6 Y: ~& D% Q: w" m                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;9 j4 O  N3 |* V8 V& l( f4 p+ ^- e
                break;
: A- X$ |2 Z- f, v. i$ G3 o        case  5: // next row PB7+ ^$ ]; t+ ]) s6 a& G7 T. v
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
& v/ T1 L# O* e4 F$ ~                break;
* d% Q, C! g% Y" c8 |9 B0 b2 C2 q        case  6: // next row PB83 J  {' R9 }0 Q" l' ^! n
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;  O( U3 }% o: _
                break;
1 q+ r2 o4 X9 Z9 e7 Z1 ]+ M        case  7: // next row PB9$ v; L0 @* B8 I  L! i, I
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;7 {( N4 Y6 G& O
                break;* y! d' A1 `/ p; D$ d
        case  8: // next row PA8
* P& E) u& H" c' J! l                GPIOB->MODER = GPIOB_DEFAULT;& X3 k8 E- i( j1 F; }) X
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;) ~# {2 z$ d6 D& m: D+ |% R& X
                break;- s+ I0 L$ K) ^
        case  9: // next row PA9
) A- F% Q  x7 ?# N( r' n0 _                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
7 L6 ^3 K/ V  M1 k) F1 Z! K                break;4 W/ N( `# d/ V8 b2 f
        case 10: // next row PA10' ?  k* L! k1 ?- I& N
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
, x+ g8 s" d' D8 [3 X. ~$ @                break;
0 n9 h+ E2 m( a; ~1 {+ n        case 11: // next row PA15
6 e% v  X8 C3 `4 F8 q$ b; o# c                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
; O( S+ T9 @) r6 o( v: O, Q                break;" c% R8 r% Z* Z8 p& r
        case 12: // next row PC13
4 }5 n; K& z8 N( z                GPIOA->MODER = GPIOA_DEFAULT;1 t  X0 i; z/ l
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;2 b, C% L0 r& y+ s
                break;
5 U/ ~1 @4 E8 C/ P; {, ]7 w* {- d& d    }9 L5 E8 V3 m  D3 b
    if(scan_row<13)
& K3 H1 D* r  A' g) S( B* b) e        scan_row++;
7 o6 C+ j5 t- b/ o  e5 B5 O    else, K5 T2 x; a" Q$ m/ C
        scan_row=0;6 c: D+ ?1 i- g
}
2 a" U+ b$ d2 Y7 y- H4 a: `0 e
8 F2 |$ p# F% {
, }1 g# E7 C+ t! g, B
回复 支持 反对

使用道具 举报

24

主题

63

帖子

286

积分

vip

Rank: 9Rank: 9Rank: 9

积分
286
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
6 s; E9 T. i( D7 O" J4 N1 z" c9 ~( \' @& {( X
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
3 c( G+ `+ j, g+ x$ s2 e9 }. d5 F/ O  _
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
7 k2 l9 f7 h' P1 u/ i
, \: s# ^# T# Y6 E" g* w) L0 |
6 w7 `  }# f2 T
const char hid_keymap_qwerty[14][8]={" c: @+ F, ?- i
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},8 f/ H% L: h( m- B0 b1 v
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},( v8 Z, O3 N2 _. J: |: ?$ f& [
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
! ?$ p) I7 v" C6 D* `0 |6 s    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
9 h2 r, }# l1 l    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},9 U- ^# P2 G# d8 V2 K
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},' Z3 d* b! x; I/ _$ w
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
0 P5 P' O+ z# I9 p  I# \# G    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
! r9 p2 \* t+ v( X    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},% T7 p6 v3 |8 C+ v8 ~
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},9 R' M6 L! o! j% K2 [$ @# I0 z
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},1 G8 ?) y1 f3 s8 O
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
' T' a1 p7 o; \- C4 D# Y9 ~/ f8 c    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},3 O$ ?' n  E8 Y# Y7 ^& `! o6 r1 [( ]
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
5 W0 c# G+ _; y9 k% z8 y8 w8 d};
* N' w+ U; j' Y& p7 Q$ W6 b* i/ `* c2 V

$ n2 p/ Y, o/ Zconst char hid_keymap_dvorak[14][8]={
: |, D- j7 F& E    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},  j. u6 m' D5 p( A, l- c$ I( U4 P* n
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},7 m4 t4 O; Q: H5 s
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},: r# p; z; E% Z; Q
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},% S/ k- r5 j& Z+ q) N% V) R
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
% q! U8 d# j8 |  I    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
0 a/ j; H, ]8 `* Q' }; v    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},5 {/ m' {2 W. r7 D
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},% M: ]9 A9 @9 k
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot}," E/ u4 M- d# I; n1 h
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
9 [0 w. C1 X% ^2 z7 Q7 W' B/ Y    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},4 J; U% \) s" D, i) U- ~
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
& e0 F5 m: L. A( ]- j% K    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
  [6 \  X, X! v! @2 E    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
. O7 J! x, m3 F" J& _};
7 P$ h3 F4 L! {" [6 q! O. q% U  L& H
5 T0 q. Y$ n* V6 A/ @9 A) m; C) s
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
1 U% v+ e, k9 ~! M
8 n9 h1 @/ \  q7 W0 Y& x# wHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

+ U/ X# Q& O( A+ U2 w# a
' E3 J8 X" D. R% [) D7 N$ Yvoid update_key_matrix(char row, char col, char onoff)
, x/ K/ Q% I9 W{1 @- B/ Q7 x9 \( z+ j
    static uint16_t hid_report[4]={0,0,0,0};. m; Z& C" p2 c( F6 v9 q
    static char (*hid_keymap)[8]=hid_keymap_dvorak;
  w# R( g) X/ x: ~- O( o2 }: O' l) z: x, Y

8 M5 d+ x5 u- y1 e, _) \    unsigned char key=hid_keymap[row][col];
; j' {* n7 J# O6 ^    unsigned char *report =(unsigned char *)hid_report;0 Z4 K! X% r0 s9 b
    char i;/ u+ ?6 ^. t' H9 y5 r! b# J1 y$ F
8 p& U' y, n+ C! s9 p, e' E

- a- n# D& D5 e% ?2 q    if(key==HK_MODE)( U0 y9 r+ G8 W5 T  [
    {1 l/ @, h0 m* t9 G5 k: `0 V0 r' S
        if(!onoff)
6 V- O# a# g: z, ]" l' x        {
1 U: u  y* z& D) Y            if(hid_keymap==hid_keymap_dvorak)
, p/ y. g: [" C6 i" t8 v            {7 V" Z8 `$ W; V2 A- }
                hid_keymap=hid_keymap_qwerty;
4 a" q( p) f8 H9 H                GPIOB->BSRR = (1<<2);
% V/ N6 }- C6 h1 p$ d            }" _5 k# d6 Y, U' |; c2 d% Q
            else
& D8 |' t0 o! @  H& b  i' D! k3 E5 C            {
6 L3 A" x% d# e3 S& L                hid_keymap=hid_keymap_dvorak;( T: u# t" O8 ^
                GPIOB->BRR = (1<<2);
( f- C8 y5 G6 u2 v0 z1 A            }, p7 Q& X( D% n4 A( \$ c# \& p
        }  Y) {5 G" Q2 T5 O; f; {9 Z  {
        return;
# D$ |7 V  M  O) {  H    }, A% r0 d! l5 ~5 q  a/ y2 Y: F

* d* @: Q% q' t3 v. y

6 x/ S3 T* T* n$ q/ ^* g; D    if(key>=0x80)   // Alt, Ctrl, Shift4 `1 L5 q6 j- `
    {
( }5 I0 f0 y* P. W; D. P        uint8_t bitset = 1<<(key&7);
5 ^  H6 V1 |. J$ v7 R) R, H% l        if(onoff)   // non-zero is key up7 K! }* @; T. I/ f( q$ w* o* d
            report[0] &= (~bitset);
, n" ?9 i0 {; z: l* T        else. q6 Z. h: l7 ]! k3 |3 Y
            report[0] |= bitset;
6 g4 J. L. u; k) ]9 b1 ~* @    }" U( v; S: s( l  D
    else4 k: H2 h1 q" t  w
    {
% w/ O3 b3 O3 l, e- e  t( s" F        if(onoff)   // non-zero is key up9 Z, ]9 k3 h( j0 _
        {
/ i2 Y. A: b, k3 ^            for(i=2;i<8;i++)
! b  u$ A. t- i            {
( `( J& F4 K; I( f6 q% `1 Q                if(report==key)
3 Q( ?2 a- V) z/ v% J! p                {- M3 R( B: }1 I3 i: L" i
                    report=0;- \3 n9 [. g& D: L2 `
                    break;* @! N  r" m8 Y: a; a; A$ Y7 h0 o
                }7 R4 a7 U3 N( _" G! \
            }  C/ b: {4 B8 T
        }
4 Z( o* q$ y& A) n' J* X& h/ u        else( t% l6 P3 C+ R) @, t5 S0 ]
        {
. y% t& f# P8 |% y2 y            for(i=2;i<8;i++)
) g0 g  Y. N6 p            {+ h/ e$ k  I2 e) J6 T0 r
                if(report==key)
- G* ^! M; w: x  h                    break;+ _+ V7 C# O7 U/ z
                if(report==0)' q/ m; O0 ]) g, g
                {
, J' H" s$ \$ C: y& ~0 z1 L                    report=key;
! U( q5 j+ |! o# V- q                    break;
& s( d3 I- D) S3 P9 R+ K0 X. V                }* q% |$ A0 ]' h3 q8 }8 T/ V
            }4 }0 y9 F# o6 F! I3 F
        }: o  y1 n, ~  m) T
    }" R- I( s- G% S6 e/ f
    for(i=0;i<4;i++)6 i, a4 G! q5 e) D+ T  A
        USB_PMA[192+i]=hid_report;2 b) o* U" H4 M, q7 }( [
    USB_PMA[5]=8;   //COUNT1_TX
2 l: q' i8 g8 Z$ [4 q    if(ep1_wait==0)- R6 q, x3 O; k! A( Q
    {: _! x& [4 k/ u4 x$ f
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
- _) M+ Q! s% Z1 J. _9 Q( x        ep1_wait=1;
) H. B( Y  F4 l    }# @3 ]7 ^3 W, Q9 o  K
}
- I1 Y" S# b! k2 P! S
1 m+ u8 u; z: e  ^5 C0 k. h* e) M+ N
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。! R( e3 `8 C8 |
keyboard.zip (8.7 KB, 下载次数: 3525)
回复 支持 反对

使用道具 举报

24

主题

63

帖子

286

积分

vip

Rank: 9Rank: 9Rank: 9

积分
286
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。% I; Z' U9 w( ^, a: J/ [% F
回复 支持 反对

使用道具 举报

8

主题

150

帖子

212

积分

中级会员

Rank: 3Rank: 3

积分
212

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
' _3 c6 Q5 o& B/ R* E不过楼主也很厉害!
回复 支持 反对

使用道具 举报

24

主题

63

帖子

286

积分

vip

Rank: 9Rank: 9Rank: 9

积分
286
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48/ b6 ~; |: Y+ M1 Y/ f0 d
刚开始我以为要把打字机改造成电脑键盘
, F; @' H) s: H. e# j* e不过楼主也很厉害!

& ?, P0 I' O. U% v哈哈  是有点像奥
回复 支持 反对

使用道具 举报

0

主题

3

帖子

4

积分

新手上路

Rank: 1

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害" @" M0 A0 y& z0 q
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2019-3-19 08:17 , Processed in 0.082970 second(s), 27 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

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