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

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

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

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

[复制链接]

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑 $ Y' N, ]" {, o/ a: H, _- m) l' |

5 _9 e2 |8 V, A6 j! j5 h+ Vhttp://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1/ v3 R) X3 }6 P
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。' Z% J0 T1 l0 {
5 B! L# K/ d$ v  d1 K* f
在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。
- \! L& t" K3 }" Q) [

! ^. w$ l9 F# g' ^  b# A 235140i3a36qivqzuvmt5q.jpg.thumb.jpg
" g5 m# `3 ?$ g4 G2 z8 S! S* C0 v
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。* w7 b- \& r3 _' z
001734klbyoluenuwz4h4b.png.thumb.jpg
% m! v% L4 ]1 C' P9 p) b为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:
) F9 ^1 E0 l3 M9 o' B6 ^8 \. u 003625r2agx2f5v922cf2f.png.thumb.jpg ! z7 q3 G5 L4 R3 ]# n2 e* W
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.
+ i) @3 G; U$ R% a+ j5 CDvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
+ {4 M% v! X/ i& [! S2 G5 O
005836yvs0wvovwsssgd3o.png.thumb.jpg
/ Q* x* ?: F4 W% b: Q; uDvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。$ X! \+ f$ B  F; x6 Z) _: Z+ D

" u1 L2 V& S0 g; c! w1 q, o% ^我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
* x% D: ?0 N. q2 n. K) U& k; _/ H; c- ?& S, ]7 O% Z+ U2 m9 j! ^
到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。9 [6 Y) S% R" b  ?9 n. @

' b# G, E% ^" L5 e* b机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
6 W5 x7 ~8 M! n
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg ( v5 u  u" K& G+ B' o
----------------------------------------------------  分割线 ----------------------------------------------------------9 N  V. d! ]4 y' q7 ~

+ j: k9 i/ i' q% x( T9 v先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

% J- C- g6 q) D% x9 _1 l4 C5 c 020011osionbunl4ui44vi.jpg.thumb.jpg 7 R" b2 ]) y7 {6 ]6 \7 e! U( X
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
; _6 a2 V. X: p- @9 q 020017j8ycmnv7788bqv52.jpg.thumb.jpg 2 |8 P: U) Q5 `5 M
特写,80C49- r8 H( f4 f! F) D0 {( z- |# h$ Q! l" r
021040oujzuvtut6iujtvz.jpg.thumb.jpg
8 m# g* Z5 @2 f: aLED部分,使用了一片D触发器锁存指示灯状态.
( J1 Y, ?8 y& j0 a7 S1 b7 N' w 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
+ |+ q0 A3 A- T3 s暴力破坏,将80C49拆掉
% a) _+ e3 f) `8 w9 N' i. q 021113e48qq98vyohvhzzh.jpg.thumb.jpg
( A% q* F* i) U* M+ d8 w( z( Q拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。% A, S3 Q4 ^' l' |* B
021125nc9az6dj33rlds2r.jpg.thumb.jpg
9 n. y2 V4 W) O  Z
焊好元件后的板子,准备替换80C494 O8 d" k! I$ P* n- J# p1 H2 g$ [
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg , U/ B* k8 b3 s" V
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。
: S. ~: ~( z# f2 u$ T) Y- o8 X 021104shifhnrqbr3o5nlo.jpg.thumb.jpg $ o  D# d- t' u8 i
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.( T! M" a$ P8 U& y. L2 M9 f
022003ym1p9u4ug40280uu.jpg.thumb.jpg
: D( O+ L: C! l; {1 W3 l( [开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。9 x9 X% ]7 X  J, K. B9 ]. R3 p
023313kt141q9qajtol7ma.jpg.thumb.jpg % t7 `$ x5 ^$ e* y3 M9 Y! M
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。7 |0 E( M, B6 c3 Q& c
023322nt7l5xb3ltttkltt.jpg.thumb.jpg / o; k2 r& L( `4 c3 a  n9 p
主键区键帽就位
. y% W( O( n& i# l- ?* c1 L 023331hin88e8wkrwzwikx.jpg.thumb.jpg
5 q% `# q; ~$ M/ C* s, I编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
* F- C0 R# G" y& Q 023336wjzlgopugg1jyy79.jpg.thumb.jpg " j# ]+ i& E  M* O1 V* p% A
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。
) _2 k$ A$ z) z; l3 L 023341sffu4j3g2323h6fl.jpg.thumb.jpg
! m2 B' i4 s0 G9 D8 M

& V4 H. B# a3 E( V----------------------------------------------------- 分割线 --------------------------------------------------0 X2 {1 @* l& J1 l) R6 d. J

2 N5 J% j% F& ]" S0 m) B
+ P5 a- R( v& G3 R/ U$ ^" h1 S
7 ~1 ^! ?5 ]$ b

1 r4 C! s$ C/ e! j. t8 \; T! A5 P  F
5 g( I! c* a0 U" E! P" X/ x; ^7 t4 r

/ `& r( h0 o6 o( R; h/ V
  S& S1 C8 p3 n" u
$ C/ X9 {. x5 f5 F1 {4 t
3 v( g0 g5 v) T7 Q; P: |( v& k$ o" b5 D6 G' o6 I: E/ B

. {: m9 \7 q% I" U8 U1 U
回复

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
& i! P2 Q* k" W80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
$ H* s1 a6 }& C 104025nzibm2rmiomhyirm.png.thumb.jpg + C1 [! C7 v" H* l

9 u4 M2 w; e9 I- c1 H$ |2 C其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
9 c1 ]5 L9 l4 _, E4 Y& V 105004zkrez5houvkkznko.jpg.thumb.jpg , T4 ?" O" D" ^7 u5 w6 j% V# {

$ \) z: H$ [. b$ {& i& m: J扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
+ {' f! l5 a7 B, \% G' h* H7 F' E8 y. ^% X4 z& c" f8 K  g
这是我设计的电路图:/ |. i; o5 z0 e4 J1 z& @9 q" K
110344ej2z2oo2rflo7oe7.png.thumb.jpg 6 [/ x1 b& e& h- w$ g
8 a& D6 z: _1 W( _8 m4 I* a
PCB Layout:5 k# s* o3 [8 g$ ^# J) G8 ?: |$ \
110847jjbjvt34vwt3v5bb.png.thumb.jpg
$ T! Q8 d5 _* A' m! M6 p- S

) g) Z/ m& P9 c1 M: K5 l- y不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
) V: z$ Z2 _3 n5 U
- {1 S! F) W( k6 ~% s" K% D- ^

( M& v3 F" l! B" C1 y
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 2 p$ g( V. h, u# r  S6 G
# B4 s' |% r0 m  b( n# R9 q
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
; ]) o/ b0 P4 k6 a* ]% @, J2 H$ ?  q  _) w
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:/ ]0 C/ v0 a& I$ Z7 k$ _
113818pmrfsb6z0byt6t06.png.thumb.jpg
6 J" {! o& T+ X
' m+ f3 Z+ B, M
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
7 b! W' f" _  B$ [  k
! C' ~5 i0 Y$ R% y* }, a# M0 J) YUSB的中断ISR,bare metal哦
- \# o$ }& G: Y5 l

. [1 Q7 j1 t% ^+ S7 i5 m5 n3 |void USB_IRQHandler(void), o0 h& E+ l& D* N
{
5 W! v% [' B7 W' M$ Q) w. ]) J    if(USB->ISTR & USB_ISTR_CTR)* o0 \* Y- m: h# W# d  c7 ^6 g$ r
    {
7 T# V6 U% c  q% q- c0 F1 _, u        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
. \: `0 ]9 P: n7 I: d3 L) ]) `        {
& k% N; K/ q" w# U            switch(ep0_state)
1 V* b! q- }- s8 i3 F            {
" o9 V& D& i8 f4 `) G& x$ F6 Q                case 0: ep0_state |= 0x80;4 J$ P7 r7 G. V: x4 L2 G
                        break;
0 o$ y. U) l1 o$ m                case 1: if(USB->EP0R & USB_EP_CTR_TX)) z+ m) s6 O; g, |0 a
                        {
4 w" ^7 b8 X: Z/ s! Y+ _                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
7 P/ A4 _8 f1 T                            ep0_state=3;
# `- T6 @; Y5 x                            return;
* }# s8 E, X; L0 Y$ u1 a                        }
, a! I6 F0 C% J; G                        else/ _7 j. ~; O9 o  C, i
                            ep0_state=0;
( x% z, `# |# W% N1 R) a& Q                        break;# J) B( q+ l& @. E7 D* |
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
$ r% [( k# F1 ?- J$ x) h6 ]                            ep0_state |= 0x80;
8 ~1 w8 Z" F3 G$ |6 K4 Q5 s" @                        else6 u8 c7 M# j' t8 P) `
                            ep0_state=0;
7 l6 R: ?* M2 u$ Q* _5 [                        break;
* x* C% `" |1 m6 F2 H7 ~                case 3: ep0_state=0;, ?/ N5 |/ [& u) o( e$ u# d8 h
                        break;: o) C" e* ~" D2 Q' O( v
                case 4: ep0_state=0;
/ {# |- t1 U/ T                        break;! r& F- J- e7 g+ J3 a. y. `0 l4 v' i
                default:ep0_state=0;( \' I% ?+ U* k- A( j: f1 y
                        break;& b( z+ W" T/ @2 B
            }
2 L6 W5 q! m  Y1 f' W0 Q            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag# p' e+ B7 i$ k
            return;
- O: O5 x' N1 L- J' y1 m        }0 g4 W- |" I1 d" F
        else    // EP_ID can be 1- H5 b2 z+ k/ {7 M  L  J, ~
        {
" D4 ?4 D* n( m% `7 Y; l  z  P            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
- U& l9 i; _7 z0 U  ?            ep1_wait=0;
: l( T; Y- }+ u: F* V2 [5 L            return;
6 Y0 D/ [+ L7 X8 l. V& X        }
! E! e; p% X. ]( y' Z    }" Y# l4 d0 I/ [  M  g" y) e
    if(USB->ISTR & USB_ISTR_PMAOVR): z# j' s/ A: }
    {
3 \3 e% o4 i1 \+ t* h, K        USB->ISTR = ~USB_ISTR_PMAOVR;
" c+ g" e$ y; Q3 L: R    }
/ u. h% X/ M5 G3 b6 @8 `    if(USB->ISTR & USB_ISTR_ERR)# I5 a, F5 x2 g- |8 z' l
    {
! W' A+ G; o, {$ J3 X, R        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear9 k3 r+ N' _" L' w
    }
  @4 L( ]  E. _. F2 o" ^    if(USB->ISTR & USB_ISTR_WKUP)
% j# z0 z7 R- h0 |- m5 e    {
6 x' b5 h. A2 d, q        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend: u8 o( z/ J* m( o
        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
  A, m7 B2 P5 l/ G    }, v% X  k- N9 l3 W
    if(USB->ISTR & USB_ISTR_SUSP)
7 w" u/ j; X+ T+ J, {& n    {
  q' ?" Y6 r9 N( t        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
$ l0 Y# G- c. N5 s5 u        USB->CNTR |= USB_CNTR_LPMODE;   // low power
2 D4 M) y9 v, P) n" Q        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear- t9 Q) ~$ f, ?3 t
    }
& l3 Q! P4 D/ C( F9 Z# G) R    if(USB->ISTR & USB_ISTR_RESET)9 N9 y3 [" D4 l8 }! ?; s5 \
    {( z$ w3 Q9 O9 l- \. _
        USB->BTABLE = 0;    // buffer table at bottom of PMA8 o$ m5 _* r" n' Z5 j, W: o$ Z
        USB_PMA[0]=128; //ADDR0_TX2 h" t% h* x' K. O2 J  n$ ]. G" F
        USB_PMA[1]=0;   //COUNT0_TX
( ^1 u2 ?* V" w7 @" i3 d2 |9 R7 |        USB_PMA[2]=256; //ADDR0_RX0 J  g4 n/ `  g0 T, @# q
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
4 }5 M1 R  W) G! |( v) T( C        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;! x" h; x, n1 J, O2 |7 t8 j
        ep0_state=0;
' Y% G; X3 |  @" h; P5 F        USB_PMA[4]=384; //ADDR1_TX' H% m$ @; k% W0 t2 r
        USB_PMA[5]=0;   //COUNT1_TX
" f- v0 C4 s& ^8 O. E        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
; u4 D/ Y/ I& K: n0 N        ep1_wait=0;
1 m$ O% ?, H+ o        USB->DADDR = USB_DADDR_EF;      // enable function' }& P. o8 z0 c: M/ k6 F; u
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear) A2 {$ M, e1 F  }. @
    }
0 z; m8 S& g' K3 {$ K    if(USB->ISTR & USB_ISTR_SOF)& Q- s9 U) x6 `" G) a
    {2 Z) N7 G; s2 c3 k0 s
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear+ e6 j/ z5 ?7 n2 p2 s; S8 T$ |5 J
    }
: ]* Z1 N! q3 Y  m}+ Y# k7 B7 s& b8 z0 A6 b+ }

! b$ R* _2 _! z$ X9 R
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。. u: j% h5 p  W& H+ P
% q% C! h/ h' d. Y
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。9 y; @5 `" Q. A, B' w; c+ r
' q* k, ^, U( _' C/ E7 B
    while(1)( F* S9 d  B5 k5 @% l6 R8 l. M2 r( C
    {
8 c6 ?# n3 c+ W5 `( W7 w        static char row=0;
! u! k( a% Q# D, U5 D        __WFI();( r- O7 J# F- U) \
        if(ep0_state & 0x80)    // request data processing7 ?) {" Q- |/ \% O% M
        {
  U* Z* G0 j. H, L            if(ep0_state==0x80) // SETUP phase
; k( B2 m+ k$ \2 y            {  W$ _% L  p' h% v) B, p6 W7 t
                if(!setup_packet_service())! x% M- x1 O* U) U5 t2 z, C( Y2 G
                {
. N8 N. ]' X% {7 K3 a                    ep0_state=0;0 I+ `9 L7 _$ k$ q. [, }6 ?
                    // not supported* A; v3 t+ }- o8 U9 n; i5 f
                }% N6 g9 K' l) W( N8 o$ a
                // ep0_state should be set to 1 or 2, if processed
6 Y. r8 J8 ]. A) k3 P5 T# d, ^: d' P            }3 \2 [3 y' I$ d
            else    // OUT phase" ^' ~5 A. L% x/ r8 z& M0 B5 Y
            {8 [- K/ w0 ?/ a! ^9 }
                // process data
2 T4 O2 Q% D, N1 t                show_LED(*(uint8_t *)(USB_PMA+128));$ U; v2 p+ s, P: ?* j
                ep0_state=4;$ l6 k% b2 i0 {5 `! b
                USB_PMA[1]=0;       // Zero length DATA04 H: ~" h0 M5 }
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;1 ~, y4 K& h: g2 D! b
            }
. R" T) {, P2 E( \" ^        }
) U  m; L. j- i        else7 B2 x* ~9 l( w% O' G
        {# j" c; D# n- g# b. J. k! t6 J
            if(usb_address && ep0_state==0): D1 Q$ e, V( Q' \! U) `
            {
: T; w: V/ `' h: D% Y# `7 _( l0 S                USB->DADDR = USB_DADDR_EF|usb_address;# Z- z' W! O/ A& `6 F
                usb_address=0;
$ K2 }0 c/ Y8 A9 U' i# h            }& n; Q1 s6 m  Z/ g7 ]+ F% H" W1 K( ~
        }9 O% V% S' t/ A% M  |
        if(row!=scan_row)   // new scan line0 L; u3 z( K' G( H0 l' `
        {
; N; Z3 o* v; r2 e5 u/ U            if(key_state[row]!=prev_key_state[row])3 H& `3 p* T% T5 ]
            {
: H" I2 w$ x1 }9 _2 p5 f3 c; D                uint8_t test=0x80;
1 Z/ j3 Y3 V2 s6 M                uint8_t diff=key_state[row]^prev_key_state[row];
+ T, S0 n* Y/ f9 W8 V                for(i=0;i<8;i++)% {, p1 I* i4 r5 u( X
                {
1 B+ g% W0 @' Q. k. j' c                    if(diff & test)) x3 K/ u7 W& U/ A6 L7 e$ P5 R. ^( X
                        update_key_matrix(row,i,key_state[row]&test);
9 x' G& _  w3 O. B* \                    test>>=1;
' }4 B, _  t9 M% S2 }  ~% z                }+ `! H7 O. G% [, B; G: \* g
            }
: x( ]/ V( d& i+ m, T3 q' V            row=scan_row;9 S( e( {( t: A1 H1 d& u
        }* J+ J  e9 _2 b) Z9 G1 N4 _' ~, Y
    }- |1 Y; K" X$ D5 g) R5 P

# n# G* w2 D5 t0 |8 p6 U3 ]8 j. z2 w" ~4 C
EP0的控制传输,把用到的请求处理一下8 m; {/ C+ _" o
char setup_packet_service(void). G1 r" r6 j8 ?  D: H+ j. |
{
. z0 X* j5 ^, y- q  k0 O( E# J, v    if(ep0_std_req->bmRequestType & 0x20)   // class-specific3 y9 B" d( |$ w# Q7 |
    {
8 U6 o" z! K) ]- ~6 d  `        switch(ep0_std_req->bRequest)# }4 }, i4 k* o+ X3 t' {2 b
        {
% }8 E" t' K% H            case REQ_GET_REPORT: break;/ W& B. o8 p  J3 W4 q
            case REQ_GET_IDLE:
" ?$ X7 q; V9 s                USB_PMA[64]=0xfa;   // return 1 byte3 W0 o& z8 ?$ t# z' S) {
                USB_PMA[1]=1;
7 E0 t; o% B$ s1 h                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
- U1 |# G) s* g7 H: V                ep0_state=1;9 v7 o7 X$ m5 i; b& ^3 x
                return 1;
4 Q5 o2 y( U5 C4 x4 w% A                break;
2 v8 F# y5 V4 r2 V3 @            case REQ_SET_REPORT:
; N: z) d- ?" ~3 J' O  o0 N# u                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
! e) a% ]& f. Q5 ^- ^- x: }                ep0_state=2;* @* F$ E9 z2 Y3 I) Y  d
                return 1;
( ?6 B9 @$ p& i" K2 E( K$ B' N1 |                break;
* M3 k7 h: O2 R- X, L( l- m            case REQ_SET_IDLE:
; F0 b! k3 \/ R* S. S                USB_PMA[1]=0;   // Zero DATA! l( _% T! b; i# Q7 s) N7 ~' I
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;4 _  |- d5 }0 ^" {5 E7 \, g5 Z7 a
                ep0_state=4;, ^$ k2 C! ?& O) }
                return 1;4 T0 A; a. @. b' Y
                break;
" h- Y: r' `, I0 A( n        }
$ }) I; N, ]" A        return 0;+ K, G* a) u1 R/ g4 `4 I/ C6 I
    }
; v( D1 e. ?5 _3 L5 R    else    // standard
3 p0 J: V2 b, S9 {" c! d0 g    {/ v: n5 G# P) K2 K8 W& T
        switch(ep0_std_req->bRequest), M; E. V' s0 |7 p$ }2 ^
        {$ Q  M3 u+ t" _$ c' t1 S, ?: _
            case REQ_GET_DESCRIPTOR:$ ?" H/ Z3 h' w0 A7 W5 _! [
                return descriptor_service();
" _! O: h5 }/ v+ g                break;- m) Y' @5 |# u; r3 M, y) v6 Z
            case REQ_SET_ADDRESS:
8 r3 R/ J, P  q4 k9 B                if(ep0_std_req->bmRequestType!=0x00)0 Z4 O; \# b9 R: Z
                    return 0;' O$ \8 I: H! h' B: A! U# E  r
                usb_address=ep0_std_req->wValue;+ k$ I+ f' W* \5 @9 M! W. L! t
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;& g2 E. Y" S: t" l, V
                USB_PMA[1]=0;       // Zero length DATA0
% ?3 K5 }0 n. A. ]) w7 r                ep0_state=4;    // No Data phase
7 c0 Z) o9 y) Z                return 1;7 k# s. q5 A4 ?4 m
            case REQ_SET_CONFIGURATION:( ~& y0 D5 l" v, z
                if(ep0_std_req->bmRequestType!=0x00), r% m9 m& P+ ^& c6 i, }  a
                    return 0;6 U% Q7 I2 D8 ]/ f, L2 k# P
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;# k7 W! ?2 ~; j$ m
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;* ]3 ^3 U' @, ~3 L) T1 {7 I
                USB_PMA[1]=0;   // Zero DATA9 l2 j' k$ _7 B- B
                ep0_state=4;    // No DATA phase
- t* e4 n( G- Q) {# m5 U                return 1;
8 M+ S9 c# }) |0 Z) {. u/ k            default: return 0;3 w, {- m' d3 C' F
        }8 C( R0 W+ T3 x1 U1 b  H2 }' e' ^
    }8 H- W) f9 [: Y. F# l% h
}
" C+ r/ ^. a& s+ E8 d0 I4 [# K5 K, y% }( a
9 l* o3 H* l8 V( q  ^

# e  w1 z# H& U  r& s/ _
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
4 A0 H3 u0 D+ l2 m( ^5 lchar descriptor_service(void)  r5 x& x& G* U
{' {4 i' p. j, [% G0 C, Z
    switch((ep0_std_req->wValue)>>8)+ R1 e5 C' z2 q) o/ B1 f$ v
    {
& _- a0 ?# k+ t$ s( |# Z        case DESC_TYPE_DEVICE:
& t' N5 z" e6 D2 u. j            return ep0_preparedata(&DevDesc, sizeof(DevDesc));) E+ c" A# n* B; j$ m6 O* E
            break;+ q# g# B* o% O. M- s
        case DESC_TYPE_CONFIG:# a) \* E& J; o
            if(sizeof(ConfigDescData)>ep0_std_req->wLength), @# j9 {/ p2 B) u
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
0 w, X% h/ Q9 u8 I4 }* G. P& A            else
, ~5 i, e4 ^; j8 ~                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
8 e3 M8 w/ I/ Z  \' P            break;. ]4 b- \6 `3 O
        case DESC_TYPE_STRING:
+ k: ^/ {  I% j            switch(ep0_std_req->wValue &0xff)
  ?% a- I8 R: j3 d/ w            {; y* i+ I5 Z* G; i
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));8 {$ x) v8 j; m$ y- w) q
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
! `4 a( o$ e# r& B" r% V/ ?                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));; O. L) s9 W3 X8 O) K. k
                default: return 0;
: C: V" e% O% V4 Z$ j            }& _% S1 x& d( g' w5 \2 O2 a6 U
            break;
  w' O- h& y$ {8 `5 h; T8 _        case REPORT_DESC_TYPE:
. D( ^" D5 `; p4 w: Z& Z) F            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
  x- n5 G4 V6 U: W        default:+ |" f& ~- Z# O6 S" G, E- @
            return 0;5 a% Y1 L/ S, ^" y% h
    }
, q! E9 y! Z5 a}: |- s+ U+ w6 C  j7 h! u
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.7 f0 ?" R8 Y& O; ^
void TIM6_DAC_IRQHandler(void)( ]/ e& L; x$ p: ^
{
; g+ ^6 W* `3 i! t: h! a" D    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
8 M5 o, l3 P! N% l) e9 M" a! f6 r8 n% U0 U0 C, j, u! x! \

* r! F" h% {% q( V! G: z    TIM6->SR &= ~TIM_SR_UIF;
5 U* A  ~/ w( \7 ^$ V2 X    prev_key_state[scan_row]=key_state[scan_row];9 u8 h% L$ L& A6 z& {" |
    key_state[scan_row]= *PA_IDR;   // update key states
( s0 _" E5 N/ L" l7 M# m. p9 M    switch(scan_row)5 @, s% s6 t# t1 }4 g, v2 t$ [
    {  v2 H- x; c- I+ z
        case 13: // next row PB14. o  B6 f; }$ X% T2 ~& ^# N( o0 Y
                GPIOC->MODER = GPIOC_DEFAULT;
  ^5 N4 [; s: z7 {                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;; I+ o4 Q) G% }/ y3 r0 n
                break;
# u( ~/ v$ X  i) q# ^! J' z1 i        case  0: // next row PB15% L) ]. L7 f1 e4 U$ B: ?4 M& e
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;
; v( {$ o0 R! J                break;
* I4 _. Y7 z2 k: b, g        case  1: // next row PB3$ O, i) Q6 _0 O! Q$ N: F5 R
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
! A8 C3 h/ A' G7 w4 g) N0 X0 n% M                break;
0 v& o5 t7 b8 t        case  2: // next row PB4
8 ^% n* V7 e& r, W# X                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
* @+ x: z  v; `0 W0 x                break;8 c9 C& H& W0 F! i0 O3 }, \
        case  3: // next row PB5
# Q7 O% W! g5 P. ^                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;) W, x* A2 d5 D1 `1 d) w, L& @
                break;* W3 X- l% v2 v, \2 _9 Q
        case  4: // next row PB6
/ \8 ^" N' h5 K! y                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
; N* s! u5 y. m% X                break;+ d7 p& u9 Q2 ?" y5 @$ v: j: D
        case  5: // next row PB7
' S$ ~; Y3 ^3 y1 ^                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;! \9 X, f4 T/ v2 A6 t8 L
                break;) ]9 J7 r' |, ?8 H
        case  6: // next row PB8. s* F2 }* K5 o
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
: x6 q: j0 m3 P5 r                break;
9 J- W7 t: k1 a" a4 Z2 t! Q        case  7: // next row PB9
/ L0 S6 |+ |2 g) a9 t                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;0 j/ J4 g" Z& Y
                break;
) A$ l' B* E/ p/ @        case  8: // next row PA8
5 }8 C5 B; J6 ^8 t                GPIOB->MODER = GPIOB_DEFAULT;3 ]! O( o: X: y4 A  C. h3 ^, {$ s, B
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;
2 [+ Y. p. _- {( I& F8 K" I( ~( v                break;
! g8 e2 M8 f% U4 A% J, g        case  9: // next row PA9
) W" n+ D4 s& `+ l  [, K! Y                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
6 V7 V& w# p$ u  l                break;& A8 y* V# m+ w- r
        case 10: // next row PA10
% e, R! Q, O( ?                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;8 f7 V$ O9 b; k7 O4 D# `* z1 L
                break;9 T$ I6 G' P3 i$ d0 T# x, g6 e
        case 11: // next row PA15
# \# E4 j7 \; S9 V                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;$ h' \3 A# Q8 E# u
                break;
% y' d( Z9 w6 g: z# R* N1 x        case 12: // next row PC13
( L8 m9 W- o& C; A3 [& Y4 s. R                GPIOA->MODER = GPIOA_DEFAULT;9 z  f; k* a  v% G
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;( l- e7 a; h4 H" h* G
                break;7 L; ^7 Y9 ?! I# B. P
    }
, T# Q3 _$ k5 ]9 [& p    if(scan_row<13)
; L9 Z" w. c. E) n! A  _( _9 R        scan_row++;
. s* N, f8 R+ S2 F5 o4 U    else
% ^9 b9 \4 y& B( b  S  _) M- N        scan_row=0;8 U' ~4 c# N& k5 a/ }* A
}
. }4 u! o- m* L6 ]2 y
& g: z; N1 L5 l3 s7 ?, s7 x; q( d& p8 i8 O: D0 t7 y
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 : B/ U# ~5 u3 M. [. G

. E2 P! m1 i4 {3 y/ s扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。3 g2 x3 Q8 F) _( d/ H$ k* m' t- O+ Y
! P" L+ R- _8 E; V( t0 C$ n
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
" W0 T8 |5 z& x; s5 F# F: c

) p2 ^8 G. G7 a! l
( P9 \+ n0 Y7 v0 U8 q$ ]const char hid_keymap_qwerty[14][8]={
. S' p7 K7 r0 H% w8 k+ x    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
! d) a) ^% i% U8 e! v' }    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
- \9 C8 @- p. n& K% @: q3 r    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},: t$ w3 W0 K' k7 x/ _9 s8 @
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
8 J- U9 g2 `" |5 D) g' z* e    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
" v( i7 w! F  Z& M1 D! W    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
( r: t* r4 E. M7 y- n9 ^    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
# C8 n. M- F' ]( ^! i* A    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
0 Q& b1 N0 g2 ?    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},2 g) j8 C7 c" d7 M
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},' ^4 E  y, d7 u; l) M; s& C
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},' A- w7 S# u4 t( _7 L' i
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
. `0 A. d0 V' K- K  a* U    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
* l9 X$ j/ d0 m5 |# W: Q, g    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}- I% {8 @; s+ K+ c. l+ M
};# O) N4 e( u' x
) _* r0 ?- O$ n# `
4 _9 [' L* g4 {# t/ t7 j7 x
const char hid_keymap_dvorak[14][8]={, H7 [/ T8 }7 n, K
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},, F: K9 m% n7 T- q: g. `
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},+ x- G  P2 f) K7 ?  h. G1 d% u4 H
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6}," O; t; P7 {4 g
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},; v* n+ h2 b1 Z5 D) L
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
) x+ m5 S) p6 h. v/ ?    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
8 N- h0 P/ K2 p) X! \8 B; G    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
0 A% T& x% L2 _    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
% e. i3 V- U' D0 x    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},0 I. _! v' m" R# J  u
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},' {$ w0 a& Q" A3 M/ N* ?
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},/ A* C/ P+ s; d1 @9 N
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},& \# Z3 _6 W$ Y1 e6 w4 j' g/ Q
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},2 t, k& `1 _  G; `5 N
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}" c% j6 G. P, f3 {# y
};. N# Y/ V1 e; N; {- w, |; E
4 G- w; i* ~  X
; v* u2 a) Q: A
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。+ k- m+ C. O9 @' s% k$ `# o- M

, W  W9 {) j9 VHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

5 g1 ^; z4 A' U/ _" u( l" s( Q1 U2 @, {3 v
void update_key_matrix(char row, char col, char onoff)
0 h/ `/ ^4 \- v2 z3 o, y6 f: q{1 W  S, b1 ~1 E" _, ]
    static uint16_t hid_report[4]={0,0,0,0};
3 p7 ]+ s% `2 ]( a! V  k) d4 g    static char (*hid_keymap)[8]=hid_keymap_dvorak;
! g* A! K( d' i% E/ M1 s, I% U. h( u7 J8 A6 i/ C; ~* o' u; k

; f. n% B) k1 h' h7 Q; w9 N  b    unsigned char key=hid_keymap[row][col];
; S4 \* Y# Z  e: G, o    unsigned char *report =(unsigned char *)hid_report;
- ?- ?' ]' V2 W) Z1 D$ g- G4 Y. d    char i;
$ d8 P5 Z% N4 r" J& G* j4 D  v& ^3 @1 `! b0 J- Q0 _$ G7 t
7 T8 |) Z- G5 s6 G) ^) H
    if(key==HK_MODE)/ c7 L$ A* r- m2 {- \9 p1 @: n
    {
, z. F: E6 J9 Z  [6 z        if(!onoff)9 k. V7 w$ D. [" X
        {
; i! ~* _- N( Y2 J. n, P            if(hid_keymap==hid_keymap_dvorak)
6 V8 u9 S/ K/ [; U            {  |  m4 H. a- K, L' A
                hid_keymap=hid_keymap_qwerty;
& _, G* n, z6 }, \& R( J                GPIOB->BSRR = (1<<2);
7 X+ {/ f3 m- K& A! F1 w) `1 U& x! c            }" t/ P0 S- Q; D" W+ y7 r8 l
            else4 h4 M& z' i7 f
            {) t& J5 L2 w" R
                hid_keymap=hid_keymap_dvorak;( _0 Q  ~! v8 ], y7 E; y2 u
                GPIOB->BRR = (1<<2);
! [- U2 ^  b5 ^- F' g            }
7 ?! R/ ?/ V' n& v; D        }+ p# k6 t' `# l* m- }4 M% s. I
        return;. l: S! }( g# P/ {
    }! Z4 K/ M6 y7 u/ _% Z

5 e8 G9 |6 p( N3 z* c
( c  n, W  I) r: @. @2 H
    if(key>=0x80)   // Alt, Ctrl, Shift
7 D  @% Z$ i# T. u    {2 l' v4 [1 q: z; |
        uint8_t bitset = 1<<(key&7);) k. l0 F3 m# ]
        if(onoff)   // non-zero is key up# A  @* {* o; \; O8 S3 R9 R" F* O+ N0 R
            report[0] &= (~bitset);
  F5 Z- @9 y+ k6 s( `. B        else" x9 r. x1 `6 @" `; u
            report[0] |= bitset;6 M, {/ x# H) ^
    }5 z; K# F: t" W: C+ s4 B
    else. O, T2 t2 `! {0 C0 K' D" ], j
    {  j, K+ G( P, s
        if(onoff)   // non-zero is key up9 c, ^9 Q+ v3 D$ I8 X$ |( b: D
        {3 C# p4 L, c; |0 E
            for(i=2;i<8;i++)! Y5 p( G' V9 h
            {3 A& b4 z' r, P
                if(report==key)# x8 y# Q1 v2 p" R& U7 Y
                {
% d7 Q/ Q/ e; x7 I# L# ]                    report=0;
" z, A! _+ u: I+ N  T/ A                    break;# w- ]  t) J4 A" w0 Q
                }5 W, }! y9 j1 r" I2 Z: s6 y. {5 p2 O0 \
            }) W$ b% |$ T  ?/ |8 N! e
        }
3 J! O% s1 {4 T' r$ j$ ^        else
- a6 d  I) W3 c. ]. _5 s' Z        {0 `  M2 z9 p& g
            for(i=2;i<8;i++)
7 k( D1 ?1 X0 D            {$ X6 |. R3 o. |8 i
                if(report==key): h$ [- b1 h& L
                    break;8 R/ h4 ?: g5 s5 l7 F; @2 C
                if(report==0)
5 \4 `) m* E" x3 ~  m& [6 x( V                {9 _' m. h' t. k
                    report=key;7 L% @0 d7 k; U* C: k! k/ ]( A) A
                    break;* W* C& u: v( G3 y. B' k$ {
                }
$ |! Q' S* t( R9 H            }
  v1 k% F' W1 t7 S5 s        }7 x8 I; G3 X' D
    }5 {  h( C( }, p* U) d
    for(i=0;i<4;i++)$ q( {3 t5 `: U5 b1 }! |/ s6 \
        USB_PMA[192+i]=hid_report;5 r' |9 j: \5 Y- p5 g0 I
    USB_PMA[5]=8;   //COUNT1_TX
5 B) `' [8 k& c$ {, D    if(ep1_wait==0)
) r  I4 x  T; X    {1 A% N1 E6 ?- C+ {) f$ ~
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;# z- Z0 O* `0 q4 \/ J( ]% O8 ^; g
        ep1_wait=1;
* P3 S! n5 d7 K5 n5 \2 h    }9 Z+ K1 J  Z1 ?& D
}7 ^, e6 G- G! `) o

* A$ n* K) L- I' c* V( q9 t4 i
  }: P& S  z3 t完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
7 N( D1 R" [3 \ keyboard.zip (8.7 KB, 下载次数: 2474)
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。% \3 F- Z0 B; a, O: o6 ^6 }. `' L3 V
回复 支持 反对

使用道具 举报

8

主题

149

帖子

207

积分

中级会员

Rank: 3Rank: 3

积分
207

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘% T) p  B7 p8 t  ^' C
不过楼主也很厉害!
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
- e% }: Z# q' z, t" [2 D8 |/ b刚开始我以为要把打字机改造成电脑键盘7 P4 K6 k- m3 x
不过楼主也很厉害!

4 f+ `, p! T9 }$ e0 h+ @. P哈哈  是有点像奥
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2017-6-28 23:51 , Processed in 0.170397 second(s), 28 queries .

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

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