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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑 - A/ U- A+ i) u+ W# i9 z( |9 c

: g( y$ M- @8 m/ u) a& q. Uhttp://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
9 }/ N$ T/ @& \% E, D7 v5 K/ O这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。
8 d/ p- I7 E7 Z% w# ^# v8 L. p, v7 B9 ^) w1 Q) n9 z# G
在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。
& Z0 I$ v+ K- w3 }8 p+ b
& ]* ]; F/ E9 S$ |: w4 Y! S9 {
235140i3a36qivqzuvmt5q.jpg.thumb.jpg
% d$ M- [/ R) s: h/ e/ k& h
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。
/ o# q. {9 L* {" u" m 001734klbyoluenuwz4h4b.png.thumb.jpg
, U$ |- z( j6 V6 ?3 K- O- ?为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:$ R  {/ }- M8 B7 ~6 |0 N
003625r2agx2f5v922cf2f.png.thumb.jpg
5 a: P6 |, `. L- N  |其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.  p/ D% R. ]/ c$ d# `6 F3 F8 G
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
# L8 Y; t: x" F- G
005836yvs0wvovwsssgd3o.png.thumb.jpg
1 G. Q" t$ ?8 j7 h2 lDvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
& H3 `& [- [, x* l- `/ C4 p  [) r: s% J( [
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。5 Z( G" |  x8 N: e" x

% x1 E& |2 t  p& o0 h到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。: W4 x) I1 D  }  N" y3 H7 d' \
3 T2 j7 X& C$ Y% S# X' ?) u1 f
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
  E3 ^: R$ U  T/ P( I$ E2 K
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
9 e5 `' I& V* I$ @----------------------------------------------------  分割线 ----------------------------------------------------------
+ S* C9 d/ N3 A5 z8 m; H) |
. T9 d! W- e) o先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

7 {" `" X% n2 o6 X! [1 y! f! Q 020011osionbunl4ui44vi.jpg.thumb.jpg - l* j6 j' u" D& o  U! k
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
) ^2 [8 w  g! {4 T' Z5 _% o' @& ^ 020017j8ycmnv7788bqv52.jpg.thumb.jpg
0 P9 U. J& l) u, a4 N: k) P7 }特写,80C49
  x" ]) b  _6 }9 i; j 021040oujzuvtut6iujtvz.jpg.thumb.jpg
; f. Y+ L: M+ s5 q, x4 N/ q7 ALED部分,使用了一片D触发器锁存指示灯状态.
. Z! l5 [& h- m* H/ f6 D9 l 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg ' b- r8 |) m8 i$ u& k3 x! Q, F5 v
暴力破坏,将80C49拆掉0 m8 C$ |! c9 o1 h: _
021113e48qq98vyohvhzzh.jpg.thumb.jpg * s. m: M2 Q; @* u
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
8 p0 Y, b3 V* z$ e: U2 ~ 021125nc9az6dj33rlds2r.jpg.thumb.jpg ; ~! c' ]& A; t, Z
焊好元件后的板子,准备替换80C49
$ `5 c6 w/ l, b% n: K 021146zmmpmh4pav4o7g6a.jpg.thumb.jpg . S) E& x! {) ~% E0 Z6 E7 Y
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。
& Z- m) H3 y0 c# u3 v+ w0 K# m+ U 021104shifhnrqbr3o5nlo.jpg.thumb.jpg / k9 l& q7 {% I/ z/ n
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.
. [" \" K0 Y, w1 q; s 022003ym1p9u4ug40280uu.jpg.thumb.jpg 3 F; x2 U- F- d' ]
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。
/ a" ?/ l! O6 Z6 j 023313kt141q9qajtol7ma.jpg.thumb.jpg 0 O1 W+ v. O2 s3 V
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。7 H' a3 c; S+ |2 e$ y' \
023322nt7l5xb3ltttkltt.jpg.thumb.jpg
2 J- }# s5 n, J0 g. ^5 w
主键区键帽就位$ Y, m% d8 }! t( h- f
023331hin88e8wkrwzwikx.jpg.thumb.jpg 5 \0 V  C, `  s. M- C0 e
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
! d8 ~- |, m# [7 |: r. t) r  Y 023336wjzlgopugg1jyy79.jpg.thumb.jpg
( w  N/ y: L& Z+ Z( U最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。) ^# T8 G+ q4 d. ?
023341sffu4j3g2323h6fl.jpg.thumb.jpg
4 v8 @' e; [* O3 }, ?# @$ n" R& {
& M: d7 |* [" c- M/ c# [
----------------------------------------------------- 分割线 --------------------------------------------------
* q: B4 n  G& w. c8 h2 l# x

+ H9 b4 P" u5 U" b0 ~% Z
: P6 b& X- X) h* I

. o) u' C5 r- ~! w
2 b: w  \4 H0 o" s8 r$ q# _1 N6 e9 E" J, d
8 K( v9 g" @  a1 L5 Z
8 I. O' S# g5 S9 b2 Z/ \3 b+ g

, Y6 m3 y. D9 v9 @( N6 Y0 i4 Q( b* W  M9 \# D6 N: E0 I

+ W6 u) |- J' j8 V
, U8 `) d; F6 B- Y% O7 K" ]
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
5 I8 a6 R6 C. L80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
: a  x9 S5 p) v6 b+ l! e2 W2 U 104025nzibm2rmiomhyirm.png.thumb.jpg
9 l9 m  a) {3 o% q, a( F# @
1 p& R) ?. K+ e
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号), ^: b3 m4 V8 z! s. m; S! X3 ]% w2 L
105004zkrez5houvkkznko.jpg.thumb.jpg
/ i; `% N& a+ o
; r$ T7 i; t% U5 |
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。" |6 M% F$ Y5 F7 _$ E
5 E7 l. r" [# H/ Q
这是我设计的电路图:
4 j+ S3 K$ s7 k3 i/ q& b 110344ej2z2oo2rflo7oe7.png.thumb.jpg 9 c! R8 v$ V# S7 z
& U9 c! m. b, C
PCB Layout:
$ Z8 V' i( u3 ?7 K1 M, t! } 110847jjbjvt34vwt3v5bb.png.thumb.jpg 3 `$ l3 Y- @* H/ B
) Y2 l* b4 Y4 i) {2 u1 ~" Q
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. & ?. M# f  I$ S1 Q: b& R+ u

! o5 E- Q9 g7 x4 [7 t. w

: c6 g# F! f& E( ^& \  `# N# D# Q
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 & M! l5 r: B, i; N2 w% t1 A
( y  y9 b% @( H3 Q* z% Z1 m* t( F5 V
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
0 f- s. s. R3 ^9 v, o6 Q. M- N* {. A
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:
6 ~0 O7 U3 _+ Q' W" U0 Z 113818pmrfsb6z0byt6t06.png.thumb.jpg , ]6 y7 y8 [8 `

) y7 X6 \, E: V4 i) [其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛), P) w0 f8 @+ R- l$ ?+ K" x; M

& g: v: k! R! G  |9 Z# q2 iUSB的中断ISR,bare metal哦1 L6 c( P, j* r1 B8 Y

# B! l" M, B& {+ _: T2 q) Wvoid USB_IRQHandler(void)
/ d7 o9 f/ ^. \* [- F/ M{
) s5 w; H/ _# y/ {6 _- B4 v    if(USB->ISTR & USB_ISTR_CTR)$ O+ x' N/ n0 Y% Z1 U7 t
    {
+ ^4 @; g/ A( q% \        if((USB->ISTR & 0x0f)==0)   // EP_ID==00 Y2 L& E+ l( g. L7 m
        {
  ~8 [1 e- a1 y, {2 O9 ?& H% h5 t            switch(ep0_state)
5 x& A% B- @+ v3 h* r; \% I( T4 B/ Z8 l            {
; \9 m5 ]' c. G* u7 r' k                case 0: ep0_state |= 0x80;
+ F5 c2 Q8 A8 a  G- s) Z$ k& ^                        break;
& E# ~" E: I  x                case 1: if(USB->EP0R & USB_EP_CTR_TX)
% Z- [4 y* k# ]7 j' Z0 X; b  p0 c                        {1 M% ]* W3 J) v6 a5 w7 P
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
7 V5 P. S; D) P4 T# ~# x% J                            ep0_state=3;
  N8 h0 P0 V! Z0 N) k( ?3 s                            return;
* f) p# k" ~( V6 x; f& r                        }  y% _5 y; z1 ]8 c0 ~$ K
                        else) Z' Z: [+ [. r
                            ep0_state=0;8 C' k$ k% j  n
                        break;
0 c4 F; t5 D# Q                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet0 V+ Y/ B9 m# }* x. `: b
                            ep0_state |= 0x80;
( X1 Y6 I7 v7 n2 L+ D                        else
# R) v4 p0 f! x' k' r                            ep0_state=0;$ Z' _; `* H0 k+ X# v0 _8 j9 K8 `+ u
                        break;6 f! y# c1 q9 y+ I7 H( r
                case 3: ep0_state=0;  F# S! v$ R& o8 u  R; q! }' O/ o; O
                        break;
4 P! k, {+ L! F& T5 h+ X& ]9 [                case 4: ep0_state=0;/ M6 \# o5 G1 R* ]
                        break;7 \1 G5 J8 F9 a% y- H4 G
                default:ep0_state=0;
. k8 h' U4 W5 m                        break;/ D: i- |9 Z, P( c; ?' h
            }7 M& O5 D9 b3 a' N) G
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
) N3 d9 w' d' @+ V/ G  C" ]  x            return;, V* T' |4 |, J5 }5 b, r( k2 R
        }
& [! Q: o# `* @' \        else    // EP_ID can be 1" S8 [0 `. n2 k6 ~( m
        {
4 B8 {4 r) S* I5 T2 @            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag# f4 z+ E" R! ]$ E( F
            ep1_wait=0;1 j0 N0 Z! P2 `* A
            return;
- l: l8 [  i. W/ y% G2 F        }  B8 e5 Q4 d9 u9 r0 \. y0 u
    }
1 G' Z( V/ J* Q) o    if(USB->ISTR & USB_ISTR_PMAOVR)
/ w4 _# J# g+ f8 t. |4 F    {
, I+ S) R5 u4 m1 p. h        USB->ISTR = ~USB_ISTR_PMAOVR;
4 V: `2 I. U& }3 t; L    }. t9 c0 w7 P) T) h. I7 {
    if(USB->ISTR & USB_ISTR_ERR)3 r# d1 H# p2 @/ T* I0 M4 A
    {
& O2 p  u2 k5 H% R  d/ \        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
0 I4 J4 B0 Q2 G4 [4 F    }4 w' v) [9 e2 g' P
    if(USB->ISTR & USB_ISTR_WKUP)  Z. H$ u0 q0 x# J5 B" j
    {
! T/ m1 b3 a, P3 n        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend6 J$ r4 B, m( d
        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear/ E% u$ ^- m* X9 n1 {
    }
7 ]8 v- s. r+ }3 o    if(USB->ISTR & USB_ISTR_SUSP)
0 \. Q. ^5 l; c, Q    {4 l3 o% U  f6 s9 q* h2 f
        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
3 _  U; {& X7 v0 T( ^8 F        USB->CNTR |= USB_CNTR_LPMODE;   // low power
# T" v" o; v0 Y. Z' r        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear7 U/ Q3 Y. t# r+ `" u0 G) {5 Y
    }
, ^, g" t. g7 i; g    if(USB->ISTR & USB_ISTR_RESET)5 |, d' Z; q6 A. K: y1 [
    {
8 A5 b) K# t) o! j        USB->BTABLE = 0;    // buffer table at bottom of PMA7 d$ W8 Q+ ~. X+ y
        USB_PMA[0]=128; //ADDR0_TX0 s- E" g# X! g8 r% t! l/ ?" S' }
        USB_PMA[1]=0;   //COUNT0_TX- _# B5 ?0 @. u, y# A1 L% K; t
        USB_PMA[2]=256; //ADDR0_RX
3 f# `+ ?$ O; o% }        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
; Y0 N6 {; z6 X& U7 v        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
- p# L. m! S( a; E9 a        ep0_state=0;$ T* h4 c  a4 m. }" m" e3 j7 ~7 T$ v
        USB_PMA[4]=384; //ADDR1_TX( L& c) O+ m+ W; f+ h8 S( A
        USB_PMA[5]=0;   //COUNT1_TX3 K  D' t  F6 `3 s$ }! Q6 r7 a, k2 g
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type7 _1 N* e% D, d/ Y! Q9 {
        ep1_wait=0;
! n8 W8 A' U1 C/ h9 K" X" J7 M# o        USB->DADDR = USB_DADDR_EF;      // enable function* j/ V; m9 w: M- b- O3 r
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
, w# I( `$ M5 `, N    }
- T: k3 w! A+ t& M    if(USB->ISTR & USB_ISTR_SOF)
9 t1 ?+ h6 [- v: }! l6 F$ G  P% i    {
' K- y, _. N  e3 M& L+ j1 ~8 x& L        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear
6 z$ j6 g+ r. M5 f    }9 Z' b  Q" a  f! o$ |$ ~/ y
}
' M4 Q2 O; [+ m# o4 G: @2 ^0 k% k9 I8 Q6 ]& X& \( i9 z
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。$ X! Q0 v' l# c5 T% o
  p) @# A) E/ n3 U: k4 ]+ j
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。! R' e+ K% ^/ n  i, P

$ J& c4 I3 i* k4 v7 [
    while(1)- c9 Y- G% S  G. {
    {
8 I$ }) G0 `5 L4 t4 |        static char row=0;
; `0 d* T$ B! g( B        __WFI();8 {  G) u  }- e+ O6 ^" B. l. ?
        if(ep0_state & 0x80)    // request data processing6 ]* h. p: z: M6 _1 e
        {5 s8 K) V6 ^7 n" P/ v6 `! L7 P. }
            if(ep0_state==0x80) // SETUP phase6 k' E; Z& ^/ V' K: w3 P8 C1 i
            {  D( u5 F4 d2 m% W, g* l
                if(!setup_packet_service())
7 O, u& c! w! N) `9 M                {% v: X. w( ~: M! ]5 r
                    ep0_state=0;% h6 b2 p7 ?! m1 T5 e
                    // not supported
. ~4 H& P- o0 R2 }                }
5 S  {: j9 B1 t. P3 c                // ep0_state should be set to 1 or 2, if processed
5 B* `' X: p/ {0 ^9 U' W# H$ w            }8 G+ Q& C' ^& \! O; M! D
            else    // OUT phase  a' A: f# g( k- l' O# {4 A" ?
            {0 U; ^6 K% M& p: E2 z% E( h
                // process data* T* e* k/ L! ]3 ^& r' R7 G
                show_LED(*(uint8_t *)(USB_PMA+128));0 f% ?3 `* r. X. E. {  J% }" t
                ep0_state=4;
  t0 l/ c* S0 U$ k5 @" u9 F                USB_PMA[1]=0;       // Zero length DATA0
8 B( [- Q0 T" k7 d8 P6 e                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;( L5 F. i3 G. ~6 M8 c% i
            }+ ^4 |( ^3 B+ {
        }0 x7 G* u5 O: S. d+ |
        else5 d/ F: u) t: C& a
        {6 L: H, X$ z4 L9 }' B2 l
            if(usb_address && ep0_state==0)
+ o6 G5 `! Q: V& @) J            {
9 z, r. F, y3 `/ U3 W                USB->DADDR = USB_DADDR_EF|usb_address;+ S# \6 e" L3 R4 d& E: c; Y
                usb_address=0;6 }; z1 s) c$ c5 l3 W  h% n
            }
& D% I4 b2 _4 G  t6 V        }/ Y, \+ D) t: N
        if(row!=scan_row)   // new scan line
" P1 X, V: J  D; ~( L. J( L        {( E: S8 D+ t+ c; o
            if(key_state[row]!=prev_key_state[row])8 Y1 s  {: u3 P. {
            {
& F2 D' \0 t- O/ `3 R) T; K                uint8_t test=0x80;
  Q9 ^- l5 _# l% t9 L                uint8_t diff=key_state[row]^prev_key_state[row];
. h; |0 i3 M# P* \/ a                for(i=0;i<8;i++)1 [6 h; j9 N* G+ n
                {
8 t3 ~$ v: d7 R* N0 J4 Q                    if(diff & test)4 ^2 p9 k, ?' K! n- Y
                        update_key_matrix(row,i,key_state[row]&test);
2 l: E' T; D; s1 G' {' t0 E3 d                    test>>=1;
% x8 i+ g; B2 O  a                }; a7 u* S7 P+ T3 @8 I
            }
) r: \5 U) x9 H1 N, Y3 K2 j            row=scan_row;
9 z- o0 [) Y% w; [        }
. a$ K& r5 \) Z7 p8 @0 T    }) p! G( |. v5 R$ m

. y( L7 {4 ^2 U6 {7 a2 |
3 q6 d* m/ G! ?1 t! R' HEP0的控制传输,把用到的请求处理一下$ @. x8 r- O0 ]6 c* U
char setup_packet_service(void)* S9 z# @% s6 A
{
7 u1 a% J8 ]9 B* c    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
7 q5 Z7 ?: Y, I    {+ D. N  C; s8 O
        switch(ep0_std_req->bRequest)
; l- X: S. L8 k        {- J" |  V9 O# {& j# Y) w
            case REQ_GET_REPORT: break;9 i* y. M( e# _* j
            case REQ_GET_IDLE:
: T* p3 C3 D& C! b, Z  z5 P0 N+ N                USB_PMA[64]=0xfa;   // return 1 byte
3 A% }/ @' |9 `' o9 c                USB_PMA[1]=1;
- ?( b" {6 i( N                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
8 @) u* f; l" Q                ep0_state=1;8 @# H3 D8 g9 N# n/ K
                return 1;
# t9 }$ j3 k7 R- W6 C                break;2 p8 y) C! f: U: ]' V
            case REQ_SET_REPORT:9 V% g$ I7 f1 F( x( E# ^
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
& ~- m+ b: z% ?                ep0_state=2;
, s- N* y! y# @' ~0 A+ u. r8 k                return 1;+ r: r7 m9 ?& o0 u
                break;
/ B- u1 x2 t! L) H! |; r            case REQ_SET_IDLE:6 c6 m5 {2 C/ V" q+ B/ B
                USB_PMA[1]=0;   // Zero DATA0 C- y6 [9 @+ n2 ]1 r  ~; l
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;4 p/ _1 ~3 |( J" B- y
                ep0_state=4;3 x8 \! F. X. H; r4 X# Z. d
                return 1;: I* ]" I! n4 T* q( s+ m% }
                break;. ]3 v4 ~6 x1 G. a
        }
7 H' N6 v; e9 G+ W7 Q; G; q( B% d        return 0;
5 r+ H5 V3 T! ^% H    }) K/ j3 v4 ^2 ~! l$ E: i: e, G
    else    // standard
) X2 x6 F/ a5 n# T  Y" Q    {" Z1 g2 Z  G5 M# a2 ^3 n
        switch(ep0_std_req->bRequest)8 }6 i0 b. x1 k* P& Q
        {% y, t+ \) L: ^. {5 v& M; o
            case REQ_GET_DESCRIPTOR:7 @2 \5 Y! N( S6 c: g  d0 B
                return descriptor_service();
: \9 p7 }1 t9 t% [- D7 s7 i                break;* x& G& ]8 C, {& d: h0 R2 r
            case REQ_SET_ADDRESS:# ]. o2 \4 v; a/ U: q1 `9 z: }7 c
                if(ep0_std_req->bmRequestType!=0x00)3 X; T9 v& R6 `+ H5 M* h
                    return 0;
( F- O0 z, _5 ]" N                usb_address=ep0_std_req->wValue;( X7 i: ~$ s. l( i$ q0 }6 b
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
  ?) Q# c) W7 q9 J% f, Q; B                USB_PMA[1]=0;       // Zero length DATA0  W9 C' t8 G' A, r
                ep0_state=4;    // No Data phase5 H* W  `1 B7 @/ ]$ ]7 M4 T
                return 1;
0 K3 I# |( t+ z2 J            case REQ_SET_CONFIGURATION:
+ _- m0 ]1 k6 l                if(ep0_std_req->bmRequestType!=0x00). b& ?3 ]+ b* }, R
                    return 0;: Z8 p* q0 r( P, i3 r; D
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;) L$ r. \9 F# W9 R8 u# o
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
6 R5 E8 L/ x, Z% i                USB_PMA[1]=0;   // Zero DATA9 P4 P$ }1 R6 m' \2 o. X0 D
                ep0_state=4;    // No DATA phase5 s5 ^" z0 u7 S8 i7 Y, h
                return 1;
8 ^* C+ H% O( d7 P5 E            default: return 0;
( [! H) G! I) \3 o* y" q        }$ Q4 f, O! M1 W
    }
( i4 d- _+ Q6 }. s5 X0 |- f/ f8 ?}
% n5 m: m/ `6 F+ d, O' v. L7 @, S3 C( Q6 Y5 c& D8 l
6 D2 f9 ?; _. o2 q: d& c/ y
4 Z! ^( N9 P( s
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的! H) j  H* @) g! u
char descriptor_service(void)
, D; N7 u, t, J6 \& p+ k8 Q{% o9 @, h; `* T* l; b. b& l6 Q
    switch((ep0_std_req->wValue)>>8)* o" S! R7 h6 u+ G, z5 T
    {
) ^0 J' }6 `' e! y4 V        case DESC_TYPE_DEVICE:, \6 D3 J" ]/ ^- G0 \
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));4 T8 E: `7 J% p! s
            break;( k8 I$ h1 N6 c
        case DESC_TYPE_CONFIG:2 e  u1 A, Y* u! |0 n) O9 y8 ]' z2 d
            if(sizeof(ConfigDescData)>ep0_std_req->wLength)% i& W* G, A: D3 S. j
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);6 A: n5 R& w9 ?7 h! k2 S( j
            else- N! ?3 G( y+ n+ H
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));1 ]4 n# r6 |# d- c& ]
            break;3 w4 s& r; r7 x+ l
        case DESC_TYPE_STRING:+ g6 I9 v7 j/ P: D
            switch(ep0_std_req->wValue &0xff)/ w. {2 p0 }3 O( `3 E, G
            {' p  C. e/ [  e  I
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
3 r* \1 _4 C+ M: l& h$ w! M                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
* p+ D3 B% _% \                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));0 Y8 O) p4 w: H2 S
                default: return 0;
9 r! M( a/ L: k; j- v3 ?$ q* G            }9 c/ y8 K3 p. B9 z) L
            break;
3 Y- O1 C# D0 y5 k6 m% \2 O        case REPORT_DESC_TYPE:
. o+ ?. \7 M) o            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
6 R4 C( M9 k% u        default:+ K2 G8 S6 e( ^) R+ _) N& n
            return 0;
3 D; S% @, o; g' G3 _    }+ O6 `* v0 d/ w; W/ N$ z
}
( L9 F. Z3 U/ f8 S" C下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
' h, K$ ?; H, w# \2 P6 q' g) `% \5 Lvoid TIM6_DAC_IRQHandler(void)' @7 X2 s3 D, l% @5 ^! o
{; c% ]2 F( I8 k. g/ Z
    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);& L( O/ }4 k, d! A1 R* N

  T2 O4 {+ |: X# A- y. C' y

% |; j3 k1 f) B    TIM6->SR &= ~TIM_SR_UIF;
8 o' V2 G' m, \, N0 t: v    prev_key_state[scan_row]=key_state[scan_row];
5 y2 h. q. Q0 W% H6 e9 {  t0 e    key_state[scan_row]= *PA_IDR;   // update key states/ c0 j+ ?/ ~0 N2 O6 y+ R
    switch(scan_row)
) Q0 U6 @; a, X  D& H5 \& }& d; D    {& u2 u+ i3 V3 H) L, M
        case 13: // next row PB145 F! O: g9 ]$ G2 T3 C5 b! g5 y
                GPIOC->MODER = GPIOC_DEFAULT;
7 R# N$ [) J3 {# w  S* j                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
) B( f4 R/ X! E1 H* k/ r' T% I                break;
' {/ \3 z) P$ X        case  0: // next row PB15
( r) L5 Z. R" N8 k3 F                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;
) S$ A* y- H/ Y* }                break;
% |, k7 K" I) f) r: w8 s- V" \        case  1: // next row PB3; c+ _0 U$ M. d3 H% \8 z, C
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;4 q8 ~5 v  |$ y- F; x  v% S* B
                break;5 Q/ n3 N) l" a; B+ t/ E. |8 I+ F
        case  2: // next row PB4
6 V' W/ ]2 o3 h3 l. H7 _. Y+ m7 u                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;& b: N0 @' z* X+ t$ i1 T
                break;' n& p+ ?8 S+ |9 a6 }
        case  3: // next row PB52 w, f9 M8 }" Z$ Z1 N- j
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;
3 k" B4 E9 i4 x. O& U# u                break;
( J1 }& h' S$ }% ^- `# w2 m2 B        case  4: // next row PB6$ x# U3 x  [0 D2 T
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
4 D5 l  X, ?1 \. ?                break;. u" _: y( W3 A1 Q
        case  5: // next row PB7
: q! y- U% r, |5 k7 |& j5 U                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
2 O' r' X6 h6 ~- j2 q) T                break;
% A3 V/ L" e$ ], r" u1 U        case  6: // next row PB82 X4 J6 e; m, u, j
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;* ~9 P% d- `* W" w0 k1 D
                break;: E$ V$ P+ h$ B# M, p) a
        case  7: // next row PB9
* h/ I) |9 {4 y; X3 k                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;
" o" H5 w- e. e8 ?; l" B2 P5 _                break;: b# s  g, U+ [+ d8 }
        case  8: // next row PA8# w# E( n$ t& E9 e5 a2 _3 g
                GPIOB->MODER = GPIOB_DEFAULT;! @' a* V& q0 v3 o- G5 K" c
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;) G0 v0 r4 A) M; J: B& Y4 P! e
                break;
% M7 f6 A# ]& v; A* y5 C0 c        case  9: // next row PA9
  p! L7 L- e2 P* B% u                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;% d1 l! \0 `/ i
                break;
6 [' u. ^4 e4 e/ R3 w        case 10: // next row PA10
$ m' e2 Y  x5 ^                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;8 k- H! T, P+ p) A& j
                break;9 N) d4 s& Z* K; _7 o/ p
        case 11: // next row PA15, L" M5 H1 B9 A% R
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;1 a3 [9 W! M4 q6 ~3 ]6 b
                break;+ _. U8 Q; `$ y) E
        case 12: // next row PC13
' @3 X1 t/ L* i  s- V6 ~                GPIOA->MODER = GPIOA_DEFAULT;% Y& v/ K' _7 g9 F' Y/ }* R$ g# P
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
( x2 R( p# [: P6 H& P                break;: |: j5 t7 q  k- N% w% Z$ C
    }. L3 a% C; k3 {0 D; o( E7 n! e
    if(scan_row<13)
0 g  r% H! _& Y" X        scan_row++;2 c8 `! `+ z  ^# x/ H  p
    else
6 M% D/ S% I" G2 h) \0 t        scan_row=0;8 R, V, o9 Y+ R+ l' l" [. z
}
5 Y, L' `1 U$ u. G. {* f, G6 N8 L' U# p) D

+ S/ O3 a. O. J
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 + c, Q' E2 D% R, l. j- g6 T2 o
3 e+ f- H9 }4 z/ z: L1 q
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
' K, B/ l5 g- d8 C; [/ A5 r! U
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:

. d" p6 i( T- l; g: u! O& k% W" W: U* Q( u: Z

' {, u& q) r, j' a5 [const char hid_keymap_qwerty[14][8]={
) d; g( q' h2 h, R" ]* h' M    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},; @: O4 ]0 ^* x. a
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},/ _4 F! A5 W# a% J  B
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},! w. N+ _9 V- j: S
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
* x& u( t2 D6 [% @: s7 K0 P# b    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
+ d7 v5 X& a% s3 E2 x' M  c- h    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
$ ]- _3 f$ q$ I$ R5 r* j    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},$ q, {6 R* y9 f" d7 @
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
# E5 ]! y1 ], I3 E* w1 w    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
3 P9 y6 @* O1 B0 E4 Z    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
  y( v" ]! s) v0 ~    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},  D9 ^' \. M4 Z2 F
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},$ L- T# e; j2 P) K2 O# P: ]8 V
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
0 R: @( X) ]6 ^  h. {    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}6 B  s2 V, b# t" E) s% G) R
};- |, p4 V1 j' d2 b$ d

* y9 x$ h7 f* e! V. E
; P0 \" a% o9 T4 [
const char hid_keymap_dvorak[14][8]={
6 m. b; x" [! J    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
& I$ {, z6 c+ e7 U& ]5 g" |    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
/ K7 d& O% Z$ l1 h3 S% p    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},1 _- e5 O2 k8 F0 y: ]: V
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
( p* N4 a) N; P6 p9 g/ n$ T3 y    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},: F8 P4 t# S  c( C9 _+ Y# x' y
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},2 A+ V8 S4 a1 E+ l
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
5 b2 O; b' P& g" O    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},+ L: ^9 Y& v) b% c% b9 E  {
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},  ]3 G' f7 D; ?) `! e6 l8 v& \6 N
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},$ v- p$ Z( t; u
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},* T: b# y7 w: u1 X4 i% g& x
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
; k  b# M% M4 Y' O2 j/ ]    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},, G2 q) x& C1 g" _
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
! |. ]9 q& `: X- q9 _};
  G% o' d# R4 N& _4 r7 o+ ^7 [5 z! }1 O, {' b6 B/ C9 V

  V+ G3 Y) ]) Z- r/ L上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
/ K6 i( E4 C3 M7 K# a8 V% |( ]
$ v3 ?6 y+ V6 u  L) N3 v0 hHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
8 A! o! c- ]- ^# F. q3 g, t

# C: [2 m3 M& b1 c9 _void update_key_matrix(char row, char col, char onoff)
' [$ n5 Z( n  ?* Q; G  N{
4 }7 G+ q/ Q6 q  J+ h+ w6 Q( m    static uint16_t hid_report[4]={0,0,0,0};
+ i( [. a+ f0 A. b" V; M( S* C! \6 p- k    static char (*hid_keymap)[8]=hid_keymap_dvorak;
- Z! q" Z  G8 {* F4 Z3 u; C5 s' p* ?# X. g( M
/ w' m" ]) l' \
    unsigned char key=hid_keymap[row][col];. \2 ^" S+ ]$ V# }+ J
    unsigned char *report =(unsigned char *)hid_report;
" D. w, ?; y: m% [9 P3 K    char i;
2 i% n7 M, Z5 c5 U
$ ^$ T! s: P! c8 u( x+ `

4 S- V. H+ d- e! B# b    if(key==HK_MODE)
8 F# `$ U0 a4 B" U/ I  v1 ~# s    {! w" f% Q5 O, _3 g
        if(!onoff)" W: K' f  @0 R$ D: u
        {
4 s) D" H/ j& B1 G$ c            if(hid_keymap==hid_keymap_dvorak)1 I" r2 m* Q* K: n& V" p( k) n
            {# ~; l* |- G  j4 }' [7 d: U$ w
                hid_keymap=hid_keymap_qwerty;  g7 u$ q. |0 o, D4 h
                GPIOB->BSRR = (1<<2);
; t' O" L' @! `% b            }+ s* N5 B& W  Q# f5 }
            else/ C% F2 C2 `! |
            {+ Y  _# g7 r8 L" K" e1 v
                hid_keymap=hid_keymap_dvorak;0 g4 S  G6 s6 C/ L- v# g
                GPIOB->BRR = (1<<2);
) C% a5 b8 O( `* N: [! N% h( K            }# s& _* W9 e0 c, K1 s- [# i
        }
8 O9 a8 q2 `8 M8 {/ O3 d        return;
- s1 ]& Y4 M1 X# p    }! q5 ]0 U' i% H3 A$ o% a6 G
& J9 I: A' v/ F

4 t4 p( O1 v* I, A8 x+ I3 M    if(key>=0x80)   // Alt, Ctrl, Shift
1 s! u# P/ |# b9 A$ k    {, @# }3 U9 i3 z4 i3 [7 k0 i# P
        uint8_t bitset = 1<<(key&7);
! O  W, D( N5 U  u        if(onoff)   // non-zero is key up
7 Q4 T( v' o3 \1 W5 _% Q            report[0] &= (~bitset);
' t" O7 P. U4 o4 y        else: R, U& e9 U6 b0 O8 T
            report[0] |= bitset;# J/ a" m8 Q8 Q2 ?0 T- w
    }
* F2 [5 O- d3 W) k4 l    else
4 W' G- v  W0 a: l- f    {. i4 X7 Q+ ]& S; N
        if(onoff)   // non-zero is key up& U! ^3 K: S4 s
        {" e+ `0 A6 ~/ X( U" O( F- X: }
            for(i=2;i<8;i++)  ^; A* s5 _% L' N
            {
; z* A& q. F& ?9 k- _+ W+ d& X! X                if(report==key)' Y& b/ E0 U  n9 B9 h* }
                {
  h: ~0 o) j8 t; z) y6 J; z0 j2 B" Z: d                    report=0;
, v  g/ q2 y7 {1 E8 G9 P5 H                    break;3 L" z$ d* ^  Q; u
                }
) A# w% }' p) J            }
$ ^1 \' W+ `- z* T# r        }
9 ?5 O% F* |- W. J8 i( e$ V, W; K        else/ q2 }0 j2 a: p* q. y
        {4 \) A4 j1 q$ u+ v5 d$ U# b
            for(i=2;i<8;i++)
4 I8 A& A1 h" v' D            {
  h/ e8 _/ A2 F- B                if(report==key)  D1 F; p  P/ I2 @7 z7 g
                    break;
) c' e1 Y2 @3 d! [  [+ u+ ]5 p                if(report==0)
/ D8 Y$ p. r  `) X+ W& R7 ~                {
& b* E* _/ l9 m) U9 a$ f3 p  X                    report=key;! G  B* q! O: |: r; f, c% t: E9 x
                    break;
% S; s; I1 {( d1 e                }* V. I0 j" P- X" h+ M4 W1 E
            }
5 G& y0 ?: ^7 b$ B        }  x7 \* O% U/ C2 b; U! @/ Z
    }4 J; {0 g0 K: C5 H' p8 [! }, X' h
    for(i=0;i<4;i++)1 F1 J$ e/ s# k  Q$ ~# P8 v5 C5 [
        USB_PMA[192+i]=hid_report;0 N; X0 M/ t1 ?6 h& I$ F8 o
    USB_PMA[5]=8;   //COUNT1_TX
: w# _) |" R2 ?) ?    if(ep1_wait==0)
; N3 g% h/ H% E& z# S% i# Z    {- b+ e4 c4 }" j8 _
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
" ]8 u0 o" {  d: w) t9 V        ep1_wait=1;
/ b/ O7 u1 d+ A& y* s' ?# q: Q$ ~* l    }& @9 z1 z& c9 T$ r
}8 o+ u" h0 ]( Z# d! n, X4 W

( X+ x: q! b# b" o! X
+ t8 [/ U* I, ~* w8 |& k完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。3 ]; b" y- J( g7 m
keyboard.zip (8.7 KB, 下载次数: 6043) 0 i, C7 S+ l1 F1 X4 _
' _" N" x& T+ s! O! `

8 o3 W  y- q. K! c* W; v$ m& v, j/ ]: f

4 ^' {* f2 k" Z
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
* @- q# \$ b/ ^. |不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:486 B  A4 O% m5 d
刚开始我以为要把打字机改造成电脑键盘
# F4 N$ _7 C: ]不过楼主也很厉害!

) `+ I! b- R2 B0 L: z3 G+ R  M6 I哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
$ d, l9 G+ \" o( {" Z
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-10-18 12:47 , Processed in 0.215397 second(s), 28 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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