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

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

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

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

[复制链接]

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑 " m: |- U' j- A5 g5 O& E( ]2 @
3 U7 K+ C0 _% H" b$ O% ]
http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1$ u, ]# V( w7 v
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。
2 e3 s" ^$ b% e/ _& a3 \7 o' q0 D" a& L8 a7 N
在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。
7 o  ]5 h- @5 |; W4 w

" Y, n. z! X. I% I; ?$ G2 n 235140i3a36qivqzuvmt5q.jpg.thumb.jpg ; V& c2 w- ]8 \
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。
/ M2 ~% ^: K  M 001734klbyoluenuwz4h4b.png.thumb.jpg
" Z: s7 i  R' a8 c/ L为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:
, f+ X5 g8 {6 C' l 003625r2agx2f5v922cf2f.png.thumb.jpg 6 r0 Z& x2 U7 \
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.
+ M+ b  e0 W. U* DDvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
) h  T; x) ?% G" |9 \( B6 K+ I2 L2 R, ~
005836yvs0wvovwsssgd3o.png.thumb.jpg 0 [: c/ q9 y. A
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
! l* w9 C- W0 Y: ?. J0 n! x1 W
5 h/ `+ Y' ~2 e我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。/ t2 j; G$ {0 t' o, b/ \- r
' W- c9 U  M8 o' Z& r
到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
! z# B+ o, E- l, S; t
5 o' W$ E$ _  L机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
$ J0 V) {. v- _1 H0 ]
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
5 x0 r$ p2 J1 v$ O" I----------------------------------------------------  分割线 ----------------------------------------------------------/ X) m0 Z0 L% M# C5 o  h, {4 i4 S

) Y" c3 d2 b1 s. c/ a: y: {$ j8 n先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
; k' k2 n* \# }  I( v7 g, I
020011osionbunl4ui44vi.jpg.thumb.jpg " N) ~4 \9 i+ s
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。  [& [9 x" y2 L, O2 M* @/ G
020017j8ycmnv7788bqv52.jpg.thumb.jpg 3 i' g/ C- _& D; {  Z# _8 `
特写,80C49+ p( [5 w% v& a. n2 m! J
021040oujzuvtut6iujtvz.jpg.thumb.jpg * t8 p, c5 g5 S8 M; D
LED部分,使用了一片D触发器锁存指示灯状态.' E  ^- u& w7 q& I, c) \
022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
! ]& Q: x; O, C( e0 k7 y! k; j& A( N暴力破坏,将80C49拆掉! D# H4 b& `* S' u' `+ E
021113e48qq98vyohvhzzh.jpg.thumb.jpg * ^! H! ^5 F1 N# B
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
+ s2 F1 g- q: A4 L( b" L6 d# A 021125nc9az6dj33rlds2r.jpg.thumb.jpg - v4 g& _1 G4 P1 j/ q1 i
焊好元件后的板子,准备替换80C49
5 y: @% ~/ |2 s  v/ c- y 021146zmmpmh4pav4o7g6a.jpg.thumb.jpg # ]' k$ b" ~5 g+ \
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。; ?1 `+ \3 G" X. N* q5 W- S3 v
021104shifhnrqbr3o5nlo.jpg.thumb.jpg
% ^# t; ^5 i; d$ Z, l这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.9 C: k- V" R% W# o
022003ym1p9u4ug40280uu.jpg.thumb.jpg 9 b  f4 P5 n, i/ X
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。! M$ L8 o5 B2 C# r- F6 O6 m/ k  L7 I
023313kt141q9qajtol7ma.jpg.thumb.jpg
" Y% o& C" K! L, T/ ?我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
2 `  R$ G0 T$ l& y* ?+ J  T 023322nt7l5xb3ltttkltt.jpg.thumb.jpg " k' X/ S7 T. N4 E
主键区键帽就位
" C6 a* ~' S5 U5 p9 D4 c* p# k 023331hin88e8wkrwzwikx.jpg.thumb.jpg - z% M# X% c# ~, y, ~8 r4 K* {- f
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
$ z7 h) l7 h# j" u8 m  y 023336wjzlgopugg1jyy79.jpg.thumb.jpg 2 X6 z, c& ]) I4 P
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。: j  U# I% g) J' F7 h
023341sffu4j3g2323h6fl.jpg.thumb.jpg
1 V( Y& q7 Z- x& W6 {% Q

" {1 O  f- A+ g9 h: E: s4 M----------------------------------------------------- 分割线 --------------------------------------------------9 T! W+ o' v+ T1 V- o9 b
& o! T5 K, ?$ o. S, v8 G

& }' x& [# |1 I5 f+ B
* P! ~+ K. s: C- t. ]& C1 q2 ~% {0 A/ Y+ O/ w0 ^0 Y' `- C, Z7 G

& E; s3 d! U$ |+ z0 w4 A" h
8 Z" V9 x* H% w, k; U
' X6 a" B9 d% i* |6 ^3 [7 Q

6 u/ @$ Q/ D; S9 J1 w2 y. K2 h
4 _% Q+ w# M- z; T2 z6 w8 N+ w) @7 B  D2 f4 P6 L. u* r7 ~

6 S: B; e  i' v- k9 W
回复

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
3 h7 Y# ^% S1 j80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
( U' ]# a2 A2 ]: x. Y. ? 104025nzibm2rmiomhyirm.png.thumb.jpg & X$ u  ^' i- S( h3 T8 z
' W* W: E! n! J" E+ i
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
" w8 B# i. \/ q) [' A 105004zkrez5houvkkznko.jpg.thumb.jpg
" w5 [' u6 B/ d+ d) H$ Y) X3 U
9 z* K5 D3 D8 U  N' ~/ l" f# C6 S
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。0 }$ C+ t% [: q' E

. b) g/ c1 N$ x$ R! X& P9 [这是我设计的电路图:
+ t6 h& p/ l# ?( Z: }  e: F 110344ej2z2oo2rflo7oe7.png.thumb.jpg
) W" \1 D  @5 V! e" Q! V8 O9 r# D
3 ~0 Y4 [& b" `; O7 N
PCB Layout:6 X. z$ n/ X) m8 u- e
110847jjbjvt34vwt3v5bb.png.thumb.jpg
/ U5 S, S9 _( i8 I( J8 H. `2 D

$ h9 z% A" }% {2 E$ u. A- M不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
0 v3 Q6 N) X9 @8 G  m+ b$ l. W+ o/ R; F9 T3 ]* I

3 l0 Y) J0 ^6 L) t4 }% y3 X
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 ' j$ H* R3 w9 D6 o! Y( r9 n4 p
- U8 X' }) }! C$ Z
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
4 K; b+ c7 J' r
/ ]* m6 Y4 s! Y" U- s总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:; N5 r: w; ^! R. R
113818pmrfsb6z0byt6t06.png.thumb.jpg
: k9 W) `6 {4 m2 d7 [8 Q

! ]1 ~/ w6 s( Y: K其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
3 @8 c9 |7 {+ P( ~) L, t. H6 `7 n- b
USB的中断ISR,bare metal哦
4 R2 K% b- x& B/ s
+ b- b$ B% e9 O) \* Z* \) _* L. Q" y1 J
void USB_IRQHandler(void)
# ^% H: `- R( ~" F; [9 L/ L' j  b{8 ~2 l2 M7 D+ k, W( ?: T9 N3 ~8 ^# ]
    if(USB->ISTR & USB_ISTR_CTR)% N, Y2 k7 b0 ~6 M5 \# y
    {, Y  a' j% K  y
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0/ g/ h$ C+ u3 G. x" ^4 G" H
        {
1 C+ T" z( x" A% U, o4 g            switch(ep0_state)
2 N5 F+ [' p( ?! P! R            {  j1 H/ N% \1 y' i' }" V
                case 0: ep0_state |= 0x80;
9 K9 D# O7 c1 o2 \                        break;7 W6 I1 L) i8 f+ v9 i4 z
                case 1: if(USB->EP0R & USB_EP_CTR_TX)
1 r# I& @! p0 r3 p8 M                        {% L! v- N, O9 j/ p1 z! b
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;( c* P/ e4 f5 N/ G, ?, g
                            ep0_state=3;. g& w+ \: K, ~0 Q
                            return;  {, X! Q, O  l# A
                        }- I0 Q" ]" X! ^
                        else
; m) x( O7 p/ A                            ep0_state=0;
! N0 G0 f$ L- P. h1 Q( }+ E                        break;
3 L4 |1 ^/ E! M6 y2 \% C7 n; D1 l! g+ c                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet$ z. |  g1 h( d; C( Y% Y2 z" L
                            ep0_state |= 0x80;
3 p1 ~; t& C* d& F- p4 B                        else
+ O( i& e/ Q- L                            ep0_state=0;; @7 b  R$ t- A0 p! I" k
                        break;2 m. y4 Q. W9 S- a" A
                case 3: ep0_state=0;+ I# m% M" S7 r* q( T7 d3 V3 I. x
                        break;, E3 L/ u7 ^2 R* ]
                case 4: ep0_state=0;
$ Z. u/ }9 w# K3 C. h                        break;
" }  V) f2 N! r5 ^# [+ o                default:ep0_state=0;1 A" D# U- J, n" O  d/ o
                        break;, k! S& z9 s9 e$ E$ f' b4 u( A
            }
, Z" e) u4 s$ ^            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag% H- T% n- x8 H( X/ b8 P9 ?
            return;( {$ j  O( _' a8 U. o  P
        }
5 j; o" V/ l; Q        else    // EP_ID can be 13 O* |8 G. K2 k1 h7 S
        {
! c2 {( [7 U7 R8 v9 V            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag* b7 L- u7 ^4 c- V  M+ e0 ?* k: ]' c
            ep1_wait=0;
) w( h  E, f- u) G            return;3 s$ }. o, a& m5 W; C: D& X' a
        }& U2 ~' l6 U1 S( y) J
    }
7 ^( X$ @% d+ a% v; c- y8 E    if(USB->ISTR & USB_ISTR_PMAOVR)
, o- @  Q, c# M% Z$ N    {9 J$ Y! S; ]5 I6 F  D+ F
        USB->ISTR = ~USB_ISTR_PMAOVR;
3 S5 F6 a& M/ K& y: O' N    }
" S8 z" G: C$ I' s( {3 w2 h6 Y3 t    if(USB->ISTR & USB_ISTR_ERR)
7 K3 I  B& Y2 x/ b; @& r    {
7 U6 V  Z+ w! c        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear0 p* _2 j0 p. [, `5 m, c8 [* v
    }
+ x5 Z( Q8 I3 P" M6 e    if(USB->ISTR & USB_ISTR_WKUP)5 U, E# K+ y* G) L
    {, s! l9 U" C6 [% B( e4 \5 X$ g5 j
        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
9 z1 K3 _; j# }$ D' m0 z% s9 g( c        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear+ z% t  x; M! z/ c: Q7 Z1 J
    }
/ [' g% v$ H! C4 O; r7 `( _7 A    if(USB->ISTR & USB_ISTR_SUSP)% M! _8 h7 O8 ^/ ]" h
    {
" G" [/ q4 q& M1 q" G7 H% @% T        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend5 X$ ~3 V- Q; z' S4 ^
        USB->CNTR |= USB_CNTR_LPMODE;   // low power7 y% U. i9 e& {- i! T. z$ T3 ^
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
2 l9 {2 ]' B7 @5 [    }
" d" [. O$ q, j/ Z    if(USB->ISTR & USB_ISTR_RESET)
0 }" I- M. C3 ?# L- V    {
  J- H2 D1 ?( d4 u0 M2 g        USB->BTABLE = 0;    // buffer table at bottom of PMA
5 M, @4 h( \1 f% Y& i4 [        USB_PMA[0]=128; //ADDR0_TX9 x& O* v$ o% ^1 t! y: [
        USB_PMA[1]=0;   //COUNT0_TX
) d; A' Z2 H3 ]        USB_PMA[2]=256; //ADDR0_RX# R( j$ b% D" ]6 h% r+ ?8 A
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes! z' t5 _& ^& i/ N- @
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;4 s# H5 w1 l! `/ m- Q" m2 b
        ep0_state=0;
! i/ C! {* i; f; c' }# b        USB_PMA[4]=384; //ADDR1_TX, J* Q0 k# W$ C# f% r( Y! o/ |
        USB_PMA[5]=0;   //COUNT1_TX, z: G  A: b) U5 g( y
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
: {! @, L- ]2 V$ a        ep1_wait=0;$ y/ P& h3 v9 p# V0 g
        USB->DADDR = USB_DADDR_EF;      // enable function* Q1 f& \% Z: C4 B
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear$ P0 r) O9 `5 ^* Q) `
    }8 N0 {+ \- Y1 f5 g0 G$ e8 p# q
    if(USB->ISTR & USB_ISTR_SOF)- B. z: w; T2 Y6 s! S' ~
    {
. Q( H; f' u, C9 d2 Z5 h        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear* a$ S2 Y# s/ n. ^
    }
7 d& F/ _( r* g8 |2 @! n}& w0 x" W/ I0 q
  s" D( m! R! N9 U+ ?- W
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
% z1 }; }. @% k( Z' [9 @  y/ m/ G# i+ o  ]
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
7 x( j$ I; Z8 J' m9 b- D
5 \9 A# r- L+ i5 a% U' l5 O5 s
    while(1)
5 ~5 q6 B* d* m0 j0 H    {
5 J, e, S: Z) v/ j' D0 u        static char row=0;
+ V% a. A$ V% u- L        __WFI();
/ h' A1 m  e, H3 K        if(ep0_state & 0x80)    // request data processing
$ e3 p/ r  _3 h        {
- X) g7 f; J; G5 P            if(ep0_state==0x80) // SETUP phase
, r# p( K& I0 M            {
1 {. j$ q4 u5 p7 \+ h( y5 w                if(!setup_packet_service())
& y$ h0 c$ |: q/ H0 x( s                {' I- F- ?2 R! v5 Y3 O
                    ep0_state=0;
# s, q  z% X- k. e! @                    // not supported7 i+ i# X2 F6 v6 o- z, p
                }
; @" x6 s4 w) N7 e1 R                // ep0_state should be set to 1 or 2, if processed6 i3 `0 {" L+ H2 ^
            }/ I+ P. Q9 n! I2 G
            else    // OUT phase/ x$ ]& ?7 f* a: ~1 P
            {) S/ N  \& Q& H4 b, N5 N
                // process data; t" B- ?9 i  }! b
                show_LED(*(uint8_t *)(USB_PMA+128));1 {! p3 s1 C2 y& `
                ep0_state=4;9 `  ^; W  f3 ?5 C" b5 d2 Z# V- h/ h
                USB_PMA[1]=0;       // Zero length DATA0
3 }7 v" _3 E- J/ s) Z0 u9 o. N$ t                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;! y3 B. n8 S$ }/ _( O+ J7 R
            }
1 T1 r0 i% t5 W2 Q  ~1 w        }
9 z; I0 |; q4 r1 z$ j- N        else
3 \, G" }# a  U: ]0 o2 x0 b* t        {/ S2 g' \6 i/ t1 i. s9 E' O
            if(usb_address && ep0_state==0)
1 {2 V- x- N! [+ {            {
' v& T8 |) O# `7 ?' z                USB->DADDR = USB_DADDR_EF|usb_address;
* ]& Z$ j4 x0 E* f0 d                usb_address=0;
( m3 O+ @4 Y" _: l            }; k% [' U. P* I+ e8 k5 f. L# |
        }6 `9 X; O% ?: o4 W& {- x0 z
        if(row!=scan_row)   // new scan line
) A* P# t/ b9 x# B        {  T- O8 c& G0 T. c7 H1 X
            if(key_state[row]!=prev_key_state[row])' u& h' o6 y4 x: J, h
            {4 S. l- K: p; F3 d. s8 P2 n
                uint8_t test=0x80;
1 X% v2 q; f$ t3 e3 G8 I                uint8_t diff=key_state[row]^prev_key_state[row];, a! I5 Q& K3 Y  M/ y+ y
                for(i=0;i<8;i++)7 w8 I' \' f& n' y9 d. V
                {
+ d3 i, i, f6 v& U0 h6 }/ U1 s: k                    if(diff & test)
7 I: s% R9 S# [2 G( l( A                        update_key_matrix(row,i,key_state[row]&test);
  x, A  x  @+ U/ J9 }                    test>>=1;% z+ S# b& G( p+ B3 M. W
                }1 j  W5 F: g2 Y) l) M1 `5 w
            }
  H' t1 s1 ?; r+ Y) f9 C            row=scan_row;) Z+ E! I3 A+ M8 g
        }
/ m' e% S$ H+ M0 r5 e! k; W    }
+ h- D: t; f% M) r) D
% P3 `" [% \% Q
$ S# K5 `% a( L- ?3 SEP0的控制传输,把用到的请求处理一下' u- _' Z/ w  i) A4 k) [
char setup_packet_service(void)! x7 h" q: ]+ ^; s( G0 Q1 z
{
0 @, ]( n" G/ d- G: J    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
) y% ~$ j& h2 E% x- p5 F    {, b7 C1 X9 Z: f; y- S7 k4 ?8 A
        switch(ep0_std_req->bRequest)5 z; f2 w. q5 ~0 `  v" e2 L  o" t- A* j
        {
- l/ e0 a: k8 r# O            case REQ_GET_REPORT: break;7 P4 z, i4 h1 g
            case REQ_GET_IDLE:) ]: @" d( z0 F- P" z0 g& r+ \
                USB_PMA[64]=0xfa;   // return 1 byte) J# O- c0 E# p. o* N3 T
                USB_PMA[1]=1;
7 {+ a2 Q2 A0 o0 O% g3 v( o4 H                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
2 ^, r. a# y/ H* \0 @  `                ep0_state=1;
( Q% S9 a. [$ d* h! g                return 1;6 }) o' m1 G) y1 ^
                break;" W1 I3 g; E1 x
            case REQ_SET_REPORT:
+ Z. D) b4 I  M7 v8 S0 c                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
' M) x& Q  \4 v/ I7 a                ep0_state=2;
0 R7 Z2 v2 H+ W2 z4 Z: U/ _                return 1;$ V2 B' ?0 m. K( Z/ E5 S4 q
                break;  H1 n; E$ g; S% H) |" g$ X
            case REQ_SET_IDLE:9 A+ f: t' l6 Y
                USB_PMA[1]=0;   // Zero DATA# ?2 D3 z* h9 v! [6 e6 v# k: i1 t
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
# D$ O; `4 a  n/ Q                ep0_state=4;9 h$ g2 z: H9 _( w2 F  Y
                return 1;# X% y6 t# A9 j+ a" ?
                break;
( u* q4 a; `8 x  \- {6 X3 z( r        }8 L1 [5 b# ]- T
        return 0;
( M, ]5 P! q4 j+ G1 l2 b5 Q/ I, V    }; L* o: c8 m* Y& n5 o- N$ e! e0 O
    else    // standard
0 l: x/ C) G! t# u( V    {# M  ?  s" F: O2 P' E" _' W' p
        switch(ep0_std_req->bRequest)
- i2 v! _" F. Y! n% T0 }        {
; Z2 J! X5 N% h3 c& q, l            case REQ_GET_DESCRIPTOR:
4 {9 |. ^/ N# D5 ^$ v" c' x                return descriptor_service();
, z. p3 k; W: h  N3 j                break;  e( T6 Z1 O7 C% @. i  ?
            case REQ_SET_ADDRESS:5 b( q9 u. K8 G3 k, s, b- N- l3 k( \
                if(ep0_std_req->bmRequestType!=0x00), j+ T" J5 R- r9 L; h2 r1 W
                    return 0;
. P# e$ [# ~4 o" A. }                usb_address=ep0_std_req->wValue;
) u9 ]" W8 E" V; V' W  _8 V# v4 H0 f                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;: c* ?! D' }; l" F2 a3 W
                USB_PMA[1]=0;       // Zero length DATA0' \% @6 @; Z0 H7 r
                ep0_state=4;    // No Data phase4 M; W1 }8 k8 @9 N) K% z! x
                return 1;
4 L1 j- u& Y+ q( ]# ^0 c2 Q/ Y1 j            case REQ_SET_CONFIGURATION:
0 K9 m2 s* p  p, ?; Z4 K4 \                if(ep0_std_req->bmRequestType!=0x00)
3 M; W: n/ i: n! S3 U                    return 0;
3 t  O1 M4 y& g0 e  Q& O                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
2 ^% i# r5 a0 M( f, J* X                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;: o5 F. Q, X& I' f4 z" H, y
                USB_PMA[1]=0;   // Zero DATA0 H: G3 K3 A3 p( U, N
                ep0_state=4;    // No DATA phase
' e' M; _* k( l1 ]                return 1;- Y# Z) y. Y) T0 R6 j
            default: return 0;
7 `" C! W$ N3 \" @        }0 N; b0 h/ L' w  R: G& D2 l
    }) k4 k) H) t9 r2 I2 e$ W0 F. h
}! k3 O% [  _6 g5 t

& }4 o- [* e# u$ b) t
5 A7 v9 m% F3 y3 _7 O  l: C& |, P' W* K. o
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的" G9 N' C5 I+ M: u+ o" Z
char descriptor_service(void)
1 Q# J2 I; H9 C! P' B1 j- ^7 R{; t: {; _) y' M8 \% j( r
    switch((ep0_std_req->wValue)>>8)
4 z" O6 a7 v; @) g# B2 J* U$ _3 L    {
8 p3 B3 d; _# z& O; V, M        case DESC_TYPE_DEVICE:
: I+ l% {; x0 s4 K5 w2 ?            return ep0_preparedata(&DevDesc, sizeof(DevDesc));6 v) ]1 m% H( E' O: k4 K% ^
            break;
' E2 v) N/ H9 K" {0 j5 H        case DESC_TYPE_CONFIG:
5 p* F7 l# |& H! u            if(sizeof(ConfigDescData)>ep0_std_req->wLength)' m$ w' \8 z$ O/ M+ P
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);: y! |9 |1 A+ N: E; {- B4 A
            else+ O2 M! V8 }! p/ |% F4 {
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));5 I/ i9 N9 \1 i' @) Z
            break;
/ t, a; }7 a+ n& j        case DESC_TYPE_STRING:/ g2 Q) d( F5 a( d' f; m# H* N
            switch(ep0_std_req->wValue &0xff)" ]+ K* O, T5 p. P! M
            {
* m0 y9 Z# W& I5 u! n' u1 Y8 u                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
# D' X2 z6 b* F                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));, e9 E4 U" P1 U" y! S! a5 m' ^
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
9 h- ?/ D& Q# r! b) M0 x                default: return 0;
0 T. j8 s! ]& }2 }% ?, i1 b4 {' C            }" }$ W$ V! W4 }1 c9 y0 D7 S
            break;
5 [+ f' p- |' V& j) O! E- x        case REPORT_DESC_TYPE:
8 P  E$ y. i+ {6 [, n! t: }            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
+ N+ P* ~: x) F5 {        default:! Q7 f/ ?& k$ G/ @0 P/ j
            return 0;. T: f2 Z2 N* F9 J
    }
2 U6 t# z3 ~) ^- u+ _}  V$ K& F7 f( Y; S9 S  _
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
9 F( I7 C* k& j8 H1 O3 }/ _void TIM6_DAC_IRQHandler(void)6 K. G8 }7 m1 q
{
: f/ V) s1 B: T/ e4 l2 K1 w% l    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
( |9 B+ D7 a5 p, d" O2 H) J5 O
& l1 G. R  q& c3 u% c" _& |! F

3 |  n3 Z& f# G1 B6 R! F    TIM6->SR &= ~TIM_SR_UIF;' h/ @$ E+ h( n" u0 t4 j. P- w2 q
    prev_key_state[scan_row]=key_state[scan_row];
, Z( `) c% M+ v) M    key_state[scan_row]= *PA_IDR;   // update key states8 N* a7 S5 [8 |/ B
    switch(scan_row)
8 A- W$ f0 ^, t. {8 B) N! E    {( S( k" J) T% T! f. T
        case 13: // next row PB14. {  M- t3 q- Q. P7 V
                GPIOC->MODER = GPIOC_DEFAULT;; j! u/ e3 z5 j- f. v
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
. T! d- |% _, v                break;
1 ]3 H! q& p% y1 F6 ?4 g        case  0: // next row PB15
9 t/ }6 d1 [9 V* k                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;1 C- @! `2 o2 i4 |, S7 q+ \9 p# Z) J
                break;
  B9 h8 X( X6 V        case  1: // next row PB35 z4 G  I" Q; |+ O6 A/ u
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;$ g! C6 d# E& `5 W% j- e
                break;
1 ~. x+ m5 o/ s1 @- `3 V/ E2 v        case  2: // next row PB4
9 T4 T. C$ \0 [: ^, S' V/ M( V                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;0 N- i, K$ F& _+ D$ L: N; l1 g$ K+ g
                break;" _9 v& W8 Q+ s! N5 `( w
        case  3: // next row PB57 H, w0 v0 t0 _& w7 |4 O7 H, ]
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;& d' `+ r! {" Q0 v" P. ~
                break;
( N' s# x$ m+ x0 p6 i! @        case  4: // next row PB6
' ]( E/ r9 l8 P. O                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
1 ]; a# R- Z5 S4 u2 v3 s& R, I7 u                break;$ Q4 y0 A+ B8 X' g: C
        case  5: // next row PB7
5 D) _7 K+ L, w( H                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;! P# W" w! D9 u/ H
                break;
% q8 ]! e( @' |; o        case  6: // next row PB8
  c/ H; t/ D! X& q# R                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
2 D' Z+ P! B2 n9 ?# A                break;
8 H# M6 Z5 j0 y% p: Q9 i        case  7: // next row PB92 ^0 I/ _& `- e6 U. |  @4 c& t
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;( Y3 R7 C3 l$ i, v6 j% ^
                break;" I) d- d1 B' U  c) X# v  u
        case  8: // next row PA8
, X: j  F5 \- R& `. }6 }3 A$ j( `                GPIOB->MODER = GPIOB_DEFAULT;) W  ^) G1 p0 T5 b- y
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;
2 W* Z0 e3 w1 V8 b9 C% Z/ r                break;1 G1 D; y2 z+ j! ]: L) d/ W1 h
        case  9: // next row PA9' e" V  D" v7 z
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
) c# ~2 k9 \/ B( {+ x1 y                break;1 S. V  D0 k' p3 o1 w) L9 z
        case 10: // next row PA103 X5 c9 x  @  I
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
5 ^0 M7 T1 J6 Y% v  i5 a- @( Q                break;  y7 \) W6 E- ~" H) E
        case 11: // next row PA15" P3 J, @' j1 @  e% y
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;: T" q' _( N; d) v3 }- c8 P
                break;/ u4 c# Y# B3 I( g3 e& A$ f8 f1 z% ]% }
        case 12: // next row PC13
, p: g0 E, z- F0 T# [4 ?$ k. i. U1 S+ y                GPIOA->MODER = GPIOA_DEFAULT;
( X+ d" \$ d* }; ]) M. o' t                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;/ x  l7 m$ v9 M8 r1 Y( n6 p
                break;
, P8 r6 r, Y' g% C0 g2 y    }4 v$ F! @3 v* B' M' P
    if(scan_row<13)2 A; B7 j2 l9 ^, S: O: ^% R7 X) p
        scan_row++;. d& t5 `2 O- @$ f
    else
" Q9 Z1 p& B) j* _: K0 o" H        scan_row=0;
, I# ]' g+ q4 E5 S, S" x}/ C: X0 r7 ?) b) w

/ K8 C( X9 W4 e7 ~; U& G
* y) L7 {. A. Y/ V6 U# I
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 + ]- ]4 F8 U- f  n+ Q. I
: p3 ]: v0 p9 ~3 Q9 r* a8 L9 ?
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
; P! l  G* @# y2 l1 R4 _$ v+ {) |- u. I$ q# x  A( t4 C% z
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:

3 R$ O9 M  r" [* ?) z2 U
; E1 W$ i" |3 W- O2 o" ^1 c4 f0 l) z  L: Y8 C/ R% {3 K
const char hid_keymap_qwerty[14][8]={& @0 o! I" g* h# \; G$ P  q) l
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
# g' y. N2 ~/ ?# |$ M    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
3 G" q& j: m# U5 x# l    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
7 J' I3 N8 x) @: O6 x# K    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
( L: I. J6 a7 o' I    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
8 N, a7 {; |5 W$ V9 f, [( z' l8 u    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},5 ]2 j, |2 T& [8 l0 s" g
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},, e. b$ S9 _( Z$ i) _
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
. k- H. S/ o; ^7 F4 Y    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
0 o* n* X9 {5 P* t    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
( p+ _% C$ Q1 n( v+ q4 d- a& _1 \' A    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},% N# u; l5 d1 M. f; i; {; L4 h" B
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
. b& E* R5 i8 }+ @    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
  ^& B( E; F5 L+ f) o- Q    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
+ I6 H3 [6 Z6 P: I, _};
1 |: P2 X" O( T9 ?0 C' ^4 J9 P) b7 m& y$ [, S7 Q( c7 ?% h1 y
" d9 |5 c/ f) F! i( j
const char hid_keymap_dvorak[14][8]={
, D7 t6 k: z& Z, }    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},6 r( S8 g8 f3 T0 R, H# a! N
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},) Z+ W) d  c, r& }1 U
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},, V/ K* L+ Y* y, Z( [
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
6 H. p9 d- h* ?" g    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
& p) |7 u, |6 x, r8 U    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},6 S; G4 [' k" b  H& L
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},' x$ E6 a# S0 P- C: @; i5 u1 D; _( P
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},! W& ^, j5 N; Z4 f8 i3 W+ P
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},5 k% k2 W/ i4 ~8 t0 g' h5 I2 M
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},5 O: L. i5 w7 |8 [# t, E$ v
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},) _/ |6 }5 e2 K  Z- _
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},( ~. y. Z% s9 i
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},+ _- X, y$ |$ Y8 u, s  v
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}7 {! v. [8 \& M# Y
};1 S. J' Y7 d- L" K
" y' A4 q$ r5 t* r

5 l3 L. Q! t; j6 s4 n( |5 B上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
7 z, w  n5 L/ ]/ y
* m/ h0 g- B5 ?( n" I! \; A* SHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

3 G. A7 D. ]$ t0 k0 e5 j) s9 }7 r6 @  t) `5 W
void update_key_matrix(char row, char col, char onoff)/ \  ]& \7 A8 M9 D
{
, v% h8 Y& @- D" W1 S    static uint16_t hid_report[4]={0,0,0,0};
0 j* [3 Z1 a# p8 z' U    static char (*hid_keymap)[8]=hid_keymap_dvorak;
. [, a0 P$ s9 N
6 U5 b  U9 Z; N7 {
1 e; X: f4 M. Q. d6 J' V3 _
    unsigned char key=hid_keymap[row][col];
& {- c  j( |$ \6 {5 M1 y    unsigned char *report =(unsigned char *)hid_report;
" o+ W* W& Z. l0 y/ g    char i;+ K4 ^  G0 {0 r: D' r/ \
2 I/ W: |5 ^) _/ s# L
% s& m1 a  Q" g7 \
    if(key==HK_MODE)$ N( r5 s2 `( `# y7 F
    {
! K# g( v7 y- E+ K7 p% U$ q        if(!onoff)
6 o. p9 R$ S- h, Z2 f# I1 ?6 w        {! y. @% F3 K: [0 L& y1 z* c% r
            if(hid_keymap==hid_keymap_dvorak)
7 f: y, A* {5 n( T! f; G            {
+ _2 c2 I. O3 o5 ?                hid_keymap=hid_keymap_qwerty;0 E2 l% F1 T' b* z
                GPIOB->BSRR = (1<<2);
3 V8 l0 F7 _6 |4 Z9 H1 W  r            }; ]. H' Z) H8 {) B' j) O! V" S
            else
% `9 Z% s: z. g- Q. v            {
+ P' t) J0 F$ `2 v                hid_keymap=hid_keymap_dvorak;# C0 d* Y9 w$ I
                GPIOB->BRR = (1<<2);
& O6 b6 G+ J0 n( G- }            }" s4 \6 {; l6 p% r/ v% j* ]  b
        }
& D+ R4 _4 G' g. @) p& v        return;
( g, y2 H8 |1 S7 I, b- q( w1 X1 C- W1 ]    }& h/ v* j3 b- G7 ?

- c8 K$ |' j+ M  ?

5 `. D$ a  ~# n    if(key>=0x80)   // Alt, Ctrl, Shift/ G7 \+ F- [/ J) k8 Z
    {
6 G, l: b# }  ?3 [        uint8_t bitset = 1<<(key&7);2 [, g- z4 r) e1 v% \! o$ u
        if(onoff)   // non-zero is key up
/ P* n! D' {9 r* g- F3 T            report[0] &= (~bitset);+ K7 j; v& b( K. H8 M
        else7 D" }7 R$ \/ J1 o" y
            report[0] |= bitset;( A  ?/ P; z6 t8 \
    }
) H6 q, E; _+ o6 T4 L    else1 C' O' V4 Z- y2 s' J$ O
    {
" G2 g( `/ C- x, q4 b  ^. z( A+ @, v% d        if(onoff)   // non-zero is key up: {/ t5 i0 G" n  s
        {% P' n# {! F8 X! D# m' B
            for(i=2;i<8;i++)) s+ F4 M: ]  B
            {# b1 k' n; E8 G9 E, r" h; T
                if(report==key)
7 r( M. e. M3 n0 A1 r' J% F, q                {$ c" }0 H! I' _0 q& R" j
                    report=0;
8 p3 T+ C1 f. ?( }. Q                    break;1 r$ V- d- e9 u& P1 d6 s
                }. [+ N/ T% p7 _/ O' _3 B$ L# j
            }  p8 N6 Z) Z; L# z7 @) Z
        }; o+ s7 z' `# J& ~. }1 n- T
        else
" h9 h8 {( C6 F; v        {
9 k& L8 {8 C& V! _# n/ N# Z4 V            for(i=2;i<8;i++)" q' p2 Y  ?" p# ~+ H1 t8 t  ^
            {
3 Y! C8 n' Z- p: T+ O2 `+ [1 f" e                if(report==key)
! K/ B5 k; _4 l$ p                    break;+ ~5 ^$ X% e$ w
                if(report==0)
  E5 y9 D( T& `1 }8 L                {7 O8 z: t: l7 v5 m2 v# }
                    report=key;1 C5 I: n, t% K; F2 J  A8 I
                    break;
1 n5 I# H& Y3 N' o, o& j                }! W$ _( t8 [/ \0 w; T
            }1 V7 b, C  G9 ~7 I0 `# g
        }  D; j/ A2 c# W  g& N
    }5 Y/ o) ]# P8 A, A' L! g$ |
    for(i=0;i<4;i++)( d4 ?& A1 P7 U9 ~  M# U
        USB_PMA[192+i]=hid_report;
5 A9 `, j* o2 j+ L9 B    USB_PMA[5]=8;   //COUNT1_TX
5 p) M% n8 x1 u( M. D1 n  u: {    if(ep1_wait==0)- m9 N. y+ O# n+ V+ c% t
    {$ }! J" U$ {* [) X
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;4 k* n6 `! H" k7 K8 Z0 K% N% S8 u
        ep1_wait=1;8 t- [4 ~& N& _; U8 E
    }
3 v. Z+ \& @5 n/ G. S}
' y7 i7 E  t+ \
/ v8 D, f# B+ G& `( e" H9 P# I5 U/ N  t4 p; G# I" a, q* {
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。# G! g( W+ q/ n; y2 ~+ c% j# ^) T0 K% u
keyboard.zip (8.7 KB, 下载次数: 2695)
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。$ m. z8 Z2 y1 l7 e2 O1 H
回复 支持 反对

使用道具 举报

8

主题

149

帖子

208

积分

中级会员

Rank: 3Rank: 3

积分
208

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
9 L: T0 B& N. Q' I不过楼主也很厉害!
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48: ]( [) n" m% ?
刚开始我以为要把打字机改造成电脑键盘
1 M9 ^- R- X! N5 g3 J4 F  q不过楼主也很厉害!

! k9 y2 d# w( M" X, U* d, U: j哈哈  是有点像奥
回复 支持 反对

使用道具 举报

0

主题

3

帖子

4

积分

新手上路

Rank: 1

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
/ w% Q2 g6 L1 S* F- z+ Y: P
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2017-10-19 11:47 , Processed in 0.220012 second(s), 28 queries .

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

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