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

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

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 2249|回复: 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 编辑 # @) v4 f8 `/ H3 f' e: I* |' P

, v( O- O1 f- n) yhttp://bbs.eeworld.com.cn/forum. ... digest%26digest%3D17 A+ @9 q! C4 e2 U
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。. E1 w, ^9 ^1 ], d

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

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

/ }! H5 P; K% o- o$ Q+ f我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。& k. a) V$ \( B
: _5 c) `  i8 S5 l- n
到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
5 D0 P2 p6 l" c3 S- H1 ~7 s8 K
6 C2 p$ t  u& \/ a' t6 W机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
& R$ i) ~+ X2 d
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg - x5 j% _: D9 w" @! ~" p
----------------------------------------------------  分割线 ----------------------------------------------------------
2 ?  L$ Z0 H7 q
" v' c8 S4 L4 p) o* r先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

+ v5 p/ R' B  q8 M5 p1 k  [* v 020011osionbunl4ui44vi.jpg.thumb.jpg
; S4 T$ P/ z' N* G  q# n: |轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。) Z+ m) F5 J0 c- O
020017j8ycmnv7788bqv52.jpg.thumb.jpg $ O0 P+ T9 _+ l: I" L( k
特写,80C49: {3 l0 v- i5 j8 h8 \
021040oujzuvtut6iujtvz.jpg.thumb.jpg , f/ k! |/ D  Y7 b2 f
LED部分,使用了一片D触发器锁存指示灯状态.
& w; y- c( A# U! O3 s 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
  i, u3 F: w9 B% G( u暴力破坏,将80C49拆掉
  C! N4 H* L  c" V; N 021113e48qq98vyohvhzzh.jpg.thumb.jpg
% @. u. u. e3 o, [, C拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。& G& Z# ^9 h& ?7 b4 o0 E
021125nc9az6dj33rlds2r.jpg.thumb.jpg
  x8 E" p; b+ h  g" g9 G- Z
焊好元件后的板子,准备替换80C49
; t2 ~* K4 k6 I/ T 021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
# D4 P* l: `# y  L- k' S用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。
" Q% c. E7 H. U% x8 _ 021104shifhnrqbr3o5nlo.jpg.thumb.jpg
' _1 p$ q" R5 [% N' \, p6 T9 V; z, J这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.5 r* @2 l0 u7 U  [* }, I' z
022003ym1p9u4ug40280uu.jpg.thumb.jpg
3 _+ ^1 j9 A: j+ D, v; A# Y开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。
* z! }) c% X, l% u2 L+ o: s 023313kt141q9qajtol7ma.jpg.thumb.jpg
3 u; `& c4 b  n* E; M& ?* D& K我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
' E" X3 d4 |5 N4 h7 j4 i 023322nt7l5xb3ltttkltt.jpg.thumb.jpg   Y4 Z  Q, \  g3 O1 i
主键区键帽就位" J5 ^! ?/ @8 |8 H9 o+ ^
023331hin88e8wkrwzwikx.jpg.thumb.jpg : ^9 F. n5 I9 H* e* s7 A9 X
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
1 o1 O1 X6 j2 | 023336wjzlgopugg1jyy79.jpg.thumb.jpg
( Z/ C6 ?& [1 x0 e/ @' o最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。6 X. r! K6 m! P
023341sffu4j3g2323h6fl.jpg.thumb.jpg
, R8 h% n& P+ w: c
( i& i: D& J0 E, S' e
----------------------------------------------------- 分割线 --------------------------------------------------. @2 @" t2 h, I8 q) h! M; k- H

2 z1 ]. j& _/ A2 L: Y2 M6 h! O4 A
4 n% U/ |- ]* ~# N. O

+ e3 N5 p2 r6 S4 n7 x' ]# A1 T/ @7 b

. m% P- S; `4 U- _* T( ~
& ]2 m4 m3 D0 A
! Z: S+ A, Z  N; P/ g3 C5 X; h
  g; [, Y/ P: E! b

5 F& k' A: [/ v& C, X" B  j- h$ `7 Y9 z* [& H4 M

  ?& k7 m# U! d5 h2 K- @& R  r
回复

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
  Q/ E- t8 b+ h, S5 n80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
( a: v" d1 N6 v8 y* @# T. i 104025nzibm2rmiomhyirm.png.thumb.jpg 5 q* T7 f5 C# m9 k3 k
4 r7 j/ m5 Y6 E! O) I) N
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
6 R1 b" ~0 a# x, s# ] 105004zkrez5houvkkznko.jpg.thumb.jpg
! a8 V# e/ O1 t& _
$ V2 W: O# K' h7 Y$ ?$ f3 H( |: R
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
  U! f* H% |) I  e7 u3 {# d8 B$ ~0 S6 H3 h% W2 S$ i
这是我设计的电路图:
( @$ z6 a/ q- M  N3 l 110344ej2z2oo2rflo7oe7.png.thumb.jpg 6 T- {- H4 P( V
+ ^+ z& D1 @1 a7 u1 `$ O5 q
PCB Layout:$ \2 q7 g( y1 q5 a
110847jjbjvt34vwt3v5bb.png.thumb.jpg 0 d5 E/ f' I7 _- `. o
- J5 h; y( e4 F, E5 g
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
$ k: b) f$ E1 k, R0 U6 J" ~# V7 S' L$ T+ d" N7 p' v/ {) `

/ C6 m7 @9 y! r4 @( L% o. w5 g9 }: e9 X
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 / B* _  d: w. \3 Q  T

' i4 R( w& Z. i) f& ~9 f软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
0 c% b% G/ c+ g% V4 J) V1 d
% ~2 G* C9 p/ N总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:/ S" s0 Y$ h% |8 L8 t
113818pmrfsb6z0byt6t06.png.thumb.jpg 5 S$ N9 K! K6 T* @
& Y& u" C6 @" n6 e: [# n
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
) p; W0 q9 u6 m+ M* ?4 X: h% {- A+ S, X" |4 H. \6 H7 s
USB的中断ISR,bare metal哦
' n; ]5 B5 q7 p. M/ C2 X
5 ~. U* y, o$ h( J4 v
void USB_IRQHandler(void)
' |" N7 L" P3 W: f3 [( {7 X{0 g& ]! n' Y% C# w
    if(USB->ISTR & USB_ISTR_CTR)- ~% H) \! l# C- \& W
    {2 A0 P2 Y0 ^. X) u0 H2 ]
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0, _" K; j5 M, a/ n1 |; I$ k
        {
+ P. `5 b% A, F) ^0 z7 ]: e            switch(ep0_state)
& T5 [) p% a% x1 I            {  f- x; K% x) G- ~% |
                case 0: ep0_state |= 0x80;! g$ a' Z2 ]; o% ?' r1 Q
                        break;
% F/ O) I& v2 |" R0 d9 K                case 1: if(USB->EP0R & USB_EP_CTR_TX)( k9 j) i- Y' U* c
                        {
! g, L1 }' M) ]0 {6 M                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;+ _6 B' d) G/ H0 L( h: W0 t
                            ep0_state=3;- N- t! {* X0 X9 d! j
                            return;" G& X" y& u7 v1 y1 _
                        }4 h  n4 A- L4 F) n  N
                        else/ J7 W. G6 ]$ A
                            ep0_state=0;
3 _% W# q3 r' {4 A! M( \# G                        break;
3 g9 P6 l9 V5 k; ?$ |- I* N' n3 @                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
9 g  ]9 i3 [. J! H' y                            ep0_state |= 0x80;
  i' f8 ?) c$ w! v. E                        else
' r+ V5 k- ]+ w$ b; J; y                            ep0_state=0;
6 c# l4 F2 [. a" C                        break;- C2 }7 s! H" h; M' V, [
                case 3: ep0_state=0;
3 G$ h. r$ h8 n6 V! V+ N6 C7 m                        break;
. N) a6 g" }7 E1 i5 R9 P                case 4: ep0_state=0;
2 I* y/ ]4 R- o% F( ?                        break;9 b) G+ |; q+ t/ S
                default:ep0_state=0;) @+ L4 @  ~) y
                        break;
3 i- `$ v9 N5 v5 a) ?1 w9 ^            }" s. }" U; Z. U3 C( f
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag1 e0 \/ t& g0 c9 {. B, [- i; H0 i  G
            return;
% a% q* h7 R' Q  O$ c        }4 S+ m; Q- C3 t/ i1 S5 p! H* @) w, A
        else    // EP_ID can be 1
) i6 [8 X6 G8 E1 s! e        {1 t2 T. P3 O  b! L5 L
            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
) U$ q1 w' f: R! m5 ]5 I            ep1_wait=0;7 q, t  y6 [8 Z9 w+ j3 {& _, D
            return;
9 ?; G. k1 T2 R- Y! A3 N; n7 W        }
6 d; l& }5 L+ J    }
* K0 R& ~9 s# B4 U9 {/ T    if(USB->ISTR & USB_ISTR_PMAOVR)/ J- e/ S7 }, k# g$ M% W
    {
) X% T& Z9 F: \" z        USB->ISTR = ~USB_ISTR_PMAOVR;
' E' _, M) c* N' K    }
; l; {6 w- l; B9 {$ D% v    if(USB->ISTR & USB_ISTR_ERR)2 }3 N1 J5 ~3 ^5 |
    {( W; p  k3 w3 K- p  G
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
3 v) \% j' f1 N9 s( I5 G& y, \+ F    }
/ p# [' s0 `) t: ^    if(USB->ISTR & USB_ISTR_WKUP)/ q; c; \4 c) W2 y: |  o
    {1 o" K* h$ ^3 f1 [* g
        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
, y$ t" ?- u4 j2 a0 [( R        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
1 ~9 z+ U9 ^0 R, H; h" C    }: X9 O- @2 H  P- a0 W) G# k7 g% G9 N
    if(USB->ISTR & USB_ISTR_SUSP)% }! C1 G; K* S' `- N
    {  O& d& x$ b, }3 b' i
        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend" y/ Z; V7 |4 h
        USB->CNTR |= USB_CNTR_LPMODE;   // low power
' _& X- d! X8 A' a2 }# a; l        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
  [  v5 I+ A# b' G4 K0 x+ R    }
& s0 ?6 ]" W; Z( K0 e" u    if(USB->ISTR & USB_ISTR_RESET)
$ ]7 b' X7 r7 w9 y    {
! ~9 ^! Q# a) p9 J6 D' I        USB->BTABLE = 0;    // buffer table at bottom of PMA% j  _  J  c: d
        USB_PMA[0]=128; //ADDR0_TX4 m+ B" E  d! K* |4 M
        USB_PMA[1]=0;   //COUNT0_TX# o% t4 }/ F& b
        USB_PMA[2]=256; //ADDR0_RX) u: F0 `6 f3 I/ P& g3 d2 p
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes0 y1 l( V3 N9 b/ Z) M( a: @$ g
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
# E9 C# d0 `: y: W) s* i0 Y        ep0_state=0;% U; D' [) y- O: @
        USB_PMA[4]=384; //ADDR1_TX
3 [0 O  L0 W6 Z# [* e7 u8 z) y8 ~        USB_PMA[5]=0;   //COUNT1_TX6 U( W3 Y7 Q/ O" b4 @' A
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type0 ?  c3 R8 f' b! r7 C* B3 {& D  X
        ep1_wait=0;! N2 U7 P% ~& {8 D1 |! B. U# c
        USB->DADDR = USB_DADDR_EF;      // enable function
7 i6 `" C7 w  {# a        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear- `, `8 X2 J7 {
    }5 l5 j0 [! |/ d' l1 p: i% u' p
    if(USB->ISTR & USB_ISTR_SOF)# T6 [% ?$ Z& L- J
    {
+ I0 n4 H+ y0 @8 D  k* m  O4 N2 g$ b: t        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear
1 R( z, Y# `: ?7 e    }0 ^) V+ }( c8 Q5 @, O: T' ~
}! R" u, _  _0 e* R$ G
! @# l  q- i( n% F
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
8 U6 R  `& }2 T8 M/ a
9 e' |. I8 i3 }. Y( k9 G8 t主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。% f5 R# e% o' A, z

! ^+ U! `& g' \$ b2 q6 [$ @( d+ }
    while(1). b7 l* |  }. |2 H+ U- n
    {
  M- S# [/ j0 M4 C% @! Z  P9 R        static char row=0;
! M$ d% Q% _: Y  l/ p9 k        __WFI();& K" O' Q3 q4 X1 u2 A3 b1 g
        if(ep0_state & 0x80)    // request data processing& U  {7 `6 l5 n* ^% d6 Y1 E* ]5 W1 i
        {9 ]! r. D6 O0 I; `7 x2 E9 e
            if(ep0_state==0x80) // SETUP phase
; h. h+ ^& c, {( M2 F6 x$ S2 p+ D            {. B+ S3 c& y( s' h8 ?: {
                if(!setup_packet_service())+ \+ e# c. Z1 D9 {2 o" n* u, ?; Y( Z$ g
                {
% D8 b+ H6 t. y" P% d                    ep0_state=0;
7 S" p' d# b5 ^. w+ `6 d: A) K                    // not supported. i/ [) @+ O  i5 b. @7 a8 s% U3 I( C2 g
                }
+ `- p' O0 F1 j0 ~                // ep0_state should be set to 1 or 2, if processed9 G& P% P) r) e; ]: X
            }
( l9 m/ K9 j0 B* \/ C1 \4 f' i; |# x            else    // OUT phase
1 B) E' q; u1 Y% e2 w2 I6 ]: b            {
+ k# p( h! \/ s4 {                // process data, o7 j4 e4 e, E! s: W% r) o6 ?
                show_LED(*(uint8_t *)(USB_PMA+128));: W) L+ V/ l3 T2 j6 T
                ep0_state=4;8 ^7 b; a. T0 Z+ ?( v1 S
                USB_PMA[1]=0;       // Zero length DATA0
7 \7 h1 l, i$ h, d                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;$ t* h0 h! L' B
            }
1 a, k9 p. ]$ R/ B/ `9 x        }# B5 z; V' N5 _0 ~5 M
        else
4 Z4 e% F% w; ~+ e/ E  a, Q        {. J8 K; _' C: F9 B- F
            if(usb_address && ep0_state==0)
% u2 [- c4 T8 i% A; J            {
3 Z6 M  l) ^+ h+ |1 |                USB->DADDR = USB_DADDR_EF|usb_address;1 E+ @/ F% e2 h" m, n( z
                usb_address=0;5 H; m. b' M6 J; v% D
            }
3 m$ [: S+ w" B        }/ T9 M! s% S" d1 Z; d3 d- m* t% J) X
        if(row!=scan_row)   // new scan line! `- _9 H1 o* T2 C& }
        {) p9 Z; k4 I3 h# S8 v
            if(key_state[row]!=prev_key_state[row])9 X. h( F. P; o  N# }, v, v
            {
) \' c/ T& ?% L; q                uint8_t test=0x80;
9 A) C1 x$ j& Y& g( v. S7 _                uint8_t diff=key_state[row]^prev_key_state[row];
' T" p! j& h4 v$ [# O" F; l3 W                for(i=0;i<8;i++)1 P  @( o6 |0 f' F* }$ b6 E
                {
6 y; Q! u0 u- h: v5 Z/ }                    if(diff & test)4 U0 T9 D6 ]4 v* u) J  R
                        update_key_matrix(row,i,key_state[row]&test);
- A! p+ M9 Y$ m7 l8 i                    test>>=1;
) ]6 E* [: P1 e; W7 l. L                }# k& P' N3 ^7 D0 @1 t1 e7 z9 j, P9 Z
            }
6 u* q, y$ N3 @  f            row=scan_row;
: v, e% K$ R% l5 ?' `5 q. i        }
: ]3 J: Z9 q$ x1 A) s) o; [; o    }
. t- u. K+ P: D3 U# z3 C8 K
7 }4 e# D& U0 [( }/ h
2 Q, Q5 ^3 O0 w" q& HEP0的控制传输,把用到的请求处理一下
2 r% T% P0 z. |char setup_packet_service(void)6 M# v' R: M3 H. j# m
{
! z* K- e. J% V5 g4 g8 h    if(ep0_std_req->bmRequestType & 0x20)   // class-specific% f1 f# ~, m5 F" G6 Z. e
    {4 y/ _( K. V/ Y; G7 {
        switch(ep0_std_req->bRequest)
; A! |' i8 c% s* J& ~6 l, M        {
, |  ~  I# M& n$ f4 ^% ?            case REQ_GET_REPORT: break;& I4 K2 j) y# K( @! R4 ?9 Y8 W- s
            case REQ_GET_IDLE:, Z  ^6 w# m1 e3 u, k  `7 Q
                USB_PMA[64]=0xfa;   // return 1 byte
/ M' g5 K) _* F                USB_PMA[1]=1;% m/ z, ]# y0 q7 \# y! a
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
7 r" b  ?: W% k; X; U* n! D2 Q                ep0_state=1;: }" N. m$ o, Y0 f
                return 1;
! P  k! b8 m: {& X  E0 I                break;6 y( w' ]. h; {
            case REQ_SET_REPORT:/ G( `6 ~- v+ n
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;2 F  m7 O' l6 u
                ep0_state=2;
9 m. P% D/ z9 j) s                return 1;
6 c+ g# T! i0 n+ }1 n( A                break;) l7 I- W8 X3 j: c/ p' Q
            case REQ_SET_IDLE:' A. z: B, s, |% g; _* {( b. P
                USB_PMA[1]=0;   // Zero DATA* _  X7 s0 T3 i: ]% c% G
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
1 p$ |3 A8 ?  q& V: d                ep0_state=4;6 L# j3 X- b3 K0 v6 B1 M
                return 1;- K7 _3 y& r9 \2 g0 K
                break;) i. `: U, F# Z) r  ~
        }" X/ y+ N8 k- q, ^9 {
        return 0;
9 i8 l  ^, \4 O7 o  u* W+ _* \    }6 W1 G' I/ i; z) \8 S) x
    else    // standard' F8 L6 @7 d2 `# c) ^
    {& t9 i( M6 ?& q
        switch(ep0_std_req->bRequest)$ Z% b$ P8 o- z9 q! C2 d) b" Q& Q
        {) `, j( f: p. B  c4 \5 D6 `0 \4 M0 K9 b
            case REQ_GET_DESCRIPTOR:' e) t% N) H8 ^
                return descriptor_service();
6 Q# I4 a' D# S" _6 ~' x0 D2 K1 S1 I                break;
$ h! L' V+ c; }2 Q  c6 z6 a9 t            case REQ_SET_ADDRESS:
2 m. u! [) L9 C. b( h, Z" U1 s- c# d                if(ep0_std_req->bmRequestType!=0x00)
7 A! Y2 k, Z2 ]; S                    return 0;
( M( |9 J/ C* n. C% z. {1 N6 O                usb_address=ep0_std_req->wValue;+ I( U' V7 D* v) b/ T. h, a
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;, i3 b- s; n/ L  B7 t" X5 B4 w
                USB_PMA[1]=0;       // Zero length DATA0
  q4 N* D3 ~; }, Y) a                ep0_state=4;    // No Data phase
4 u/ L% ]! Z. [# S6 l. x5 D& E3 l                return 1;
0 H# z6 x5 @7 U: m; \% D            case REQ_SET_CONFIGURATION:
1 `: ]' Q3 M5 }/ ]' u" l6 g% Y- l                if(ep0_std_req->bmRequestType!=0x00)0 u- P8 \, r1 E1 H. V
                    return 0;; k- O! g7 R/ b1 g+ L* x
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;6 A: K+ {% A- G1 @: y9 `: j/ A
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;, U( m* }$ q8 W) @+ S- e
                USB_PMA[1]=0;   // Zero DATA1 j4 r! k. e$ m  Z
                ep0_state=4;    // No DATA phase
8 r; x7 K& K9 B2 X5 ~/ [                return 1;
* _* V8 U; G; f( y& Q            default: return 0;1 G: j8 I; X. \0 H2 l
        }
; x0 z* l1 {9 d& f9 `" r    }
6 b* G1 i, U- @8 ?  }}) F1 A7 C1 ~& W% T

3 K7 N8 Y  N# \: ?9 }, Y  b- P5 [, o: ^# y, f8 v  Q4 _" p

4 D' K# y# j% W7 L# ?; I- e9 g+ N
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
0 r5 f/ i7 x% H1 O# Qchar descriptor_service(void)  z( ]# y0 H6 n( e" {1 ^
{
+ p1 @: ^: F* b! @2 v2 |    switch((ep0_std_req->wValue)>>8)
! f( o) f7 u+ h    {
0 D% a( }/ i4 N" h5 [$ u# S# ^        case DESC_TYPE_DEVICE:
) [; H- C/ W2 t0 l7 T            return ep0_preparedata(&DevDesc, sizeof(DevDesc));
$ |6 Q; v! W9 |4 `' t$ Y  b            break;
0 Q9 k4 m8 v% Q' s" h* ?4 w        case DESC_TYPE_CONFIG:! \( }8 j8 s9 p( C: L6 G
            if(sizeof(ConfigDescData)>ep0_std_req->wLength): U( p+ G7 b8 [4 a4 i
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);2 u8 X/ `% i- q0 |4 a/ [
            else
9 G* c  [3 E: }                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));( a1 ^9 J# a/ l+ F2 Q
            break;
# n7 f$ o  K7 X1 k; |        case DESC_TYPE_STRING:
2 _, v' F* C: x2 r6 q! Q            switch(ep0_std_req->wValue &0xff)1 O# N1 _4 j6 h1 c
            {
0 `) z' C3 `) f# r! n- Z. }' n                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
& D0 L) d) v9 Y* x, I                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));5 ^8 [1 o4 I! b; h; |" Y
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
) }8 D% y/ o4 D                default: return 0;$ O8 N; F$ ]/ S( @4 p
            }$ `7 r. f. o8 M, {+ e
            break;2 t* k! d4 x& K( p
        case REPORT_DESC_TYPE:: |5 D  t7 C& s2 I$ H. ?
            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
9 h% {, g7 Y9 _0 M# ?: B& l! ?: j        default:
4 i4 Q: e- V7 A! v. v            return 0;* ^7 z7 v- q2 r2 x* F, A
    }
; ]0 K; p; f( [4 U- y, p}& a' l2 \! G% _8 ?
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms./ D% {+ ^2 Z: |: @$ ]
void TIM6_DAC_IRQHandler(void)  e9 a8 v) F1 T$ D
{
8 m; Z! a3 A. T1 W/ f& T4 ~    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
& K5 U! Q4 ?7 R3 t
$ S- p8 U% G( o/ b

! S" H+ {3 r# U" o    TIM6->SR &= ~TIM_SR_UIF;6 c4 T5 b: G9 R' n. I
    prev_key_state[scan_row]=key_state[scan_row];$ t+ Z' z8 W) c2 X# ^' `
    key_state[scan_row]= *PA_IDR;   // update key states& o- \: `# Z. F7 {
    switch(scan_row)
- }/ i% ?; ?: z' E2 R    {
2 o( N; n; v/ K6 g        case 13: // next row PB14
6 @' t. W7 G& F9 d6 B" o8 d* X. W- `1 M                GPIOC->MODER = GPIOC_DEFAULT;
6 I2 v4 @9 Y9 L6 o* Z+ U) h% x                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
" q! @& j: [  x) Q0 O. ?4 i. s                break;4 l! c; i  f4 R
        case  0: // next row PB15
" Y9 Q6 ]4 P1 Y2 K* n3 ?$ b! u" _                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;
+ m* I; |5 j- b: j8 y/ h% P                break;
4 k. b3 H6 P$ n( j; S8 {4 _        case  1: // next row PB37 }* `" S% ^3 k8 z  O
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;) s5 Z8 h. d  k* [
                break;( m( e6 ?: H& W# Q7 r; x
        case  2: // next row PB4
& N3 o% U( P; f# ?, V- w' r! L                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
5 p1 M. g, z4 K8 S% C: j( N                break;2 c5 T% S3 l3 l# L! h' j! y+ p
        case  3: // next row PB5: D  }  o+ [5 b1 g! r& c  ~
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;
0 z+ U( z% Y7 N- Z4 J2 G' d3 i                break;2 z6 y9 c8 W7 Q8 |# x
        case  4: // next row PB6
  V/ l. b' A: T/ @, f" \                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
3 W6 U7 w4 D9 |; Y- S' Y                break;
: I# ^1 S5 t7 z8 L        case  5: // next row PB7
. q9 B; u) X1 ?                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;* O8 L0 s% I5 ~( h
                break;& Q/ m1 Q$ P1 H8 E0 k
        case  6: // next row PB8) |  e3 p3 {, x+ J: t
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
# L+ z! ^2 [5 y0 s                break;
+ h7 G6 x: [! b. G        case  7: // next row PB9
8 M/ a' t. v* v+ z4 M8 T, T                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;1 J% F: B+ ~# I: J9 F3 z" r! ?
                break;
( y& E% e2 [! M1 a. k; O        case  8: // next row PA8
2 P( W, i& f; s! `) p                GPIOB->MODER = GPIOB_DEFAULT;
: P6 c* P* Z1 y% h9 z9 P+ J# Q                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;% ]: N& C8 a% T4 O/ v9 ]" C8 F
                break;
7 h; E. D: P( v6 V& Z        case  9: // next row PA9
9 U- k1 G# G5 a                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;* f. A' N) e4 O; [) n1 P5 d
                break;
  O8 q4 e* f# f) v  s        case 10: // next row PA10. m5 r' u0 @( U' E9 U
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;. I& U+ Z+ J& _$ S. _# i
                break;( A7 m9 q" z  U# I. l8 O  ]- j
        case 11: // next row PA15
) A' S0 H0 H# E& l2 ^- m                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
% z6 |% }6 S8 U0 o. [' E0 W" B                break;! O$ y% U, g: q
        case 12: // next row PC130 \( s: U5 l4 C+ x* j' Q0 ~; e
                GPIOA->MODER = GPIOA_DEFAULT;8 j% ~0 V( `  \4 ^" `
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;! Y, C# h0 \# L4 X" N
                break;6 y( Y" U. S( o* B! M
    }$ l/ x) d2 i4 f! `
    if(scan_row<13)
. k" e  B1 ^" J8 D0 T1 _& I        scan_row++;
7 r* y" p2 @& m' J0 K1 [    else
. f$ K4 O* b6 C' c, ^( r# L7 [        scan_row=0;
- Z- ^6 N1 |( U7 B}7 a1 h3 Y, W, E- k6 L

% f  E( o5 D# Q' w
% L7 X6 O! K& p5 z2 I8 g
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
+ y0 `, I0 J$ {: [, c( v! b# K7 Q; Z- s; [7 N
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
' Z9 L5 [4 ^. x1 W. `# \! q6 P0 a0 s% ^- e
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
% t& i' S) o  g& j) k2 L
( c" d% m0 k$ i* v, G5 {  H

7 q3 Z; `: D$ w2 P1 Mconst char hid_keymap_qwerty[14][8]={6 e( ~% @, D) m
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
" I; A- D0 c6 a- Y! {    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
) k# b6 T& x. k# Z9 s/ R* N    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},5 h& z5 q0 c( n3 L: d& h
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},: \$ k- S/ F3 c% y' \; V" E
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},6 E4 L) z5 g7 i7 B% z
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},! \7 B$ F$ C7 f8 F, j0 V
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},: f6 m% k& G$ \7 L2 [8 T/ s
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},% `% l4 V* P1 q- |
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
  G+ N. l! j5 a  P8 e    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
' {* V) A% o" M: t+ w1 }    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
8 }' f& m6 N9 n- c# h    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
: w# H; R+ g# v) r    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
) @0 \( C2 V$ Y3 V4 w    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}7 b# ~. z+ O* p  |$ p  r
};' ~8 {1 t) C9 i2 i4 e* d
, g4 C- t- u8 |- ]1 J$ U* }( L

% O& b( R9 X5 {const char hid_keymap_dvorak[14][8]={7 g8 _4 m+ t7 |9 t/ s: o/ I6 r
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},$ u+ a, r3 b6 \4 o8 Y( y/ W5 S
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
6 o# k% t$ ?2 g    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
, r% H/ j) c* x6 x+ D, b0 f, T    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5}," R3 S/ f" r6 m0 d6 W3 N
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
6 W0 e# K. S+ k( i1 f$ g: u    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3}," m+ u, Q, x( L
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},- t3 }4 W' i: U  q0 U4 n  D" [" h) A
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},0 O! M9 P' |+ {6 G6 e6 y6 Z- h! ~+ @" g
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},! a3 B5 M" s0 E% m: I! T; |
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},0 k# W9 m! Y  v1 Y6 J% P0 U7 k3 d
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
2 D9 _# n- A8 u- @6 F) F5 E    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
, U1 `+ H% j8 g% A' c2 l% L    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},0 [# G/ a$ \& \6 x
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
4 U; ~0 ^! W+ s2 H2 `3 y% w6 `};
8 J( ], `7 W( _+ v, R/ H. o7 |8 f6 b9 |* [1 X
5 z* A, q. P) u: x* F" ]! B
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。  T" q: L! Q' ~, a

  Q) h  q6 H1 I2 w. tHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

0 W# o, M9 U9 C5 a$ M
; c. R( D  z, ?void update_key_matrix(char row, char col, char onoff)
) p% u1 v# Z8 \. ]3 P$ J$ L0 D5 q{* t; \0 s/ p3 b8 t( {& y! Y+ T* U
    static uint16_t hid_report[4]={0,0,0,0};
1 w" o/ v4 r* F- Z( w    static char (*hid_keymap)[8]=hid_keymap_dvorak;* V: t( |% h5 W8 W# u

. Z2 v. C4 B4 v; ~
2 n; M+ q$ s5 J! y  f
    unsigned char key=hid_keymap[row][col];! l6 }, j+ }( B- s: @
    unsigned char *report =(unsigned char *)hid_report;
) X  |" V1 \7 M" x' p8 C" m    char i;
0 W$ ~: h* L0 Z) d
0 `; Q  v0 u, R" V6 h

# k, H/ y8 {4 K: y* m, G. p7 z' v    if(key==HK_MODE)& B7 r9 H2 ?# M, ~2 w+ p; {
    {
% }6 o; U) L: V3 z        if(!onoff)
0 E/ w$ _; X; \% y! \/ E( n) p' r9 q' s        {
1 @9 f* q$ y5 c2 Y, a- b            if(hid_keymap==hid_keymap_dvorak)
! r$ H# c' L/ h: A& e! d# `/ `            {, Z( X  c' P3 B" o2 ^
                hid_keymap=hid_keymap_qwerty;' \8 Y/ N4 R3 ~: {6 K
                GPIOB->BSRR = (1<<2);' p6 h( h2 ~4 w
            }
0 t# A: T; p8 x# l            else
  Y0 q% B6 W* V. ~4 ~            {6 a1 c$ z5 o! V9 g1 q, P
                hid_keymap=hid_keymap_dvorak;
4 c& P; J( _5 e; N4 w8 O1 l8 S' J                GPIOB->BRR = (1<<2);* s" ?- [, ~) S( c$ D) W1 \
            }* x6 ~  b# ^# b2 c( z
        }
' n6 N) ~0 o1 s7 Q1 p5 F        return;
' l" f3 o. s7 D! w2 Y    }- p  V& h, |3 P( B( n
. E4 j: t# z' d% ]8 D/ {

) E. w; E( J% N. |5 A! Z2 t4 G    if(key>=0x80)   // Alt, Ctrl, Shift
( E  M7 ]; E! b( I: p5 o    {
# a( e" R9 U* a2 ?# R" \. p        uint8_t bitset = 1<<(key&7);: u. D  f: _+ }( x
        if(onoff)   // non-zero is key up1 |# r7 c  @8 o& p) M' x
            report[0] &= (~bitset);' ?) F. x  p3 E. K' h
        else
3 ]2 }8 ?( ?0 x: a7 x9 n. ]9 ^            report[0] |= bitset;8 ~9 Y& e+ u7 b" _% A+ C5 e% }
    }( O; n2 I" s" S- U# f! u
    else
2 H) o# v6 h1 f3 T    {2 s' ~) B1 K, ]- B) v3 m
        if(onoff)   // non-zero is key up
+ I! p7 G. s, T4 s        {) d$ w9 M9 d' q& t
            for(i=2;i<8;i++)- u7 e5 M( Z+ ?, |) D0 e
            {
: A% D& f/ e' ^+ c$ i) c                if(report==key)
8 S/ }3 ]5 z: V9 @                {3 d. p5 y/ A5 M0 T; X4 H
                    report=0;6 _/ w, t: A7 ]% J$ k- O8 [
                    break;8 S+ @- T; N& J. v
                }
. d# B% H2 A1 f; A* i! n4 g6 s            }
, o9 O+ z0 W4 a! C- z  G# Z        }+ A  ?- i2 D( q, \" t
        else
5 ^7 G5 z( f" n6 e5 B3 b        {) N1 J% I+ @/ w) W# k: y4 S1 q. N
            for(i=2;i<8;i++)" ^. |# g4 ]6 L0 D  Z: t
            {* c; I' y) W' f
                if(report==key)
8 r) P8 M  N& {% c" H: d                    break;, g+ Z: O9 s+ f( ^
                if(report==0); f. k% p" N" O
                {& ^) }' q9 T4 H' w4 f0 H
                    report=key;" P, u0 @5 }9 q% L$ z9 |" L4 U
                    break;
. l6 v" @- g9 h; c7 E* R) t                }
+ `5 v+ \+ R) Z0 e& d8 w" d( [/ t            }) o1 L" M" M9 i; U
        }+ H; v) g  `) }$ B# I+ P
    }0 F9 h! P! W9 K  C+ D  ]- B
    for(i=0;i<4;i++)# r' P2 @( ^! k4 r
        USB_PMA[192+i]=hid_report;+ |2 j8 v& _7 N$ Y! T# i
    USB_PMA[5]=8;   //COUNT1_TX
$ |2 d1 g) w2 v  }    if(ep1_wait==0)
4 q! G7 p1 P  p+ e) {    {" R& n) c; p; u1 K
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;4 o2 v* x: N0 \0 k7 p5 H4 t
        ep1_wait=1;
) A- U* W4 P( [: s3 A' n. X2 X    }
; }0 N2 |( w4 Y}
  @7 M* }' d0 U( E+ }# q" P- l- a( j, x7 B, w

( s7 m6 \" Q: o; O- ^4 _( g完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。% E$ E- Z/ w: m' e4 t6 F2 k
keyboard.zip (8.7 KB, 下载次数: 2976)
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。1 ~  b- n  r2 v! j" [
回复 支持 反对

使用道具 举报

8

主题

149

帖子

209

积分

中级会员

Rank: 3Rank: 3

积分
209

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘3 W2 z# V  z& w  J! ?- k& m  \
不过楼主也很厉害!
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
1 w+ P& E  L- W刚开始我以为要把打字机改造成电脑键盘
7 ?4 \5 d" W) ?不过楼主也很厉害!
6 {+ X$ ?7 _6 h9 Q# S2 Y
哈哈  是有点像奥
回复 支持 反对

使用道具 举报

0

主题

3

帖子

4

积分

新手上路

Rank: 1

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
3 _9 K, K) @  z$ a, Z3 g! _7 u, _
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2018-5-26 08:01 , Processed in 0.194585 second(s), 28 queries .

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

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