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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑 " q8 F4 j0 o5 C  k" w& m/ O& `
/ `8 v$ Z- K, _
http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1- z+ e) S( [. c; w  t1 ]
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。
' d+ W6 R3 ~! c4 h
7 `5 y1 R. y( D+ ^# H. V. o在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。
( M* m, j5 `' S0 H& S  e: ?
' n+ s! |7 c( v) R1 L
235140i3a36qivqzuvmt5q.jpg.thumb.jpg
7 S( o, @' e7 R
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。
2 n; ]. y' F) t/ ?4 k 001734klbyoluenuwz4h4b.png.thumb.jpg
, P5 \( a! c% m! T为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:
- l8 ^1 V  x/ Y: n" e9 M& u' @ 003625r2agx2f5v922cf2f.png.thumb.jpg
- R( z, A# l% |6 M$ R* L其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.
9 E4 x& E$ @! c/ ?Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
! Q7 _" V- u6 P- I! {: m2 d
005836yvs0wvovwsssgd3o.png.thumb.jpg
. T7 |) f/ P  kDvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
% r! {$ B% `: {% r2 @. ?
+ \( r" e# Z2 [; n( Z6 i- r7 j# J我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。( P- W# j5 Q5 O" Y+ H

# o" {/ `5 K* X9 A7 ?; C到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
9 [8 v6 p7 t6 y# h+ }8 X% E4 T' u7 G  u& |. g' d. e% D
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
* ^" ^- ^5 @/ r( G  h
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
" u, @+ v. x1 U+ H8 d: V----------------------------------------------------  分割线 ----------------------------------------------------------
  N. C& z' Z6 x9 x( Y+ u: k; z
6 E& q5 x. a) N, S  \9 ?* ~先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
) [8 {9 F: A" t3 n8 \) J$ l
020011osionbunl4ui44vi.jpg.thumb.jpg 0 L& b& g. U+ I3 z2 Q
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
4 q8 b  e2 S& X! b 020017j8ycmnv7788bqv52.jpg.thumb.jpg ' A  ^7 Y+ b. a  }# |/ j5 a
特写,80C49
. C! l2 \0 s: B8 a; M' O3 @* x 021040oujzuvtut6iujtvz.jpg.thumb.jpg ) H8 e) p7 V: ~
LED部分,使用了一片D触发器锁存指示灯状态.$ X7 B9 a+ N$ I& L8 f! n0 k
022209ixzjfjc9crmrrqjm.jpg.thumb.jpg 2 g" Q  `$ W5 g; f1 T/ Q
暴力破坏,将80C49拆掉3 L4 j" e6 ]; ]0 b
021113e48qq98vyohvhzzh.jpg.thumb.jpg ) B4 H5 x; p6 x3 u6 T: K$ i0 q
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
3 Y1 q9 u& k7 M0 t 021125nc9az6dj33rlds2r.jpg.thumb.jpg 1 W& S6 |3 G1 u; H! P/ g( F# p1 g8 t" v
焊好元件后的板子,准备替换80C49, X; ]- _9 p6 \3 n/ {# [' I
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
/ Y" |4 F3 ~7 _# D% q% D8 W用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。: F* K0 C! Q' D( ?# I
021104shifhnrqbr3o5nlo.jpg.thumb.jpg
0 D) N6 [2 Y6 a6 r8 ?1 B3 j这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.+ @( W7 |. w7 A$ B
022003ym1p9u4ug40280uu.jpg.thumb.jpg
5 V2 P8 C2 u/ G6 m; F7 n( i开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。9 E; ^3 X, ^0 w
023313kt141q9qajtol7ma.jpg.thumb.jpg ) S/ F3 W4 d$ ^1 z9 S- K7 @1 b
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
3 }5 P3 _5 p" m+ C 023322nt7l5xb3ltttkltt.jpg.thumb.jpg 3 t  X. M) m4 x0 J# o
主键区键帽就位$ a0 R1 v. O  b2 d
023331hin88e8wkrwzwikx.jpg.thumb.jpg
& u) ^' C; M" ?3 n编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。$ L) m/ i& J' O5 {' q
023336wjzlgopugg1jyy79.jpg.thumb.jpg 5 u, r+ N; [4 F- a) @' o
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。
. q' u& E9 N% O/ \7 c 023341sffu4j3g2323h6fl.jpg.thumb.jpg
4 g4 f" ?+ V% W2 D5 q
' D" T, X3 \! |; |1 w3 z3 i
----------------------------------------------------- 分割线 --------------------------------------------------
( V, W& Q: J/ t4 x+ a

' P3 ], P& E% m8 N3 d

  y7 X+ g9 P$ s" A# n' {- ^( j2 @- \' T

  g4 t* ?9 Z; [& w0 U$ c/ V
- H, V5 x* O6 P- H) a" W
4 I2 F0 ]9 W  n" R4 q  ?6 b

3 P7 [/ S- S% A- Z6 \! |6 K
8 F# C9 J( Y* \$ g( E7 l
; G# k3 h% `# @1 R: |
& L0 }, q$ l  Z. `2 A
& t) m4 E- K6 _) {
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。% @" h/ d+ V, f7 i2 w
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
6 N; ]# S' I7 U. C+ f: u 104025nzibm2rmiomhyirm.png.thumb.jpg
7 s' D8 j( ]; i4 j5 T! r: H
6 E: e  s# U% v; ^" W
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
+ V) @& U; X# S0 J9 A 105004zkrez5houvkkznko.jpg.thumb.jpg
# Q  E6 d3 v: D' A# C+ q- o

, ~8 c( P) e, i: a1 d) G扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
% x# k9 a/ q2 F$ C9 j/ Z4 a) F2 @  M% D% A8 c
这是我设计的电路图:5 a: s) R' E5 z
110344ej2z2oo2rflo7oe7.png.thumb.jpg
% l; Y3 e1 o1 A( ^
. _; G- G- H! H1 i; ^1 M
PCB Layout:) }6 G( p; F! Q; Y7 [* {
110847jjbjvt34vwt3v5bb.png.thumb.jpg
) n8 V4 F: Y6 U/ q7 ], B6 Z7 Y! C: X

# y: R# o' `7 K1 g5 @不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
6 G! h* B$ R# l6 T' P' J% f# r3 f% A3 L& Y
2 t6 z7 @$ e! U( d& B
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑
  g# t( B) H; X# o* m2 A9 J+ B% a; v5 {' _' Y5 N% W
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
- h/ y& {, _# F. P. C* ~, o
1 M9 p$ p  a' B( Y0 z总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:
/ y0 e0 l% H1 k 113818pmrfsb6z0byt6t06.png.thumb.jpg
# L, J, F- Y- w! O6 q& n( ^
: i1 \. O7 }+ q
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
- ?9 K+ L, [" d6 Q: R7 f
# H  z9 h& `5 uUSB的中断ISR,bare metal哦
' V& t- i+ a  `; h$ ^; z

6 h9 b  a  S! u3 G) J+ e* ]; h. l2 ]void USB_IRQHandler(void)5 f0 X) D% O* W: N8 @- r6 A8 }/ \
{
* K2 r- V, H. {    if(USB->ISTR & USB_ISTR_CTR)& c: _0 w! F7 [( ~0 \. X3 K
    {
* x% Q1 l% H; d: r9 W* W  c        if((USB->ISTR & 0x0f)==0)   // EP_ID==0; i! v% G% A: {/ L9 I& q) q
        {
8 w+ U8 q5 t9 w            switch(ep0_state)
6 V8 N) j# |, H3 \: \$ _, E9 n' j& }            {
* b9 ^) g: e8 ~1 V2 X) U8 {                case 0: ep0_state |= 0x80;( v* I6 N; o! `# J
                        break;
- {% [# m- C. A/ ^; B, f                case 1: if(USB->EP0R & USB_EP_CTR_TX)+ ?. G) n1 ~! l5 E/ D
                        {
4 s8 b# u8 }* T$ j8 L2 x( s                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;6 e; ?+ R5 Y1 H
                            ep0_state=3;
+ N5 B3 H0 B* W                            return;
0 R* c. l  N/ N. n& k                        }
  \1 j& `+ E" O                        else4 C; i* X: n+ h) D( G
                            ep0_state=0;/ z/ [7 e& P+ O4 h/ |
                        break;
# ]. O9 H- c6 T                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
8 ]  Z( }& W3 W4 [: ?                            ep0_state |= 0x80;: ~" X3 I4 D; Y$ r2 k1 r% B
                        else
$ t( P. ~* i( B9 k' m3 `' A; M                            ep0_state=0;2 _) d9 K; R' B$ b( O, ]1 c
                        break;
" F6 q  @2 r# Y                case 3: ep0_state=0;
; r: H; d5 M) ^- I  i* v7 M                        break;4 s% I. u) E/ m# _+ |* `
                case 4: ep0_state=0;% O- Y& U5 n$ S0 {$ G$ u  J/ f
                        break;
3 H* {/ ~- c0 m& ~                default:ep0_state=0;- Z+ }; C# b1 T
                        break;
9 `/ Y) l; T& j! \1 S            }: q0 P4 f- U7 C1 u  h" o4 j/ _
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag' v- l& t% J4 e9 d  ~
            return;
' J% T7 H/ `7 m/ j; M        }$ H7 b6 P" l6 }1 r$ J: ^
        else    // EP_ID can be 1
3 O: l3 S0 ]2 C1 r% a        {
/ P: H% N/ g7 s1 f3 E            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag0 H6 ^9 _& d6 r; j2 i
            ep1_wait=0;
8 K1 n) x3 x4 q$ L- y+ f            return;) c4 m: O' K  V* g. o
        }
0 Z: L. b, S/ a+ ]& l" J    }
6 L4 d5 {) T" j  K2 i( e5 o+ L' ]    if(USB->ISTR & USB_ISTR_PMAOVR)
0 P! ~- H8 D) z$ }" n    {
. p  Q. B, ]/ I" r' F        USB->ISTR = ~USB_ISTR_PMAOVR;- l) u. l- y" q
    }
' U4 T$ t' R1 ?2 n5 [1 x: D    if(USB->ISTR & USB_ISTR_ERR)
; E7 d& a5 o8 k/ d    {+ s$ y, l$ H4 G7 S: @9 N; ~/ @& E
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
6 P" Y3 B- y+ P5 d) }  Z( q  ~8 r    }0 I' L* C* f2 }+ z, a1 @( M
    if(USB->ISTR & USB_ISTR_WKUP)5 ?, r/ i4 }7 M5 x4 T
    {- q" m5 e2 R7 b6 C, h# t" ~
        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend$ P  J1 @# K/ }/ Z1 \! b- Z# S
        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
; z9 O. i) X' Q  M. x+ f; j    }
; S# g/ V2 |# I    if(USB->ISTR & USB_ISTR_SUSP)' o2 k, `0 B  L. l5 o% y
    {! w5 `  m5 P/ E. m' l
        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend# j8 S( u% M' R1 a
        USB->CNTR |= USB_CNTR_LPMODE;   // low power
; m8 m2 v, o6 R! I# G- P        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
4 T4 L0 v- {+ f: g( c) W8 W6 b    }
- T4 s' L5 n' l' z& _    if(USB->ISTR & USB_ISTR_RESET)
! d: l% D/ }/ v$ u+ Z    {
, h$ ]! c3 Y. m        USB->BTABLE = 0;    // buffer table at bottom of PMA
3 \- H( j& y2 M6 V* P% C        USB_PMA[0]=128; //ADDR0_TX( ?/ E8 _7 Q8 }/ Z0 Z
        USB_PMA[1]=0;   //COUNT0_TX  D7 j0 [' U5 D7 c' [
        USB_PMA[2]=256; //ADDR0_RX
3 ~- T7 }/ o( v0 F7 a( ~# e        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
# s$ J. W7 B2 m/ k: T        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;# B6 Q2 E( s/ F0 E# ?; r. ]( Q
        ep0_state=0;
- j# N+ [' |! j' d9 f: M1 T% r        USB_PMA[4]=384; //ADDR1_TX
7 a' d3 v& P3 u; z" C6 e, T8 D! V        USB_PMA[5]=0;   //COUNT1_TX
! P. s; J+ w4 Z0 j        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
  [* a3 X7 L7 b        ep1_wait=0;$ ]' A, Y; G1 e5 \2 y! |& m
        USB->DADDR = USB_DADDR_EF;      // enable function
2 n$ b; C; t: L) e6 z8 i        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
, t5 t$ T3 S2 t0 |/ B3 m- K    }
, I6 B" A2 Q& A% I' e- y' S3 t    if(USB->ISTR & USB_ISTR_SOF)+ E: o; j1 G6 B- `
    {1 t  G+ p3 A0 {) v
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear% n! A9 ?  L$ C3 A3 A: _
    }7 e1 P: `3 B' D% X3 f
}6 [3 i( O/ B* ^5 J

4 @% L* x4 a* _3 T+ l( f2 r
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
* \  O2 m! b. A8 G: m" W; r: X7 R- v# C
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
" K7 X3 x! ]  L6 Z4 F4 j; K& e. t, v" S& `& \
    while(1)8 u$ v1 R* |4 I
    {2 z- ^4 P. o. L. z' \7 k1 w' _
        static char row=0;
7 P( }; d  ?+ m7 Y4 k  w        __WFI();7 [6 c; T- h; I& J( J
        if(ep0_state & 0x80)    // request data processing' `& I6 E$ |6 o# G0 k
        {  _  S/ ]' E* N$ S8 B1 Y' X. N
            if(ep0_state==0x80) // SETUP phase2 a6 |% g! g/ j: i* A
            {
7 [6 ?, }( P4 U                if(!setup_packet_service())
9 C$ L1 U* w) W# H2 ~                {
+ _$ p$ ]% {/ |5 r( r                    ep0_state=0;
0 X; y* u& ?1 k+ _9 ^, q/ E% a                    // not supported* Q0 \3 x6 ~8 u. K; x7 a
                }5 ?7 c9 B# \2 d+ `: g4 Y
                // ep0_state should be set to 1 or 2, if processed
" e# D! n9 Z4 d% r            }: v* I& T+ M% z1 J1 V# A' K2 i/ r
            else    // OUT phase0 A9 Z* ~( O, u! i2 q+ {! h- E' o4 n, _
            {% P1 v. z, [' _& U' [0 H
                // process data# l/ |2 r- u+ a& }/ {9 |) n: W
                show_LED(*(uint8_t *)(USB_PMA+128));- E: \- o& A  W% T+ ?8 w5 V
                ep0_state=4;
. X1 C# {/ r, ^5 ?5 V: `' ?                USB_PMA[1]=0;       // Zero length DATA02 s# Y3 `5 s5 Q! {5 s9 G0 n
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;( i4 @/ d# O, t6 A. R0 r
            }; x2 }0 D8 e: `( e; Q
        }% k- o: C' K, Q+ C. Y
        else
) n  _, U: t8 h6 I7 E- C" ]        {
; T4 x- R) M% ~            if(usb_address && ep0_state==0)+ U& r  F' a  Y  T3 Z9 `
            {0 X( P: p% l5 F" v! m
                USB->DADDR = USB_DADDR_EF|usb_address;
3 Y/ m" A- e0 y$ G$ `, Z3 ~                usb_address=0;# {$ D7 P4 a! w3 A: F, [+ s
            }$ i- o: P, w, U/ U( }' Z# b/ K
        }7 j, e7 i$ [0 i; k2 h8 }1 {' N
        if(row!=scan_row)   // new scan line. s. @3 U  Y9 e; B
        {) l2 O' C+ [7 q$ u6 ?
            if(key_state[row]!=prev_key_state[row])
. Q2 t5 p; o# C- [* o) }            {
5 X! ~: n, @/ Z  E: V                uint8_t test=0x80;6 o& u' _) u: Y  R4 A2 E
                uint8_t diff=key_state[row]^prev_key_state[row];
+ G8 t# w) X2 n8 e& v# T8 P                for(i=0;i<8;i++)! q) y; p% S9 q* I3 B, E0 W
                {* g# r9 J& w7 c- R
                    if(diff & test)* N; `% h! }" I1 h
                        update_key_matrix(row,i,key_state[row]&test);  m/ p, Q9 `( ^2 T+ l) ?, V5 Y6 c! n
                    test>>=1;% m, L$ d% l; c; Q8 {
                }
  j: Z1 H' [/ @8 D$ U            }- K3 q5 C3 h, f, @4 Q; c
            row=scan_row;; Q' {; W( o8 p" N( t
        }
: c  ]( N* r; e1 g" h" f0 h    }
+ I* L5 T- x7 p2 _
, h0 K% B, g) I& |6 w0 \/ ?3 G
EP0的控制传输,把用到的请求处理一下' Z4 z5 E# F* D
char setup_packet_service(void)
- Z' M4 s# {" Y7 x5 q5 h0 ?6 r! K{- U& v* }" f7 M) @# z
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific' t0 Q& L& v7 E: ^4 |
    {  ~, ]$ b& O6 N* [+ A1 {
        switch(ep0_std_req->bRequest)
: p% v% ?3 f3 w        {- u9 D+ v/ y0 H9 j* e4 R& t
            case REQ_GET_REPORT: break;% g# r. c9 b4 K2 _0 a- @
            case REQ_GET_IDLE:3 U& d6 O+ j) ]8 Q8 {% Y* X0 h
                USB_PMA[64]=0xfa;   // return 1 byte2 w' k, [0 U! G
                USB_PMA[1]=1;
, \% {) Y; w& k" t6 L                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
$ h5 C/ B$ l2 W: \, ^4 `& G& u                ep0_state=1;/ f6 |7 Q  [" l7 Z- Z- I' l! m5 k: e
                return 1;
" o6 d; `0 [; b% v  M8 c- ]                break;  q3 D; w+ ]8 A% B* Y0 b+ T$ J
            case REQ_SET_REPORT:- Y8 W/ q, }7 F5 f
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
6 J! j0 ^! q, V0 \                ep0_state=2;0 x) j; V2 R4 D. z' |% A4 `1 R
                return 1;
& B  b1 R# _9 [! J# v1 b/ P$ \7 e                break;
3 N. P) ?8 m( o1 z! {* [            case REQ_SET_IDLE:6 u; t  v, g2 Y
                USB_PMA[1]=0;   // Zero DATA7 M: q  ]) a9 b
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
5 Q! I$ [( E5 Z9 {' I% p                ep0_state=4;
2 Y1 X2 M+ H' J" U8 p/ V* E% R                return 1;
$ B6 j7 }  K' W0 j7 C- }" o                break;; M/ Y5 Q* \* A4 f% _6 I* ~, u  r
        }% q4 D6 ~6 C' I, K% f6 i
        return 0;; j& J7 S: p# f
    }
8 U+ z8 W" u) f2 |( F    else    // standard
0 d/ {! s2 _0 O) ]4 Q, j0 a: [    {
. ^# [' B  r1 x4 O) Q1 e        switch(ep0_std_req->bRequest)
7 m, s7 z6 Z2 S( I/ \+ w: `! e1 T5 p        {
$ Y; k4 k0 e+ \- L; A2 ~9 G            case REQ_GET_DESCRIPTOR:5 X+ s8 @/ a) c$ w7 W
                return descriptor_service();
% a2 e7 K* Q2 N1 {                break;
% U: b7 e& }! A            case REQ_SET_ADDRESS:& Z2 b" ~; r2 [- s7 ?
                if(ep0_std_req->bmRequestType!=0x00)% B3 Z2 m2 u; A% T6 }5 h
                    return 0;
9 y' w) X& V: |3 W& _                usb_address=ep0_std_req->wValue;
. |# q5 Y( |0 R8 ^9 E" Q" T8 D                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
2 \: _' k6 G& V% R                USB_PMA[1]=0;       // Zero length DATA07 ^" m% h7 ^" S0 X! K
                ep0_state=4;    // No Data phase
$ _3 v& n/ n3 R9 D                return 1;
+ G* H4 k, Q4 j! `            case REQ_SET_CONFIGURATION:, C3 `$ m  P9 X) i# n8 K* j
                if(ep0_std_req->bmRequestType!=0x00)
3 ^* q: I' h! k- v                    return 0;9 {$ K5 n, N* c5 V- l$ U
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;; s6 u3 @3 \) t/ M7 c' G! l
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;. r4 F6 \% O0 p1 ]6 T! c% f
                USB_PMA[1]=0;   // Zero DATA4 g- m( U4 X7 D; q% U
                ep0_state=4;    // No DATA phase
1 M* |8 W2 I% \9 h' z. ?                return 1;, l3 l1 u7 r( [  H5 V
            default: return 0;
7 N0 |* `. D! Z& h2 m" Y, _        }: z2 u1 F% I# M6 h' E  a( }  Z
    }
# E" L4 q% Y) r: E}
3 v& A, A9 ?; o
9 X/ Q* ?9 O9 W  ?: e# _: M. |; ]' J7 |9 v  P' j1 C$ Z
3 ~. ~9 q0 \( M" A" Z2 l" {
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
' k1 P+ g, P- C, F5 o* l5 Fchar descriptor_service(void)
# m2 ^5 d  o) }3 W: Y+ ]{) E: B9 ^( T" `* ]6 V
    switch((ep0_std_req->wValue)>>8)* A" b# |8 ?( }7 Q" |
    {
1 g6 M; T4 p# |7 I* }        case DESC_TYPE_DEVICE:7 h' a; Y0 o8 E# b& W
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));' b: |: w* ~& [% G& H
            break;- W3 m7 M9 f$ h; h; M7 r
        case DESC_TYPE_CONFIG:& s$ C2 M/ L. N# S
            if(sizeof(ConfigDescData)>ep0_std_req->wLength)) f. x8 ^! A9 h& w- W% ?) c( B
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
- y, L* X; J) d7 _            else
# A% n8 X% f: n; w9 B& n                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
% Z/ U1 a1 |2 e& Z' k% O# \, }            break;# @1 V) m, T3 N3 d$ x# y4 W
        case DESC_TYPE_STRING:
7 N" l$ V  V& H$ e2 X            switch(ep0_std_req->wValue &0xff)8 H7 Q9 ~5 Y0 `
            {5 w( u  |: h2 S9 v0 V) n8 T" O1 H
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));$ q7 v9 U; ^9 y! G9 i) {
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
/ V  S, ^: h' l4 |' _                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
# L* Q+ Z3 P6 p1 \% h                default: return 0;
& h- L, N& e: L9 \  f: {. R            }
( ]0 w; M: B' x4 l& q            break;
4 F2 ^7 O2 A4 ~) Q4 k        case REPORT_DESC_TYPE:  w/ i/ i1 S, ?
            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
/ V8 r+ m4 F! F0 z        default:0 t+ d( Y! ]1 h5 a9 v" O7 h
            return 0;& P) X( H9 h5 X0 l& ~$ Z  `7 u  ^
    }
/ s; v( M, s% s+ l; r}/ _0 u0 {5 @# X+ {/ S- L  c9 I, ~6 P
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms./ _5 W$ W. Z( x7 s+ o
void TIM6_DAC_IRQHandler(void)
7 o6 S3 g5 X: b! r" R3 @5 @{
6 r8 J- z/ M" }# ]5 x    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
+ _+ `6 B" R# Z0 c2 z/ h* ~" y$ \( {  q0 }/ D: }3 f+ \
8 B: X( o: j/ I! E
    TIM6->SR &= ~TIM_SR_UIF;, i* E9 ?9 ?% m7 l5 l$ w+ u
    prev_key_state[scan_row]=key_state[scan_row];* d8 q9 k; }# x& g# y
    key_state[scan_row]= *PA_IDR;   // update key states3 f6 U8 i% R& i
    switch(scan_row): |- U! Y1 b% L! d- P7 I
    {& S4 Y# o! [6 g! c5 U
        case 13: // next row PB14
4 x9 A. h$ W8 M  M! [. l  P. k3 R# i                GPIOC->MODER = GPIOC_DEFAULT;4 A! `+ {/ S+ K% z3 m9 J5 l
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;* c* O8 l+ z4 g) q! G8 z& a
                break;
) U4 e4 v+ T+ A+ f( ]# x& D        case  0: // next row PB15
& [2 E. M) I& n                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;) E) p& r; t( N. v! d/ I6 m
                break;5 f; K9 e7 p2 ]# J3 W8 l2 I" _; i
        case  1: // next row PB3
  b2 }! x6 T* E7 _4 Z                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
; I1 L$ Y" i- E/ B( ~                break;
9 d% z3 I! f9 g. y3 m7 {9 X$ |        case  2: // next row PB4- p- N3 h) ^/ i7 P
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;% @$ J0 V# X$ ~1 ?. ~
                break;7 |9 N8 A% S" z* K  J
        case  3: // next row PB5
" ^  E5 ^/ w8 Q, c                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;5 K$ Z' `, L, a. J
                break;- {6 Y1 m) O3 b9 b% ~: p' V2 J7 i
        case  4: // next row PB6
8 c. R  N. z1 B                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
8 B1 f( p' S- S. ~4 q                break;  j3 b8 P* h. H+ {" t# k8 f' |
        case  5: // next row PB75 B" [  @9 v. a* N3 |
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;; `; N2 _7 M+ ^0 ?" R; [( t9 I
                break;
8 _( a. j4 u) `* m5 \        case  6: // next row PB8
  i9 n! h) `* B' L4 o                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;( K+ L9 Q5 t7 ]7 }
                break;
! \: ~" D+ x  e/ r' u4 w        case  7: // next row PB90 }) ~4 k; u- Z! l, I0 E
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;# D9 Y6 Y; j( Z+ B) V8 Y) e* c
                break;( h0 J, G) q" D. c- A
        case  8: // next row PA8/ k: h. S* h; y; B
                GPIOB->MODER = GPIOB_DEFAULT;
, {! F+ r6 y, j& {; p4 }                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;/ w8 v7 e  l, A  g7 U2 v
                break;0 v' W4 B1 p0 v% _8 X: k+ B, R: y
        case  9: // next row PA9
' H. Z0 G- K+ F8 a3 Y                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
; G# Y& m& U9 J9 q" a                break;! W/ T- \3 Y: \2 C! p4 X, n
        case 10: // next row PA10" U' G5 t8 o1 R) k6 T- X- y- p. {
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;) r8 M) M- t* N: z
                break;/ h! ?  M+ l( V. m# U/ n
        case 11: // next row PA15" b! O# j- ]0 O2 Q6 h
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
, f7 p8 s/ U; U5 f                break;
0 F: M* m( G" [) a- U7 Q1 n3 {        case 12: // next row PC13" V' q2 k  b' d& a) V+ a
                GPIOA->MODER = GPIOA_DEFAULT;
# n7 x/ Q' U6 M+ j* d2 q                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
" p2 e- F0 m2 z8 E+ Y0 R! B0 |                break;; |1 {4 C1 g- z& p
    }
% C! [; |5 [& |: G8 m3 ^    if(scan_row<13)
" _& C& M( B% N5 Q8 t        scan_row++;1 D' i, \1 N# e/ ~5 d8 d
    else. g! w5 j4 \9 g& B( d3 G
        scan_row=0;
! {2 h6 f: `$ y3 h( N}7 u6 g. Q; U9 O
+ t/ G4 V3 C: B' I) z! e
8 R; z/ V! ^+ a/ J; `; i' X1 H3 ~4 s
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
1 `4 n( ?- `1 H2 v* E% i
. \  L: p( c; [# y+ r扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
( F. w7 V$ R) n- ^1 ~. c
5 @7 D0 V! [8 Y, c' f; e1 O+ b7 G要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
6 E1 n; y7 d3 F
4 f" [3 A4 c7 W8 p- G, f' C

0 ~( C; S0 n5 K. p, }const char hid_keymap_qwerty[14][8]={
% W; C  e; A: J    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
- ~+ I. @" u# X# @1 c) D2 {, H# |9 f    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},/ `( ?- F( u3 S+ Q6 F1 z+ b
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},' l$ @" F4 y8 H. _* ]
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5}," A0 ?5 m& d( s
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
5 S* G# a. L6 ^2 l+ ~! i    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},  L) W7 f3 {1 ]- u& Z
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
# Y) h  k5 Y, O: j* o    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
4 e0 e* K  |" j0 e9 p( Z! B    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
  _8 i0 k2 }3 L* S7 u    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},( B" L. u8 W8 ]! ]1 {
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},1 a0 T6 k/ Y5 b, W4 }3 w( i
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},$ ^6 L6 {9 S" m% A0 X
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
* E8 _* u* a0 v# R& L" r4 D: h$ O    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
$ v! v; p) B' Q0 c};
' A, U; A' }$ v6 M* [  b) _+ ^
% P5 Y/ V7 i% f- W$ j
7 D0 n. c0 h' K6 e7 X4 M
const char hid_keymap_dvorak[14][8]={" H' r2 E9 Q% ?3 m( V) ~
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},' F  y3 `, L" Z9 E( _1 j/ E
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},: k2 f1 G1 `7 H
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},1 N. D2 U' q! r8 ]: N( y
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
$ k1 N0 ?& P  t) f* P3 k    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
2 x/ [  q  v! L/ v# g0 j7 z    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
( o, l; q) E$ F+ S    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},( U& z$ u, ?" j/ A& ^9 a: X/ O2 N
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
, a. M7 R9 j- f. b0 J8 O4 w    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},8 k" x$ M0 J: L  V; E2 w0 X" W
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
- I/ Q/ L5 W  X    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
+ {* _/ Y, t3 x7 x    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
5 ]' n5 e" |! \- w& K% Z    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},, U( ^0 `/ T; P6 F
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}5 Q; Q# L: l9 x' S' B! A/ e, L
};
/ K- x5 ]: ]& _
; _% r2 ]  x+ D
2 K& A: X) Y0 K* A7 U上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。$ D% N+ g& \$ T2 I
. |/ N; t5 t" M9 k' m
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
2 g7 X+ n+ q+ }- J+ z

. R/ |; Z- |+ K& X1 a' a8 ~void update_key_matrix(char row, char col, char onoff)' z2 q+ p$ ~: @" D. E
{
1 f' W+ k1 h1 Y  R' V8 X* P8 j) X    static uint16_t hid_report[4]={0,0,0,0};
8 R5 M0 C; R+ C( U; I: K' f9 {: i    static char (*hid_keymap)[8]=hid_keymap_dvorak;+ g* Z# p( n: Y; J/ f% L9 d4 a1 @

. ~7 m7 v% O/ o+ A

2 k' M8 \& n, b# w8 }* o# X+ e0 N    unsigned char key=hid_keymap[row][col];
4 K, @; e/ J+ [4 F; V( W+ A    unsigned char *report =(unsigned char *)hid_report;( z  U' g9 I/ ^% @! a3 G
    char i;
: Y) \% E1 B; E( K3 [! f( G3 |' V6 X$ M$ T; e& h/ s* i

. s  V/ W5 Q  W( O! e) \2 v    if(key==HK_MODE)6 u) V3 ]0 R3 |2 d1 H) G$ P' ^& ]
    {9 @6 d  v- j& c' [( s2 T
        if(!onoff)
' m' }4 o( A3 i; B9 e        {
9 D3 y  G% ^, i! b            if(hid_keymap==hid_keymap_dvorak)7 v+ H( P3 A' }. B% i5 t& {
            {, Q* C; K6 A/ B& c
                hid_keymap=hid_keymap_qwerty;
! Y4 W, b/ k2 ?% y1 l                GPIOB->BSRR = (1<<2);
" ^( X) w' ^& n' P7 ^, Y6 _# O; ~8 f            }( @6 U0 o" H2 O5 b0 e8 c7 e
            else
- |# m6 v6 y, N, g0 k5 `: b9 F            {
+ r- S* K3 k, ?5 ]/ \9 [$ o+ f; y) U                hid_keymap=hid_keymap_dvorak;
' p7 i1 d8 V8 |* L' w- d% `                GPIOB->BRR = (1<<2);
. h0 i. F9 K6 ?5 c3 Z! r            }
, ~# Y. ], k8 M& {( V( z3 Y% a        }" h, K0 D2 F  _+ s9 l: Y
        return;
+ H8 j8 k) U2 H    }( ]  n" Z4 k! c3 {1 C

0 ~, A- q) ^5 r2 e% p; s, Z! G% b
! a( U3 y5 f2 L4 E  z8 Y) [) N1 T
    if(key>=0x80)   // Alt, Ctrl, Shift
( m; g) c* U& ^+ W" Y) @( Q2 u& z    {% K4 ^+ H7 D# x6 y+ `) z* C
        uint8_t bitset = 1<<(key&7);7 D5 h8 X* m8 L1 t( F
        if(onoff)   // non-zero is key up
/ O# A1 p7 s; }" d  N            report[0] &= (~bitset);2 [' f2 ]3 Z( z* |
        else
9 O' Y' I* @7 v' F6 k7 t0 v% Q            report[0] |= bitset;
% R& y- v* l* C. v. l, R2 [    }
0 g4 v" h2 U7 W( t    else! e9 |9 D+ E7 Q4 j5 ~) O1 D& Y
    {6 a* {" V3 S+ ]
        if(onoff)   // non-zero is key up9 G" N4 h( }# P3 r) i  U( h9 p
        {
# l8 b4 U( e: W6 j            for(i=2;i<8;i++)7 S0 N+ s$ ]9 a( q, r, E" q
            {
! S# ?1 D3 N' f; \$ E( l. X0 X                if(report==key)
+ x! j, w2 M" e& ^7 S                {
; Y0 m8 P- V/ {- l4 k                    report=0;4 v, Q) \9 R! L* |' b, _
                    break;) w! v3 H% v" i6 d
                }& c3 g& z* S% ]# M5 b; ?
            }5 |$ L6 v5 K, X% o
        }
# V3 H- h" Y! k) S        else
8 F7 v+ g4 {# y" U: p8 O! U        {, O* H' ^+ X, h' q' d# i+ L$ @
            for(i=2;i<8;i++)
9 E7 W( w( p3 F% y            {. d% o$ l! l6 _+ a- x" e
                if(report==key)$ |5 E. ~; y. Y3 G( v1 D5 p
                    break;
0 F* b" ?. t. g                if(report==0)
! G. }, N/ {/ r8 N) z0 I9 |                {
1 h8 X7 @9 _3 f/ J3 L+ P. L; m                    report=key;
7 A- \, Y; y# B( t; P) b                    break;5 m' ]) W& J% U( M1 [
                }
* l0 i- L9 j- y8 f9 A3 G; }            }
* f# H* f7 w1 _2 e- X7 B+ L& ?        }/ ?* H7 I$ C5 l5 a' P6 c8 e; M
    }
  t& T. ~- W) K6 r: I( |    for(i=0;i<4;i++)
+ P% }0 ?( k- X4 k4 D- i        USB_PMA[192+i]=hid_report;7 K2 V; }! P$ W' }% k- S4 H
    USB_PMA[5]=8;   //COUNT1_TX! m: R) F' m. n8 i# [
    if(ep1_wait==0)
, P- h' W; v% U' }% Z    {( ?; V% ~# f2 {; P# s
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
( E4 l# s+ A1 f        ep1_wait=1;% I7 Q2 j$ ]% {9 p
    }6 m. `& g, F, |$ ^1 f& S, u) N
}% g2 _4 u( T" k  w  A4 v9 y7 I
! P! N* `; [6 W6 y4 J* f, u

# A. q7 L$ ?3 `% i完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
3 s' o8 i+ B1 |1 e5 j keyboard.zip (8.7 KB, 下载次数: 6711)
9 W6 x% S/ J: q1 f1 D: A

9 \: b+ x# f! F: |
$ D$ N# a* K; G+ ?- @$ x
. ^3 A" A, [" |" V6 E8 D' z
! N8 \/ x# W8 G, `
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。
# p& u5 {6 z2 K( N# F) l" ~
回复

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘2 r1 B& l3 W# e3 q* g/ O  i0 k* H
不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
; q3 d/ |7 M2 E8 F# S刚开始我以为要把打字机改造成电脑键盘: r9 Q0 m3 S4 r* c& A
不过楼主也很厉害!
6 G7 z0 Y# d1 L& E2 u
哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
! G9 o, m) u1 T
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-11-14 09:20 , Processed in 0.188912 second(s), 27 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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