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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
* L) K7 M0 L2 ^/ Z# l5 \: g- J0 L0 {
8 D# n/ t1 h6 S! p) c7 ]http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
  V9 q; Q3 v1 K! ~% W9 L( k' x0 Y这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。
3 [. P, }9 n1 ]$ B7 [9 @$ ]5 m% Z) p1 f: x5 L
在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。

3 F  P, z/ H1 L: L4 h0 p) h

& I, ^5 c. ]7 X4 y$ M; s9 U 235140i3a36qivqzuvmt5q.jpg.thumb.jpg 8 K2 {, Y, B! Q- {& H6 u
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。5 U0 C- N2 @% x8 D+ `4 M
001734klbyoluenuwz4h4b.png.thumb.jpg
' O. j( O4 W& J& a, Z" L. P为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:( ^$ Y9 n7 M, L
003625r2agx2f5v922cf2f.png.thumb.jpg , ^1 w" r3 s% ]8 @6 I
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.
+ _  z- b" W# p  {6 A, aDvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
+ t0 I: v- l3 k# J- d
005836yvs0wvovwsssgd3o.png.thumb.jpg
  Y4 I7 o" O* ]! }7 E5 X3 sDvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。" s, h$ j* I/ i* e
3 [) |; ^; s, K- W
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。9 [- d: k( K& g

) o' ?3 }1 V% s! X, [- G& m到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。0 Z, R  _: S8 j

2 _4 M& A8 ?& O) K0 m机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:

( @: d; A5 a& Z5 e6 l% q4 l 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg : G. f0 B4 N: h$ l
----------------------------------------------------  分割线 ----------------------------------------------------------+ r" g7 }- l9 Z8 Y

/ I! m1 G4 _' T& D$ n先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
; ?2 `+ Y8 e2 v6 v8 J
020011osionbunl4ui44vi.jpg.thumb.jpg
# I6 W; h9 p! |3 o轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。! _0 _8 F+ f5 y1 C. h
020017j8ycmnv7788bqv52.jpg.thumb.jpg
$ t) Q# C: a5 K. g* F$ i7 }$ t; j" e: ~特写,80C49
- |% u2 G" G9 e' A7 t3 @) D 021040oujzuvtut6iujtvz.jpg.thumb.jpg
, U$ g( w8 i8 J2 |/ F6 _5 QLED部分,使用了一片D触发器锁存指示灯状态.# L! h$ P& O7 n6 l+ U
022209ixzjfjc9crmrrqjm.jpg.thumb.jpg ; l; w7 s8 q2 j* ~/ F( d
暴力破坏,将80C49拆掉' J4 s- _2 N' S; ?
021113e48qq98vyohvhzzh.jpg.thumb.jpg # M: x( y6 x# K/ \# B9 e
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
0 o" P+ C) Z5 k, Q- ]* Y* u 021125nc9az6dj33rlds2r.jpg.thumb.jpg
* B5 M! {% ?& {0 A' p! y4 T2 ]
焊好元件后的板子,准备替换80C49. z" p% d3 Q8 w
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg 2 y  U3 A( P  n
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。
7 u) @6 N) \; A 021104shifhnrqbr3o5nlo.jpg.thumb.jpg ; {" w: m, W) E! E  b
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.$ K% n1 a4 D9 N4 M( Q1 X$ N
022003ym1p9u4ug40280uu.jpg.thumb.jpg
$ h( T& z8 S; K3 M/ Q开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。9 r% G/ d. B) {
023313kt141q9qajtol7ma.jpg.thumb.jpg
) ~3 @" ?1 P( N8 K0 f% c+ l我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。6 w- @$ N( R" p- g
023322nt7l5xb3ltttkltt.jpg.thumb.jpg 3 {: m+ l4 L; L8 l0 P7 w; @
主键区键帽就位
0 d) q% z5 }# g# V' S; l2 p 023331hin88e8wkrwzwikx.jpg.thumb.jpg 1 a% H, [8 X) T; Z$ g
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。! o* E+ F, U) V* K8 t+ l
023336wjzlgopugg1jyy79.jpg.thumb.jpg 3 f, }. P- Z5 f/ p& b$ i3 o+ P2 @
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。
  b! b0 \$ n; }  P$ W' x 023341sffu4j3g2323h6fl.jpg.thumb.jpg
  N, k# o: I; R( T
3 a2 M) V8 S& g3 p, g! X) C
----------------------------------------------------- 分割线 --------------------------------------------------
2 ^9 }  v8 x4 H

# q) W* ?0 o8 a! m9 I

9 N! ]. @9 @5 D$ L& A6 P9 k5 E: u' o- N. V6 T1 [  B3 C

$ S: c7 z1 D# \* m& g5 V7 e8 ]7 I  U2 X5 l6 S

% O& A* R0 ]$ z' y; n7 n% a2 F( d# r" z6 a/ V5 d
  N" P& b9 ^# R( X" O* c
3 O- I% G/ b) |8 n( p, ^

- b8 q; ]8 V0 v4 V% M' q% z# X  o* g& B2 c* o2 q( z
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
7 r, i$ l. M; m80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
- O/ o' N3 d- [+ @* e$ v! Y* `0 d5 t 104025nzibm2rmiomhyirm.png.thumb.jpg
6 I6 x1 K( Q" k' m! W

' v+ k+ v! k, ?9 k1 r其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
+ a" [* G' h, ? 105004zkrez5houvkkznko.jpg.thumb.jpg
& `/ w6 w% J: C  X  U

- o0 A4 M: Z% n' s  t) Z$ C0 o扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
4 c3 o* k0 [$ S% u& L
4 u3 N6 ]1 K) C" ]这是我设计的电路图:* l2 E7 \! R+ k% x
110344ej2z2oo2rflo7oe7.png.thumb.jpg
! Z" ~5 B4 e& g7 d, ^$ f
' ^1 b! i9 C( e
PCB Layout:
0 v; [6 Z! q( s! K 110847jjbjvt34vwt3v5bb.png.thumb.jpg
' F8 p- ^/ c3 t: o/ I, T- V

3 ?* m4 U. L0 v- Q7 V不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. & z2 d3 d! G& z& x& z
2 |% }& d6 \/ j2 v) V# M# F
9 g' K( \: h. o
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑
* e2 G2 X! Q, u7 g* ~' u) K5 ?1 N3 \) v6 J- H! T
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。$ x6 _) ]) f% X1 @' z

5 V( Q3 z5 H" J4 p% E3 T总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:- c/ N% K7 t9 B& y3 x6 @! s
113818pmrfsb6z0byt6t06.png.thumb.jpg 0 Y, w* R! ], l4 C. i

' G6 T/ D: ]- `# w  v/ |( u, C其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛), b/ g! p$ e: R/ m4 @

, B# M& b6 M7 X$ ^USB的中断ISR,bare metal哦$ u- B7 a. y! d. _3 P) L1 J
+ ^& T$ @$ c+ O7 ?
void USB_IRQHandler(void)
3 J& ^/ E2 x6 W' J  ^+ j2 s{6 L7 E! t/ t7 W1 n) Q* M
    if(USB->ISTR & USB_ISTR_CTR)
( C$ J& W% R. G! ~$ u/ K    {2 o# `+ F2 |; g& E& i
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0" h4 J$ C) \! T% F' C; Y0 {* F3 V
        {
" U  a' h$ y  g3 s# y9 @: l* h$ X& b            switch(ep0_state)
- ?% s1 I: n# h, T/ B, ?: X            {4 A& o8 i1 S3 S+ {0 a# n# F$ ]
                case 0: ep0_state |= 0x80;
0 m, }) l4 s: i3 c                        break;
1 {1 ?% e, F! e3 D; g" f, k$ d% B. Q                case 1: if(USB->EP0R & USB_EP_CTR_TX)4 L& J# `6 w" L, K; K
                        {/ }8 q2 b0 G, q) \, U
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
  _; V. u5 ]# T7 `                            ep0_state=3;
* m- I+ m1 c+ Z4 v1 u; S: n                            return;
* ]4 `% E* @8 ^$ V                        }$ V" i+ }  k7 m, X/ r/ v
                        else! _4 s4 F5 s5 e0 t& b( A+ o% B
                            ep0_state=0;
- M  x( ^7 l, {1 y5 }+ G                        break;/ y4 A& @: U  O1 E$ c
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
: t9 H$ i5 n6 M6 n                            ep0_state |= 0x80;4 ^5 e( {: Y( _8 K3 j
                        else
# R1 H* d! [# y3 v/ h  B                            ep0_state=0;8 s$ H3 \% U# f7 y5 ]
                        break;
( V" R1 o8 {. ~, _                case 3: ep0_state=0;; h9 H# R5 O. `& m
                        break;
: l( l* j+ ]# A# Q0 S2 B                case 4: ep0_state=0;) ]$ S: }- D# Z1 ^
                        break;7 {4 E6 w; V" `' J
                default:ep0_state=0;$ r* Z" e( Y( i
                        break;' p3 L8 c; S# g% l% c8 q
            }
$ @: E' n/ `& D( k8 J            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
5 d: f4 @$ p/ x2 s, I/ {" Z0 u            return;& m+ P! v( e4 M
        }
2 o2 y% u* |. N! z" N, n5 M4 b        else    // EP_ID can be 1
* L* y( R* P7 S. E6 m        {: w* N' `! X) w. {# I9 d
            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag. S1 C+ j; A& l# C$ L
            ep1_wait=0;
' \' Z& B; h5 ?0 Z( p2 i, W            return;% l  n; b! h( a/ J* m
        }: x& ~1 t  q9 X1 F1 ]1 T
    }
6 u- w$ \; U# q4 f/ q( q! W    if(USB->ISTR & USB_ISTR_PMAOVR)
* ]5 o' F) B! a5 e* b3 W3 a, R    {
; ~+ E4 y9 P% J+ K1 I* q; R        USB->ISTR = ~USB_ISTR_PMAOVR;
6 Z( s! p: ^, a( f- p3 h    }
( ?$ a6 f: D* @' Q/ l2 r( c/ G    if(USB->ISTR & USB_ISTR_ERR)
$ ~* x8 I7 a) \; w& Y( D4 L: g2 X    {
2 G0 `$ p  h  r' j, G. |% {        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
" v: ~" {7 \; R+ k    }
( ?8 [& `3 M3 \2 P) Z* |    if(USB->ISTR & USB_ISTR_WKUP)
: I. f% d0 K; r, x' Q    {
. a" x% b: F& E: T, i% ?        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend6 i) W. e6 K0 N& A9 l* l# c
        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
& K' s" M% W% Q- |3 o    }
( g9 e& [( C# H" [4 _    if(USB->ISTR & USB_ISTR_SUSP)
/ A& X3 d2 R2 R) }    {3 h2 z' g8 o3 O( S% A  ]  F
        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
2 g. d; m& c( n: K        USB->CNTR |= USB_CNTR_LPMODE;   // low power
" B6 T, x$ H0 b. ]        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
) ~- G% g5 B" [4 }    }1 ^8 s/ m, O( V4 y0 O: m1 w6 Z
    if(USB->ISTR & USB_ISTR_RESET)
' Z  C5 U, Y' ~. A# o. U    {
# ]" M- _. E8 g4 `+ X        USB->BTABLE = 0;    // buffer table at bottom of PMA0 z' T# ~6 v/ h8 g: @2 y: q7 N
        USB_PMA[0]=128; //ADDR0_TX
4 C7 z1 _1 M- J# V        USB_PMA[1]=0;   //COUNT0_TX
2 [- R5 h0 ?& @/ b; [        USB_PMA[2]=256; //ADDR0_RX, y& R) A" j4 _% t
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes" w/ B& b7 K4 z! `: [/ |2 U" g6 {
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
- r6 h4 n* L, v4 a        ep0_state=0;/ j* Y+ i) k$ Y, R
        USB_PMA[4]=384; //ADDR1_TX
$ _+ m, L* e; k. u  O& P        USB_PMA[5]=0;   //COUNT1_TX
0 @! ]3 D$ O+ w        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type# |8 S( r$ j! L
        ep1_wait=0;
. b* g# w+ n8 r! o3 h1 _        USB->DADDR = USB_DADDR_EF;      // enable function
. m$ Q) r" A6 W) O; r0 E$ C        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
! U$ p' u% C8 f- I1 x; ]    }
  J4 \3 f, @' b/ s2 W$ o    if(USB->ISTR & USB_ISTR_SOF)
7 K$ h8 d6 a; Z. H: f    {" G7 R  c3 I! r& m
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear- q4 J( k1 L0 p/ ]% Z
    }
& Q7 R" m$ Q7 u# ~1 z}
- N0 G  D  i, D4 q9 C6 y; Y
+ G3 L- R( _8 W5 K9 [& l
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。# g5 ^  g2 E- y9 \5 {
  w* |3 `) \2 Z
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
$ o+ Q2 [' M9 ]# o8 n
( A+ U! T0 |) e/ h0 v& E
    while(1)
' Z$ h/ s* T; S8 t    {& X) J* [) Q7 `  C( f( P
        static char row=0;
( K0 |6 e% g. P- t        __WFI();
+ c8 ?8 t: D! g$ A" x# `7 }' C        if(ep0_state & 0x80)    // request data processing
& n4 v6 a0 c% I2 s( B5 r: N9 h* p8 m2 |        {
0 _" j# a0 ^+ b5 J  f* L, _            if(ep0_state==0x80) // SETUP phase, ?% w0 h+ k3 }; D6 M2 l) a6 d
            {
- j: i. @: I! m, ]) ^                if(!setup_packet_service())
6 K3 S1 J  P0 w5 B                {9 P9 V7 y' |6 U3 d
                    ep0_state=0;
& A) T4 T% y6 F7 Z2 l! x                    // not supported
4 e# t  s9 f8 K) E3 Y                }
: x3 O4 p% H+ d$ O) @/ n  E                // ep0_state should be set to 1 or 2, if processed
! l' r4 _+ n" O( E0 V& h# }            }9 V; X2 i  D: f  `" X9 A6 I0 d8 h) r- A
            else    // OUT phase
$ y" u! y6 ~+ C9 N            {$ K2 Y8 W) y9 b: g0 R
                // process data' F& [* g9 H2 S6 U3 K
                show_LED(*(uint8_t *)(USB_PMA+128));
& \7 M6 b  N5 d/ r% o* Z9 G+ z9 w1 B! c                ep0_state=4;
# h2 G: O/ D; j7 Z                USB_PMA[1]=0;       // Zero length DATA0! V9 |% u4 \4 ^3 ~% X! Y5 @7 Q4 |
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;# A- M& p& [: `0 v# s0 P# W
            }$ K* R0 B* `/ l( e, O; e# f! B
        }; H3 T% l/ S% K
        else
; Q; u9 k9 h5 U- O        {
+ A0 R  S& Q& I            if(usb_address && ep0_state==0)
) S- T& s: n, a% q7 d5 b2 g' j  [& q6 w            {- y6 t% G3 d5 M% [# X- o& l# b) J
                USB->DADDR = USB_DADDR_EF|usb_address;
  r) Z, R8 b# t                usb_address=0;2 ?( A7 p) m3 I- n* A/ d9 |1 ?
            }
% k  g1 T1 N8 Z) B7 |* t8 p2 b6 R        }
! n) t$ _( Q; l2 q) p5 s        if(row!=scan_row)   // new scan line
, N- `2 L; f5 N+ J" [        {( F7 t- E* s. m/ c7 ~
            if(key_state[row]!=prev_key_state[row])/ d- D* X# X# T
            {4 z  q+ r2 B- i; g- P* c7 w5 C
                uint8_t test=0x80;1 v2 C+ o$ m5 |. p) g
                uint8_t diff=key_state[row]^prev_key_state[row];
( a( S. w3 W3 c$ q& s                for(i=0;i<8;i++)
1 k# z6 B# b2 j. u2 o% \! S                {; R  O" I, v+ t* j* W
                    if(diff & test)
9 @6 L. n5 f" R+ d/ W                        update_key_matrix(row,i,key_state[row]&test);
) X" E+ C3 d/ `; r9 ?+ y* ]                    test>>=1;. K7 o# R' e- _! k; q9 |4 @
                }: o+ C9 l0 x+ _6 {3 u1 K. o) k
            }
3 o6 O3 _5 a" y) a! z7 U; U            row=scan_row;
0 u) _8 y" W. K- ^$ E        }( v+ A3 ?7 ~9 o- P' @2 _* X! v3 }% T
    }" H/ K# |  D# j' I0 a2 O, A
7 q, e% I. o( S! C) [
4 T, N& e( A6 {$ @& K. |6 ?
EP0的控制传输,把用到的请求处理一下4 _4 w! E. @# }& U, e' s
char setup_packet_service(void)( g0 |7 d6 i1 G9 d4 Q9 S3 w
{4 }  h$ k5 J! x' f2 ]
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
  i1 R/ T8 G5 X! y9 I4 \    {: `( b$ F) G* T6 E5 D" M
        switch(ep0_std_req->bRequest)3 g3 V" Z, Q* m$ e
        {
  z" E$ Z7 X- W$ [9 Q# S            case REQ_GET_REPORT: break;
2 V  ?9 c: F1 ?' {3 X7 y0 F" o1 L            case REQ_GET_IDLE:
9 U2 Z5 m) V% D/ X( ~& F. D! h' o                USB_PMA[64]=0xfa;   // return 1 byte+ L/ Q+ Q, X) ^, j. m  U* o# f
                USB_PMA[1]=1;* q. J: k$ r9 E
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;/ u0 ?  L" u1 t' [
                ep0_state=1;
. K5 X+ r& o9 ?                return 1;
7 I% `3 B+ h7 Z& X0 L0 G3 O                break;( {- b0 u! x) ~8 v, N
            case REQ_SET_REPORT:
* w* y5 t( o8 I3 p                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;1 j& @9 f1 U8 G
                ep0_state=2;
: P# h9 M* W; p1 Z* L# H: Q' C. [0 ?                return 1;- _: g9 Q3 a  f* @3 |
                break;0 [9 b9 h3 k- }( e
            case REQ_SET_IDLE:2 a8 D+ A8 l4 Z  Y
                USB_PMA[1]=0;   // Zero DATA6 Z0 h' p4 K( C* G
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;4 a( s, k. d) A: Y7 m3 ?+ K! w
                ep0_state=4;
2 b7 I  I2 Z) w                return 1;% X2 D  v$ Z, f, \) Y8 c
                break;* |/ z, b/ d) D4 M; v8 O/ ?1 x
        }5 J, Z( o( O& I8 ^
        return 0;# C1 r$ W8 j$ `& T) q, `3 J+ f3 x
    }
  f5 h) @: k6 K' d4 Z    else    // standard9 I! `- z! k/ f! T' F% w
    {
9 l) n' }! f5 L; e0 A, Q' W        switch(ep0_std_req->bRequest)4 q% x4 p( _( W7 ~$ D0 D1 s
        {
1 ~; B  K$ ^9 q+ E            case REQ_GET_DESCRIPTOR:. C- j3 z; D& q4 H( Q6 T
                return descriptor_service();
5 C, G5 g) a5 a                break;
4 \" R6 x/ F# w8 X" F6 E            case REQ_SET_ADDRESS:" _6 r/ g9 @6 R$ V2 N6 q
                if(ep0_std_req->bmRequestType!=0x00)- K0 j5 |8 |& W4 q) B2 `: Z+ Y
                    return 0;
3 k- Y0 a; r+ R' Z' t' G                usb_address=ep0_std_req->wValue;
7 J7 Z& _% P8 a+ P' f' P                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
' ~6 r0 W1 h4 D+ P' P7 t                USB_PMA[1]=0;       // Zero length DATA0
/ Z# ?9 K8 h  P                ep0_state=4;    // No Data phase
1 w% B1 c0 g' E, v8 n- _                return 1;
9 q! o/ a9 r; n9 Y, j, f: e            case REQ_SET_CONFIGURATION:$ \! z- s+ L( d- F% v+ p- q
                if(ep0_std_req->bmRequestType!=0x00)$ O1 e5 W. j' A$ q7 Q7 O+ p3 t5 o
                    return 0;* U6 {$ L. o! h. q  F/ L! N$ _
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;% e. S. |9 {3 p+ j9 ?7 t6 m
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;2 F5 w: C: K& a2 Z' p" e
                USB_PMA[1]=0;   // Zero DATA& R. ?: X9 M  |" g0 f6 ]) l! [# n
                ep0_state=4;    // No DATA phase
4 Y! W1 \6 G" V" m' f; S0 r                return 1;
) m5 f9 f& e6 O( F% x            default: return 0;" l% F$ e" y  g2 k8 O% j% X# ~5 @
        }
4 V$ D, m' j: a9 H5 s% _" o    }# d% D) b/ C$ J) Y+ A
}" c6 p! \9 Q) `& X+ i

$ C% t  }" ~- b9 |  k  G' M: S8 f. K
9 Z  ]5 x% ^9 l( |$ x
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
) |* [* V' v% @char descriptor_service(void)$ l6 ]) I! e) z/ o9 s: Y8 ^9 M4 O
{0 U: D$ j. l9 R  @2 K
    switch((ep0_std_req->wValue)>>8)7 ~% t3 f: _8 l& D; |
    {$ J3 a. Z" {6 ~0 ~( q
        case DESC_TYPE_DEVICE:
. A7 p6 B6 e. b! B( A            return ep0_preparedata(&DevDesc, sizeof(DevDesc));
8 T3 k$ [( i0 w0 _8 W            break;/ u0 y; c: M/ f% V& h, f8 O: w7 b
        case DESC_TYPE_CONFIG:
3 K% v" u( p$ `5 _5 v' Q            if(sizeof(ConfigDescData)>ep0_std_req->wLength)- t2 O  Q8 |8 Y  [1 F7 c3 c
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
% ~  H6 a! @- u: J! `! J0 R/ q; u! }            else
* e# |" X" @/ @' V                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
! Q1 c) n5 m) t, O5 R            break;
. p( H% q: D. c# p. G7 d        case DESC_TYPE_STRING:6 k) w% h/ {/ m3 m
            switch(ep0_std_req->wValue &0xff)* v" A8 C4 L7 X; O" N( g5 y! k; _
            {
- E% B3 i" M" u8 m: \2 s# a& }$ v                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
8 {) D! T. v+ q/ T: m                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));: W9 N$ B( Q( y6 y: A
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
2 p! f/ f4 g4 K0 \; z9 c                default: return 0;. H4 Z9 g: s( D+ r6 M/ Z! L1 n" v
            }
( u6 y+ K* W8 g- ^! C            break;  l" X- S6 F* H
        case REPORT_DESC_TYPE:) C" v& _, ]4 o/ d
            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));  w. d- j) P: S! q9 [' u
        default:9 Z0 \, ?# V5 i( o3 |" m2 D
            return 0;; N1 o8 v# K" u: ~( a0 ?
    }; ]3 p/ [- s- o0 g2 G
}# `& V' t1 R3 a( T) {/ g& t5 }9 m
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
' _+ u1 K* x; j8 o; M) W8 tvoid TIM6_DAC_IRQHandler(void)7 G! @/ ^' j$ O, r9 O' F' d, m
{* g: Z( O2 u  G# |
    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);0 {& t0 ^9 ]) y0 c) P- Y/ M
5 A) X2 P( J2 [" Y" ^/ A) G% v

6 q! d, J  r' j3 o+ f    TIM6->SR &= ~TIM_SR_UIF;4 ?  C3 ^& i/ j0 V4 f, D
    prev_key_state[scan_row]=key_state[scan_row];+ U* v: t0 |7 }* A: m
    key_state[scan_row]= *PA_IDR;   // update key states4 ~, f6 w( _$ ~. h
    switch(scan_row)( n9 J2 u  D3 k( S! D
    {
3 q$ F+ L, z" \% l        case 13: // next row PB14
  r1 c. f: p" K                GPIOC->MODER = GPIOC_DEFAULT;
: L$ n, V+ I: s, u7 w7 M( n  y* b+ l                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
$ @) g0 {- j% }1 {  m- [                break;
& n5 T7 x. ?" k+ A- V& d4 U* Q* q        case  0: // next row PB15
/ S" Z% y9 |4 x1 O9 {4 T  S- B2 m                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;6 r$ H. Z. A0 @9 E) G& ~2 q
                break;
* {. |+ c& B; R1 x7 `  x        case  1: // next row PB3& t  i- W2 G6 u$ \' @9 a* ^
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;7 J1 L, h1 f3 k: v4 g
                break;  o; b' z/ ~' X+ U, y9 X
        case  2: // next row PB4% ]) l: g. \8 h4 J3 o+ l
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
4 h5 n- X+ n7 L- S9 k& E' p4 Q                break;
/ m6 `% N" ^' R! p        case  3: // next row PB5
9 k8 L4 p: w$ {0 i9 p; y" i6 G                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;
! r) ~  O( o0 L1 o/ O3 R% |" ^                break;; k8 w' C/ W. B# ^+ U
        case  4: // next row PB64 x. L. b7 W- Q) s7 J( a
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
  C8 j; Z0 r/ G1 S                break;
. a0 a& L$ Z$ S9 U. I# Y' b( k. q        case  5: // next row PB7
2 o2 ?3 o' S- x3 y. |/ v                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
  n: e5 Z3 ?1 t0 G                break;( ]; S2 Y. V( R( O* w
        case  6: // next row PB8- z  q( I( w' `; p* I+ D9 r
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;( C! W* B% B4 Q/ H3 U, F' ]) T7 I
                break;0 v! T3 M- j7 k: n7 S! W. N0 d
        case  7: // next row PB99 s# N, v1 B+ k8 E+ H" |5 o
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;
6 d. z5 a+ k) ~/ D' Z3 Q8 [0 V                break;# Z7 `* Y4 N8 c4 k6 X3 ~/ q* ^
        case  8: // next row PA83 ~% E5 j  v* l. J  b4 U1 q% i, Q% P
                GPIOB->MODER = GPIOB_DEFAULT;; x# T" x1 d# r0 E* k
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;+ D  f. ~. [7 `' Q; P% _
                break;
- R7 ^' X. f+ b, I6 T1 D        case  9: // next row PA98 u* \; d- r( R2 n+ V
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
& L9 \2 }6 i+ L/ q% E7 |                break;# z7 j# E  v2 I2 [! G& `
        case 10: // next row PA103 n6 K' X- {0 m# d$ `* j- w* E
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;' ~  v/ G2 H) ?2 K0 v- E/ v9 ]
                break;0 D. z: O& H+ F) v6 r! X
        case 11: // next row PA15! S7 T/ R3 k/ w2 z
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;3 S$ r0 O. P  \  X5 \' |
                break;( J' M( w) r+ B7 H
        case 12: // next row PC131 X" w5 P( l/ B! H: v6 R9 N3 w$ y
                GPIOA->MODER = GPIOA_DEFAULT;
" c, X: X# ]8 G6 X' B& Q                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
$ s$ U; F, r" X( Y: M# T$ j                break;" w8 u9 {. e. Q
    }
4 H* l+ u2 w  v& Q    if(scan_row<13)# o3 o/ e/ V( A
        scan_row++;+ s  O) p% Q- e+ W, L$ o4 ^1 r! C
    else- q# ?2 V$ i$ k$ u# y
        scan_row=0;
1 y# w" O- y+ R( i}
  o; W  b$ L+ L# A2 G8 G9 j$ P2 |0 n) _) z+ {8 k) d0 r
/ h2 O, m8 i5 i
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
* w' e* N+ [- p- S" m+ c: P  h8 [- Z: J( P  X% `- k! x
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。+ r. K3 C. C% G% }8 c2 c. ]

, \# Z7 {7 f. g要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
3 G6 Q" P& E2 G: K
* ~9 W" [+ G! W1 y6 A

# W. P. J- P& t' c* [$ E+ iconst char hid_keymap_qwerty[14][8]={/ j) o' F3 @: S8 F
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
5 ^, b4 ~! g6 C, E* |5 A0 D* D    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},) @& W2 K  J" u! Q; g/ z1 e! |
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},2 O! f2 u$ B  p" c% y$ P% B+ ^- E1 ]
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},; Y- D% d/ B. K1 M  _; u
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
9 Y5 o% E" ~7 u) w# x% `" R    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},- S" L3 B/ B0 k# H
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},$ N) f$ J! R; d: F+ Z0 ?
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},' j! A4 O( w7 i- K
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
" r3 t0 D. _. ^: V9 L% |* }    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
8 K! C' A- G9 y& T" v: k7 c    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
3 b$ ^: p- I9 X! }; s; F5 r- j    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},! l& x% q, C2 B" v4 X% u$ @
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},4 v9 B* q1 y7 c
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}5 T, Y/ Z1 K1 l. Q# J, H/ `* {
};3 P1 y4 R* c# V! Z" j% n

0 M. A- ^' r7 ~6 L# W

' J% \; y' z( C* `const char hid_keymap_dvorak[14][8]={7 v8 P2 g( A5 Z2 ?, }9 f) |) f9 Y
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
  s" m2 j  ?% t# j4 k) l! c8 A    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
" Y3 ?% i2 n6 T1 ?    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},! q) i8 s/ ^# v5 L% T2 l
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},/ y4 k6 A' B) Z/ n
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},' q! {$ d% c6 h" |4 {* a) L- p1 \
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
2 E& g4 ^& Y7 m: {- W5 G    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},. {/ M( E7 r. {6 p. z5 Y, S! }
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
2 D1 T1 T6 x: s    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
, g5 A/ g. x9 A6 l    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},+ |, y8 Z6 i% }
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
, Z3 M% V# Y* ^4 c  {( U! ]! j0 l    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},7 a0 I' X# l0 K7 R3 a; ~; H
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
! ^5 ^- n: \- K1 e6 z1 p+ M$ q    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}% h8 G6 g. k; [
};9 O& t; e4 k+ K- ?2 J7 p" }/ \

! C# s+ R9 p1 K+ \' [, q1 J$ q
4 u3 K: T; ^$ O3 p. V上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。! Q. \. T6 T: g$ i- D! g
4 V4 F3 e% p0 j) s: @+ G
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
6 h2 y7 Q* |2 S

6 x- V, K, h' m* X7 @' jvoid update_key_matrix(char row, char col, char onoff)
$ U0 j( J# T! y# \8 p{
4 V1 K/ D4 b% I+ Y3 V+ e    static uint16_t hid_report[4]={0,0,0,0};
5 {; ]  g. ^& R    static char (*hid_keymap)[8]=hid_keymap_dvorak;
, A2 |; I3 k  _, [7 U" k8 h& i8 Q2 b8 P' b) r/ \& U$ u
0 j1 O4 K9 l4 A
    unsigned char key=hid_keymap[row][col];) l: G/ l6 O1 l
    unsigned char *report =(unsigned char *)hid_report;
6 s5 q+ k$ ?+ n- P0 K7 }' u# _    char i;
5 J5 ]2 w; T- }# N) g3 ?  `* V2 B8 c

. k% i1 \. y' d8 z' L4 y0 h    if(key==HK_MODE)
: v% G- P/ V  a7 a7 x  M! H    {+ W: r( }4 z3 V9 H* o& {
        if(!onoff)% z( T, w$ ~& e' p( i
        {
: v" C4 i3 F* Y5 o            if(hid_keymap==hid_keymap_dvorak)8 M7 {# e$ z0 M
            {) b5 L; A8 d; Y
                hid_keymap=hid_keymap_qwerty;
9 B* b( Z/ v/ t" E4 e                GPIOB->BSRR = (1<<2);
$ {% L8 c% |% }* u+ M            }  m+ j0 ~1 O/ H3 f! l+ ?
            else8 l2 v, D+ W' j/ w: T$ E) k) K: X
            {) f' P: x1 p" g1 }
                hid_keymap=hid_keymap_dvorak;/ O3 n9 b: t' k: m9 a) S' ^, {
                GPIOB->BRR = (1<<2);
9 ?% T' i$ Z+ \' A7 {7 N            }
% _  U) V' r( N; _7 |5 P        }
1 S! \% v! f5 z7 C8 G4 X. d        return;
0 A6 c+ f$ O* {3 U    }0 Q+ B. Z/ W, z+ Q

" W1 b* d! a( E, `( r7 q! R

1 M  A* |1 \" B; ?4 Q    if(key>=0x80)   // Alt, Ctrl, Shift) p% X5 p' B6 \; F/ Y+ b1 ]; m
    {
7 q5 P/ d: I8 t6 [: Q6 |( i% [        uint8_t bitset = 1<<(key&7);1 @7 W5 `2 U& ~1 ?  Z  V+ z! N
        if(onoff)   // non-zero is key up, T9 _1 _/ z, [5 z) N
            report[0] &= (~bitset);' i+ F$ ^% e' b( `! |% V+ U
        else
$ m5 i8 J& I) ?! H0 [  e            report[0] |= bitset;# P6 p" d; t5 c  R1 k
    }0 }" k& v  \8 A
    else
$ A6 n2 S$ j8 j  z    {3 _9 _0 {! a2 I! C5 N5 H  c4 Q# O
        if(onoff)   // non-zero is key up  H4 Z6 g  Q- a1 D. c$ u
        {
. W% b$ K+ O- V+ H# U" _! N% z            for(i=2;i<8;i++)
4 K. t1 w3 k4 a            {
' r: C1 N. X* g) w* ~  o                if(report==key)# R1 w' i; ^8 i: `
                {
5 \& T# ?+ Q( q' [( F+ u$ J; c; Z4 v                    report=0;
/ D; k9 k+ v/ N% D8 r) r& Q5 L% c                    break;
5 N, J& W9 s' w4 N4 ]                }, \% r4 S: m& k  r- d
            }
6 l4 s& u" o) X7 |: _! D+ o        }' W3 Y) H; N9 c4 \" `( _  G
        else" y- o+ _& e2 [
        {7 U' |1 y9 n$ E7 X
            for(i=2;i<8;i++)* B+ f8 T1 Z( J$ Z4 ^  s5 n  L
            {% B+ k+ k  @9 Z
                if(report==key)2 j0 x4 k' J( n) X- |/ p1 j
                    break;
; E' v  a. z2 v6 g                if(report==0)- q/ @# P. v8 Q
                {
- J2 F$ Q# }* f' s                    report=key;
2 a4 L( H# p; W# ^+ x" |) R                    break;- f& w, k* e; d4 T3 ]) z$ Z
                }: n+ ]) W3 c( q+ V8 e
            }
" p' |, r4 V. D3 E% S1 x        }
; u0 A: Q( C- p    }3 a5 S6 M  i. m2 y
    for(i=0;i<4;i++)3 L  m- s( H8 p& V
        USB_PMA[192+i]=hid_report;# a4 d  @/ |* S9 j% t& _4 i# Y# I' U! [; w% i
    USB_PMA[5]=8;   //COUNT1_TX% r+ i1 K; e# b8 q
    if(ep1_wait==0)
. U) @: A$ J) o% u: K9 h; U1 R6 {; J    {* L6 i& m6 B: e  _) h+ W# c3 N
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
( i+ `8 e- @# S- m        ep1_wait=1;' s  y  g& \+ T, K
    }9 Z0 J0 S# x5 \) U4 _7 S
}% L3 v8 p+ W& Y( _; i- r4 \" O
  g% X+ n/ G- M: L
' t: J$ E" R( t
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。; c, b& Z2 k$ Q
keyboard.zip (8.7 KB, 下载次数: 6064) * _2 v3 N# E9 N
: E: N) ~3 p9 K3 \2 E* j' ]( `7 K

& }9 |. h8 Q% m2 a, i! B/ a9 H1 N2 h5 e* _; l# Q8 j( h

, b+ s! z! F2 S) b9 A" @
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。1 A. N: Q' g; }% q: L$ J! Z' C  q5 y
回复

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
+ s# {7 T+ g5 S  |6 m+ S不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
) m9 \  S; h* ^刚开始我以为要把打字机改造成电脑键盘* z- j8 W2 C9 P, g2 _5 l* u* n
不过楼主也很厉害!

+ ^9 Z, |3 p7 ~$ s* [% v哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
( x) M1 C) ^: Z- }" b5 X, B
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-12-22 00:18 , Processed in 2.611980 second(s), 28 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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