找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
楼主: miaozhuang

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑 ! @9 G, f5 z! z6 M6 C: p7 B

: a5 D5 z, c* qhttp://bbs.eeworld.com.cn/forum. ... digest%26digest%3D18 ]1 O( D% n, F$ E/ ^, {
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。  z0 X" A0 s/ k5 x
: O0 E0 A4 x/ F: h
在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。
% U! j$ R& Q9 f+ L

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

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

- [, t, c9 a: t- `: l 020011osionbunl4ui44vi.jpg.thumb.jpg
' }: |/ H) D5 {* A" W轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。* P3 Q, B9 R! |$ T( {
020017j8ycmnv7788bqv52.jpg.thumb.jpg / |8 D9 l% f# @
特写,80C49
3 ]  L* u' j+ O* d( Z: g" s- C  [ 021040oujzuvtut6iujtvz.jpg.thumb.jpg ! T$ s3 @. N; y# f* T2 K. s
LED部分,使用了一片D触发器锁存指示灯状态.
  X6 T! E4 q$ U1 N( M0 c 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg ; Z1 C; H! c0 Q, R1 G! N" Q
暴力破坏,将80C49拆掉1 `# m( t3 G1 b
021113e48qq98vyohvhzzh.jpg.thumb.jpg
& o0 A  W! m8 t  U  B拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
% j4 ?4 O" e% W& [1 z 021125nc9az6dj33rlds2r.jpg.thumb.jpg
% ^0 \- W5 N) K% Z# Q$ y
焊好元件后的板子,准备替换80C49: z4 Z9 s2 z. w6 O8 {. A: [
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg ' W( J6 a/ a9 C& ~# K, H
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。
2 s1 ^2 @1 W1 k' H3 y# K5 | 021104shifhnrqbr3o5nlo.jpg.thumb.jpg
/ s' f# @+ z% b这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.! C6 W% Z! F1 t2 ?1 h! f
022003ym1p9u4ug40280uu.jpg.thumb.jpg 2 W6 j4 p8 @2 I9 g8 ^
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。
" U7 H( X, J, c9 Y4 l' ~( c7 H4 W 023313kt141q9qajtol7ma.jpg.thumb.jpg
4 e# Q- y' W, H" ]; Z- F我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。( H. V' w  f. u2 R
023322nt7l5xb3ltttkltt.jpg.thumb.jpg 1 X+ i0 [2 U5 N$ Z! j8 f
主键区键帽就位
6 g" V9 A& Q, }- U 023331hin88e8wkrwzwikx.jpg.thumb.jpg
9 C+ L* V5 A  ?; F- M4 u编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
% m5 b/ a2 s% `9 s1 I# p* F0 @0 D 023336wjzlgopugg1jyy79.jpg.thumb.jpg
( I1 B6 A  `; K1 t* @4 c最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。7 y; O( @! K( r
023341sffu4j3g2323h6fl.jpg.thumb.jpg " ~8 G2 r2 [1 P* M  N

! _! r! k8 _) l: {5 E7 a----------------------------------------------------- 分割线 --------------------------------------------------: C+ j8 Z7 h3 T) }# e& W
: f5 H+ v$ b# S

! o) V2 p) q2 ^, R. _  }5 F8 L0 v8 r$ m
7 ^2 D- {7 I" e2 e# M3 }4 g) O: H8 b

  X3 e6 A! A/ R  j3 {
# Z' u* G. {% Q( n' A; S

2 e  X8 z8 p0 p" Y% [( C0 r% f

) j. a# O2 U/ g9 e0 g, ?3 T! j8 P( b, f* J) u: R! A- T  ]: ^+ s
& Y) A, z% p" |0 {* Z. ?) l
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
5 N$ H5 }. S1 O& v2 F" ]. x* g80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:! M0 }! t# b5 E* W- d
104025nzibm2rmiomhyirm.png.thumb.jpg 3 b8 @& X3 v( g$ X/ Y4 }4 h6 E
% h3 p/ C: d: ?  I& }
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
- x( d: g" G- Z; O 105004zkrez5houvkkznko.jpg.thumb.jpg
4 L' ?) P5 z8 s* K6 t
  {" r: O3 @! {) o$ I- R
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
& b/ b$ W% J' K
; Q% q/ ]$ W! W0 A9 U2 K8 N4 ?这是我设计的电路图:+ O' _# O* r. C/ e  R
110344ej2z2oo2rflo7oe7.png.thumb.jpg , @1 m+ D4 ]* b& m3 W% y  U

) P8 P$ H% `  l; @, z0 kPCB Layout:
7 u' G8 w7 _3 {( E 110847jjbjvt34vwt3v5bb.png.thumb.jpg
& e- G4 A6 ~3 b

4 @; @! J- w3 b: h( v不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. 6 N& p% F, Z  ]# m2 v% Z) m

+ E: N. {" L) h! G; z
6 A# y% H- S3 I' y
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 9 n8 }! e# g1 l
) ]# L9 t- n9 c4 g  P8 K1 \
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。7 w9 k+ s( U0 W" a& S& O) U$ N8 }" }
5 ]8 X; ]1 s% T3 _
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:; q8 Q+ |( R& O) L2 {1 I
113818pmrfsb6z0byt6t06.png.thumb.jpg
! J4 t: X! d9 @6 f* O) L

3 v2 ~0 N& G( S其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)2 M8 \1 P4 G( H0 A: W% W( r

* i  a4 G. ^2 `) t: bUSB的中断ISR,bare metal哦
2 w+ L; w# }" ^6 p7 P

9 n* ?( U* p7 B3 qvoid USB_IRQHandler(void)& H, Q6 j/ H4 v* ~5 ^
{1 _: P3 C. b6 x6 L6 z, w2 G5 @
    if(USB->ISTR & USB_ISTR_CTR)7 L1 T+ z: R# I% w
    {& y; h# o- e( a
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
6 z, a  `* [8 I/ N9 H7 O6 P        {+ b- `5 S' k' t
            switch(ep0_state)% m# P  `$ b1 \) N; p4 X
            {- r9 ?& m6 v' Y1 `2 j: z. u
                case 0: ep0_state |= 0x80;
4 a1 ^1 B" R2 f1 N$ h5 Z3 G0 ~* i                        break;
( w, r% n; J2 f/ A6 y9 {. c                case 1: if(USB->EP0R & USB_EP_CTR_TX)  Y! I1 H* |+ H2 ]( V/ \6 ]
                        {
6 t8 P& n/ L6 ]                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;+ }" K7 k! L$ q' u! A8 C4 s/ p1 @* ^8 f
                            ep0_state=3;
5 J7 O+ X1 K/ O9 h+ B6 n                            return;/ W+ |$ T1 Q8 r$ V8 {' o6 R
                        }3 P9 c/ h8 a* @0 S+ r
                        else
7 S% C( M2 N' \) j                            ep0_state=0;
2 o; |+ U/ T& J5 v1 N8 Z/ r                        break;
" A+ t8 P$ e' {- c3 n                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet5 l+ U! V+ [' _- x
                            ep0_state |= 0x80;* O; t6 U5 d6 o+ K9 P1 l9 Z
                        else0 [: {9 D4 S" e# C9 b
                            ep0_state=0;
$ w& u' }2 E2 E                        break;# d( U2 T  P& g. ~! f9 \
                case 3: ep0_state=0;
0 o# `# Y- R1 i$ u9 j: m  n- {3 s                        break;* `. O& a/ U+ h% j# C8 |% B; A
                case 4: ep0_state=0;
) s% C! W) B- d                        break;
( I( l+ m) O0 \7 b( ~                default:ep0_state=0;6 [+ h2 z! f' x0 Q
                        break;6 t; J( P9 ]3 M5 v4 F. w0 ^
            }$ y4 D5 `' W& d1 d
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
7 q# B7 h% g4 f: S) E            return;
0 Q# d) Z$ ]0 f4 O- k+ _        }
2 q5 t1 `+ m3 b        else    // EP_ID can be 1
/ h0 e/ Z. F: p4 w, \6 H        {
/ _" ?% ?: K2 S2 `3 o            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag& Z: Q+ |2 U  C' v9 p' ?6 W3 R" G) z
            ep1_wait=0;8 x' v& h' ^1 ~+ G& ^
            return;: F& @) f7 t9 q1 v7 B
        }* P5 ?6 x: J; j$ R6 T" P2 \2 Y
    }
6 n6 N" @' P. C: L* m1 s    if(USB->ISTR & USB_ISTR_PMAOVR)# o1 U9 N3 {, H
    {6 v8 ~. v$ I1 O1 q: V8 D) `
        USB->ISTR = ~USB_ISTR_PMAOVR;5 \! u0 b  ^; s2 Z
    }$ L, {7 G" V' r0 g
    if(USB->ISTR & USB_ISTR_ERR). r( D/ h9 w( F7 a
    {; G! Z3 A+ e% f2 n- }
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
0 {! W1 T& ?# j3 O  ^/ _- {( l+ o    }' ]  t2 P  @+ s! B
    if(USB->ISTR & USB_ISTR_WKUP)- ~" Y' j' f. J1 s! g1 a' ~) w+ ?. Y
    {
. P' l1 x: K. O% ^! f8 K        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend$ q# O, W4 F) |. A1 L; ]/ m2 X; x
        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear6 A' v$ D' g. {7 ?
    }
2 @1 a" S& ~$ [2 A0 Y2 ?/ w; S    if(USB->ISTR & USB_ISTR_SUSP)) N# v! c0 ^5 [0 b, B1 Z
    {
$ H/ }; }* Z/ i" h9 t1 F3 n" j        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend5 K* A3 z) v' S3 A
        USB->CNTR |= USB_CNTR_LPMODE;   // low power- ?& e( B2 }- T- K9 e. }; B) n
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
* u+ R' j2 y7 E9 K4 {& h    }
! H4 G. l# i! _. z    if(USB->ISTR & USB_ISTR_RESET)) ^) N% F6 L5 N9 h; j; y2 V
    {
8 a' A9 Z* A3 |& U* K3 v8 n! u        USB->BTABLE = 0;    // buffer table at bottom of PMA
' x5 L. \8 V" L% l6 K( U5 G7 \6 Y        USB_PMA[0]=128; //ADDR0_TX. A6 P; X) N) v8 n) W: N1 w
        USB_PMA[1]=0;   //COUNT0_TX
+ S) |- R  q& T* W. p4 I& A        USB_PMA[2]=256; //ADDR0_RX* d7 D. W2 f, S6 p, w. V3 X0 n
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes  `) n- _0 O: ?
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;+ B, ?3 Y6 B3 z+ R3 C4 a9 s" X7 E
        ep0_state=0;" ]* t6 O; X7 i0 o4 W7 a
        USB_PMA[4]=384; //ADDR1_TX2 q; {2 D: G1 a1 U+ \
        USB_PMA[5]=0;   //COUNT1_TX
+ C+ y" D6 Z5 X) l3 t# x1 N$ D1 ^7 \        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type! r+ s( r; @+ R& _
        ep1_wait=0;
) x, S" W* `. M' Y% }3 p        USB->DADDR = USB_DADDR_EF;      // enable function
5 m/ T  a# L' i3 i6 i# e9 r) x        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear7 r' @& H/ m) j+ j; `
    }
* e0 z. B5 ?' P1 {    if(USB->ISTR & USB_ISTR_SOF)
, N3 t+ W3 l$ x" ]# Q    {+ A! a) E8 f5 V# n
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear/ b( `! p# @) X% |' k9 {' `! ~
    }7 Z- k1 l8 Z% z0 L2 r# v
}: k+ W) ?' w# t
2 q# x& Q  P& D) X
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。4 S. L, a+ @# }5 j6 Y" {- Y
: s) O0 Z8 l  e- i% L
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。* C) i4 V3 I4 a
" z! x8 L9 Z, w: J: r
    while(1)# }& y9 ~* I& `& b1 E+ r& i; t
    {
8 ~# @7 D& l  L. e7 ]; T        static char row=0;3 l5 M  T1 t5 P  a4 ]6 ?3 |: J2 }
        __WFI();
* n( z8 |; ]3 p+ d4 \        if(ep0_state & 0x80)    // request data processing
" V, c1 K4 n/ C; d        {+ D! ]( r: G4 y' @
            if(ep0_state==0x80) // SETUP phase2 b8 |" t% d1 S' v+ z
            {3 ~9 E# q4 |, z
                if(!setup_packet_service())7 K: Q% G( }& e0 P: L
                {/ z$ d% t) I3 x
                    ep0_state=0;
$ w6 W( }  ^* F* n                    // not supported2 u  Q5 F5 H! w& r1 Q0 t
                }$ U6 R, j0 }% m- R6 M
                // ep0_state should be set to 1 or 2, if processed+ r8 S. P  R, y3 I* d5 |8 O
            }
% v; V9 j! \* C) b6 [3 k            else    // OUT phase
# ?+ c6 ~6 I# u- [+ e5 g            {
: A8 o, H5 z5 S$ c9 q$ V) E                // process data
) D" q  {+ Z0 c- x; Y+ d: \( J                show_LED(*(uint8_t *)(USB_PMA+128));% n3 B' w  y; y* b
                ep0_state=4;
4 e" j( H  ?# p: j+ u9 d                USB_PMA[1]=0;       // Zero length DATA0; }# U, E+ F, c7 b& }
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
: P8 I3 T1 x8 O  }# d/ ]! F; q            }- W( p7 l' q8 q0 R) G
        }
$ P' a) \  m$ t( b/ _        else& ]4 V, r$ C5 A; z8 z" G& M
        {3 {/ M8 Y* w. o1 @9 i
            if(usb_address && ep0_state==0)
& s+ C  |, \: Q5 f) l            {
  i8 R( F: O) m$ d3 P; {& R4 ]! j8 r                USB->DADDR = USB_DADDR_EF|usb_address;2 {/ U: D& Y1 w  G. e3 F) l
                usb_address=0;$ _% _, n* R. q% j+ A% h% i5 V0 T
            }- q. A; _  d! M4 J4 v7 p  a
        }5 S2 f1 u( Y4 G5 c
        if(row!=scan_row)   // new scan line
( x" c, K/ F8 H% i- C0 R        {% j6 N3 a: r5 |: `- R4 b
            if(key_state[row]!=prev_key_state[row])
/ i. ?& U, |/ x; d9 B            {( r; h1 r+ u$ S
                uint8_t test=0x80;
& Q" O9 M. c1 k, B$ s                uint8_t diff=key_state[row]^prev_key_state[row];4 k3 e* @+ W/ _! ^! R
                for(i=0;i<8;i++)) R/ {2 ~4 G% y6 a$ K+ |
                {
6 h2 C# W2 B8 |. f4 U                    if(diff & test)
/ H) ~7 Y: b3 t9 R                        update_key_matrix(row,i,key_state[row]&test);7 P: d0 }- [  P* i/ Q
                    test>>=1;$ w" x6 |8 [2 S
                }. P* s3 \# j) w0 Z) ?7 V
            }
6 L, o" I, l- g; Y$ x7 _            row=scan_row;9 ?/ n" x  r2 Y) l
        }
+ L7 m6 s# }4 c2 j    }
7 O& N( v; b: G! k  B, U6 q1 [, E# E. l* d

3 d6 F: {) J2 |5 V2 ]. X& _EP0的控制传输,把用到的请求处理一下
* @0 q! k% z6 z, X8 R* r' |4 H5 pchar setup_packet_service(void)" s  {7 c6 Y3 @4 r
{9 R7 s3 s' t/ }
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific: {0 ?1 j% W# Y
    {
9 v! I) ]! N( P" c        switch(ep0_std_req->bRequest)" ?8 T$ F0 W; x) C( p
        {
7 x8 X2 q0 P0 v7 H8 b, f) z; c7 F            case REQ_GET_REPORT: break;
* E( l  @; s  x            case REQ_GET_IDLE:
) D* g; x# K4 F2 A7 S* R1 G                USB_PMA[64]=0xfa;   // return 1 byte) `3 w# G/ H; O0 T+ T4 u
                USB_PMA[1]=1;
; X0 M- H9 l$ u# `6 `! R                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;  ?$ b. J) ?! V) ^+ Y
                ep0_state=1;) F) Y" ^" h8 s( q$ y
                return 1;
( W$ ^. f( x- W1 ~# w/ G/ l1 |                break;
9 k3 b3 Z0 e5 Y8 T, Y            case REQ_SET_REPORT:
. @3 t$ n& p7 e; ]* P                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
8 q: Z  ~) ?$ Z) s1 p2 z/ A                ep0_state=2;  y' `8 f: Q9 W8 B2 p9 q
                return 1;; P# K( \1 M2 y  M0 i8 R
                break;1 L# u3 v# Y7 ]- s& |
            case REQ_SET_IDLE:
/ E8 s6 i* s2 s7 f! f3 w                USB_PMA[1]=0;   // Zero DATA
, e4 s! t# ?0 j1 o2 s. [1 }3 X                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;, O- \9 ]. f* L  p( a$ t( y& c
                ep0_state=4;
- ~; ?! v* m7 E# r# U% A3 {                return 1;
- W9 O1 S' a% u. O. m# ~- Z$ N                break;
% \. f7 `, Q* ]$ w- r& d0 [" z        }
8 l& m4 i- Q4 Y/ m) U6 v9 x        return 0;: E: C) z2 S* @7 r# R0 [
    }2 p0 T* [' R; S, P
    else    // standard
/ j, R5 [) q+ p& O    {) B$ m3 t* w% P. i6 q3 m4 U
        switch(ep0_std_req->bRequest)
' l" H( l) Y6 c: q& M. w- H5 f9 {        {
3 T& ~9 r8 q: v# K, ]. ]/ U! m! b            case REQ_GET_DESCRIPTOR:) O7 G. u" M: m$ F( F' E% r
                return descriptor_service();
, E! U8 {5 \, i& T% d9 |                break;; H9 w, l7 r  S; K! n7 _
            case REQ_SET_ADDRESS:( m8 E6 X% X/ A! v9 B; ~
                if(ep0_std_req->bmRequestType!=0x00)
3 ]* b) H: j3 N) x0 X$ j                    return 0;7 v1 Z. ^5 k# P) w
                usb_address=ep0_std_req->wValue;4 \7 l. x! x3 u. c: G
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;" F5 d0 ?( C+ z5 v- ?
                USB_PMA[1]=0;       // Zero length DATA0
& {" V0 ]) H4 c! Q' i, u: q                ep0_state=4;    // No Data phase
( @: v- F/ o0 h1 _2 Y8 P+ z                return 1;
9 `; l4 s1 W5 Q/ m% Q! L0 t4 O; b            case REQ_SET_CONFIGURATION:
9 |/ a* W5 L0 _                if(ep0_std_req->bmRequestType!=0x00)
+ a6 ~4 ]% ~6 z8 G4 _* W                    return 0;! Q' R& `; T( G0 |9 G+ Z
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;( m( A6 I9 M  D& q3 E: g# b- z5 g* |
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;/ O$ C9 ?# H& Z
                USB_PMA[1]=0;   // Zero DATA
+ h: u/ ^& c* @                ep0_state=4;    // No DATA phase4 I0 j8 c2 R6 w' W$ ^5 m" m
                return 1;
% M  C0 S1 [- q; N- A            default: return 0;1 H' z+ E  W  R4 Q; ?& _8 z6 e/ v
        }/ @7 h7 u3 g" }" B1 ]. ?- q
    }
& c# [; M, U" g% O2 q% g1 X. D}9 U$ ~) b* R& v2 a6 A4 T' s

$ c& x" ?3 r. f8 a& {) z% \) r. R1 \: X
6 z# z  n9 Z' M( ]# q
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
  q6 v6 x: G. h2 y- gchar descriptor_service(void)6 L5 ?) h5 A0 y6 L
{9 T1 u! }* x  R3 J1 \" D! q/ D9 _
    switch((ep0_std_req->wValue)>>8)
0 C- q2 ]/ G; ^& F3 t    {
8 b5 b1 v# H3 b; B        case DESC_TYPE_DEVICE:
+ E9 K6 |3 J" r( G5 a& H& v3 z& F            return ep0_preparedata(&DevDesc, sizeof(DevDesc));" U+ z: ~9 e/ u- q9 Q! p
            break;
9 P* e1 @& i2 c        case DESC_TYPE_CONFIG:
3 b: Y2 ?" ~& S& h, ?: u+ @            if(sizeof(ConfigDescData)>ep0_std_req->wLength)
+ N9 l0 l. v- B/ A                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
  Y+ L" w2 W8 O' m8 m; @7 k7 P            else
* Y7 |- v% U  u2 p$ o1 P1 A5 E, z/ `                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
) H& k' j& b8 w            break;
9 |6 T$ a0 t, |8 I* Y        case DESC_TYPE_STRING:
1 M' [- c- m  D0 E5 ^! N1 o1 A            switch(ep0_std_req->wValue &0xff)
. `. h; P/ w3 k8 f5 o3 g8 `            {- l1 _7 H7 T! a; F1 |
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
2 r! w8 [! R  F- r# T                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));/ h- w# w' Q! j% C4 W; d
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
2 ?* S0 y" e( F& i                default: return 0;
+ o4 l6 W. }/ W4 ~5 E7 p- {) |            }
& m, I7 V( t5 h. B; g- ]0 j            break;
/ ]" n3 ^$ l, X& n4 O        case REPORT_DESC_TYPE:) D9 M) u% V0 O/ @% [7 M6 y
            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));2 N- J& h& u: f) N& G- p5 {
        default:5 c7 ?" x+ q$ h5 x5 p1 f7 G+ c
            return 0;
6 ^3 g- M* F) w' F! ^5 B. g    }
2 a5 L) F* {/ O7 ~  b. v}
7 D" `3 x. y# l6 i下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
9 Z4 N. \0 u1 h, R  W$ w+ E$ svoid TIM6_DAC_IRQHandler(void)
$ B- C4 I. H1 Q( ~- o3 Z5 }{
% `# n) g% J  L" ^" M    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);2 `3 j- t2 B- S
& {& {5 p3 ?. H2 j7 p; J$ V

' T4 N6 c# ^, S9 v    TIM6->SR &= ~TIM_SR_UIF;) f1 R/ W0 p3 @: b
    prev_key_state[scan_row]=key_state[scan_row];) P8 {* e% S9 Y7 j! o
    key_state[scan_row]= *PA_IDR;   // update key states* T0 l  T5 c$ s2 o
    switch(scan_row)! I  H0 A$ @5 L/ S2 W) e# x) G
    {3 G( }/ [+ q+ j% C* U7 C
        case 13: // next row PB14
1 @( p, J9 E$ T: b/ F. S+ ^( `                GPIOC->MODER = GPIOC_DEFAULT;
" p8 b. d3 m5 Q( B9 K                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
, i0 j) Y0 Q" K. E                break;
3 ^+ ]6 D  T5 m9 ^, @9 d- s) _6 W        case  0: // next row PB15
& g+ e# J( g+ V5 J- m. z2 t                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;
' ?6 j/ Y5 u1 M) Y/ f% c, G+ z2 O7 d                break;
( X0 c8 G! S8 B; h8 {8 e; s- _6 G        case  1: // next row PB3
6 `5 v8 z3 B. A, R  x                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
1 b% h0 q  j9 Z7 o" Q: X4 ]                break;  o8 C+ Y% f( o
        case  2: // next row PB4
7 H: g; A+ g$ ^  _                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
/ \. V6 c% F3 @  n, o- e8 r                break;( x, D! G1 m, q0 ^- V* I- r
        case  3: // next row PB5$ Y4 A5 e& f, n1 c+ E
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;( x( W* T- A& I- k. c% k
                break;
$ b4 u( F: a; z1 d        case  4: // next row PB6
( s9 y& r4 a9 e" L4 P" S# ]" D) v                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
7 }& j) Q+ _7 K# y! ?                break;/ K% `' C! y1 P: Q+ @" d- k/ I
        case  5: // next row PB77 Y- D3 ~& u3 Y2 k9 b
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
1 k2 R. Z+ ^; Z$ z2 ^5 a9 q2 X                break;" T  K6 m$ _( _( X6 f1 S. U% [7 \
        case  6: // next row PB8
9 H  W6 E# X1 r$ @% X9 A                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;- z& \; Y. [/ Z1 S, U4 c' C
                break;
  w, _) j$ G4 T0 n" ~6 r        case  7: // next row PB97 c4 l' W& S; i, U4 ^; N& Q
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;
$ P. }- j7 J3 M" e3 _0 [                break;$ U( p$ c% c9 D6 X) p& j4 K- ^
        case  8: // next row PA8" |. A5 i! F& Q2 u9 `" K+ M
                GPIOB->MODER = GPIOB_DEFAULT;; w7 x0 z6 f' t  m! T* E
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;. A, ]& |7 n: m) K" f
                break;
# \3 Q. Z) N) e( V9 ^        case  9: // next row PA9- O% o" e' x+ k7 j  q; N+ m
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
. h4 U& \5 L5 ^* M+ g                break;# _; k8 R% k1 G0 S0 x7 s1 o* M
        case 10: // next row PA100 C7 d/ P( Q& J6 L( F: D4 H
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
9 `4 }4 M6 @3 R# O% g% j                break;" ^+ F, N1 z  I5 N" @/ }
        case 11: // next row PA15: y. {9 F" f. L
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
0 V" y2 {6 u! }5 s7 ^                break;
" q3 C# Z! u+ `2 u: `# D& x' r        case 12: // next row PC13, `- N, o, c7 s; g; L; B
                GPIOA->MODER = GPIOA_DEFAULT;1 \$ i3 e2 j% `
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;+ I* @8 G5 w" B; J7 z, K
                break;4 R+ }1 v: l& l7 J9 v5 Y
    }
, {3 f$ W" g6 L( e& k  ?' \* V- k# K    if(scan_row<13)
8 p  i  `2 a' J5 F" [% U        scan_row++;
: K3 ^' N- }9 e" P5 N  @/ F" @    else
4 V9 P2 G9 u1 e  q! C9 I: r8 k        scan_row=0;
! N& E" k8 c9 e1 O% f}2 U+ ~- i$ Y0 V: Q2 e$ [' B1 K
$ a- k5 _4 X& I5 I9 A

$ E* m7 r8 S5 k- ]+ F) a3 e
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
  p: K) @0 @! y! e
  p) M$ l1 C/ ^4 Y+ D扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
9 E% B) Y; ?! K, X
; L) v  o) _4 L, q" f5 l. e5 ?要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:

) t" s: h+ T5 q4 B
- X" L7 X/ m1 F8 ?7 `* e* e6 M3 u# t3 E0 C
const char hid_keymap_qwerty[14][8]={6 q' C+ u7 u) [2 Z
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
' R# D* g& z# L% |    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},8 J3 m+ j* ~4 @. D' {  v  O
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
0 r% L- v; C: V8 b! c    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},3 V% |# F5 v8 _$ X! f) f6 Q' h
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},+ s: K5 \  D! I( s9 U6 ~% t
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
9 e, ]6 |8 b5 ?# V    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
1 r9 q) h- e2 T. q- Z4 H7 D: Z5 z    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},, u7 Y& P- r+ y
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},8 ^5 S! ?6 I- m3 ~  y  {# P/ j7 H
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},; z$ e1 P" Q7 I8 l, J
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
# N; h; o8 X5 J0 E    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},& M2 C+ ?8 S! `- d2 _4 Z
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
9 T) D) G+ G8 K% ~    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
" _4 f2 `. h* X  z};0 R. W8 l) Z0 y7 V5 [; h8 o" w! N3 ]
/ A; y- y1 g6 D2 E  J

; T$ ~# b* {* `  y6 j, Pconst char hid_keymap_dvorak[14][8]={
- \! s: R) N7 ~5 `    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
! ~, B  ?/ S. x9 {/ B  H    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},/ f0 H' r/ g7 _
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
; ~. f3 O9 S: U    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
! j( u$ e; B% O' b& c4 a    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
  G# S# ^: n- Q; g    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
/ q1 ?- r  t  v* t; I  l! M    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
$ G' f6 u5 G+ A& _' y9 D    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
9 E2 l6 n" J- p2 ~. C    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},- n3 j. J0 B5 X% D( g8 N
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
: M$ p7 x4 J. o9 Y3 h& ]! J    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
. S6 \- J  d( y9 Q( g+ U    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},( N1 d: r* |: ?+ `5 o4 d* t
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
3 g0 o% r0 k! x3 A' \$ x    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}) P7 @8 ]0 h7 ?
};
9 y1 r/ J4 d' W" _# ^: K- N4 _) @+ i# |

- Z$ e5 l0 h  f& r: f5 C上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。' j' Q, Z0 c7 L1 m9 g: O) r5 w

; p! j+ N5 g# H( c2 C" X# {& aHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
9 B2 G2 a. R6 D
3 [% h' j8 T5 k; T
void update_key_matrix(char row, char col, char onoff)
6 T" x5 t+ V8 f; ~6 a{/ g* P+ i) l8 x8 I4 o* Z7 ?  H- P
    static uint16_t hid_report[4]={0,0,0,0};# S* k0 C% a5 N( l% q
    static char (*hid_keymap)[8]=hid_keymap_dvorak;9 Y% Q- R$ R2 U6 v% K
: S4 y4 r0 \: h
" Z* D# I# i) X, |0 b
    unsigned char key=hid_keymap[row][col];
( j+ ?8 ~. g/ _+ l4 Y: Y: V    unsigned char *report =(unsigned char *)hid_report;
- B+ V; y8 t/ L6 o6 I# J# W# Y    char i;
( l) g& ]& f1 r& I9 d8 ^5 T
- A, j& Q7 d' N2 S5 W2 F

8 D2 S0 Y2 @" p+ P5 S2 v+ k; _* i    if(key==HK_MODE)) W1 I7 W9 E" ~$ N: a; [/ _
    {
; ~1 J2 h+ K, K3 m, v4 K, H        if(!onoff); k. C0 ]# {, D* N) l8 H
        {( V" C! B* C9 {' D& u- n4 ]0 B
            if(hid_keymap==hid_keymap_dvorak)" `& m2 e3 [! t, |7 d' c. `' E
            {% l& F3 E' I# K, a( j3 a( W5 s( l
                hid_keymap=hid_keymap_qwerty;4 V2 q: b: h/ k
                GPIOB->BSRR = (1<<2);- C: w0 `! O9 e4 C% \
            }
+ a. m# c! V" U# d0 b            else3 i$ d$ _/ \1 a- T* G: w: J7 u
            {
* ~4 `1 e) v# G9 h                hid_keymap=hid_keymap_dvorak;
% D4 X( Q" i7 M) ?7 n" |: \                GPIOB->BRR = (1<<2);" P8 _" X- f- f, I- `% t3 u  S
            }
1 m; H5 i! }  R* e- ~: i6 e        }: H; u+ W0 c( v( w$ i& U# _) m9 c5 M$ _
        return;* m0 s, z, x' @
    }+ Y0 F! {  o# Y9 z& `+ ~

% u$ O( Z2 ^/ E% U4 G( K9 R4 q, ]

/ q$ R9 n/ z( H% F    if(key>=0x80)   // Alt, Ctrl, Shift
' o* j1 ^* V' {5 K, B    {
+ Y# u! M8 y% {% t# }8 j        uint8_t bitset = 1<<(key&7);
9 L3 ^- \$ A8 x- J        if(onoff)   // non-zero is key up& K$ T6 n5 S1 A
            report[0] &= (~bitset);% j# f9 c; ?/ J: X4 |
        else6 g; }* e4 G$ y! {7 \1 P
            report[0] |= bitset;. M" c+ c9 T/ [) l7 A
    }
& g" g8 s$ ]  X4 u    else
( {% `. P2 w6 n' V0 ^8 Z% U; h    {
0 x" O7 j3 z5 b* K/ x# M        if(onoff)   // non-zero is key up# T# p- v$ T6 s. [: ]
        {; Q9 x0 p0 t  ?8 j. c6 \
            for(i=2;i<8;i++)
1 w8 J' [7 n' v, h' @; x6 ]6 P: N            {. j  F2 @! T* y# H% ~! {$ J
                if(report==key)
0 U% d4 ]: p5 ^: A                {" ]5 R( t- Q3 y9 N/ l$ ~  T
                    report=0;
# i6 b2 V& s% j1 b3 q' u* f1 N' v/ _                    break;
$ L( W7 c) A. D                }
3 ]8 E0 W+ z3 y$ g% u& {            }) A0 j7 i: F2 J7 G. i3 P
        }
# R" S" V6 b2 o, Z" h7 t        else
* `3 n2 l- h: w5 N) e' c$ G        {
7 J8 B- g) E1 |( l! s# `            for(i=2;i<8;i++)3 Z9 D5 D$ o& z* q% Z
            {" O$ c: f) p2 [) a5 m5 y1 a
                if(report==key)
5 ^7 J+ G$ w2 L( o. k5 M4 R6 k                    break;! h) E9 q1 b8 J2 a8 t1 A2 O$ t
                if(report==0)
# n$ K' D$ A9 r+ E# T* n% G* v                {8 \" d! |: U6 m6 E
                    report=key;! }$ ^. `" {) Q4 {$ V. e6 Q! O
                    break;- ^" x: w8 G. K, w7 o
                }; I# O& z$ g, z  v1 t( u* |9 ?
            }
, y7 @: n& w2 b5 S        }
& V' m* n" ^' u( }9 x7 J# ?    }. B7 @; ^7 }+ E: @
    for(i=0;i<4;i++)
) \- v4 y. Z9 z7 d; L# u( B        USB_PMA[192+i]=hid_report;1 M' H/ q+ _; Z) @7 F
    USB_PMA[5]=8;   //COUNT1_TX' N) J& E/ Q  h9 W# Y( p6 e2 h
    if(ep1_wait==0)* M, ~5 m/ u$ g8 N0 r
    {
9 u* D: g1 a9 ~. P        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
  K: a+ \, k* m4 `        ep1_wait=1;; I/ a8 t2 u- O( s4 D
    }
: G5 n8 _. \1 [! c# p}& n# [; m' C7 M7 y9 L+ P  A

4 j; T5 Q+ u/ u8 c+ E- \( u' N% ^5 `! _
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。) |( t$ k# F8 b( h, g: ?& {1 s# d
keyboard.zip (8.7 KB, 下载次数: 6442)
- C1 d; b( f0 r: o
$ I' J3 A; s$ F6 }) P( c3 A

9 X, E7 t( Z+ k+ N* o
2 ]. q+ X0 j9 m3 v
/ ?( C! n4 R* t/ ^& _5 k- Z" N
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘+ V( H* {7 e) C' a: y- Q! E
不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
1 U$ a* V. E9 Q0 y. |* c& j刚开始我以为要把打字机改造成电脑键盘. m! h' @" D( K
不过楼主也很厉害!
$ i. Y8 m# O$ E# X5 x; c
哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害; u1 M: F8 h7 K& E7 D' e+ W. o1 J
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-3-12 16:00 , Processed in 2.323598 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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