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

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

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

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

[复制链接]

23

主题

62

帖子

266

积分

vip

Rank: 9Rank: 9Rank: 9

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

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

/ M# Y0 {2 h8 P3 ?- V  w 005836yvs0wvovwsssgd3o.png.thumb.jpg & N: x9 B2 y1 T  j
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
) D. d* Q: P* ]* [" s# v  [1 G3 M; o
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
0 Q  v2 ], e6 C
4 T& R3 H( |- K' a到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。7 z5 [% X! o7 w- w- a
$ @/ Q0 z+ e: [" N/ G6 m1 j
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:

/ F9 y* o- ~' D- \4 v 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
1 h4 \/ G( P, ^7 S, t----------------------------------------------------  分割线 ----------------------------------------------------------  d6 q; M6 H6 g+ E/ j. P! U. l

! C2 _3 ?* P( c& S先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

. d5 |( O7 x. W! }& ?8 y$ o- c9 I 020011osionbunl4ui44vi.jpg.thumb.jpg
/ G' J. z3 k1 I1 J% R轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
' ]% J5 i% K1 r" ?" y& } 020017j8ycmnv7788bqv52.jpg.thumb.jpg 9 H+ y. ?; M1 t$ f# j5 a
特写,80C49- H+ k) w) f% N  d( T( v
021040oujzuvtut6iujtvz.jpg.thumb.jpg
: _. D3 m# n% A- m3 b3 r- NLED部分,使用了一片D触发器锁存指示灯状态.
* d  S- `  c8 C" b, j& M 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
1 j, x/ G9 F+ [% r+ ~暴力破坏,将80C49拆掉
8 m8 K  Y' v: i% Y' Y$ o' J& b 021113e48qq98vyohvhzzh.jpg.thumb.jpg
" B+ _: Z' k2 I" F- S" L( t拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
+ H. K8 Y8 @" W8 r- e. Z( D 021125nc9az6dj33rlds2r.jpg.thumb.jpg 2 ]6 a9 }* l, R
焊好元件后的板子,准备替换80C490 d/ a) d! T! C! E+ ^% [" f
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
1 T( A, p6 G1 I: s% {' I用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。
2 g! w6 d* V) f: p- V2 O9 Y8 Y 021104shifhnrqbr3o5nlo.jpg.thumb.jpg + b5 }, b- w3 P0 g5 u
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.. l9 h$ C4 F; F$ z$ Y/ R$ a
022003ym1p9u4ug40280uu.jpg.thumb.jpg ' h" `' C* ~0 z
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。$ w$ A4 {! o3 V4 V& f
023313kt141q9qajtol7ma.jpg.thumb.jpg
! q4 g9 C, n! ^# A. @% E9 j. E, |- J我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。$ r" q* c4 K% n$ O
023322nt7l5xb3ltttkltt.jpg.thumb.jpg 8 ?0 ~2 I& L* b* n
主键区键帽就位, n. P! N) B. D
023331hin88e8wkrwzwikx.jpg.thumb.jpg , J* H$ ^* ^1 ]8 a0 g6 c: S
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
5 w. x( }, r4 B) E/ L! W8 Y 023336wjzlgopugg1jyy79.jpg.thumb.jpg ) Q6 Q+ E( @% u6 o. J6 X% e
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。
! M8 f6 v; F: ^/ G3 n2 X+ V8 g 023341sffu4j3g2323h6fl.jpg.thumb.jpg 1 D, t- e( u  D. H. C7 ^$ w

5 m- P1 [! n% E& \3 M( ]7 j+ c----------------------------------------------------- 分割线 --------------------------------------------------0 `0 J% ?; R# D. D: ?/ B  R
$ i4 k6 X2 q& f: `  V" }
, G4 p% \4 S6 N( x  i+ y

: ^! s# ?8 `9 G1 X- k& J! \  |+ H2 b
& b, @, k9 q( [8 ~2 T

6 d- D# j& ^+ c! M& h" T, P+ @
( F7 I$ r/ T  [& n2 e
, N( J- Y  n  W# Z
" n# j" v6 o$ M  J4 Y% t7 l4 {" B% y; p0 P$ K9 W1 W

0 \- S  M1 y, y  [; H6 @' x
回复

使用道具 举报

23

主题

62

帖子

266

积分

vip

Rank: 9Rank: 9Rank: 9

积分
266
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。# \7 U, B5 A; C7 R2 Z+ ?
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
0 v0 |- F4 M' p 104025nzibm2rmiomhyirm.png.thumb.jpg 2 W# ?$ j( [7 t  A, n9 j

1 B* t& e$ D& n其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
, z4 F' ~* D; F3 X/ d- A5 V 105004zkrez5houvkkznko.jpg.thumb.jpg
! o' B/ j% @: ~8 |4 \3 u
" b/ V6 s7 B$ ^5 g1 P/ w2 _
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
& ~7 M9 G. a9 v8 o
- ?: [% y  C1 O9 G- d, G这是我设计的电路图:, ]  O3 o$ I% D4 L- |2 J
110344ej2z2oo2rflo7oe7.png.thumb.jpg $ g8 A" x) |) T% K5 t
* ?, D. e. |% ^5 R  ^5 \' k
PCB Layout:
! }, X, x4 v; F; [+ O7 ~ 110847jjbjvt34vwt3v5bb.png.thumb.jpg
9 u8 O! ?# }9 |! e
! j+ X& M- j. L& q( n
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
3 q+ r$ t# _# S! B; D6 Q! `, S
; y) x& y' r/ G5 \) ~
$ v- I* X4 c1 L8 v- F
回复 支持 反对

使用道具 举报

23

主题

62

帖子

266

积分

vip

Rank: 9Rank: 9Rank: 9

积分
266
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑
: j. ]9 f: j$ f3 l2 m
# L) C; G2 C% t; r& q' n' n9 L软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
" Q5 A$ V+ N7 {5 w; o# ]- \$ h4 ?2 W+ Y; z) p
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:) R  y/ ^* G* |5 _
113818pmrfsb6z0byt6t06.png.thumb.jpg # X9 p/ `7 O+ p: M6 @
( t- w5 W/ `( R/ ?
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)9 I4 T3 N+ M4 H' G+ t, Y
/ B+ I) f  k+ X; H4 H
USB的中断ISR,bare metal哦1 V# @: C: p1 ^  J, ^

- |! x5 G9 g7 V0 @% h9 M( `void USB_IRQHandler(void)2 c8 [  `; d# R# [5 }, X
{! k% ^3 I- a# B& ]# g; A
    if(USB->ISTR & USB_ISTR_CTR)+ O* f% p7 o. f2 u) C  j
    {
7 a$ y, W% ]0 ^  k! M' ]& @- u4 w        if((USB->ISTR & 0x0f)==0)   // EP_ID==0$ I# G$ l, D! w  c2 Q: F' g
        {) {8 W7 c2 J# b. G- @& @3 s
            switch(ep0_state)4 T2 f) J# o. \: o7 b4 x# Q" H
            {& O, `3 ^( H- h) I( K
                case 0: ep0_state |= 0x80;
  z# p: F# K; c  H! Y                        break;: m& d  \2 h, Z* m1 ?5 Q/ D2 [; a
                case 1: if(USB->EP0R & USB_EP_CTR_TX)" I1 M$ e0 u0 O. f# m1 W
                        {6 w2 n  _9 R3 Z7 ~& `! L: H
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
' |8 V- Q  w2 j) j3 ~                            ep0_state=3;. {8 j, o: j( X# q4 W; H) B* |8 H& S, ^. v
                            return;
) C  O$ i2 n0 }7 \7 S                        }1 G8 L" W- D5 I" w+ ~
                        else
& x; G9 B! K8 _  j/ \# e3 X                            ep0_state=0;1 }" e; d+ i8 z) F& m& m' v& _9 r6 {
                        break;
3 M$ ^% R# ?7 y' R                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet4 q" n( F* Z9 T; Q! I. j" l& g
                            ep0_state |= 0x80;
1 ~$ ~0 r4 c6 c) {7 Z                        else% H1 F  b4 y- z& w& h; k: ~- I
                            ep0_state=0;4 W7 J/ J- j) J
                        break;
  X  e' ~! l4 C1 \7 D' o8 n                case 3: ep0_state=0;2 A# @$ @, \& L1 j0 }. y
                        break;- D0 V3 T+ p, O0 Y# e
                case 4: ep0_state=0;( q4 p3 N5 h3 K
                        break;! `/ ~" M9 b& ~  E# o" R
                default:ep0_state=0;
$ i5 h- H4 Y) p- R                        break;
  G! e9 Z0 x! c* Y: W            }" W9 r6 V: i  \' L' Y
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
; n, h6 s9 ~5 J% A* r  t            return;
: s4 J* F1 g( N/ J: w0 ^        }- |- o- t& N* }2 b8 u. ^6 j
        else    // EP_ID can be 1
% n/ G4 h, \/ K/ l        {
' x4 I( N) H8 o            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
# P3 b! N7 ]1 j$ d  R            ep1_wait=0;
; `* O  P& j1 D$ n5 w1 {            return;* c5 V  T6 |1 b/ Q( C
        }" d) S  |/ L& P% G* n
    }
2 p# B0 i6 O7 `4 T! V    if(USB->ISTR & USB_ISTR_PMAOVR). K; h1 Q5 Y( D- ^: B! W
    {. p7 M; W. L: v5 q3 I2 ?0 e% C# e" Q
        USB->ISTR = ~USB_ISTR_PMAOVR;
# q! Z, R# f' Q% `& Q    }
7 ~% L+ a3 J8 f4 \( m, j: H    if(USB->ISTR & USB_ISTR_ERR); R& ^) G1 x$ g+ g, B
    {+ f, {, K1 L+ X$ n
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear2 n& C# Z: |  s# d
    }
" w* y% y" A( X  A4 z$ v    if(USB->ISTR & USB_ISTR_WKUP)1 N3 N" U- }" c
    {
# f; m* w6 o2 c# T' L        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
9 {4 l0 T+ w% R' G+ P# M0 _( U$ l        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear( j* e3 _8 X1 G6 x; W. q
    }
! b4 q6 S" ]# U. K# a    if(USB->ISTR & USB_ISTR_SUSP)
; P% b4 o1 _& r7 F3 m+ v4 R$ e    {" p5 |8 u% |: q$ S7 @
        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend2 B% i' W# O- T( K, V
        USB->CNTR |= USB_CNTR_LPMODE;   // low power
$ R, ]/ {- ~  m6 @! q        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
; x; n" @; b4 q: l% l; ^* N* E. ~    }. }# n, ~" y! W5 V
    if(USB->ISTR & USB_ISTR_RESET)
, O$ |! q' C1 z5 D) K* A! l    {! Q8 v# E  i; F* `- G; T- G. T
        USB->BTABLE = 0;    // buffer table at bottom of PMA% s7 W/ y& Z6 k6 a
        USB_PMA[0]=128; //ADDR0_TX
8 J# D! `0 n0 w        USB_PMA[1]=0;   //COUNT0_TX
* n; s, z0 C  W% Q" z        USB_PMA[2]=256; //ADDR0_RX
/ [; H' }* t: f0 S        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes* p. \. `) d1 s( k# f
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
+ W2 ?) Q( Z5 Y) t- w' s) _1 g+ Q        ep0_state=0;
. k! M7 ]6 I  n2 \        USB_PMA[4]=384; //ADDR1_TX4 N! ?( p+ U( I/ O; z
        USB_PMA[5]=0;   //COUNT1_TX
  I6 |  H' j/ P* Y! C& S5 Z        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
) t) z* k: r: T" n) t        ep1_wait=0;+ Y" R  r/ R2 Q
        USB->DADDR = USB_DADDR_EF;      // enable function
; u$ U, }- z  H) |        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
3 j* K3 w8 ?) G9 _5 h3 u    }+ O/ g  r7 U7 i8 W/ B" }5 W, E
    if(USB->ISTR & USB_ISTR_SOF)) E6 U  a( z, o; M
    {, L. h' X9 T* ]4 ~
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear# f2 i) F. p$ w; v* m9 y
    }. v6 ]6 E# x+ N, R( b- z, c
}
2 z6 A4 m) ?6 n, u
5 j4 \2 \& l6 Y! _
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。# A0 \4 q$ Y6 J9 G( s
, x& p: n4 T0 m6 ?8 ]/ h' _
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
  p) q) r! o$ |0 d* \6 M7 Z. M
; v: I7 |6 o% ~' _- e5 C/ r7 [
    while(1)
5 v# o% v0 `8 |5 c! M    {
8 M4 [4 L$ c* d        static char row=0;0 o3 C" Q) r/ b, z8 c3 I4 E) s  r
        __WFI();
5 T' R8 q+ ?( J8 Q! t, }        if(ep0_state & 0x80)    // request data processing
  T4 Q2 w1 e" P3 r2 M* Q0 I" J        {1 v& l+ |3 `4 `2 J2 h/ R
            if(ep0_state==0x80) // SETUP phase! ]" J! g# Q" O, N. T/ K5 q
            {5 I" B0 S+ D; {1 B. G, Q$ l
                if(!setup_packet_service()), s9 v( M, E* _# e
                {
8 N* }: ?) f  j" x0 L                    ep0_state=0;
/ H* l- V* R& F( q/ L) j                    // not supported+ G5 ?6 h+ |3 }# J( Q2 o9 W" ^
                }. W- v" M$ X; J/ c7 n; h8 E
                // ep0_state should be set to 1 or 2, if processed1 v3 p/ N2 a, G
            }; i- l) Q$ W( l! w: P$ N
            else    // OUT phase
0 V+ L) V5 [! y+ a. [            {) N3 h  w2 u! h0 F) K
                // process data3 i: u) P0 R( T
                show_LED(*(uint8_t *)(USB_PMA+128));
+ [; S( o% `+ J( B                ep0_state=4;
+ y. L; A* v* h  \/ E  d                USB_PMA[1]=0;       // Zero length DATA0
, k% C; e7 n. ~" B/ u/ u                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;4 T; W4 H& P* K% I4 |0 g
            }6 ]. z6 U$ m2 r3 G
        }
/ W0 e7 D: z: _) r        else
9 d) D1 y* M# @/ W        {* L# p) D% ^4 h! @9 R
            if(usb_address && ep0_state==0)
- m# K: a* B! l* G4 Z9 v            {
6 B7 {; c/ r1 D                USB->DADDR = USB_DADDR_EF|usb_address;
, k; K  W( [% ?" B; g! y% @                usb_address=0;
! a4 l$ y9 K  Q, ^7 t            }! @& D# g' s. o; o" [9 B9 e5 ^9 T
        }
, {9 z7 C% ?" ^3 `- u        if(row!=scan_row)   // new scan line
* p) l% e& w8 P/ ]4 }: k) y0 @6 j# |# _        {
' E) ?- }3 N; H' n. i6 M# ^7 u7 j            if(key_state[row]!=prev_key_state[row])
2 K0 U! J) t, a+ H            {
+ _+ B5 J# C+ k                uint8_t test=0x80;
" `# h) }4 F# I, ]* L+ q                uint8_t diff=key_state[row]^prev_key_state[row];
% {; o, k6 M3 M: d6 I4 K                for(i=0;i<8;i++)$ q7 K; }7 ?" B+ u
                {
3 R4 E7 J2 r/ \- Y; Y( L& d                    if(diff & test)9 ~' @7 p9 a8 ^$ D( {6 a% E: v
                        update_key_matrix(row,i,key_state[row]&test);
# _: v. E4 |1 X$ J" w! @                    test>>=1;
9 }3 y$ n, _3 {. C: |                }7 ]- K1 [  C( I% X9 A% R4 Q: P
            }8 W( j5 W# n5 |8 t- U7 ?
            row=scan_row;
" S$ C% F5 H) N& F: V2 L: Y        }
. P, }( ]  L9 E4 g- ?6 |4 P, P% j$ p& q8 K    }+ f) c" @% D0 v/ N
$ _  [. R8 c2 I# _

: c7 |* J( Q) `EP0的控制传输,把用到的请求处理一下
( t* x. ^! h9 s' v  pchar setup_packet_service(void)
& _0 Q6 y/ D& {{
; H% Q3 x  K: J: K    if(ep0_std_req->bmRequestType & 0x20)   // class-specific* B7 \' j$ E! I! `+ U) w1 {# l
    {
* D( d  ^' E4 ?, {. ~! Z        switch(ep0_std_req->bRequest)& t. {( g7 K% M
        {
9 p* B0 ]3 k1 v0 t/ l7 p, q# X% ^* Q            case REQ_GET_REPORT: break;
4 h  }, ?) y' U1 u* O% J            case REQ_GET_IDLE:
# I: ^, n+ i0 a; K4 Z/ [1 @+ e6 {# @( k                USB_PMA[64]=0xfa;   // return 1 byte& ~1 y$ [& |# M3 V
                USB_PMA[1]=1;; v( ?2 G) d. i) v
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
. n, B+ x: k" x# L6 n" D  c6 h                ep0_state=1;
1 Q( B! V4 m7 g$ h9 t                return 1;
# N; S& T1 u) {$ h9 R                break;; J* ^; E7 }  u% t3 r
            case REQ_SET_REPORT:6 q! |: b8 z5 }' a* s$ M
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;% g7 ~- v" Z. E5 w# O
                ep0_state=2;# t4 W( Z0 q3 w3 y9 p
                return 1;
4 q( h" O! \6 e1 r3 s* r                break;
, X( J- G* X  t8 k; a5 c            case REQ_SET_IDLE:
1 K' M. x3 T- b) `- L: x                USB_PMA[1]=0;   // Zero DATA
7 t" w( L- e3 \" M7 j5 A                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
  v* }' Q0 i; p) L, D- A: T                ep0_state=4;2 y7 [9 W' _& w% I) L
                return 1;" R; m! r, G9 Y, T, j% b4 W
                break;
! n& u5 c& ?! j, Y' W. y        }+ i7 V6 T9 n. `% x2 J% N; v6 n
        return 0;
7 s' b* t/ f; }6 x) _    }$ N: k6 J" g( u) Y6 g, Y
    else    // standard. y+ O, Z$ E# H( T; Z
    {7 I9 M4 L" |1 I3 E* I+ k/ }( w& U. Q
        switch(ep0_std_req->bRequest)
$ Q6 n- K' h0 ?' f1 _4 R& ~3 ?        {8 G$ G) p3 u1 u5 H1 N* Y* f8 \
            case REQ_GET_DESCRIPTOR:
* ?$ l( X& Z% Z3 X1 ]' g- t7 N                return descriptor_service();. q/ c; n- \8 h" S
                break;
9 y4 d# N& Y/ Z7 g            case REQ_SET_ADDRESS:
" A& {; T1 `% Y9 B9 j+ p) G                if(ep0_std_req->bmRequestType!=0x00)
: g5 N8 M, T6 _) y6 b; C8 k* X                    return 0;# l( o& l, G4 I0 c, O9 u
                usb_address=ep0_std_req->wValue;
# M0 {* b  y% W                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;- B" [9 I8 c  B- _" ^5 Y5 ~
                USB_PMA[1]=0;       // Zero length DATA0& F2 g8 l0 [% ]! n- G
                ep0_state=4;    // No Data phase* I0 ~- R$ g7 N; S* c
                return 1;
& s) {. I) \' V$ _            case REQ_SET_CONFIGURATION:! `5 E. }3 s' l/ R- I* f6 O
                if(ep0_std_req->bmRequestType!=0x00)
" N. B7 y  d3 j2 [                    return 0;) L6 J+ D1 x5 |% }
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
/ Z: x# ^; A: e. R1 |4 P4 t( k                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;7 G* X  K% ?4 ~% p
                USB_PMA[1]=0;   // Zero DATA9 K3 J: v7 m% n
                ep0_state=4;    // No DATA phase. [, q; a* O9 x2 F; \$ Y4 p
                return 1;* V7 p+ D2 g9 g* Q! d
            default: return 0;
6 [4 X& ]" Q( L4 F0 l5 o. {        }
) N7 P0 T) y- K; l1 o1 \0 r    }
) B/ J+ y+ d: S3 \3 r7 f6 M}* c! N! j  s! u% Z* Y9 C

  r% f' T9 M. r2 U% P4 N, \4 j; U0 R5 b7 Z, c( S, m9 r

( }; F* y, [- J( O5 D
回复 支持 反对

使用道具 举报

23

主题

62

帖子

266

积分

vip

Rank: 9Rank: 9Rank: 9

积分
266
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的3 h: \! O8 G7 S% V. D& R" s2 t
char descriptor_service(void)+ N5 o* \" d2 }- \
{4 O$ p+ C5 a3 C. Q% Z7 [
    switch((ep0_std_req->wValue)>>8)( c) _2 o/ _  |$ }( L
    {% K0 ^4 H1 R" }: U
        case DESC_TYPE_DEVICE:5 |; d+ z0 q! i
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));/ O. M5 m9 I+ Y% G% l3 _5 S0 l* x
            break;
) Y, T) v$ w3 B" @; f        case DESC_TYPE_CONFIG:; k, D  q, v/ z4 a& m
            if(sizeof(ConfigDescData)>ep0_std_req->wLength)
0 b8 ]. H) r5 J9 N; C3 r                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
2 r! n* t/ G3 g            else
+ N2 d, g$ }0 `, e! Z6 V. [) \                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));$ a3 l4 m0 g/ c0 W
            break;
3 H% _+ o' z, G8 S        case DESC_TYPE_STRING:
* ~8 d, }8 y. V5 e3 ~            switch(ep0_std_req->wValue &0xff)
- |9 O8 Q0 Q& a1 `3 A            {
$ W; b; N* J: o9 s6 r% n3 W                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
* ]& o& n( ?( u, g; l                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
/ |  v4 K3 Z7 @* l: V" P                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
' f. }6 V/ R) d; ~" O                default: return 0;
* Z0 _7 b* u. X& C5 F$ u+ ^* c$ e- R            }
& e5 @( ~% S) u" X' s            break;6 b: J) \5 u& j* O7 f
        case REPORT_DESC_TYPE:
2 c8 j8 e% H# @            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));' V4 \1 k% {: q
        default:
  T5 h1 O+ T- ?6 v2 K            return 0;! e! {" @6 z4 t' U1 O, b/ Z
    }4 k# U: p! \7 E6 b  V
}, f9 \. S  V+ m2 B
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.+ H' o2 o6 f6 h# O5 \* f9 {3 d
void TIM6_DAC_IRQHandler(void)1 A% r  o+ a: |2 W
{
( @8 N8 k# i2 X4 D. I7 n9 {    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
& k6 n& Y* l( J  b+ L5 S- J0 x+ I/ T, A2 ~
3 ?% ~* \' p1 u/ M$ Y
    TIM6->SR &= ~TIM_SR_UIF;( o; c! `7 }* E/ a8 a9 A
    prev_key_state[scan_row]=key_state[scan_row];
  L5 x' s/ {, T9 _( j1 Z& Z5 s4 w( K    key_state[scan_row]= *PA_IDR;   // update key states7 u4 Y0 [# k3 z- P
    switch(scan_row)  x5 g2 }% b2 r1 [' M; B% b- ^
    {- I9 A4 B, I' F: }/ A7 k( n$ ~
        case 13: // next row PB14+ p5 V: H. l1 {* z. `
                GPIOC->MODER = GPIOC_DEFAULT;1 W: C7 N. x! s
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;+ @/ q' K  O6 s5 z, q& b. f
                break;
$ ~- h) Z( e' `% p8 B        case  0: // next row PB15+ Z8 {' ]% G5 J. G1 _
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;  n+ ^! U$ D3 d+ c( ~) r& u$ V
                break;
* V; Y: }$ E  G8 M* [        case  1: // next row PB3
, {' a/ b+ F2 J                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;+ u$ R" C$ @9 g
                break;
. ^% K" |! Z% U. m& r        case  2: // next row PB41 q3 Q' O% n! U( c: K2 p- o
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
7 G$ A* t8 d9 c, u6 {7 r                break;
+ h" Y9 b2 v+ ~  q        case  3: // next row PB5" Z2 o' s/ E" K; c
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;
* i$ S3 y. K# {                break;' i2 L* N" i( _- _' X
        case  4: // next row PB6" g; l* ^$ ]8 j
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;# E# S$ v' X) q" f
                break;9 i$ J. i3 M7 d
        case  5: // next row PB7+ t! E; ?0 V  ?, F1 x
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
8 }8 {8 U2 T# I# v  E                break;
" p( N1 U$ r' w+ C" ]4 l        case  6: // next row PB8
, P. \/ }  \$ f; s                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;% |6 N& t: B: D, _8 N' V$ d
                break;
. v: ^% l; Z: V1 n4 @; ]        case  7: // next row PB9; m8 u/ l& ~. L4 u* T4 L) J
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;& n5 z0 @# U% L4 r- \4 ]
                break;
$ M5 b6 _9 o3 Y' w        case  8: // next row PA8
! d8 r8 w7 G, z" S& x( A                GPIOB->MODER = GPIOB_DEFAULT;& c: V+ v7 _5 f. I" R
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;
+ x7 X; S% j2 y# F( [                break;, _2 G. I; c2 K0 {8 V; C: P
        case  9: // next row PA9
2 `" U  }% M2 D. L: X                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
9 `0 ^) O' k; P; q                break;
$ k/ x1 w  U  e" h1 V        case 10: // next row PA105 t% l0 D( r3 L1 H/ `5 ]. q
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;6 z* Y' R. k' H: S
                break;6 E! X% i+ _2 l6 ~
        case 11: // next row PA15
7 B( i; H6 ^5 A6 H" n* i7 o0 {                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;# |7 t( P8 R$ u+ d0 y* E! g
                break;
2 Y8 g% @& I6 W7 \$ _& A. O        case 12: // next row PC13* @* g& u3 k' T: S: M1 n# q
                GPIOA->MODER = GPIOA_DEFAULT;
2 G9 o  P6 g; z4 n" R; |- t                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
" k- e  O% C4 @- @, z                break;6 C' Q. C$ A1 G. n- T9 }% S
    }7 {6 j# k, }" h; K
    if(scan_row<13)- \/ X- ~! n. i. A+ {: t3 C
        scan_row++;
4 O. H+ d0 ~! a% {+ g; K    else
+ M5 m* ]) I! U0 i& C        scan_row=0;/ L& u! H4 v) W1 l! u& _0 s' f
}
. q9 L* Q! p! l/ k! |! J; ]4 T
6 G. M" w( Z! P: X! \- V  i' y, Q. F. P" z- _0 a- f
回复 支持 反对

使用道具 举报

23

主题

62

帖子

266

积分

vip

Rank: 9Rank: 9Rank: 9

积分
266
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
+ [4 V% c; l! b# X7 G  O5 f6 j1 X* o" k8 J
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。" e& z( j9 Q6 W: ~, M0 l$ C
& Z9 N- l$ |; l( u( o: ?4 b: I
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:

; }; N$ A* g, l: U
# t* E$ k1 c1 R1 ?
; l3 D7 e( k0 q5 `9 \2 o( Tconst char hid_keymap_qwerty[14][8]={
5 m8 O' @9 f. o5 I% `0 n    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
- f/ D- I  p! `    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},( ^9 C7 p+ c! b* E) |! F; z
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},) |& }! u4 A+ G7 f8 `- u
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
" E  @/ B, L+ Z$ Q' D; k  ~    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
9 a( E2 B% U  E, s1 v    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
/ K$ v9 X& s2 r. P: p/ p    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},6 {2 J% E1 _7 ~6 s3 O% ^, F
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},* ^; K9 y. o, Z+ p; }" Y5 Z$ X  _
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
/ I2 ]4 i# c( a    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},4 m' {4 i% f! a" t% K
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
' x9 y+ S8 F9 A9 |) e7 v! [    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},7 k4 S: A# I8 _, U$ _) [0 {. j: \
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
2 H, v1 j# g% Z9 G# Z+ t5 w    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
9 f9 e. }: R- I" ]) R  j0 W5 s9 U};& P- f7 n6 S6 E  M

7 M5 C! x! J. d, T% p
6 c1 ^3 T- K. {
const char hid_keymap_dvorak[14][8]={# S- {7 k1 W4 ?7 v
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},2 U) M, w; J% Y' ^4 f9 R2 |4 S# f
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
/ ]$ Z4 a% C) g! |* k    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
$ l: S+ _: \' t9 ~3 V1 [    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},3 u( p! j  U# A# ]" ]& A
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},8 }: o9 W  u9 n. a7 l2 j" J  `* @; L
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
, Q/ I8 j+ O. ]9 |0 B% D& x    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
/ m# w9 R4 y+ L    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},9 ^5 K* l) j8 }( v
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
+ K) E5 X2 |* j1 W4 C) M! K4 d    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},/ Q9 I& ]" x/ M) {9 H
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},  W3 K/ ^; [0 @2 Y/ T7 U. S
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},4 K2 F: x4 |7 T
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},# J) V- k/ e6 X2 H+ Q8 n
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
' l. X! C- L- K2 L# F};
, P: h8 ~4 W& \* N+ c% [$ g
; W/ m: L2 s+ P! ]% V8 a: r5 W$ U! L1 O) C
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
7 f" t- K! J8 a+ ]2 b8 O4 n& l
: {' u+ r1 F5 p# PHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

' o2 b5 |, d; r) e3 |* n
) a! G% A# X; d' a# [void update_key_matrix(char row, char col, char onoff)% ?- }  F+ _- B9 {1 C0 J5 `% u$ m
{5 j1 T4 D3 E' B) N: I' }: ^, |
    static uint16_t hid_report[4]={0,0,0,0};4 |/ s6 y9 z; n' I
    static char (*hid_keymap)[8]=hid_keymap_dvorak;2 i9 N9 w3 ]# Q7 i! i

) r$ J  G5 M+ g" ~  o$ r( K

& n3 P# S4 Q* M' ~" Q    unsigned char key=hid_keymap[row][col];
3 n, z5 s1 K# i1 D9 D    unsigned char *report =(unsigned char *)hid_report;
7 o6 v$ o: l6 w- h5 L. n1 P& @    char i;6 |7 J: T8 P2 w' C; _6 z" g% D' s
- a5 H8 n1 x/ N6 _/ \

0 I6 q6 l: `  P, r. j    if(key==HK_MODE)' T' E/ j& e. B$ E& {+ [
    {
; a: G( V/ ?% E! @8 L        if(!onoff)* ~" p# M% F4 B3 n0 }6 ^3 F
        {) `  @) q4 I5 }( S
            if(hid_keymap==hid_keymap_dvorak)
8 q" o7 g, d2 C1 r  F            {: a$ g. L, j# f
                hid_keymap=hid_keymap_qwerty;
, d' ~$ P- ]+ h) ?: @  T' W                GPIOB->BSRR = (1<<2);- Z: g4 T* _8 M5 R; j8 y
            }5 A% k* z  p: [. n: ?
            else; |" g+ L$ e0 i- E/ @7 z* A
            {
: p1 {; [3 x8 [" s                hid_keymap=hid_keymap_dvorak;
7 ?; v" O, w6 F+ z/ Y' J' A! C                GPIOB->BRR = (1<<2);
$ Y' j+ Z0 e1 H            }( L7 ], y; n- A5 p7 H
        }
, r( Y' C" s! u. X  t. B. S3 H        return;* Y- R1 R2 v) a9 u1 q' _1 w1 ^8 {
    }
4 X# R2 M) C8 P0 C0 Y) `/ y
3 P, }  j8 g* X% s% A

# L* M5 V! N  Q3 X% T+ i5 }& s    if(key>=0x80)   // Alt, Ctrl, Shift; `; {/ Z4 Q5 U: l; i8 e5 `
    {* y' ~' F8 s; J& _5 B  M
        uint8_t bitset = 1<<(key&7);* K! n  k" B, D
        if(onoff)   // non-zero is key up
$ I" N8 v/ |5 [8 F6 M9 |            report[0] &= (~bitset);) C& v6 S: h1 g1 E3 ?0 u/ W
        else4 G; r+ u7 w$ V8 ?3 a
            report[0] |= bitset;
* S# V* W1 B5 s# [4 B/ a) M# o    }5 _4 e# A5 ]5 H& R
    else1 C: [6 A! y$ c( G3 b& s9 i
    {8 p% Q0 x$ ^- u+ r0 k' h! `
        if(onoff)   // non-zero is key up% F- U# {2 P, @* `5 k7 `
        {/ r- }$ p3 B" \5 u: H* ]  }1 W
            for(i=2;i<8;i++)* w/ P( b4 d0 `, i( u1 j3 a( _
            {7 [6 h0 ?0 {1 B( ]$ L; a1 o# N3 u
                if(report==key)
+ Z' u. C$ u3 A+ r+ v2 c, z/ q                {
) F/ P! m2 S2 I: a4 J                    report=0;
' S' t+ J) B) B9 U                    break;2 e& v. Z0 Z+ r8 U) A" x; S
                }  [+ \% i- k, ^( t7 Q8 d2 ]8 G" u
            }( t  Y1 {  ?  G$ r' o9 X7 ]5 ]8 y
        }9 L! n2 J* l+ _" C" f
        else
+ I# @. L8 O! p* i6 v4 {        {
  k* P- N# e$ q            for(i=2;i<8;i++)
) L1 `3 E- o* b5 Z2 ]! i8 j% L  @            {! ^. _  I1 ?% k( r' F: ~
                if(report==key)% K% S* K$ D5 P
                    break;* x5 a) a1 ^. f) D% F# [7 f
                if(report==0)
% @' H  H) Z4 P- F                {) r& f. o) I0 z' k& q
                    report=key;
# }, X9 e4 r* a  F, x6 t5 M5 r4 H& C& G                    break;* c3 B6 k3 J4 J) k( U
                }
& _: k* x% |( `7 r  q) h2 i            }
/ z2 ]3 ]9 x2 w4 u        }, F1 S$ h# z: \- t, h4 G
    }% }  C3 U( ~) u+ Z
    for(i=0;i<4;i++)2 w5 R8 x  P. M: Q& b
        USB_PMA[192+i]=hid_report;+ a: D3 T1 g( O3 h- k
    USB_PMA[5]=8;   //COUNT1_TX* s1 X8 j; t+ h1 z: b; r
    if(ep1_wait==0)
% U1 u/ p3 ?' ^# K2 N    {
9 l, `/ t) Z' r( I        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;/ G' w  _  H' _4 P
        ep1_wait=1;% g; l3 Z1 W) I5 P: D
    }5 O/ ]/ i# I, x  O; z
}9 ^( e5 y- l9 V6 h7 M$ \* ]; s

; o( `: M5 _" J" G5 ]# i2 S2 _" k. {
% P* x+ A4 s9 w" B0 m完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。; {' J* |) T& s& s- e- N
keyboard.zip (8.7 KB, 下载次数: 1679)
回复 支持 反对

使用道具 举报

23

主题

62

帖子

266

积分

vip

Rank: 9Rank: 9Rank: 9

积分
266
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。
& r* E& h, a8 C% a3 w8 _* S) n
回复 支持 反对

使用道具 举报

8

主题

146

帖子

203

积分

中级会员

Rank: 3Rank: 3

积分
203

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘$ C7 n2 g8 P+ o1 q; Z
不过楼主也很厉害!
回复 支持 反对

使用道具 举报

23

主题

62

帖子

266

积分

vip

Rank: 9Rank: 9Rank: 9

积分
266
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48# [+ k5 Y% U1 E4 A
刚开始我以为要把打字机改造成电脑键盘
  J1 J3 s& D# C- Q: d* e6 d不过楼主也很厉害!

" R. t- L8 U9 N8 O7 x" i$ D% ~哈哈  是有点像奥
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2017-3-27 10:50 , Processed in 0.174500 second(s), 28 queries .

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

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