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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

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

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

: {4 V1 O3 P5 H+ }! w/ I6 o. P 005836yvs0wvovwsssgd3o.png.thumb.jpg ; |' S4 [3 y+ H" r. N9 r
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。7 J3 B: a( g$ W2 ^7 b
- s1 h/ t# v. X0 m8 S6 ]
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
& s3 [; I* o1 c' K$ @; h; U% p7 n7 U) l4 |6 v7 K: ^
到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
) [% D& E. u; f+ R( Y! x1 J9 d  o. _$ q* w/ ~( b- U! P* U4 [# G
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
7 x, F+ Z/ U+ E9 Y
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg 7 O5 u3 z& N" F
----------------------------------------------------  分割线 ----------------------------------------------------------9 n) \6 n8 M* z; S$ [
" G2 A0 R( f& O' S
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

3 B1 P7 p) {  q) K3 G# T1 o6 `! g/ ]6 N 020011osionbunl4ui44vi.jpg.thumb.jpg
$ m, v% ?5 S$ l6 s  o, v轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
# U8 o& ]/ }) v) E9 u$ m 020017j8ycmnv7788bqv52.jpg.thumb.jpg , s& G6 R* q* j) D3 ~3 X
特写,80C49( h3 n; n* h+ U9 M, ^: Z: d6 I9 _
021040oujzuvtut6iujtvz.jpg.thumb.jpg
$ \( k7 P! g& [: p" {6 G& o: N5 }LED部分,使用了一片D触发器锁存指示灯状态.; m5 i# B) o7 A& Z
022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
( C# o  @0 m. r( q% H7 h3 F3 |2 W暴力破坏,将80C49拆掉
; F3 y/ o: t, b% g 021113e48qq98vyohvhzzh.jpg.thumb.jpg 8 b* ]6 `" D- l" e1 s' f  \* D4 I; m/ h
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
' r6 n" B2 K) d. A- @ 021125nc9az6dj33rlds2r.jpg.thumb.jpg
4 i& f  B6 p' Q" F+ q+ w# ]  Z+ V
焊好元件后的板子,准备替换80C498 g$ Y0 g7 O  i5 s1 V
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
" m6 ?3 N8 D: S7 Q' v- p用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。; j2 e" Q! t1 i! n* Y4 D
021104shifhnrqbr3o5nlo.jpg.thumb.jpg % f' e" d: n" Q( o- I# a
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.6 |5 \0 E8 K- u; m
022003ym1p9u4ug40280uu.jpg.thumb.jpg
! k4 t/ n( z4 g开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。
0 J, I9 ?. W4 s* a. { 023313kt141q9qajtol7ma.jpg.thumb.jpg ! D$ c& s& l  g6 o" v2 W. t
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。; l& F- h+ N/ @& D# {* P
023322nt7l5xb3ltttkltt.jpg.thumb.jpg
$ q+ z  s0 N2 V2 ]& ^7 a# U. a
主键区键帽就位( w. ?& x! E. x; Q( [: j, \
023331hin88e8wkrwzwikx.jpg.thumb.jpg 9 L) F# D; Y: L- }' ~$ I0 D
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。) p+ P' J7 \+ i2 m0 R
023336wjzlgopugg1jyy79.jpg.thumb.jpg
2 D# t6 c% A. \, t" S最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。* p9 R4 H, {  I, p4 `
023341sffu4j3g2323h6fl.jpg.thumb.jpg
5 r! m, o1 N: N/ d

4 v" _7 b( b- b9 T----------------------------------------------------- 分割线 --------------------------------------------------% P/ \- L5 ]( k7 `+ B& a0 f  s( @* v' K

" D4 |1 x% G- E; R2 i4 P& D

! Q- K* q* z- U/ v1 \3 X6 \# `' {$ f1 |% \5 a& a+ A7 H, g

) h9 j* M9 s# w; r# P) T& `, ^! R) S! e2 j; |& Z' }) C
. r5 I# m" J, |" Q, A: R# p* W

9 y+ X1 c6 a/ V. {+ t" p  P4 V% H# F0 o, L9 g. J/ M% R! X- L

6 ~0 Q, [& L  ~3 P5 f4 |9 Q$ d2 @* [) \$ G% ]# {! F! n) Z
0 G# r* l3 a' l: B8 l: w
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
# ~8 [% y8 `% @' L80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
7 y3 c/ H( x: K  u* D6 G# B 104025nzibm2rmiomhyirm.png.thumb.jpg " p; m2 w* P* n- y, O9 Q9 b

  d( X( g$ B' Q4 c6 l8 E! J其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
# q6 d$ t* `+ g5 I5 g 105004zkrez5houvkkznko.jpg.thumb.jpg
2 r$ m& }7 n$ t$ i6 N
0 @- q9 D! h( G: c& v% k
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。2 k, S" |* j' c# q3 q, _  N

1 O5 m5 m% {# a9 h这是我设计的电路图:
( C; B4 t: Y7 x6 n5 z" j 110344ej2z2oo2rflo7oe7.png.thumb.jpg
6 x% v; g. F: Y( _. R& b" \

. g3 n1 }0 G0 e1 @& J: HPCB Layout:4 n( c5 F5 _' }9 C  D/ i
110847jjbjvt34vwt3v5bb.png.thumb.jpg
' R( o" |/ E9 C* Y5 S' G

4 u3 U# Q/ o+ D不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
4 j4 F. q- t% E6 ^: z
1 H" X1 m: Q7 [3 I
  _0 Y2 e6 I: s2 R, D8 j; L9 T
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑   v9 B- y9 ]# B1 h  ~* c- i6 c: A

7 o: o1 ?$ \9 v8 |. S; |  R软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。' R, c7 s! T/ h1 R8 ~
- q+ w2 K- I7 w8 G
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:! l0 \& M/ P1 w( G# B7 x4 K# s
113818pmrfsb6z0byt6t06.png.thumb.jpg 7 L, c" Y' X9 |3 S  w6 x

, j0 z( O' M2 Q9 b4 _; ]其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛), L+ D8 T$ u6 k0 d$ t2 ?1 B+ I- i, H
/ {+ o" c0 n: |0 n% a# W4 N
USB的中断ISR,bare metal哦$ R3 s& [8 @; S# J
1 x" V0 z0 ]( \# D, T8 b: [- G( x4 r
void USB_IRQHandler(void)4 q3 g# q. Z" L) K/ {( `
{, ?( L. j3 A. B5 V6 d% L1 X( M0 K
    if(USB->ISTR & USB_ISTR_CTR)
  S5 b* F% d: W$ ]7 m, w    {) f0 r8 c/ R' c1 C' n5 I5 \6 l
        if((USB->ISTR & 0x0f)==0)   // EP_ID==00 V/ ?6 i! E) g' J- X
        {
) i( ^% P+ R$ T8 x            switch(ep0_state)
8 h# \! n& o* u& @( W            {9 h! g- B2 C* {5 H! Q0 i+ H- x0 v
                case 0: ep0_state |= 0x80;
. d5 \  Q/ l% B( E' ?                        break;' o3 u3 r3 q6 B4 k' {; u$ k
                case 1: if(USB->EP0R & USB_EP_CTR_TX)
; l+ w! ^5 A6 T9 i# T6 e                        {3 U$ g1 n- x; J7 v# c5 [
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;: R. i$ T. y* k/ v$ J
                            ep0_state=3;
: t6 ?5 k  L& ^                            return;
- o; l. s* H& q$ k5 p0 w                        }' Z( s  X& u8 S; I8 W1 W( Z
                        else
7 z' I' l" M7 ?3 u% H; P" Y                            ep0_state=0;
% l1 j, X9 R* i  X0 a0 D- u                        break;
0 f& P' h% v* U& W& o                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet1 ^: I0 D  ^7 O
                            ep0_state |= 0x80;' C5 u/ D& b2 f8 c
                        else
+ O1 y9 |: T9 _3 ?2 S# @                            ep0_state=0;
) @# `$ H9 y4 v- l) E6 l% r                        break;
) k- r7 E. Z* z+ S5 |                case 3: ep0_state=0;
7 |9 h' l3 d. Z3 y                        break;
  w/ g$ s" [/ n: v; N7 A; b' Z                case 4: ep0_state=0;
' f% g# h4 e4 n) h                        break;
9 Q% y8 n& J# {7 b0 s                default:ep0_state=0;
$ H. b/ M) I0 w5 o                        break;0 g) N4 }8 I) [9 s# W7 I4 ]
            }. {) Q3 h9 n0 O1 e5 Q% l' z
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
4 f( @: d6 h& g            return;: R- a3 _. E: E% Z  E# k
        }
3 `! P$ k! i) J7 T8 _; ~) z" w        else    // EP_ID can be 1
. ]* b) T3 J; J/ u9 e        {
4 n% H+ E' c9 e" b6 A" B  G2 d            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag' U  y' I1 ~1 `, T  l' ]. ?
            ep1_wait=0;
2 Q- c( ~4 r5 t' w4 y, ?            return;
/ p( P  a" H, h0 ?        }
& @4 W+ M: o+ q5 q1 u    }# z2 J5 p7 T* Y0 l; A; ]
    if(USB->ISTR & USB_ISTR_PMAOVR)* A$ p$ a7 g9 E4 x
    {1 k1 G5 q, z2 q2 x" x
        USB->ISTR = ~USB_ISTR_PMAOVR;
4 m  n$ x6 N7 @) `; c5 \    }. y/ U" F" y7 Z( I" ^, A2 x& \
    if(USB->ISTR & USB_ISTR_ERR)
( U9 I/ H' S9 o; g2 J    {
! ^- ~& c* ~8 x4 c; R        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear+ Z* P' i' p$ H4 f4 Y# l! M1 q
    }
% j/ ?% ]( m0 h; G* Z% E    if(USB->ISTR & USB_ISTR_WKUP)
' s: \2 v1 b2 t0 k" m# U7 \    {
6 E  q6 W7 Q9 g* Z0 L        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
+ O. h5 J& G7 N        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear: E/ T( H3 @- E) N1 _1 Q4 C
    }+ X8 y9 t. Z6 T7 T# Q( P
    if(USB->ISTR & USB_ISTR_SUSP)3 ]0 w' z! \( M, p" \
    {
* ?5 d1 g7 o: K        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
, {. `* j( Z- o8 ?        USB->CNTR |= USB_CNTR_LPMODE;   // low power0 i$ {& r* e( e! e
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear# J8 O) h" Q( {4 n2 J5 k
    }; O2 E3 Q- t! G9 y
    if(USB->ISTR & USB_ISTR_RESET)- Z  D- X4 \4 k, j9 A
    {
9 J# O8 {. G) S        USB->BTABLE = 0;    // buffer table at bottom of PMA) h4 ^& Y" A+ }9 \2 P
        USB_PMA[0]=128; //ADDR0_TX8 N" g# G* I; {
        USB_PMA[1]=0;   //COUNT0_TX0 W' S! Z: |8 i
        USB_PMA[2]=256; //ADDR0_RX
% C1 |% _. l, R& ?        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes1 y: x: J( c- T
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;! R5 Q6 p( P, b; I+ x( j
        ep0_state=0;; s+ N" N( g' V
        USB_PMA[4]=384; //ADDR1_TX+ A0 G  a7 n6 ^! B* X6 e0 c) S
        USB_PMA[5]=0;   //COUNT1_TX1 F. p% j) Q% k- Q7 B
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type' d5 ^% ?5 V' f: V, |. w1 G& i. R
        ep1_wait=0;" A' Q3 |; d$ C
        USB->DADDR = USB_DADDR_EF;      // enable function& a* ?: T4 r% B
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
1 g, ]& ^7 P/ V$ b' _) \    }
- e/ N/ H( e# \  w+ X# B    if(USB->ISTR & USB_ISTR_SOF)
( H. T+ L& O! f    {
3 k2 ?' o# x$ F; b9 i2 O        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear
- E! j$ w+ a, k' c7 n, N    }9 z; H" x% T/ p  }7 P1 a
}& g; k# _1 |; ~8 h

) `9 @7 J* ~' X2 F$ y' q# L; x
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
. O9 `- ~& o; P; C
. e) P' ]0 L" r0 v$ `5 C+ B主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。+ b) u9 }$ k3 L9 i1 t

6 i9 d) u( Q3 q9 n
    while(1)
2 N+ u  m. _+ {4 B0 [/ ]    {4 `1 Q) S' ]/ v0 ~
        static char row=0;. B" d) M1 n8 r/ ~6 i! V
        __WFI();
/ k# P; q' x" H* G8 J0 h, ]        if(ep0_state & 0x80)    // request data processing
& f, z* u2 o: [0 I9 r" Z3 U( @) `        {
$ k. @4 ]5 D0 U) r: [& Y            if(ep0_state==0x80) // SETUP phase
6 t0 D( F: o5 u8 ?3 X            {
9 n- x( v8 k! j5 ^                if(!setup_packet_service())5 p% D8 Z% u" o% V% z' C3 t
                {: ^8 L) a" `* T0 b# a$ h$ |' ]- u
                    ep0_state=0;
( X& L8 K/ x# n/ o, j9 }9 G+ u                    // not supported. X# |  {9 @1 H& t& K7 w% H& J" Q( C
                }" g  _0 [# E9 o3 q5 p% ~
                // ep0_state should be set to 1 or 2, if processed/ q. M  h& T% L# B; W% L
            }- b& s2 o3 E7 p2 X$ t$ S+ ]
            else    // OUT phase
5 W; h* {/ t8 z# e1 y- O! V/ E            {! U$ _. x# C! p' b: j: E4 z
                // process data
: x# y4 s3 J! x                show_LED(*(uint8_t *)(USB_PMA+128));
) o0 G2 z9 r6 w2 Q; K: c" m, d% N                ep0_state=4;
2 C% E) b$ O' _, k/ P                USB_PMA[1]=0;       // Zero length DATA0
; ?5 f  z- \- D+ N6 M, x                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
" M& u" N- U9 e7 e) D9 t  Y            }
  u9 V2 ?8 E- h  S  x: |        }
9 Q/ j8 s  N# H3 u$ q        else
& R, {5 j$ o/ L7 O7 I6 t        {
3 s+ o! |+ Y& y6 b2 H) h) Q            if(usb_address && ep0_state==0)
; B& H2 [, s/ Y5 v2 [) d6 X+ h            {
, K; @# L$ v1 [9 P                USB->DADDR = USB_DADDR_EF|usb_address;( Q8 Y% l6 S: I* T
                usb_address=0;
% z  s9 j) N3 E8 e7 \1 m            }
* c3 w. w* d( f/ K        }
7 t3 w" t$ A$ f/ m+ s- N        if(row!=scan_row)   // new scan line. q! k& I% }8 n% H3 `
        {. J  z" _3 n3 i
            if(key_state[row]!=prev_key_state[row])
8 D/ h6 n6 ^1 u  V. I% ?            {2 N: i" L) f5 d& \
                uint8_t test=0x80;
) P3 P4 u/ M2 m* ?; U& R' U4 G                uint8_t diff=key_state[row]^prev_key_state[row];7 s3 D& T5 c! e8 G
                for(i=0;i<8;i++)
& v$ x7 w9 [! O6 f9 ]( n  O                {
) _4 b0 m9 b# p5 Y  D5 K                    if(diff & test)
+ P+ B4 v9 K; r( A9 K& z$ K                        update_key_matrix(row,i,key_state[row]&test);. X( D% {6 y7 [9 i$ |
                    test>>=1;# {: T0 K' I+ i3 I
                }
) d- W: [: g7 T: `) u* X6 D$ l            }
: B' j; a% q, j/ q: q! [4 a+ \            row=scan_row;& N5 z' ~& s( y2 L: p( N
        }
) i9 G! O" V& L) Y+ ?* W    }; n( B( I, a$ K2 H

9 H( J' x! D5 F# \. N5 v# [. N. w! w# |4 d  p: K
EP0的控制传输,把用到的请求处理一下) _- |! e  o. x0 w  G
char setup_packet_service(void)
- ^! A5 U$ i$ f/ j4 r{
3 v- _7 {& x2 T$ @: u2 V    if(ep0_std_req->bmRequestType & 0x20)   // class-specific6 @5 c7 |. m; l/ c
    {
: k* w% Z/ v) |4 K        switch(ep0_std_req->bRequest)7 N& n& }$ @" q! Z# h
        {- D, ~) N; Y- a, ?$ p3 w
            case REQ_GET_REPORT: break;
- n% w0 R! f3 d3 E1 H            case REQ_GET_IDLE:
8 w8 v; }$ ]/ h8 S" _4 ^) H                USB_PMA[64]=0xfa;   // return 1 byte; d8 r" e5 u' L9 i' D9 E! v
                USB_PMA[1]=1;, T6 B" V( }4 v7 I! y+ W
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
& ?5 F, m3 {  k6 p- v- W                ep0_state=1;
2 a0 S$ _" }( Z0 @                return 1;- F) s, [+ V8 n* Z& q6 C
                break;
% ~- N% J( }. x$ K2 L/ F            case REQ_SET_REPORT:
# b4 i+ c8 t6 \6 a                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;2 n& b3 A5 Q) e( M0 y& c5 e
                ep0_state=2;, Z& m5 p$ f6 X
                return 1;
# a' m' k1 W9 q& _5 X, X                break;
. G6 \- {$ B2 ^9 {( H            case REQ_SET_IDLE:
1 }0 W1 i8 d% m' _/ I0 N                USB_PMA[1]=0;   // Zero DATA
. z( t8 i4 Z" y$ Z; a                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;1 B, g  H6 [. T8 ?, i7 W% `) y: K
                ep0_state=4;
- R8 O" g: U( S9 s& R                return 1;. E/ u8 H/ F; z( B+ b! `  ^
                break;# ~2 O. h) @& L: s$ }% k
        }) a* a4 }1 V1 u) i4 k3 P4 \9 E5 t6 |
        return 0;
+ }" f( g0 B% G, e8 A$ z5 k    }
9 D6 S; G3 \& j3 d5 }# \    else    // standard
( o# D, w5 E* j/ X* m/ {; y" b    {+ J$ o0 j9 @1 Q( ^- ]
        switch(ep0_std_req->bRequest)0 _' a; F( B, _) @
        {
" n8 t2 k! m' r% T/ H1 ?8 c            case REQ_GET_DESCRIPTOR:& q5 B' u# m  j3 V- ]
                return descriptor_service();1 O% S# O2 W. r
                break;; L5 D& X  t& C" N/ p
            case REQ_SET_ADDRESS:
: a6 v  X& a6 v- t2 B( v! ?                if(ep0_std_req->bmRequestType!=0x00)  [0 e& ~" y; K2 i* J; n5 ~9 P
                    return 0;
' E2 @) O$ I3 f) b: u" ~                usb_address=ep0_std_req->wValue;
0 c+ B- g: V) K! j0 h5 e; _7 r* N" O; }                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;! \% q# G" ~# S$ f1 M
                USB_PMA[1]=0;       // Zero length DATA0) r# u+ v' @% `
                ep0_state=4;    // No Data phase( f- ]( g" b& w2 b7 C, g9 v! b
                return 1;
5 C+ }2 t0 P$ v( h/ i& _6 U/ G            case REQ_SET_CONFIGURATION:
. P+ @' R" V* L9 i) |3 ~                if(ep0_std_req->bmRequestType!=0x00)
2 H9 U& A4 U; x9 E% w% I; O7 C                    return 0;8 U' f: e) r, H4 L7 B7 G, c) G
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;/ i) @) R+ o+ k: T* }- Q; }
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
0 x  }. r2 ]: Y, w5 t  y                USB_PMA[1]=0;   // Zero DATA. C) q5 H! q2 I8 A
                ep0_state=4;    // No DATA phase
2 r, D" A, r& u, j+ }1 q# t                return 1;
( Q: f, R. |6 e) W9 a5 K# i            default: return 0;
- e. `6 T4 @; `5 Y: c        }
! X% }: b3 z1 [6 W5 R! p: g* K    }6 y- {; F( B9 F
}
& f' E: p9 K7 i( s: Y* n' q. K/ i7 T5 G4 \4 X" ^
5 p2 ^6 `& w0 y, f- `8 ~
/ x: x7 m9 v, x& \
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
  D; x& H! y/ {char descriptor_service(void)$ M7 G/ ^& O- x& @- I9 S
{$ n; W' Q% c7 I% b" x4 M# Z  k
    switch((ep0_std_req->wValue)>>8)
' X! A; m% O. M- x5 p* m+ H    {) p2 Z8 K3 i' a: k3 i/ `
        case DESC_TYPE_DEVICE:' D; j6 ]6 p! n6 n
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));: y  ?( J) C1 i' a5 p6 n  _9 W" c
            break;3 o8 i  Y, r/ W1 Q* S  O; J; R
        case DESC_TYPE_CONFIG:- I' z) w0 e' u3 D3 Q6 o
            if(sizeof(ConfigDescData)>ep0_std_req->wLength)* o% T- t& @) C7 A8 \; @
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);  X" q$ n9 p. f: c5 j$ Y; J
            else
7 n- e; D  z4 I0 o4 X4 P5 P5 D                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));3 z0 H) f' L/ C" k
            break;
% [; I' T, L6 x0 y3 a/ G! a9 V        case DESC_TYPE_STRING:7 v" @+ }- ^& t' X8 B$ }" k7 j1 ~/ ]
            switch(ep0_std_req->wValue &0xff)9 k8 w* _/ a6 K5 m0 j% x
            {
& Q+ }0 D/ \9 V" n# Y                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
. p/ p: m* T1 `# }* @                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));! w. a4 f1 @. A" x
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
; t' v& Z8 g8 q3 W                default: return 0;
5 Q1 q% @% E: }$ Q            }1 `" ~: T; H' W% c# Y) n6 m* T
            break;8 `( u% m+ j( W! ?' S7 r
        case REPORT_DESC_TYPE:/ n- b* c' U: U. s0 @8 d
            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));) ^: L( U4 `: w
        default:
$ |& p! u- S) K0 ]$ W; W6 ~0 W7 Y            return 0;% X2 k2 g* J1 }
    }6 a3 |; C0 n+ v4 K! N0 x
}
- W+ V% z! M$ ]- I* E3 p: y& ]下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.+ E- q9 x) g, x' U1 X3 M2 j
void TIM6_DAC_IRQHandler(void)3 o, e9 t* u" C& O$ w' T: T& I
{
2 c8 n( q  ^1 S& H2 a    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
6 G8 ~0 V7 E' O
( i) ]6 V. [) Z) ?

5 G) e9 N& W. U    TIM6->SR &= ~TIM_SR_UIF;4 W# X3 b# [4 f4 [% W
    prev_key_state[scan_row]=key_state[scan_row];
- {+ g. v) M3 L  y7 ^    key_state[scan_row]= *PA_IDR;   // update key states  O( N, M, r% x; u9 `$ G
    switch(scan_row)
' k1 k; r7 v7 h    {9 a" \5 I& z) \
        case 13: // next row PB14' u" k% j* W/ y& j/ p+ Z
                GPIOC->MODER = GPIOC_DEFAULT;8 \+ ~. P; h& L. t6 F0 H1 G
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;1 `) V: K1 h/ {* V
                break;9 k" s4 b' r$ W$ M9 E
        case  0: // next row PB15
$ \& \/ r5 T. W9 ^9 W1 S' H                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;' K- o& \- a' d
                break;- j5 i) I6 P! T6 o4 M( s
        case  1: // next row PB3
4 r- {- q% }6 R- L& |                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
2 N/ `$ v- B8 i8 J5 z                break;
* J% B# F- ]! Q0 [+ z2 i4 ]- ~9 c        case  2: // next row PB4  D* y0 U$ D8 b# X7 u
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
! _2 p' v  F% s4 c% l                break;6 k. P" W4 L9 V- `* R
        case  3: // next row PB52 `5 o& u6 n- D$ R
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;
/ G7 b# a5 w# ^4 ~                break;
' Q5 ^8 c" v5 e: o' t! r* Q        case  4: // next row PB6; V( e9 a" E" X' A9 E
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;2 [( F# t% s. Q% K" a
                break;, n9 j  m# G1 L4 X
        case  5: // next row PB7
* |4 K) [+ A3 d0 ]# b                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;* c1 _- P% ^0 a7 ?" z9 ~
                break;/ {4 g( r  `2 n, r6 m3 t
        case  6: // next row PB8/ @6 v/ X, j' T: V! r3 L
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;* G; S$ B  m) G+ _8 [3 C
                break;
  F6 R9 z/ S' k) T1 Y6 W        case  7: // next row PB92 ~6 K( e8 I; d; V
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;. O  _. N* i- B% ?
                break;
! O9 h" M  V& H        case  8: // next row PA8
1 o+ w+ ]9 \+ C6 i, _/ o                GPIOB->MODER = GPIOB_DEFAULT;9 K# R  X) v* s+ H, r9 q
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;
- A  ~8 l" _# \5 `                break;% p; N5 j, ]# o% P0 m
        case  9: // next row PA93 g7 T' y1 [. ^) i
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;1 s: C7 g  S. V1 b9 X2 f+ u- C
                break;  k/ M1 F- g5 H8 H) r
        case 10: // next row PA10, l+ E& O( B, k4 Y! U$ X  P8 m
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;/ P& |+ a  t" O$ D
                break;, X; L" ]5 o0 x. `2 D2 E
        case 11: // next row PA150 f" b2 A( b1 |! v( X
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;  m/ l- }; n/ Q, }0 r6 ?' Z* _- F
                break;
- F1 |! P+ R9 y7 q4 V        case 12: // next row PC13- t% k$ ^2 T' z, ^* u
                GPIOA->MODER = GPIOA_DEFAULT;2 S9 Z- l* M4 E0 U
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
2 l/ N. e) D( `, g5 o/ |3 n5 g9 ]9 N                break;, }6 H2 o" h5 k0 M$ l6 |3 r
    }
% M8 z1 |) u; i1 Q6 `    if(scan_row<13)
! H7 O% r! \' z1 ]0 k. m        scan_row++;
- \: `9 j. f' x- b    else
# {& f( x) w  X, P9 x' M        scan_row=0;
  t5 p8 I+ O& a, c  `3 o4 {}
& v; k/ i7 L1 F* A/ H/ G; l: j1 j- @8 _4 C  k4 Q
6 d& w6 z; K) V0 y* T9 C( L
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑   M. z7 M& j& @% U; w

! a3 q9 U: o; ?/ @# N5 b0 C  A' x% I$ Q扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
: ^& G( }6 u5 U( C' R9 s. P9 D2 L% k& C, ]6 `0 K
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
: u' }5 D! r9 z" h: w# ]4 f

$ ?; J0 [3 h4 |5 \* s* h4 c7 w# P- O
+ E# ^' C, |4 X' r( Y$ Lconst char hid_keymap_qwerty[14][8]={
% S2 Z; K% }. u# o3 a8 i    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
  z1 Y" ?0 v. x4 f: a7 ?% t+ m    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
2 ~" E& h4 q  o; s    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},/ d$ R, Z& i) z
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},2 N; _0 y  M5 r3 ?' P1 I& o) H
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
" P" v. ~3 V3 e3 V( h    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
/ f9 s& b0 M% k6 C9 n( p/ _    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
  v( U  q8 X+ l# X' [" P    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},: Y( Y' x* a# \6 c% `# Z
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
2 N) _7 p% H+ E: W    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
4 s' u1 G! m1 A1 B0 D, X    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},1 D( V) y: u4 E% X7 U$ J
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},5 V: I  f4 r  T: V" G) O- h' f
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},5 p9 i. C6 X7 ?- v5 q" d& i
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}5 X& _+ \7 a/ H( z6 s* K9 h" Y6 [
};5 M( }" X; o: [+ ?; E* H# P

2 m, T' v9 L' t
( X7 P6 L, p- J/ T+ \' f$ C0 O
const char hid_keymap_dvorak[14][8]={* f$ H, Z8 A" V+ e8 W$ l
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},! o& j* W7 @4 X2 E7 m- `7 H
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},2 m+ n" J4 w( Q& S  D7 v( w  a; m
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
0 z( V0 ~6 a* F% [3 V    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
$ Y! C+ b: n) c& u& f! N: S& \, C' L    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
) `+ W6 \1 j8 R0 R% J    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
) {/ }- {% E1 m' S) V1 N0 V    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
! K8 k# S8 u4 q9 t% H    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
6 m+ j& _5 P9 j    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},2 W! m0 `) P# i* H$ D+ S2 h
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},) ?) }- Y- @) L- Z' Q8 g$ X
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},# z6 P" c6 ~: C9 d* Z% r
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},0 K9 N* L8 M  f% e, d8 S
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
2 v5 z' n5 F+ Z' v: p9 b( k# L    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
$ g5 A2 Q7 M: Y, p6 {4 H: A7 m};
, k! M7 s4 k2 S$ _$ r+ p* m. j
# r* z, `" }' `- v
/ Z5 {, L( U( A/ Q3 P6 }上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
. }' E# p: H% A# Q, i' D
" C  x2 ?4 n  @$ ^1 Y6 KHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

9 y  o9 `! [2 a8 [
+ v& k0 a5 O( V. e4 I' v3 @# Kvoid update_key_matrix(char row, char col, char onoff)! Z) G. G! V( v# T) `/ Y% [
{
* V% l' y: {3 m. I+ p    static uint16_t hid_report[4]={0,0,0,0};
- I) M' I( c& x! ?    static char (*hid_keymap)[8]=hid_keymap_dvorak;
5 F( ^7 h# L. j1 y
2 n: ^" P. o- p2 w/ ^/ d7 w. h
1 m) C% T* ~' L3 {3 ?) U
    unsigned char key=hid_keymap[row][col];9 A  A5 E0 G9 j. v
    unsigned char *report =(unsigned char *)hid_report;- H3 j1 ~2 F  B' i0 e8 N! D
    char i;
" d1 s' r0 k1 q# o- {* T* F# E0 n
7 {9 o  U% S) l2 S3 a8 {+ R
% e) Y% K5 u5 a5 W$ j1 [( H
    if(key==HK_MODE)6 x9 Y# ^# P  Y1 x  k! H/ P4 E2 j
    {) O# O4 o7 }, y2 O- e
        if(!onoff)3 M; \0 {. B- M
        {0 c+ {$ N3 h4 c) W
            if(hid_keymap==hid_keymap_dvorak)6 ?$ k# Z+ v! f" {) J' J
            {
: p3 \4 p& P, S2 r0 R                hid_keymap=hid_keymap_qwerty;
  C% |/ i, r: _9 h4 `                GPIOB->BSRR = (1<<2);
  h* f* ?  q+ L+ K- {* ^3 v% F            }
8 e5 P0 K0 w' A3 i& e0 d% X            else/ S% C( o* C2 u8 J3 k
            {
: ]& E( v" @8 D1 B9 i: L                hid_keymap=hid_keymap_dvorak;
9 T4 |- G5 w/ v                GPIOB->BRR = (1<<2);. Y! |0 \1 d6 T4 e" ~' l6 \
            }
) a0 A. Q$ x2 ]        }0 w& c8 |  `3 L
        return;
/ G! p9 ^7 m$ w- {1 v3 W! g# R6 t5 c    }% i4 i0 B( H1 i; d) D! G6 x) O5 r
' P3 h8 ?' \9 a# B, j9 O+ b

7 d* t3 t7 H4 H+ K    if(key>=0x80)   // Alt, Ctrl, Shift
! k% w6 R7 ~8 L" W  w    {$ E4 T0 i% K/ m
        uint8_t bitset = 1<<(key&7);
$ N! n; a9 b+ a        if(onoff)   // non-zero is key up
8 p5 N& w! ?% B. E" @9 x6 [            report[0] &= (~bitset);
, o/ h6 f, L8 x) I3 D        else
! y  \" o1 N6 E9 @! B- L: |" q7 a            report[0] |= bitset;
4 L( o8 Q1 K) H  a: \    }* K- W1 z9 @6 _# m' t
    else
' _7 D( e9 s# t( e" g    {
  H0 h: o# r. o  @6 w        if(onoff)   // non-zero is key up9 ]. K) q2 Z) q( H$ l+ a9 `6 X
        {
. r  n' z5 Y8 _" F            for(i=2;i<8;i++)
2 X2 J: e) o2 ]; ^            {5 Q, ~+ v! y  P
                if(report==key)3 J( p+ V) n) @* n/ o. N
                {' L& k, A2 N+ i0 E# |% p( w
                    report=0;
$ d7 K$ H* D+ v, X) r                    break;/ g( ^( C! Z" C5 E5 M/ U
                }7 Q# }3 i' n5 H! t+ Z( g
            }
, D% u5 j- z/ d) c        }( k+ j1 z, k- N, ?6 C6 B! i" W
        else
% u- M: m, K1 y        {: p$ t! Y1 V. ?
            for(i=2;i<8;i++)8 B0 M) j5 |+ |' w" A
            {
0 q& V0 \4 ^  f' |' y) O2 j' X                if(report==key)
8 N: {, W( Z& X. ?                    break;
2 K2 _9 p, Q; F. F: [/ b/ A  K4 S                if(report==0)) G' [- u, c# F0 [- t, E6 @/ W
                {
- v5 W. N: a5 }" _                    report=key;% \3 b# B3 J; H6 O1 V% @
                    break;; \5 T1 e0 p! c0 ?2 A" N: n9 k
                }
6 C: C3 \+ q. y' l            }
/ ?3 P) w$ L* w1 Z$ T+ ~        }
1 o* l- U. z# h  a    }, w4 U2 T- v' D) @- h; {0 `
    for(i=0;i<4;i++)
& p7 V# D; q5 U' `9 s: g- g4 f        USB_PMA[192+i]=hid_report;
7 N, P% E* M. @" k8 Y" @    USB_PMA[5]=8;   //COUNT1_TX5 ]  K0 {8 I# Z  l
    if(ep1_wait==0)
8 q5 m: k& b5 E" ^) X3 R$ m    {
: y4 Y; D$ w7 M3 @4 E4 s        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
; e, R2 s: |$ a" S* f        ep1_wait=1;
6 g) q  k) ~* o' @& C( w# E    }+ ^/ K. M0 b* [; Z- [
}
$ m5 e5 e# p% Z
( W0 z. `+ e9 i( _# i
  G8 g! t. ?4 O& F7 a) K完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。+ u; j6 L  n8 @  i, Y
keyboard.zip (8.7 KB, 下载次数: 6377)
' c* C  m" l$ f, f! }
' W) A' ^. n7 O5 M' s, D) I
4 s: ?  g+ l5 {+ B! c* C! _0 |4 X
, W* L! c* V- m: k
7 N5 O0 [! i4 ?6 }  ?. b5 G! h. |# ~
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。" j( A2 L2 }, o! d1 X5 u4 c! H
回复

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
* o) q; d" Q1 ?1 P6 S不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
4 W# W7 Q  W6 [4 F$ k刚开始我以为要把打字机改造成电脑键盘9 s) `- W. x$ ~% v
不过楼主也很厉害!

+ G' i, a6 Q9 Q6 N% k哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
+ m' Y) _8 k  X8 A$ x4 t
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-12-31 08:54 , Processed in 0.189727 second(s), 27 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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