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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

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

' Z+ _, g7 ~7 s; B4 R
3 x- a6 h- b; W
235140i3a36qivqzuvmt5q.jpg.thumb.jpg
; E& p) F( T. j) M( x2 e
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。9 _1 E4 m" z4 B# I6 \( s5 ]
001734klbyoluenuwz4h4b.png.thumb.jpg $ h; ]; T$ m3 {$ s
为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:
/ z3 u' R0 H& K. q- j! W4 S- H/ _' ~ 003625r2agx2f5v922cf2f.png.thumb.jpg
. H7 `% j+ Z# g3 ^% b4 V* g其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.) i$ O) L' R8 h& w
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:

4 G$ B& ~: `" P- k 005836yvs0wvovwsssgd3o.png.thumb.jpg . R' ]2 `0 r, E  S
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。9 q& {; \; }* V$ v$ v, y0 _
/ b* X! B* O: a( ^2 ]
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
1 L; x/ F6 x+ |5 ~+ \$ `# M& f2 T5 L7 C8 l, B& |/ `
到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
. t3 X/ K! S1 ?) V- s& q2 l
: Z2 X2 O- l' J% `1 Y机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
0 \/ W9 b5 a, R  i3 I
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
/ f' n! @/ v* S2 `" E----------------------------------------------------  分割线 ----------------------------------------------------------9 z( G5 Q6 m; j4 f# q6 _2 G1 x

# b5 R, D) i% c7 Z先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
* I; O, e5 q: V
020011osionbunl4ui44vi.jpg.thumb.jpg 8 J( ~$ Y, J" ~0 o: i3 a
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。) K* T& i. n- C7 {& i* ?. b' M0 r( w
020017j8ycmnv7788bqv52.jpg.thumb.jpg
# h+ d6 [" z: u特写,80C496 ]+ y0 c% J7 y* X$ r
021040oujzuvtut6iujtvz.jpg.thumb.jpg
& b9 \; j8 ]0 ?, X* oLED部分,使用了一片D触发器锁存指示灯状态.
; |: Q. O. g1 w 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg ' q& ]) Y" \: W+ p3 N
暴力破坏,将80C49拆掉
& T; |; O9 M  Q' e7 C& H% `" W 021113e48qq98vyohvhzzh.jpg.thumb.jpg
5 ^" b; T, d0 Y1 }- o, f3 Q拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。2 B4 q* D; r+ D7 [4 J+ ^% ^
021125nc9az6dj33rlds2r.jpg.thumb.jpg
2 B" o5 a$ E5 y" ?* _: q  [
焊好元件后的板子,准备替换80C49; U$ M* t+ P1 {4 S' P& G9 F# {0 f
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
% J. l) m6 c+ a+ ^$ W0 d4 X1 A用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。' `8 ]8 [6 s- o$ P) P7 v5 `
021104shifhnrqbr3o5nlo.jpg.thumb.jpg
3 o8 U* g! f6 F" z: |" X这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.5 R; c0 d& ~: s4 Q" F" a: f% H
022003ym1p9u4ug40280uu.jpg.thumb.jpg , N0 [2 H) b3 l. i6 y/ \9 |
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。/ y: O  O) F! v
023313kt141q9qajtol7ma.jpg.thumb.jpg
' i2 C" z: L  l0 q我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
; s/ D9 I6 l0 p 023322nt7l5xb3ltttkltt.jpg.thumb.jpg
% N' V# a+ z3 z* t( d+ [! @7 G
主键区键帽就位
. |+ n7 v/ g+ c# D& P 023331hin88e8wkrwzwikx.jpg.thumb.jpg % V2 l8 n; n2 V, K* P4 O- V
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。& q, m( T9 G' y7 u7 n
023336wjzlgopugg1jyy79.jpg.thumb.jpg
; T. T* z( X1 n3 h! r! J/ l最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。( t* [$ [2 Y; k/ E
023341sffu4j3g2323h6fl.jpg.thumb.jpg ( o, X6 m( J  G! p9 I

8 j' r2 m9 D; Z----------------------------------------------------- 分割线 --------------------------------------------------0 D: z) y5 r! t% e* ]- o( W5 Y' I) P
1 l" q& p4 B7 Z- w& |. F6 x
' Y0 n7 D. `1 ?' k- `' L! F6 T
  u: n5 v  S3 j, y5 h
. M1 A6 M- k4 J

  Y7 x( |6 e* j6 H+ f' z! u
! Q) |% r+ N0 E  k/ v/ u

. _4 z& i  j0 E1 c# d
, A& a  X+ S( ^1 l/ U& q  Y1 n5 V" g7 N8 V# A$ }
8 E8 d, R3 l. y8 |% p
3 m6 O% ~7 k4 t" n+ p7 @  V
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
9 e/ b0 F! r6 U, p80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
0 o( Z* n  w7 X" v 104025nzibm2rmiomhyirm.png.thumb.jpg ! G2 U2 R0 H8 m( z! O$ F. q

) V8 Q2 }0 s' s! V( d4 S# a其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
4 c. ?9 h% I) h4 }+ ?  h 105004zkrez5houvkkznko.jpg.thumb.jpg
: S. M* x4 ]& l: ], t5 e3 t& e# ]. K; \

3 m3 ]9 b5 B1 A- i! C, b. F扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
! d- _7 r5 q: R7 D" ~. {. o% C' m5 N8 D% r
这是我设计的电路图:
$ ~$ @& h. O9 O! d" n9 Z: u' G 110344ej2z2oo2rflo7oe7.png.thumb.jpg
6 a! E3 l& D+ N1 d+ _

; e" t$ |# z9 E( q  cPCB Layout:
# M8 }% @$ |2 q' A( O 110847jjbjvt34vwt3v5bb.png.thumb.jpg 8 d! U) W4 T/ l: n5 v  B9 O4 c* s2 e
# O8 ~) e7 ?0 M7 a& j# }
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. 5 P6 p0 b7 P, L, `4 R0 ~
5 A: }9 t' w) B' L2 B2 t
! ~/ Q4 p% ]% n, H" e4 b* [
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 8 R7 l8 Q7 T0 T  v6 ?
  u6 q+ J$ f- o; m6 o! O, f( L7 {
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
( z$ h/ b. o8 s* J9 M5 r2 ?: [, e4 X2 Z; x8 w. Z' u
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:
. c$ d/ l# O! @; h$ d6 ]' y2 v9 i 113818pmrfsb6z0byt6t06.png.thumb.jpg
4 g4 ^4 H2 I2 I7 T3 F8 p

1 {- T9 W) e+ g7 r" ^. [: c其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
* \4 e5 P! L, z6 f2 n5 B
* ^. F. l  F( W  o0 C) R* X8 ^. dUSB的中断ISR,bare metal哦' P9 G2 p+ T. a0 }

: ~5 O" K+ U& g) M2 p% h8 dvoid USB_IRQHandler(void)
2 p0 {0 D# o! g( K, Z/ }{5 o) U, M. B* `0 F
    if(USB->ISTR & USB_ISTR_CTR)4 m' I8 R3 {' i
    {, ?) }* M' I" R& G9 s% m  F8 B
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
( |1 f9 ?' e, G7 m8 e        {
, H; _) X0 ?  J* g            switch(ep0_state)
" O' h- }$ v: u, }/ X            {* r  N6 q7 A9 t+ P, |6 J" A# K
                case 0: ep0_state |= 0x80;  m! J3 y6 O. l) }6 ^
                        break;. C6 |! q5 g' E) n: G
                case 1: if(USB->EP0R & USB_EP_CTR_TX)
# V4 ~# ~+ Z% X/ g2 C/ ?2 x7 z                        {
$ B# `0 j" t! U' X2 {$ P! j                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
! ?6 v- W) n% ~% x# E8 c; [                            ep0_state=3;! I9 ]5 t5 o: j% ]! T* J7 z
                            return;+ j# C/ t  _% U4 ~7 J- f
                        }. A. D2 C) [4 X6 a/ c
                        else
& B" g( Z% h/ C1 C                            ep0_state=0;' I  `9 |1 \; Y. `' p) s
                        break;
2 D. Q$ i$ z4 _7 w& r0 v+ o                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet) C6 s% P$ ~& V. x9 r6 e; w8 e; E! t
                            ep0_state |= 0x80;
" Q; i& [9 I; e% c: N0 ~                        else" Q0 }, x$ a( h( N  p+ P
                            ep0_state=0;
4 S4 }" G$ \( O- @3 \                        break;% d0 m4 D6 e# X3 e$ [, L
                case 3: ep0_state=0;! O# h6 @6 U* }& _4 n: Q
                        break;
! V3 o& U% X! A( |4 D                case 4: ep0_state=0;
+ g- n7 d" K3 U: S- n3 y                        break;
5 U9 Q! s; ?0 S9 I; m( d                default:ep0_state=0;
5 `& I: z4 q! z# w/ h3 i                        break;
+ n/ ^) t* e; v! X2 G! N1 w% U( A            }, e# z' l4 t3 A4 P0 H
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag2 N+ o- a4 \( c6 C0 {
            return;  X) F  A0 P9 T8 g3 m6 t
        }
7 a1 p6 d( _  q! }        else    // EP_ID can be 1. ?3 m1 o7 V6 G( ~
        {
# b/ E. O" j: Q5 `7 n8 v            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag: H) o& P. v5 J0 I
            ep1_wait=0;
1 H6 ?% H& ?$ P% v2 U            return;0 x; _' t  a. k+ N3 e: z. H
        }9 D6 j4 X, e( [7 g
    }
5 q7 L; W3 e/ x1 c' h    if(USB->ISTR & USB_ISTR_PMAOVR); ?2 u- |' `. [( s/ |
    {
& d! V2 C9 N4 A) S# u! Q0 c        USB->ISTR = ~USB_ISTR_PMAOVR;" k+ N) j0 w- Q* D4 G- S7 v
    }
# R6 L4 F7 g/ F    if(USB->ISTR & USB_ISTR_ERR)1 F7 o: b1 w" F/ D6 P3 m0 c4 O
    {  b! L) O, t4 X' [) k9 ]# }
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
& E: H) K6 }( M  [, s9 f3 [6 n    }0 [" t1 Q5 f- ?, x  ~- X
    if(USB->ISTR & USB_ISTR_WKUP), i3 U  y# ]8 T2 w( l* ?$ R' l
    {: o0 k# K5 s& u+ N# F
        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend. T' @% }! v. n5 C' i: [
        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
' _, J# K8 s& ^# F' m: H1 L    }8 Y, y- ^" ~/ n# K
    if(USB->ISTR & USB_ISTR_SUSP)
$ @# v1 {9 b: z( z" B5 O    {
1 e# ]3 K; n# \8 g) r        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
* ?. E8 D. k, A) C/ S        USB->CNTR |= USB_CNTR_LPMODE;   // low power1 P4 U/ n" S0 I. B9 z
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear) G" \0 J% x0 H5 K
    }
. S4 \" O, E! {' s- m. @* K    if(USB->ISTR & USB_ISTR_RESET)
1 V; [3 d/ s3 ]7 q- W2 i    {
9 O+ O( W# `$ j+ I2 i        USB->BTABLE = 0;    // buffer table at bottom of PMA  V1 W. G. l" A8 H- g
        USB_PMA[0]=128; //ADDR0_TX
9 O. N* L0 @$ I        USB_PMA[1]=0;   //COUNT0_TX( v  Q3 o) j7 g& P/ ~
        USB_PMA[2]=256; //ADDR0_RX
* ]; b3 T+ Q- A/ {, A        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes8 F! E5 _$ [3 t' G( j+ U
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
  K1 S! F$ R- `        ep0_state=0;7 \: F$ f/ M; F  J
        USB_PMA[4]=384; //ADDR1_TX% a& I' l' }" [* z4 Y
        USB_PMA[5]=0;   //COUNT1_TX" `7 b: O3 }% s6 m6 u/ f
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
  Z, g2 ]2 o0 H4 E0 A7 z7 y        ep1_wait=0;
+ n+ A  v) X( W- w2 R( u' o, K* ^* |        USB->DADDR = USB_DADDR_EF;      // enable function+ u- ^4 E6 S- v1 _
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
' I% x" ]2 N- l, x0 u! E    }8 v# X& g# ]6 V- {$ {" w4 r
    if(USB->ISTR & USB_ISTR_SOF)- A! ?9 x) t9 A6 r- n) }
    {
$ r+ \$ m' b8 ~0 t        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear0 h! T6 Z  ^. g1 K  `
    }7 ?4 i( |2 X$ h; g; y$ |. L' t% j8 Q
}: [0 Z0 M8 @0 Y: x7 s4 ^
1 }2 i! d( e  a8 R; ~
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。3 _$ t  U  J$ \
5 s, I- Z3 |. z
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
! c" G' U8 x: P0 ^2 W; ?2 u& B* G: Z) H9 a; ^
    while(1)5 Z6 Y( B2 ~( [: f
    {# j7 [) t1 R7 y4 |5 C1 p# x) {# b
        static char row=0;0 [8 V5 x% b9 F1 v9 u
        __WFI();& d& B4 @8 `# L5 K  h8 m/ Y
        if(ep0_state & 0x80)    // request data processing
, R& v2 b& P# C- ~( l, W) Z* {7 i% b        {
1 m: z8 E$ Z$ v            if(ep0_state==0x80) // SETUP phase
  K& I" `9 E( {% k2 P+ W            {( P; m4 l( ^3 s( {" H
                if(!setup_packet_service())
. t( \2 }! C5 G; \# R                {
1 G7 e0 A& @4 e- m- c5 N                    ep0_state=0;. g( K) h* q9 x- O- I
                    // not supported  X+ M. @# ~* c& @$ R4 m/ G
                }/ @8 S0 ~" ?. }0 g, M7 Y2 v7 X
                // ep0_state should be set to 1 or 2, if processed
3 O' m9 ]% G! @            }
* w7 H4 H1 K3 [" P& F/ L# k- I            else    // OUT phase1 g* N2 K& @( N; V1 n5 m
            {: F) s9 j4 S6 e- L2 T
                // process data
0 L6 H: N- m  \) P) f: @) y                show_LED(*(uint8_t *)(USB_PMA+128));" S$ l$ \: @# Z5 N$ B8 R' X: f
                ep0_state=4;
6 G; q- \+ o; p6 }4 ^                USB_PMA[1]=0;       // Zero length DATA0
8 S! v2 A8 k; D6 e; w0 @- I+ g9 x                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
8 e+ z6 S5 V: U& t1 X1 n0 r+ v7 O            }% j5 J1 E4 L& M3 c4 t
        }
2 i3 r: x+ v" E; u/ {* B$ w        else
0 M2 ]# J5 S7 a, Q* ?        {+ S# `$ O- E" Y5 l; d" W
            if(usb_address && ep0_state==0)9 ~# s4 ?5 p! {: b/ F
            {
. c7 K; s8 c# M, v                USB->DADDR = USB_DADDR_EF|usb_address;
3 K$ b1 ^& o3 l5 l                usb_address=0;+ P9 m2 I" u! n' C; s  X
            }
9 S9 g8 o# R' G3 u: b        }- I/ |; x  `7 B% b9 a* B& Q- g
        if(row!=scan_row)   // new scan line5 S; C5 @( G! ?8 M7 D6 z
        {
; K. E; `& b0 V. _- l1 [8 P! w            if(key_state[row]!=prev_key_state[row])
0 V: A, e( t8 R            {' |, ~: r% G! c! n4 @+ L+ w
                uint8_t test=0x80;) n# ^. v+ K* r& A) l) U' X
                uint8_t diff=key_state[row]^prev_key_state[row];/ w+ j8 g. t& M' {4 A) A
                for(i=0;i<8;i++)1 l2 S- S5 l! a. h' p* g$ @
                {
0 f* V2 W$ }; I4 l+ P                    if(diff & test)
  N% x, E8 V3 A: B2 Z                        update_key_matrix(row,i,key_state[row]&test);, g4 A. J) f" E# i% z  i
                    test>>=1;, m$ H' W+ X2 M7 }6 H1 `; T
                }6 ~& p' E3 D, r' T5 E' a
            }
7 h) A7 t8 {' i- c0 R  ]            row=scan_row;2 I" @9 Q- B1 W* p7 O( U
        }" ~& l* `2 \$ u. |) i
    }# {: o, n; ^+ @
4 I8 D# G- @" B6 P( ^3 G$ m+ e
3 l. h1 }8 M  P/ P
EP0的控制传输,把用到的请求处理一下
- [% {& f" c; V: r. n9 Pchar setup_packet_service(void)3 a3 n* G6 o2 J
{8 e% p; f3 n6 D' [! P
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
, e. G+ R5 D5 j/ c2 D; r, z    {
  @$ w, S) V/ g7 J  ]% l1 F        switch(ep0_std_req->bRequest)
: @6 R7 h  W6 d& x1 C' Z. g& _4 a' @        {$ r+ u  H8 W" d7 I) I) E* W; L
            case REQ_GET_REPORT: break;
, |* Z  M$ s$ ]( ^& m- _9 [/ Y            case REQ_GET_IDLE:+ v1 P# C+ [! c8 X
                USB_PMA[64]=0xfa;   // return 1 byte
' z) K4 x9 t/ B  N, F7 t5 ^+ @" h                USB_PMA[1]=1;
7 q$ g+ c/ e8 k8 e$ M                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
5 Z$ D0 h8 y- w3 l                ep0_state=1;
: A( H( O! R' e                return 1;
# X- @2 a8 U$ o+ F) M                break;
/ r2 ~9 K: @$ k# T/ l: b            case REQ_SET_REPORT:& T' Y' Q/ h" b1 O5 y
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
' K! P  r% }( d! f+ ]                ep0_state=2;
1 }: A. i7 D. J  j7 b& r: f                return 1;
. O; D0 O: P. k8 a1 y                break;6 b2 ]  @( u3 R, R4 ^
            case REQ_SET_IDLE:& K9 K" U3 y0 B
                USB_PMA[1]=0;   // Zero DATA
2 g( o3 g9 ?3 x/ j8 v! U                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;+ w# q- d& r5 N$ B
                ep0_state=4;4 L/ O+ l7 q" `( o
                return 1;. ?$ |. h. W( k) l1 l
                break;8 u7 W3 u3 O; X, r7 E- ^
        }
( H7 d* K2 o, g  K) r2 v# B5 N        return 0;- p$ f4 P8 C! Q& o# O! S1 ]
    }
# h6 z7 T' m. ]$ |# L" r    else    // standard0 Q$ b1 g! [, Q2 ~- G
    {
# P9 K3 e$ ]  h  c        switch(ep0_std_req->bRequest)1 e. _/ U/ p1 R
        {
2 S* l6 }, M) `3 N            case REQ_GET_DESCRIPTOR:3 M9 O! Z8 M# \8 \- N$ z4 K2 r
                return descriptor_service();/ @8 s7 e  E. z( z: R" a. B, ^- v. R
                break;' K  v0 n) ~( |" z
            case REQ_SET_ADDRESS:
9 X# s* w; F% H9 l0 X: P                if(ep0_std_req->bmRequestType!=0x00)! g/ a0 Y7 c+ m, ?
                    return 0;
. T, e- E6 w/ X  q# G8 `                usb_address=ep0_std_req->wValue;4 e# V# ?. ]6 W- ?
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;- s( j5 w/ X+ V7 F9 `8 f' }
                USB_PMA[1]=0;       // Zero length DATA0
, T; W9 @) P. z6 w, {, d, y                ep0_state=4;    // No Data phase
) A* M9 t: D# I6 {% [% P                return 1;
2 j0 O  @/ C6 ?/ }3 K: j/ E% t            case REQ_SET_CONFIGURATION:
8 \' v" c$ O; p: {                if(ep0_std_req->bmRequestType!=0x00)' I. j# c" O0 T! p" }! W
                    return 0;* d1 ]( I% b# \# C7 M
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
2 B0 x9 G/ I: e  T                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
$ L3 w0 Q; ~6 V# w# U; m                USB_PMA[1]=0;   // Zero DATA% }& f: K$ s+ k. S7 D. t
                ep0_state=4;    // No DATA phase% [7 Z" G3 b& H
                return 1;' ~+ U$ t; k# ~  G; O
            default: return 0;
2 L* \1 M9 o( e7 T) Q        }
+ F3 F) {' w9 m) S8 M    }
) j3 d  b0 q) e1 U' H}) f$ F7 {* C5 t/ b! d- s9 S+ d4 h
' j* G% h+ e! i; {3 N- V
3 z4 @$ U  ^7 g& g% O8 _1 d; U
. f* ?' B2 w- a; z6 U: \1 q0 L9 N
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的: L! ?! v% \9 ]# t2 f9 ^
char descriptor_service(void)
+ c: R) p" w6 J* ~/ I% n4 o{* x9 p5 G% o1 k  s, K
    switch((ep0_std_req->wValue)>>8)
  V! r. W2 \, N5 j/ q4 A! r1 }# e    {- b; @; c! S; N1 l
        case DESC_TYPE_DEVICE:8 y8 Q! y1 d1 i. j; c( ^
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));2 ?: Z" X% n5 j* P" q+ @
            break;
0 D. w" y% i3 {  {% z& z        case DESC_TYPE_CONFIG:5 J' W7 X2 m/ U* \/ p
            if(sizeof(ConfigDescData)>ep0_std_req->wLength)
& V8 l  [  ]2 J* ]                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
1 m3 a- k5 R$ K5 ~& A2 l1 p& w            else) C9 \4 `, \$ p. \9 L3 s& ~' o  q
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));$ K5 O7 K: q& \$ r' G  ?5 ?
            break;1 u: A  Q& d' Q- c( I9 C! h& P
        case DESC_TYPE_STRING:
; v! w0 o  U! l% @: B! P5 x  @            switch(ep0_std_req->wValue &0xff)) W' ^) O: Z) ~. J
            {
8 h8 B& d% x6 i! d5 J7 j                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));; S+ W, K* d9 Q. h  B
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));% q3 A/ c2 f- d; s/ K5 \: Z- o
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));* S) _  W3 m2 K$ R; k
                default: return 0;" w2 Z6 Q, P+ ]2 D
            }
! j4 A0 k  b% }' D- J            break;! J: b1 K1 T& Y# j3 X) h2 r, D! J
        case REPORT_DESC_TYPE:" y  G+ v$ S3 {5 G
            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));( V% ^! d7 T+ e8 @# m( V
        default:; L( e3 R0 b' A* k* Y9 d' R# k
            return 0;, z" @/ ]4 y7 T3 ?* z
    }
+ @8 T8 Q$ w' ?4 z7 n}
2 G! y* L3 L! w8 x2 J0 j下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms., o4 c, }3 l- O* Y( _5 Q' [
void TIM6_DAC_IRQHandler(void)
& _6 }1 }3 m, o3 d' \( c( I{
9 b. t! J; i! n% a8 ~  r( h  T    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);9 U" s6 c; v' L$ B

' P5 z6 m( o' G' v/ B- o6 h

4 }$ ]9 L6 s  J2 V  K' d- H  Q8 W    TIM6->SR &= ~TIM_SR_UIF;3 Y6 _7 J3 \: x
    prev_key_state[scan_row]=key_state[scan_row];
5 l* [$ i2 R& Z" y    key_state[scan_row]= *PA_IDR;   // update key states3 z1 w; H% U% {' Z% C- y0 j* H
    switch(scan_row). }9 C+ A! E) }& z
    {
5 i5 ]- d8 |  y2 z        case 13: // next row PB14; V) Y7 D6 o8 n3 S
                GPIOC->MODER = GPIOC_DEFAULT;6 U  O8 X9 q: R  o
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
4 J+ f) w0 i: N( K2 F' S                break;
9 F2 g; n& q' @. G        case  0: // next row PB15
3 }3 P5 v, D3 _/ J                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;
+ E# T6 ]4 c+ t2 H1 p                break;! @" ?* ?( [9 e6 o2 R
        case  1: // next row PB3( K2 r  G' F- U* ?
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;! j' ?, w; A7 d, j3 s) O$ g  {4 ?
                break;
  C9 M- H1 m4 i5 x3 \( d/ |& n        case  2: // next row PB43 ~5 A2 f8 h1 C% ~, M" U3 q
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
$ m  G+ e0 X' w! l  X& \% R                break;
, Q( a( |* e2 Y3 g2 l& h2 x        case  3: // next row PB5
3 l1 P; T8 G8 D$ y) F" j, G0 c; e9 f                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;8 q& u1 e( \4 R6 N
                break;
+ A9 g: y7 _5 J) O        case  4: // next row PB6
( s+ y4 Q. Q7 Q- G, ]- p# B                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;3 k% u8 s0 I) K. v: o
                break;
: E1 a, T$ w! }        case  5: // next row PB7
( j" q( `. J2 F( q' Y7 W1 F                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
  J' u" \9 L; E                break;5 S1 T# E5 l! ]) e
        case  6: // next row PB8, p  r' r2 x9 b( o& o: E: e: R
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
  D9 e% U- K- v                break;
2 c1 Z5 G4 v/ ?- y8 Z, `  I1 I        case  7: // next row PB97 ?4 {5 l. i/ `  p5 W$ P  u0 e$ ]
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;
  J5 i, R% s. Y7 o/ h' `                break;
: k0 U) o8 b) Y1 z4 e! d8 @2 K# ^, a1 Q        case  8: // next row PA8
* a$ b2 u  a3 ]                GPIOB->MODER = GPIOB_DEFAULT;7 e# H9 K- [, B) L
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;( X8 i4 u( w5 S1 X
                break;
% z6 W7 D2 `! p& g# D! [' F        case  9: // next row PA9% ?( P  U& \  g/ ?: X. Y
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;# f7 g. V* g( W8 W3 m- O9 ?7 M
                break;7 r* I: c" w: _: h! @$ a
        case 10: // next row PA10
( p$ a' X" g2 |1 D                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;6 L  x1 w' s7 F% W' Y- _9 H
                break;
9 R, g; U6 ^* A$ e1 W2 k3 ^        case 11: // next row PA15/ T8 N% b0 L) q, X: J& G
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;# F9 V. t/ D5 t8 {+ f
                break;
# _' T/ p# E4 o, M0 |7 s        case 12: // next row PC13
: L7 h5 u; T$ E7 L- {% h                GPIOA->MODER = GPIOA_DEFAULT;
5 I0 V1 e# D* O7 r                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
' L& \1 g. u9 R) {3 C                break;
. O3 ~8 _4 c( Z7 a5 l    }
( S: V, ~% u4 n) {5 a3 t# T! k    if(scan_row<13)
4 p7 W, s( q. V1 W1 i4 V6 x, z        scan_row++;
' N+ Q0 h  O9 X7 Y: I    else  F- r- X: i' K7 H* g2 c
        scan_row=0;1 X/ t& s9 ]2 x3 N6 K, H3 O8 Z
}
+ R" _0 T# F; h! ~. H0 e9 W+ }

% M. |& q% c) e2 Q
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 0 n5 _; R# O* ]7 N3 G( R7 ~) Z

8 ?8 D5 N# N1 X) N; w# q3 ?# p! R0 ^扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。+ B) A4 }& @1 L4 R" N% |5 ^
; x, a% }) ]- L( ?
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
4 E  Q0 v4 L) X' q9 j
. e4 d4 h. x7 H( E; I7 J

% l, l- g& D0 b( V. Fconst char hid_keymap_qwerty[14][8]={: d8 x; j5 u  q+ ^2 H- V
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
) V% g9 F- U9 U; V( N    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
  [3 d- l  r1 O* N  h1 X8 Z, x    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},/ r. E, m& H# e  `
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
, C+ T9 ?- e9 `$ e3 z    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
/ V6 V  o: m  Y' {    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
1 X8 K8 p7 B1 Y, J1 |    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},0 Q8 q$ `# G" \! n7 y- L* i
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
' a! M, U( P7 _5 }) C+ i0 `* b    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
: V0 n1 `! g  Q6 j    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},( p4 W) j/ `  f- a- q3 b
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},2 @( N: a! L: G" p) m7 q
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
$ d) _$ v/ W  j4 V4 r8 Q    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
9 A/ E' Y9 X. u2 a2 K; I    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}+ K. v& {$ H! }7 t
};+ U- }; `9 @% O2 ?) T
, M4 r, N; U! q' H4 {$ {# B

8 K' s0 w, f- n7 Jconst char hid_keymap_dvorak[14][8]={
) M* u9 a0 }: @0 c4 w4 f8 _    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
4 V2 g8 R* m4 b1 D( F; O6 Z    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
8 U; G# g2 [( P    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},. t5 L7 F0 C. r- }4 J* K
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
3 a$ x, _$ m2 u4 i2 ^) L% S    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},3 x1 ?& G( L) R+ {/ \+ v
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},7 Z0 y) H# p/ r4 z. V" v. H- \
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},8 Q- a# L# x+ {5 F% @" {1 G
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},. ~) t& n& W: n7 ]3 A
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
6 b  n1 W. Y, ?! @0 N% i$ G* g    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},( `4 o$ n" M' s9 P- D4 Z
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},) W1 n6 D/ w8 t- I4 i
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},. ]6 @; g: v' u! ^: R
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
) s) R+ k9 D" [    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
; f3 K# W2 t+ Z- c7 I};
2 o5 ^* f9 Y1 `8 [9 }! w
0 H$ ?/ a+ L& ^; Q: N$ b6 L" }; Y  Z8 }
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。  d3 S6 {4 M  ]! c0 N

9 o) H8 W1 n, l, l7 |8 cHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
* H' ~: d# M0 _* T( N
+ h  s* J) O# T) \+ W9 I+ Q8 v, s: b
void update_key_matrix(char row, char col, char onoff)( r, d+ d- F1 @
{, E$ Y; T+ B: t
    static uint16_t hid_report[4]={0,0,0,0};% [1 C1 d' w# M
    static char (*hid_keymap)[8]=hid_keymap_dvorak;
( t. ^) i/ E) h  T" E/ m
; \. y9 y1 J4 T9 p( f

2 {. ^& X( {% C    unsigned char key=hid_keymap[row][col];
4 R& E; P0 Q4 J; |7 `& P    unsigned char *report =(unsigned char *)hid_report;/ E! ]( N: a, P8 b( Z6 O; X& [8 L5 T
    char i;; r# _6 A/ `% }9 J& }/ X0 g
+ j" p! ^, W# q* h3 x) j3 A- c

- J+ K: M1 H3 A. B- B" A' F    if(key==HK_MODE)$ l2 y% D. Y* U3 h, ?# [' T
    {
/ K2 F3 x) k5 R  `: @, }, N# N* z( m' o        if(!onoff), F3 i/ q) _+ Y6 `5 w
        {
) ^7 a( n) y% U& T            if(hid_keymap==hid_keymap_dvorak)* ]1 F/ r, j6 }# V
            {6 p: Q. `: u/ @1 x
                hid_keymap=hid_keymap_qwerty;0 `& N2 i1 h* Z& o8 h
                GPIOB->BSRR = (1<<2);' u3 f/ H0 M4 k* ]$ l
            }% g  j% [' C& T; t0 j$ s% S
            else
# A! c! h! v  [            {/ N- l9 p; j+ D2 ?5 h2 c1 H, k) K
                hid_keymap=hid_keymap_dvorak;
8 n5 s' C3 ?# m/ F+ m. C/ ?- P( J                GPIOB->BRR = (1<<2);5 t+ i, a4 v& m6 N+ H3 J6 R' w* m
            }
6 o" l1 v0 O2 t* w: u* O6 ^        }, X9 K; m, a& O( ~/ |0 r3 d) ^9 n
        return;. Y$ g5 Y1 E1 t, ]* \; t
    }; {/ |" _2 Y8 c# N# j% [
4 `# k  N/ J7 c1 J6 ]$ l: q" b, g5 ^
' b! D$ t+ s' @) ^# u- d2 e
    if(key>=0x80)   // Alt, Ctrl, Shift! w! z, ]+ k, V7 X9 N( e; i% z. I
    {8 m2 e$ e& b, ]; K( o* A  W
        uint8_t bitset = 1<<(key&7);. e8 {% a, y: [2 ]/ K$ ]
        if(onoff)   // non-zero is key up
- b+ m1 A# k0 b. h: n            report[0] &= (~bitset);
3 j: |3 q! j" m3 |- m$ ]. o- c        else
9 k$ U. p6 P$ `            report[0] |= bitset;& i, w7 X! ^; \+ h& q# Y
    }, U# A* W. }$ e- A/ a
    else  v9 {& j2 `2 u9 z9 m+ E( m# l
    {
* s0 h1 g: x3 q2 e        if(onoff)   // non-zero is key up) i: q3 _8 k; s# S- p
        {
0 f7 z  r' z6 Q' }: |: H            for(i=2;i<8;i++)
+ ]$ H& P5 B- O% m% R$ a& _- t( \# }            {% X( ~5 q' S" o4 f6 @
                if(report==key)
) b* P) V9 ~. o! S% G2 s- R                {, }6 D0 D1 n6 P' c: i6 Q
                    report=0;; u* r9 ~% E" ~& ?9 p& @, S
                    break;0 r& N* a/ U6 q: P' ^  j: F7 u4 {
                }
* W, Q" e3 K# t3 z+ W: u" H/ Z            }
. ^8 {' B1 q, ]1 c3 f        }
! I% @6 k% m. M1 v        else6 w& H/ z+ Q  G2 i) K: j
        {
: S, _) v3 z* E4 e, q7 k            for(i=2;i<8;i++)
- X! Q& C; @0 @8 U- B& n; M  v6 Y) J            {2 ~% w' H0 |4 c5 L7 {
                if(report==key)
: _3 u" C: a+ I/ O  o5 p& @) q                    break;  q% z* ?+ K' m% U3 w$ z
                if(report==0)$ D. Z# T1 v* z* j8 t- z$ E0 o. L
                {" m9 N$ O1 L1 @  Q0 C4 d0 Z
                    report=key;
. K# A3 [( f2 _2 ^1 V/ R% X) w2 C9 P                    break;
- Y, z1 p3 k% h; T- u                }
& b& S3 R; {2 |0 `            }
( e9 d9 L+ A; Q        }( A  x( v$ s8 F
    }
* E1 W4 m8 y7 c) v) ]% r6 k0 ]    for(i=0;i<4;i++). t" R4 l+ n8 r: b0 P
        USB_PMA[192+i]=hid_report;# c! _) P2 w- R/ o! Q- v# C
    USB_PMA[5]=8;   //COUNT1_TX4 o$ M9 o0 [9 O8 S/ ?" ?
    if(ep1_wait==0)4 m# L2 ?8 F8 j8 T% m) T3 ^1 D
    {
, s1 z6 C! O& O. @( `8 l$ b' x        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;7 Y: z3 M4 }. U0 P7 v
        ep1_wait=1;
, l8 _1 g$ p' s# w    }
" ?4 A6 w* l* p$ G! P" u}( d; U1 a, d9 _2 ^, s

4 C, r0 x5 v& }3 X# R( E  |% [& X" G
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
' U( a/ {+ D8 R6 y keyboard.zip (8.7 KB, 下载次数: 6377)
# |! z! a' D. g0 d3 d
4 R) \' M5 W) W& p

8 f* i( l, ]- Q7 s' i
  `( F* [0 i4 s& v9 C
' W& K! p; a5 W3 y8 l$ a4 F
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。
* j% z- h7 V! C
回复

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘; ?+ @  Y7 d7 L3 S6 z1 e
不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:488 F5 W  U. l& ^6 V7 e( F4 K: a
刚开始我以为要把打字机改造成电脑键盘1 }4 A6 D: u: O: p! e9 u
不过楼主也很厉害!
) f9 d; i+ U- G" c0 `( Q. }9 @* d+ q
哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
) i, \% S7 ?- v, Q) ^) O4 o" m
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-12-31 06:35 , Processed in 0.171391 second(s), 28 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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