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

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

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

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

[复制链接]

24

主题

63

帖子

287

积分

vip

Rank: 9Rank: 9Rank: 9

积分
287
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑 0 B  z* l  G! Z/ R$ L, a3 M  X) M* k
# f* r1 i! d* p" X) `* p2 |6 y( o0 G% f! g
http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
/ b: R6 g( E( \这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。: y+ I- p6 ^: c) h; K6 o1 Z, V

& h! ]5 w: z- B, O! `) j; \在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。

: @3 G5 B4 I/ x1 O5 O3 t

+ l! Q1 Q4 }  c4 | 235140i3a36qivqzuvmt5q.jpg.thumb.jpg
- b) [/ j; h9 S
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。/ B, f: Q% q7 r+ v) ?
001734klbyoluenuwz4h4b.png.thumb.jpg
2 [1 g! I0 H7 Y& Y为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:1 Q: N6 m+ @/ c8 B
003625r2agx2f5v922cf2f.png.thumb.jpg 5 O8 N, s, N% O5 R( Y
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.; p( g5 ~! E7 X0 T. ^7 l. j
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:

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

% |5 Q  M- f1 v/ D* H8 w 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg ' i( k! H: Y+ h, }
----------------------------------------------------  分割线 ----------------------------------------------------------
0 s+ M% F3 s$ h6 ?1 Q1 o7 Z3 \7 V/ g5 \( j. A% a! x% V, U8 k0 _
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
; a3 r8 ]- o5 D# Q5 d: G3 i5 m, J
020011osionbunl4ui44vi.jpg.thumb.jpg
0 t9 ?0 B, N( ?* v* p轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
8 e6 M" Y; Z3 y 020017j8ycmnv7788bqv52.jpg.thumb.jpg
1 l. @9 H* ~7 n- c% N# b特写,80C49
7 j' a6 x" \1 [% v( z5 T 021040oujzuvtut6iujtvz.jpg.thumb.jpg ( \) L7 u# X0 c2 \! u2 W
LED部分,使用了一片D触发器锁存指示灯状态.
* f7 n; Q: o! M5 {( N 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg : m4 H1 }; G. d) P
暴力破坏,将80C49拆掉3 w! j. \+ g2 k9 t4 D9 R
021113e48qq98vyohvhzzh.jpg.thumb.jpg
+ R* T8 [/ {" P" W* A+ g拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
4 |& ^: u) ^8 B3 N2 b- L1 M$ v2 L 021125nc9az6dj33rlds2r.jpg.thumb.jpg : d  C- @) s% }6 R9 Q6 w8 ~$ C1 @) Z
焊好元件后的板子,准备替换80C49
6 r( U& S1 c9 B7 @+ U, V 021146zmmpmh4pav4o7g6a.jpg.thumb.jpg * D1 t6 u. B# z  l# i
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。% Y2 V5 n0 o) C$ R/ G% o3 L$ s# I
021104shifhnrqbr3o5nlo.jpg.thumb.jpg
6 K' R: r% G8 Q: J& \: x这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.
8 p1 q6 ?  v1 C+ Y 022003ym1p9u4ug40280uu.jpg.thumb.jpg , Z% I5 ]8 w( f8 ]
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。
5 f7 j6 Q# I% v5 S$ ?# z 023313kt141q9qajtol7ma.jpg.thumb.jpg 5 r( Y8 J6 [9 n% U6 w2 n
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。$ v' \% Z. R0 R- S
023322nt7l5xb3ltttkltt.jpg.thumb.jpg
$ H9 J. X8 \1 c1 y" i6 }& t& }  b
主键区键帽就位8 G+ z3 q) X* T
023331hin88e8wkrwzwikx.jpg.thumb.jpg 6 q: }. R3 `  W& \1 p0 C- s
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
" K1 C# U4 t; n5 L% k$ R+ c: K 023336wjzlgopugg1jyy79.jpg.thumb.jpg & E1 U+ r: ]1 g3 x/ \
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。
: b8 v3 d  e/ J# a; w- | 023341sffu4j3g2323h6fl.jpg.thumb.jpg
7 X% H+ q: I. O7 M8 Z6 p8 a

7 ^0 C& y; z' F; e7 t. n----------------------------------------------------- 分割线 --------------------------------------------------
' ~8 U( o- a$ [( ~: r5 B9 J; v

# G) Q5 R. P9 R# x* l+ ^; Y
) i. s- [" l5 Q) ?- a6 g+ h6 o
4 `( Q9 q  `, ?" m
5 d3 v- U$ S; d. X; Q6 }8 C5 i

" J8 `6 `6 E* q' I! L) f

: j/ U8 \' n# z/ s' t4 G  B% u3 S5 f) F
) f; |1 E  ?8 M+ _3 L
2 N. |$ Y6 [2 p1 N

2 s/ D1 O$ f& M+ [9 r7 X1 N5 M! ?; ^
回复

使用道具 举报

24

主题

63

帖子

287

积分

vip

Rank: 9Rank: 9Rank: 9

积分
287
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。4 ?* E1 c: j  {2 K4 c2 C1 E
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
, U7 C3 ]# Q1 O" c9 C; q, f 104025nzibm2rmiomhyirm.png.thumb.jpg
$ Q5 Z% a) D* |+ k7 _

& P8 W7 l; s2 d' Z+ O# I. c其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
2 I& u) _/ |/ } 105004zkrez5houvkkznko.jpg.thumb.jpg # F' ^" Y1 |/ s' K/ K

9 w# d3 M3 S3 Q$ ^  p# |8 ?扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。7 r$ r" e- V. H! I- S$ F

+ p, K2 k) V3 x( X这是我设计的电路图:
* _% c  @3 u! |7 Y 110344ej2z2oo2rflo7oe7.png.thumb.jpg
( T& D) _! \) K) c7 o8 x

$ j/ Y, x7 m! ?  R( N: ^PCB Layout:
' b& `+ j" C) H# Q 110847jjbjvt34vwt3v5bb.png.thumb.jpg
5 e! q0 ?( |) l8 S7 H' E

6 h0 U  p0 n, X$ x/ E3 E不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. ; f- ~! Z: c4 q6 l1 j
6 C9 f6 X' f- _9 C  v
0 o0 ?' Z# e/ G$ K4 u
回复 支持 反对

使用道具 举报

24

主题

63

帖子

287

积分

vip

Rank: 9Rank: 9Rank: 9

积分
287
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑   S' ~  I  }+ D

& ?' X. E' Y* o( T5 s; O软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
+ l9 [5 r; ]% e2 b
* p0 U1 \, l( p5 ?. P4 u( l总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:5 @4 [  A; S" [" J* }7 H* p, t
113818pmrfsb6z0byt6t06.png.thumb.jpg
4 D" T# c4 N; i) G
8 T8 q; T- l0 e2 J8 @
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
- K2 T& b  y+ z; ]6 x. N- s, ]& |2 q4 W
USB的中断ISR,bare metal哦
3 Z5 c% x- ]; ]5 k0 Y
4 o7 x$ V7 v8 O3 w* `
void USB_IRQHandler(void)$ |1 A9 G, q* }2 [7 w
{
& I! `) Y' I4 h) J    if(USB->ISTR & USB_ISTR_CTR)
" X( o# ^3 u/ X  z6 L, K. U    {7 t0 t' {. s9 M' `2 r  b6 C% O) w
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
6 g" C% A; e7 W1 X; M# @+ ^. t        {) k0 c) x2 B! P8 K! C/ T" ^/ T
            switch(ep0_state), Q: u* r1 T, |
            {
2 n1 {# t' O7 Z7 B; e, u                case 0: ep0_state |= 0x80;( r( y& t% @" z
                        break;
) i7 g4 r$ V* R# p: o% X% `& v                case 1: if(USB->EP0R & USB_EP_CTR_TX)
( u" Y, x$ y$ O, e* y                        {$ B) V0 w# s) C5 o% O+ d
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
- B( Q! w! b( ]# f+ m! f                            ep0_state=3;: j) q+ a: y7 b" F* i( E7 {3 |7 ?
                            return;
' p1 C9 f6 k. K2 j: e                        }2 c, Z  ?& X/ X) D' }5 K% I
                        else, A9 @( ^' H$ {+ X9 g) H$ ~% M
                            ep0_state=0;" f, V& z3 t2 P( }/ D  v* h
                        break;
3 |  a( P$ M/ G- X, O                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet+ M/ X0 v- ?+ R8 T8 d) h
                            ep0_state |= 0x80;8 A8 f/ K& o) @! ^2 A, s( t
                        else# \: @/ `3 S3 n9 @7 w
                            ep0_state=0;
+ L% l  `# S  |3 S* Y                        break;
6 D$ c+ J: o) N$ p; a% |* ]- E                case 3: ep0_state=0;
4 n* X  ]0 `# z+ ], H$ a5 {                        break;
. H* o( B# S2 s! }                case 4: ep0_state=0;
5 E' a8 u; d6 G) O$ r+ M                        break;
9 ]- Q4 z$ j2 F4 U                default:ep0_state=0;( I& I7 L$ ^, h" h" P1 M
                        break;# d; H0 x( O' H- d5 n" @7 u9 f# }
            }- Y/ ?' A" e' h, s) X1 v  K0 y
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
' i. V* y1 e# f8 P1 x% d            return;
# |$ X4 w7 y, q* x        }
+ Y7 |) ?' a) D% |- J" T9 o        else    // EP_ID can be 1
6 f- l' Q6 x& ~: e        {
; o4 X* c* r: j* U! T- J            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag" i3 O& C% o8 c7 B8 k' q
            ep1_wait=0;
& |3 x$ c' \0 X; ~* v            return;
$ K1 J1 b# X! s2 F, H, t        }3 ]+ ?  g: _5 s
    }, H" W& H  c; p+ ^6 ]) p
    if(USB->ISTR & USB_ISTR_PMAOVR)
* q( A( T% L% S3 s8 Z    {+ u7 k' u* g' @
        USB->ISTR = ~USB_ISTR_PMAOVR;
7 `( v" h1 F/ e    }7 X2 D& p# d- X* N. D
    if(USB->ISTR & USB_ISTR_ERR)4 p) S( v2 t# g0 B6 M% T
    {
" y2 w7 D/ B: g2 b% l/ P        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear( I9 V, Q. l3 ]" X  m, ]9 X) m
    }
/ D, G; v1 `3 ~' T/ j    if(USB->ISTR & USB_ISTR_WKUP)
" x, h8 Q% S) M( l    {
9 e9 g( N7 f, l0 Y: n" t& b+ d        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
) ?, }% e7 ~/ F/ m% A. ^) I  G        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear  p: t. V/ W2 E
    }
  h! ~% E- F2 g( c7 b& i0 z; p    if(USB->ISTR & USB_ISTR_SUSP), T; `: P8 }/ l5 v. t2 i+ p
    {
. M8 y- l6 D. S% a" M3 A1 d9 @        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend5 s# s* z- a. \" U5 v
        USB->CNTR |= USB_CNTR_LPMODE;   // low power
" X: w5 p, ]% _' c3 \$ K9 J        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
8 a- }5 T5 l$ P( M    }# E+ K( l7 O  I0 A) e
    if(USB->ISTR & USB_ISTR_RESET)
! a" e) v% S. w8 G$ x$ N    {
# a- g: E- v$ f" \2 L        USB->BTABLE = 0;    // buffer table at bottom of PMA! e5 Z! r- S1 U0 k1 N7 J2 w, t
        USB_PMA[0]=128; //ADDR0_TX) O/ w5 Y* k: g8 j& \
        USB_PMA[1]=0;   //COUNT0_TX! D" [6 A9 ?  E5 d4 B7 ^
        USB_PMA[2]=256; //ADDR0_RX
! v1 \2 G1 G" Z+ D7 h7 h& S        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
1 ^0 n( W9 C' \$ b$ R% q$ y1 \8 d        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;9 {3 f* M7 y* M
        ep0_state=0;3 R$ g( r. [4 F% ?7 A9 x. G6 ?
        USB_PMA[4]=384; //ADDR1_TX
- W3 s7 R! z: z9 Z' M5 C        USB_PMA[5]=0;   //COUNT1_TX
: u: u5 ]6 F2 s: ]4 Z. @4 d) C1 _        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type+ E. B( u" K; ]. h
        ep1_wait=0;
$ m; ?( {% _6 A        USB->DADDR = USB_DADDR_EF;      // enable function
- p* ~/ R' ?  I" H        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear. v2 C# m' K3 ^* a: m4 a- Y
    }1 H, U* u' w; o! K" F7 i( ^2 B
    if(USB->ISTR & USB_ISTR_SOF)2 K  o0 [( q2 _0 p! d) G
    {
% }' H9 p: E8 k, A        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear
# X. l7 d! f& T. e! u    }
* w& b5 `; p- ]4 ?6 G9 d& \}& n8 k, e( T9 Q. D9 n

4 R# \9 z& X1 K* K% `: R. Y- w
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。, |. i" Y* P6 g
0 S  H) Y( |& ?0 A( V- x7 r
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
% s. I) U0 }7 _6 H% I  k; v) x& z% c- {2 I- o* ^- F7 E. ]
    while(1)! |  M: D3 X+ V- F- j
    {
6 ?8 o$ L, V5 o* r+ h        static char row=0;
; `* x0 _% G  e, a! f6 {4 m        __WFI();
3 Y- K, ?( N% e        if(ep0_state & 0x80)    // request data processing" c5 m/ _! g; B" {; l* Y% P5 }+ _
        {& u' Y+ K. G' S, q8 {- t! A
            if(ep0_state==0x80) // SETUP phase" i1 s  z/ D+ A
            {
( r! l6 Y7 u: a9 S                if(!setup_packet_service())
7 x" I. n& g/ l, E                {& {% l- k5 J, q7 Y% {
                    ep0_state=0;, M- X: ^6 Y/ p3 d2 P  }+ l
                    // not supported  T& |4 r, {7 o7 i$ b% J* _! B
                }( w3 a9 C# z) H
                // ep0_state should be set to 1 or 2, if processed- N6 M3 Q6 n5 J# k
            }
' Q5 L! O$ j9 U1 M* r" i* v; t8 i7 x            else    // OUT phase
) ?- A( n8 `) S3 v$ G1 @            {1 ?2 `  ^5 J5 W  \1 a0 O
                // process data
  j- z# l8 k' y/ J& X: D                show_LED(*(uint8_t *)(USB_PMA+128));# s. i+ g: L$ y
                ep0_state=4;4 g# }5 `1 h6 h+ ?/ v  _0 k
                USB_PMA[1]=0;       // Zero length DATA02 E4 s: Z5 G" j! Z% p
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;. X" o& K: L9 W# r0 p
            }3 H; [8 V! ^: Z& T% }1 [) {
        }2 H% h7 h3 Y5 _/ S
        else
. z4 N0 ~4 A& b% B; m' ?( ], b        {  ^! [+ O. X5 b5 e6 M2 p0 R
            if(usb_address && ep0_state==0)' w/ I( r. D3 }& r" m
            {
4 i) ~. {3 y. B: Z                USB->DADDR = USB_DADDR_EF|usb_address;
5 {' M+ c1 U0 ]# B: |8 P                usb_address=0;: }. O% y. w4 q! L5 L1 b
            }& ?0 p& r2 k# X0 K* O, W2 }+ b
        }1 i- u3 y% `' M0 c  u$ U& M7 i" v
        if(row!=scan_row)   // new scan line( M( F1 q9 j5 q
        {# G, U! R+ \; E5 W+ k) G: n8 @
            if(key_state[row]!=prev_key_state[row])
& b7 H, f* h# b: s% `/ q            {
9 M$ u* P+ a& x5 n, h  _" z4 D                uint8_t test=0x80;
$ L% O+ R# I' W+ Y* z) e8 N                uint8_t diff=key_state[row]^prev_key_state[row];+ K2 n& D4 M' ?, v8 G& j) ?
                for(i=0;i<8;i++)# R/ Y0 R3 _2 W0 H! ^
                {* d& L$ H) M/ ~! |
                    if(diff & test)
% R( V: D  `$ R& q, g                        update_key_matrix(row,i,key_state[row]&test);
0 y+ z9 w$ \) U! `8 s                    test>>=1;) l( A1 s" B, l3 c4 L
                }
) z; t2 c# d0 z/ c) }; |            }
: \' S, r  O! Q  H1 s            row=scan_row;
3 H$ P4 x/ u/ \5 c6 G. h" O6 D$ L        }
. h* j! ?. [9 j    }1 r) A4 R; F" q  u3 s% j+ N+ d
+ p$ N. Y6 Z4 p* K; b
  a% _9 i1 U0 P+ H3 ]' M$ M
EP0的控制传输,把用到的请求处理一下$ c$ u9 B3 o0 }9 x! z3 I, l
char setup_packet_service(void)
1 V* f, e9 D9 o; d2 }{
3 D. f( m; ]% o) ^    if(ep0_std_req->bmRequestType & 0x20)   // class-specific8 c7 h7 B2 b3 T- u
    {
* O% U, d3 N; y) D! L6 d# j0 G; I+ n- ?        switch(ep0_std_req->bRequest)
8 E7 m9 j0 M' z9 E. L/ t2 X+ @        {
- N! y& ]6 V/ F" V  g5 e2 c1 h            case REQ_GET_REPORT: break;. \. U& m5 @* r9 \: X
            case REQ_GET_IDLE:; W2 d6 Y/ Q/ b/ U% L: I4 {
                USB_PMA[64]=0xfa;   // return 1 byte
( j7 B) |8 S. @1 W                USB_PMA[1]=1;
' p* x; k) t2 f3 z( P' J& V! y7 t                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;3 G( ~. r  R8 x& e7 q4 z& v
                ep0_state=1;4 v1 C* G; G/ V! \
                return 1;
6 ?& ?4 R" g7 m! [# \9 h                break;
# A3 X* t8 n2 h            case REQ_SET_REPORT:6 c0 P. k. U1 D( V1 M, S; H
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
' h+ z2 U2 {5 h( X                ep0_state=2;+ y% i9 Q: C7 d- }1 s
                return 1;& p8 C+ ]5 G3 M; p; j9 x+ ]  E3 T
                break;6 z0 ?* t, O$ k' N2 b7 v
            case REQ_SET_IDLE:) [" q4 j0 y7 r6 ^5 {3 q
                USB_PMA[1]=0;   // Zero DATA
2 ?$ T$ Y; ?: `  o                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;# z/ @) W! {, l  @: M$ I, G7 J) O
                ep0_state=4;- L7 n6 j. X' U6 l
                return 1;
3 |/ }" _/ V% A$ N# k                break;, Z3 \5 @& A9 K* ?2 U4 N5 X4 h5 X
        }
% Q7 z/ s: y# H/ f        return 0;
* B, X, c: N& h/ B' k+ f' d: Z    }
  N" t- N6 k! z5 z7 [: V    else    // standard
% o" d) F# Q, O  d$ f, N$ l    {" e( C* b1 @9 N7 g
        switch(ep0_std_req->bRequest)/ L+ Q& f2 S! i
        {
+ K+ @, x! a( A9 e            case REQ_GET_DESCRIPTOR:
+ C4 X; r( f/ I                return descriptor_service();, A4 d$ ^6 `0 g  P9 F7 _
                break;
3 [  n9 w7 K% K" m            case REQ_SET_ADDRESS:
: m0 T* @. c/ P1 Y* O+ D* E7 {4 e                if(ep0_std_req->bmRequestType!=0x00)1 D8 z# m+ l# |, y: `% N7 l
                    return 0;6 P0 f% Y7 Y3 d2 x1 c; i
                usb_address=ep0_std_req->wValue;
5 @' j' Z5 }) w  c+ f7 u                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;3 f+ a7 D6 V8 l3 C
                USB_PMA[1]=0;       // Zero length DATA0
0 W8 j" O  b! A1 ^) s. z2 n/ c1 m                ep0_state=4;    // No Data phase! k  e3 u4 ~% H* D  u1 k
                return 1;( g4 D- r, {6 B) o4 L  L& t
            case REQ_SET_CONFIGURATION:
5 W. @  M$ p2 H$ p                if(ep0_std_req->bmRequestType!=0x00)
/ O) c1 e2 M8 g* q/ O                    return 0;
9 I' e8 L4 F/ V$ K- l! p' f1 i2 A+ C                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;$ `8 b. C$ s0 W' X" ~
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
. Q! |* H; _% b! n                USB_PMA[1]=0;   // Zero DATA
2 Q( Y: K% s5 F                ep0_state=4;    // No DATA phase
! S8 T' A' ?3 a+ E& w6 D. K                return 1;; _6 e" V2 f* l
            default: return 0;
- |) E7 P) l; a' S' u4 T        }
& h: L$ y# V, b    }
; k+ d) g8 d8 O}' N/ y' L9 j/ }
' _9 b* y; }/ O- T& Q. ~' ~  U
) Q" {1 i6 _" W6 j7 J8 {
, y# D- w. k& J6 z5 w  k% c9 s
回复 支持 反对

使用道具 举报

24

主题

63

帖子

287

积分

vip

Rank: 9Rank: 9Rank: 9

积分
287
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的3 r6 t/ i  ~% `
char descriptor_service(void)( a1 c3 i! d6 p
{
* ]. F  A, ~: [1 T    switch((ep0_std_req->wValue)>>8)( M  l% {0 ]' ^6 S6 R, c
    {
. I1 i3 i" ]* v) P. A9 f+ I        case DESC_TYPE_DEVICE:
  f+ T; w$ F; h0 f% k2 s, n            return ep0_preparedata(&DevDesc, sizeof(DevDesc));% h8 k: F* ?4 {! j8 [  _/ Z) n+ v
            break;
( J. i6 G4 P! Q8 ~. ]: f        case DESC_TYPE_CONFIG:( A! k' y( N0 r
            if(sizeof(ConfigDescData)>ep0_std_req->wLength)
$ ]% i+ Q3 y# a- p! f                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
% w; i4 _6 v1 b: S            else3 B$ ?1 B* I2 K, r+ l' [
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
- A+ c; c  R) F4 L            break;
/ y$ P. R1 K3 U. A$ w/ m' G        case DESC_TYPE_STRING:$ j1 X$ p4 k1 W8 R2 ?3 F; b& f
            switch(ep0_std_req->wValue &0xff)$ p* {7 O+ C* K1 m
            {
, M# T* L- k6 `  K1 }                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
' D. L! u) ]! v" ^- W3 H                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));% ^* f$ L  M3 u( e) H% u/ L, A* i
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
, n2 t+ J& ?3 B' C                default: return 0;5 m' j9 }+ Z; F6 o
            }/ [# C$ C5 e( k/ f$ F
            break;
8 b# M0 X, N( f3 R        case REPORT_DESC_TYPE:
4 @/ u8 ]5 o& n  N% R: N            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
0 V! i1 Z5 {0 y5 w6 ^. c        default:
; f: O+ T% m5 c" H8 l+ l/ P6 \8 j7 P            return 0;
/ w0 W; r$ \5 V4 H9 S    }
) d/ p* R% Q9 k" p# X- x}% s, l5 p+ c2 ?! G) i
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
9 o+ }$ O. n  x/ |6 y7 o/ Kvoid TIM6_DAC_IRQHandler(void)
4 g9 V. `6 d. h2 {8 G{
7 D' [6 I2 Y/ {2 P    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
' y( u2 B6 S9 z+ o
4 n. c8 E7 R6 t9 N( u0 P4 Q( m
- U4 L. l- V! \( j2 ~
    TIM6->SR &= ~TIM_SR_UIF;
5 j8 Q: t/ K, K2 X" K5 d* k    prev_key_state[scan_row]=key_state[scan_row];4 U' C: W: G, }/ t% m3 |( Q! ~
    key_state[scan_row]= *PA_IDR;   // update key states
; @  N1 A# Y9 t# W! k7 s    switch(scan_row)
6 D# r( p4 K3 n  F' d6 S3 T  o    {
' e; X! \* U) Q' O3 q6 t( K        case 13: // next row PB14
9 T+ C" M- j3 K2 J! F                GPIOC->MODER = GPIOC_DEFAULT;
$ V) g% }; j3 l* G# M2 x                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;) I1 e; i0 y8 t6 W3 N' z2 t
                break;2 X% L" `5 t6 ?' V8 o; F
        case  0: // next row PB15  _/ C2 g' D$ e! C5 B/ s4 k
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;& ]( t2 d- _7 u/ D* M
                break;% n& h+ D4 u4 z& a9 V8 W* Q6 ]
        case  1: // next row PB3
8 j& U% o4 [7 D+ X                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;" j9 X# F* x+ r
                break;
- `3 z( g- A& ?) P& v, u* [        case  2: // next row PB4
9 j9 M8 o# g7 ^. ~- B) n2 f- V                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;* N& S1 l4 }7 f6 b- k
                break;1 `% t# U/ `" N* {* o3 C! L6 P
        case  3: // next row PB5
! q2 X* s  Z# n/ O& {                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;" a  H% `# Y) M
                break;
& W% Y" S9 ?& y0 H: O        case  4: // next row PB6
4 l& b* u0 {7 o( n                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
% a  |, U' ~( R! o* o' G3 g: \                break;5 y8 A. t6 u; r$ g
        case  5: // next row PB71 |- H" |* m8 Y
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;* a% C2 _  `- z/ |) a- s
                break;
, l; y$ h5 ~. M8 t0 ?6 q0 ?        case  6: // next row PB8
, `$ m5 n+ O/ i3 a                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;4 ]/ `, s' K/ S' v# Q% o8 T8 j
                break;7 s- @1 j! Z0 Z8 Y$ X
        case  7: // next row PB94 q$ b% s0 A' z) X: T- h1 q
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;9 E4 N* d7 K- l! P/ m0 y
                break;/ T) e  {! x: r3 B# ^, L
        case  8: // next row PA8) ]! D( @+ j7 @
                GPIOB->MODER = GPIOB_DEFAULT;
9 q( n! t9 G3 P& ^# }' Q                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;  h' \4 o0 s) ^; O' x+ K4 k
                break;
" n% \& \; r: v5 R! ^        case  9: // next row PA9
5 F+ V+ w& k9 g1 h                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;% Y3 X7 Q8 e- T! Y& E
                break;1 E/ `* W( l7 k. `6 t
        case 10: // next row PA10- T8 w% s  m  F) M
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
# G; h2 y3 a' H: b& q" S+ J2 M                break;, D: J. J  ~: G4 y
        case 11: // next row PA15
! [, q$ n1 Y- t2 Z                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
) x9 q! o  ?1 }6 F1 y  l& g                break;
- E% I5 q% n2 n; a        case 12: // next row PC13+ s" A( X4 p. Y5 I' }8 h6 t* \
                GPIOA->MODER = GPIOA_DEFAULT;
( P" Q% N/ u0 \! T. t6 L9 I2 f                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
( t4 W' W1 p1 l% Y0 ~6 Z: @* g                break;
" T: a, ~- B" v: I$ o  E# @    }) T& }/ i2 Y$ _1 s% \/ p
    if(scan_row<13)
7 ?! n+ `8 G& z; U1 J8 l' Y        scan_row++;) u" P2 T" D) e! u& p
    else. C" Y0 n% ?( j' c! y
        scan_row=0;& w9 L0 t. p7 E' d
}
* N& @$ w" q9 M% ?9 j4 E* u7 t3 x) F9 S- o' J+ W3 v( a2 b% Y

$ X! U' P$ l  H9 k* M6 K# Y
回复 支持 反对

使用道具 举报

24

主题

63

帖子

287

积分

vip

Rank: 9Rank: 9Rank: 9

积分
287
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 6 F1 t% _0 \0 D# Z! U; k8 G
/ T/ t4 V( `0 U- ]9 t
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。( w$ U* `+ y/ b2 }6 d$ P0 B
- b3 @: d# X# X: i2 k7 p. R
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
3 l, [* M5 v- i( j! f

( P" n( z( U  N- ^8 e+ Q: n( W$ A' M: g+ J' c/ g% r$ a
const char hid_keymap_qwerty[14][8]={/ U% C+ @" s# O6 l3 z, O
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
7 L' T8 B' g. D: k    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
2 h4 P/ [& ]( p7 l! G' l$ k, a    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},3 i5 a( `' f, W& e- T$ s
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},- m2 |& \% ]* {. ]
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
% `7 E5 f; T; f, t3 K    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
  ~* n# @; r: ]1 K/ T, Z* M  |2 Y    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},, T( T' Y. n: I! r+ j- S7 D# e
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},6 ~+ C4 c8 }" p/ F  r
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},# K, P/ O- X# C( o( A
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},  t7 p. O7 w' f/ ]/ _3 W8 I" [; p
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
0 n& f* o/ S* ?/ k" ~! |4 Z    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},% A. l3 d( {1 j. j8 H1 b& ]: \
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
- I) @% F* L0 {# n1 c. g) t4 C    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}& q0 ?$ F; F2 ]/ N3 S# N0 c+ f6 N
};
) n. z' Y: z! q# z0 Y% ~' G# `. J0 J1 `
) b1 w3 U) U$ T$ b0 ]3 a
const char hid_keymap_dvorak[14][8]={
6 n# t, a7 I% W& t( g  K" v    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},% }5 z/ L' h7 q, ^' g" y
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},6 |& f5 O; D& a7 b! S# L
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},  G5 n- B8 Q6 A$ q$ ~
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
0 ], I7 \9 J+ g: L$ l& [    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
; R) ]2 i3 H' _  F& W; j    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
7 @, V0 K3 ^( H2 a& p    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
9 g3 @: O9 j3 u    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},/ V. h- M+ S7 [3 ?7 j
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
" Y3 D% c! E; I( n1 m0 U    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
' K6 C% w* ^5 _9 c, c2 O. `    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
2 ?* C2 h  r, b# e1 u9 }8 s    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},5 B$ |0 C. B/ V- _* D! i) H; u
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},/ p- U$ L& {" Y+ M' q* M' Q9 ~; r
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}& V4 Q. ]0 `% L, _" \4 y
};1 j6 ]' X8 f' }( D, p
- M4 p) ^1 W. P! C3 H
) I# C% Y8 Q- a3 [
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
  T9 G$ R4 Q& ?
8 o2 ?2 \% [- h( G, HHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

, Y0 T% c, {0 |1 F9 V$ `( ]7 `" z: w& z, S4 H
void update_key_matrix(char row, char col, char onoff)
/ p3 e- x1 n1 Z* K: v" C, R{
1 \0 N& t9 V. [+ x6 v. f    static uint16_t hid_report[4]={0,0,0,0};
. ~$ V5 P8 d6 I% z    static char (*hid_keymap)[8]=hid_keymap_dvorak;& r( P6 Q2 A) Q

1 W/ L6 }# v7 j6 X  r; [
7 G: A- @) k8 F5 y2 F/ y/ a$ G  u* W$ V
    unsigned char key=hid_keymap[row][col];
8 _8 i' V' y( m7 L# A% U/ E    unsigned char *report =(unsigned char *)hid_report;
) u6 `. K* U. J" m$ b/ M, @    char i;
7 X$ D5 e& x7 T3 Z  X% C6 p# v# N2 M& ~' q& {2 g4 V

" E: E2 q( E! N% m& `! Y    if(key==HK_MODE)( ~! Q' y  u- x1 }: \
    {
: r3 r- U. a$ G9 N        if(!onoff)) G( P8 z& d/ `0 {. k. e
        {
, a, [7 c1 A- K) D7 n, d  P$ X            if(hid_keymap==hid_keymap_dvorak)8 {! {! O* U% \5 @6 Z3 a, C
            {
2 }. w3 Z& A% o                hid_keymap=hid_keymap_qwerty;
7 c" c1 I* \- h  T" I# m  w7 N                GPIOB->BSRR = (1<<2);
3 J+ f# ^+ {! f/ {8 K( |            }) x! Q# T8 K! @9 l- z0 N
            else$ f4 v( h/ m) q; a0 J( H
            {, |2 g% L4 A' l
                hid_keymap=hid_keymap_dvorak;9 H( ~4 q4 c: e
                GPIOB->BRR = (1<<2);
! z; h: N3 \! t5 a& u# D' b            }
- ?; {; ~3 }7 t5 @+ H5 n/ a        }( B+ b" m. J+ Z3 _( ?
        return;
" K( l3 p$ M& ~3 b) s! _9 ?8 L2 Y    }( E8 H$ e" `% q  i9 K# J

' s4 Q/ a# R# i' u, S
) s8 d# X* f; U4 w) ~
    if(key>=0x80)   // Alt, Ctrl, Shift
" M* m# t7 z  ~, M3 C+ G# F6 I9 U    {( M% o9 _# a7 ?2 }
        uint8_t bitset = 1<<(key&7);
3 F. z! B, a0 M* ?/ R        if(onoff)   // non-zero is key up
4 R; t+ N9 h: ~* R: t4 G2 V            report[0] &= (~bitset);6 K# M3 x7 t. _2 |5 U" `
        else
% X$ H2 U0 e2 P% O' {; h9 C            report[0] |= bitset;! ^% {% X( _* w! K1 V
    }3 I4 z4 \  r' a. T
    else* w  }8 b! P0 y8 V) O2 {) O( I3 M1 Q8 O
    {' Y; R+ g- p! [* u; U0 R$ q5 \
        if(onoff)   // non-zero is key up
* |9 i, t/ z+ r# G1 L+ U+ }% z        {
  v1 G: N! s/ e            for(i=2;i<8;i++)
$ S0 I) v3 i5 F5 M% G            {
' l% L& N& m/ a4 C/ b                if(report==key)
! n2 y0 L* b) T' J" \                {
; R' |. z- _2 J% F2 C                    report=0;
& h& G! v5 u% j: j                    break;0 G0 a+ A4 {, H) @+ C% ~$ ]
                }
6 P& u6 v0 v7 k% A) q) s            }
: A/ a; u, q8 M9 E        }% f9 a  E$ x8 o% A1 q; ~
        else
3 ^2 b! C$ Z' c, Q, t" ?        {$ H+ A# T+ R! K/ @0 J) \
            for(i=2;i<8;i++)* ]2 R. t, L8 S6 L
            {
) n  J! b/ p$ B) o! H5 H                if(report==key)* {+ ]: L) I" `' \- b
                    break;
$ n* Y8 O! Q1 y( {# J                if(report==0)) O  k# e3 j5 U# l# _
                {8 f3 {3 @) C3 S, y+ X
                    report=key;( g6 H" a( L& S# c! j. r7 p4 G/ F
                    break;
& M' v5 Q7 b4 b6 V* a0 U                }3 O0 t/ W0 j5 i$ r6 f
            }
+ `; `+ q4 G: k& z1 Z6 ~        }( Q7 V& R& I' j! V* \* X3 b
    }
: \8 e. D9 J9 P( C    for(i=0;i<4;i++)5 }: O- W; u4 p1 j8 c" l: @
        USB_PMA[192+i]=hid_report;
9 T3 |1 A. `3 g) h$ f6 `( b$ \+ \5 x    USB_PMA[5]=8;   //COUNT1_TX& E# p+ u. ^0 F- Q2 X* U4 ]
    if(ep1_wait==0)4 r' p1 O3 r" K
    {4 q9 k8 ~3 t2 p% i, _( R
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
2 H  {; D& ?' |        ep1_wait=1;' Q) j0 `/ O/ |" [" l. L7 `1 n
    }/ _* ^7 H; V3 }' J) [1 \3 k1 w
}5 w1 s0 `3 ~' P7 X6 B+ p$ J0 c
, g7 M. ]2 o4 I' r) e/ l
4 I, c* I5 e7 S! }3 v2 W5 ]
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。0 S' c: u% q. [5 a3 a$ L, L' x
keyboard.zip (8.7 KB, 下载次数: 3737)
回复 支持 反对

使用道具 举报

24

主题

63

帖子

287

积分

vip

Rank: 9Rank: 9Rank: 9

积分
287
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。7 I: e! G. t! e0 F
回复 支持 反对

使用道具 举报

8

主题

150

帖子

212

积分

中级会员

Rank: 3Rank: 3

积分
212

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘4 d  w7 @- d. j3 C* ]/ F+ y3 f
不过楼主也很厉害!
回复 支持 反对

使用道具 举报

24

主题

63

帖子

287

积分

vip

Rank: 9Rank: 9Rank: 9

积分
287
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48  u# ]! [9 H  i8 q2 r& _4 ]
刚开始我以为要把打字机改造成电脑键盘- V9 V5 h; K8 L  z1 I5 `* H
不过楼主也很厉害!
% L* [; b( x& D( x. a7 a- o3 I7 h& M
哈哈  是有点像奥
回复 支持 反对

使用道具 举报

0

主题

3

帖子

4

积分

新手上路

Rank: 1

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害) d$ |: q( y  j: i! K) Z- G" Y
回复

使用道具 举报

0

主题

2

帖子

2

积分

新手上路

Rank: 1

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2019-8-22 02:41 , Processed in 0.777986 second(s), 27 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

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