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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑 5 T; R3 w8 L3 t: A4 U

- r5 t1 r4 Y. u1 [6 |6 j$ J( G0 [http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
  V" Q3 L* k8 @9 `: J这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。
0 f" z; x' |6 W9 y5 k8 f
3 ]) `! d: i5 u  j7 G在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。
9 K; X8 i( h, O. N: E
3 u& K8 J/ ^- ]( C3 k: s5 ?0 [! l
235140i3a36qivqzuvmt5q.jpg.thumb.jpg + r- M8 t6 C' a0 a
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。' E$ N' P$ o2 _" |# b
001734klbyoluenuwz4h4b.png.thumb.jpg
' J0 N# j/ ]$ X$ s; G" J* b! |为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:- E  ]7 m3 v2 a& d7 P$ y4 [
003625r2agx2f5v922cf2f.png.thumb.jpg ! ?. }0 V2 b0 a  ~
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.
+ O3 n7 i" @9 qDvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:

. o+ y4 a3 p9 k! x 005836yvs0wvovwsssgd3o.png.thumb.jpg
# s6 c. S$ k( s7 u) K6 @& WDvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
: p& Z9 E1 i* z, |( J; h
' w6 U/ _, Q; n* N- {7 {7 J$ n  @我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。& ]( e- L8 M3 x, L+ Q3 Y4 w

; J! V  U- }$ _到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
3 p. j7 f% Z! f$ N( V  E7 z* g; M/ @  h1 g
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:

0 H2 f# F" Q& H( T0 I. v$ ? 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg 8 T% F9 h6 q% L6 A
----------------------------------------------------  分割线 ----------------------------------------------------------. ?/ r1 }) ?! l+ }, |
2 u& ^( e- o3 i3 E4 x
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
+ Y8 Z$ b0 r4 g- Y+ n$ h' z( _
020011osionbunl4ui44vi.jpg.thumb.jpg + B5 f7 M( w# }; z. l
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
3 a' N* q2 F- T; u  k5 r 020017j8ycmnv7788bqv52.jpg.thumb.jpg
2 ?7 i8 u* q; M! F( N7 g特写,80C49
0 Y5 u' S4 j0 M0 \, I 021040oujzuvtut6iujtvz.jpg.thumb.jpg
, p# i2 F) l" E0 b$ e0 T/ _LED部分,使用了一片D触发器锁存指示灯状态.
+ a+ A) X, b  y+ J- r# T5 @: v. } 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg " t% p: p6 A, p4 c8 X
暴力破坏,将80C49拆掉- v2 \9 [: R4 d& L4 |9 H" {8 B
021113e48qq98vyohvhzzh.jpg.thumb.jpg
  O1 C4 I$ ^% @; C拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
6 ~8 u2 k7 L' ]5 x2 G 021125nc9az6dj33rlds2r.jpg.thumb.jpg / Q# J) y) Z4 o9 S: E& z* }/ L, `/ G
焊好元件后的板子,准备替换80C49  Q; l0 T" R- a
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
# e$ N. E  d/ P- C- p% ]用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。0 |5 A, T. P* K, z$ Q& u5 [) R
021104shifhnrqbr3o5nlo.jpg.thumb.jpg
1 `$ t9 v5 l6 j3 K这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.0 y4 d, |* }# b
022003ym1p9u4ug40280uu.jpg.thumb.jpg
* n- S( V5 U3 b* Y. d开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。- `8 J  D5 B+ ]! n
023313kt141q9qajtol7ma.jpg.thumb.jpg 5 j% |4 X$ U1 v; E5 t! H# _
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
' n* l6 j3 @) e 023322nt7l5xb3ltttkltt.jpg.thumb.jpg
: }% \- \& v3 F
主键区键帽就位
4 z/ y9 d( |. i1 Y: n 023331hin88e8wkrwzwikx.jpg.thumb.jpg
: X/ u& B7 R, z: @) c: m- a编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
1 U/ A) ?  d2 A% ~ 023336wjzlgopugg1jyy79.jpg.thumb.jpg
& N3 |+ J/ D' u4 z( a最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。
+ z- Q1 n+ E8 @" m6 t. ?; k 023341sffu4j3g2323h6fl.jpg.thumb.jpg
( U/ U7 z( |8 [

, q( x% _' t, Z----------------------------------------------------- 分割线 --------------------------------------------------
7 F( p6 X5 f) x

7 H5 k5 D1 P5 n, t/ Y

. R3 v2 M6 f: S& o5 H2 |- L% }& x/ B6 c) E+ O& h

0 \! W- r. B+ k8 B: F! N3 b* d! y* h" M; E0 ~

4 A# J; m2 k# H" h  m. j- B' Z  ~8 J/ P: a% t# ?

0 L' w" h( }2 P% _) \( w( O' u% V* P! T" y( |8 V

! y0 ]5 P' t% f
) ?% J: E% ^5 H; }7 _: M
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
6 \# L7 O  E7 y5 b- z80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
( Z& [; P$ p6 |7 d+ X) C/ [2 i1 W, Q 104025nzibm2rmiomhyirm.png.thumb.jpg ) {9 t' [. F: }3 N8 c$ ^2 S& L( X0 k5 }; {

1 Y2 W- S. `1 K4 u; z其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
7 ~6 ^- }! y3 l: [! |$ u) { 105004zkrez5houvkkznko.jpg.thumb.jpg
% G2 ^3 _! u# h

3 c5 a' p. V$ B  G# Z- k扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。5 c1 t7 }5 S; F# r+ t  z- ^# `# y
  T; ~3 [6 A) X8 a
这是我设计的电路图:6 m5 i5 ?( k$ P) }+ y7 i
110344ej2z2oo2rflo7oe7.png.thumb.jpg
# K6 O+ s& t4 u) {, |7 Y
) N$ Y! @' k4 K( J& W5 C
PCB Layout:
6 u4 @7 e6 x, z$ D* y 110847jjbjvt34vwt3v5bb.png.thumb.jpg
# f+ \+ T7 X; m, f
! ?- T. l( P* V+ B: q4 F% P- G
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. 9 K$ Y* V/ f7 D4 a( b

/ F0 s! [9 k: @2 l

2 S1 Y0 J9 K% W! m9 I: h& Z9 r
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑
! Q2 Y5 J9 |' c& U/ z+ Q, y9 `( J, T' I2 T! N% K/ a
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。1 s8 Y+ z+ M5 w4 I' C/ d
% n6 T  I& Q' |5 L! [+ [7 s
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:: n  W2 C0 g" n# Z4 J
113818pmrfsb6z0byt6t06.png.thumb.jpg + {' Q9 Q! H9 K: k* \
+ @" O- O9 U8 A' c% x! {
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)) g& o. K% d7 T! v, G" D

0 U6 F: ~: e6 ~5 k5 d( P$ n3 V7 p" uUSB的中断ISR,bare metal哦
' x* E( s- H, e4 ]& I0 o5 \
- _3 g4 ?' C. g0 \+ \) V' {/ r
void USB_IRQHandler(void)1 q) B1 I8 c! ^, i# V7 s: |* Y* m2 R6 J
{8 p# ?3 w+ U1 `4 o4 B7 ^+ S
    if(USB->ISTR & USB_ISTR_CTR)
& u& A" Q7 V5 r1 e, b    {$ c* B3 m: x  X' g2 E  T! Z
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0) o* I6 E# P# o& e, f6 |- ~
        {
3 `9 i' a' ~5 B0 w3 T9 f6 ]" `, F0 S+ G            switch(ep0_state)4 o* a' o% d( K* q% b, b) [, ?) X4 i
            {$ u( q+ ?: b+ \, w% B# q
                case 0: ep0_state |= 0x80;; K1 o2 u9 l+ h8 F; W, {
                        break;- k% E" |* u: ~& h+ X; D' K
                case 1: if(USB->EP0R & USB_EP_CTR_TX)
- @; q; e2 z& P) p                        {
4 S) @) J, q& [/ K/ L' `/ o                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;) {! P2 ^$ j/ B9 l" \
                            ep0_state=3;
8 p  ?, x) J; o9 }                            return;3 \7 |8 I: i1 h' y
                        }  ~" ^- H& ~) |
                        else
* Z# r8 O1 p0 h6 e" l1 j( E6 C' N                            ep0_state=0;
* S2 _0 N4 A' N+ N5 G2 E                        break;. B4 F3 _) v% d$ b
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet2 N* G- {6 `. e3 I. H, |8 \
                            ep0_state |= 0x80;. v3 T7 N  c7 L( m+ k
                        else% [& o9 h, l! |- b
                            ep0_state=0;
& W( ~# i6 e+ Y( W+ b                        break;
; i$ D( l' E) ]' o( D! z5 Z                case 3: ep0_state=0;
" {8 ]/ F1 |" H/ l                        break;
6 f5 w! J0 U5 G% U5 l) w$ k8 \                case 4: ep0_state=0;
9 `# m) Q1 x( R. i) R                        break;- i/ g, g- N- l% E0 B  ?( q
                default:ep0_state=0;
, N" D+ j* G* J0 O                        break;" g- O' S3 w) Y% \2 g) Y. y0 q! r4 E/ u
            }" }& O* i0 N7 n- P
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
+ ?; H" f# _2 V. T" X# W, k            return;( G- `4 ?4 |6 C, s; L
        }
$ v( B5 f4 h& j% ~# o. Z$ q        else    // EP_ID can be 1
2 f! q$ `+ v& ]% A        {
  ~( E+ v+ A& y- {6 p/ E# U  ]            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag; G5 v! B" ^: |4 I7 u; T; v
            ep1_wait=0;
! L* A. K% E  l3 {: h" g: p            return;
2 d$ o' ]8 P' b3 N; T9 x, |% l% \        }
7 |- R; g8 K3 t- x    }
. L5 L+ P" S, B8 b; K9 S# X    if(USB->ISTR & USB_ISTR_PMAOVR)
. u/ I1 z! ~% r& b! _0 B    {
* K2 l) Y2 c9 o        USB->ISTR = ~USB_ISTR_PMAOVR;
) K0 m0 p: Q4 K. H1 S  l) b    }
1 g6 u7 S# U! i6 L    if(USB->ISTR & USB_ISTR_ERR)
' H" p: j7 R: s    {
) C5 P# @9 ~2 H) b, C! E        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
1 \& `0 [2 o7 I9 @6 F5 ~5 V  J    }
5 o5 w+ J& G5 {1 c! l    if(USB->ISTR & USB_ISTR_WKUP)
( T6 P. x& `6 A! e    {
; `% o2 W$ h6 @1 |        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
2 k+ E- R( u& _" {8 L1 P        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear8 c  z3 |+ I  W6 j; h0 u4 c8 k
    }  T9 g. B! D/ e
    if(USB->ISTR & USB_ISTR_SUSP)
8 {7 h9 I7 D% _7 H8 t. a% P# y2 R    {
5 W0 X# U$ ~( ?        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend; Q# |: Y1 i$ x9 P7 A
        USB->CNTR |= USB_CNTR_LPMODE;   // low power
: R2 ]0 a8 j) ]( I* c* x- g% @        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear2 c( r6 t/ m- t, P# U7 r
    }
' O) V* {8 v+ h4 Y7 c9 b    if(USB->ISTR & USB_ISTR_RESET)$ `+ {' k" i; k2 R" N% {0 R
    {
0 b- Y; a$ {/ u% M2 e        USB->BTABLE = 0;    // buffer table at bottom of PMA* ?. y/ Y) T* t
        USB_PMA[0]=128; //ADDR0_TX! ?- ?& ~- u  U; O  }
        USB_PMA[1]=0;   //COUNT0_TX! R& J: b3 h; ^
        USB_PMA[2]=256; //ADDR0_RX- r) z$ D- h; j
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes$ g" k* }! G9 _; w' F1 u; J, i
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;' h; B7 q, L& N0 a; S
        ep0_state=0;
+ l2 @7 E$ F/ x7 F9 [4 o        USB_PMA[4]=384; //ADDR1_TX9 H' }  a, R* i; w
        USB_PMA[5]=0;   //COUNT1_TX
) q0 L  C- s  _        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
' N1 V) Q7 Z4 m        ep1_wait=0;! A, N1 d7 G2 X8 Z4 O; e' v  ]
        USB->DADDR = USB_DADDR_EF;      // enable function" q) @) k) k* J1 w
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear. ~7 w4 r( q1 K# C- S4 p+ {
    }
4 B+ r# n6 n) Z' v' I    if(USB->ISTR & USB_ISTR_SOF)
7 R9 |5 r1 b; j  b    {
5 M+ D5 y9 ]# e/ z5 {/ p, p5 P+ c. S        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear" @" K% I. z% N4 V/ U
    }# l% `' a7 g! r
}5 C# L$ O+ h3 L1 y1 {! @" l* _

* J8 Y" Z/ {+ F0 m3 `: |
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
7 ]& b) U3 L  X! b
: A2 P6 D, ~( r5 w1 ?: y! B" L: `7 h  g& j主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
/ N3 @. R9 S9 F3 f# `  j
  a1 O1 ]& C. S  w
    while(1)( }8 w+ G) O3 [/ ?' ]
    {
  ]% c& Y3 ^  b. a3 q: A        static char row=0;: f) M5 ]+ B, a1 Z3 X( L
        __WFI();
3 N" g3 q. C# k4 e        if(ep0_state & 0x80)    // request data processing8 x3 b6 O9 S. \4 `' J) H
        {
& v% m# M! a, ~# p            if(ep0_state==0x80) // SETUP phase
2 a8 p6 Q/ E+ ?3 s6 O9 @            {5 {7 C) B5 j- T% n) |$ \1 q  U
                if(!setup_packet_service())4 }$ b. P- z; P7 U
                {
$ h& r$ F' K# f                    ep0_state=0;! t: B3 K7 ~" d. H4 z
                    // not supported
/ d+ [+ V' k( T' f2 R                }) e8 L$ d. H7 ^! m3 ]
                // ep0_state should be set to 1 or 2, if processed0 j6 U9 ?1 K9 v
            }: t: Y: ^* t, {# c: J7 I! K
            else    // OUT phase$ d, k+ n7 p2 ?6 R) g
            {
$ y6 I9 D5 t+ O! X$ e6 k+ u                // process data" U# [; T- D2 |! P
                show_LED(*(uint8_t *)(USB_PMA+128));
4 ^/ Z! W6 N& u+ @1 x/ P                ep0_state=4;
2 l! g$ V4 T* Z3 {3 {1 B8 x                USB_PMA[1]=0;       // Zero length DATA0
$ k1 h# E) ?: H% L                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
7 y( O: Z  L# d  a            }
7 {& A: X7 D4 |        }
% X6 E' _( q: ?: R        else
6 P4 c+ s( |( L0 w1 I3 F        {; L6 Z5 X4 E4 H2 Q9 R5 C% {+ R4 ^
            if(usb_address && ep0_state==0)3 r  x0 s( F5 w' I0 D7 Z
            {
# N' w( W. x7 f$ V) T                USB->DADDR = USB_DADDR_EF|usb_address;  ^1 b4 r3 k4 U. G; B& d
                usb_address=0;
1 {3 |' ^9 z4 X$ c            }
3 A7 i/ D* l: `2 K1 O+ L+ L        }5 E  v1 n8 R. }, I6 Z1 A
        if(row!=scan_row)   // new scan line
0 V* v: D$ w$ O* t9 @        {
! Y( @- j2 V. `/ R            if(key_state[row]!=prev_key_state[row])2 o4 D1 C  O4 i/ _
            {3 B0 K: y7 Q( W6 s5 G; ]
                uint8_t test=0x80;
! L& @: ]5 W' J& N- F) R; n6 c. o                uint8_t diff=key_state[row]^prev_key_state[row];
% c4 [, v" g) ?6 G5 t& s4 B                for(i=0;i<8;i++)& z0 x' l$ @: |# }5 U
                {
# |4 H( R) ~/ b2 z& b                    if(diff & test)
6 t0 U; E/ Y8 ^1 b' B                        update_key_matrix(row,i,key_state[row]&test);7 f" k8 r, Y' g9 X
                    test>>=1;' F9 h. G) J8 H1 x
                }5 p! Z/ Y. [0 s7 O/ ~$ K
            }6 i: a/ S4 X2 F. w* X7 H
            row=scan_row;/ ^3 |: v, H7 g3 t0 c
        }
' o  z  y7 n: Q6 B    }- R, w# I( U7 v

. a8 ?6 Y; o' P/ @4 S) q8 y5 p; W& d3 p2 i6 N
EP0的控制传输,把用到的请求处理一下# G; O8 E8 @8 X4 W! @! B
char setup_packet_service(void)
) N6 A& s, v! }& t{
( A- E& i# _; L/ _+ }7 ]% w    if(ep0_std_req->bmRequestType & 0x20)   // class-specific0 Y4 X& K7 l7 H( {3 i( K
    {
& X8 S( ^2 D1 l0 H5 F6 M( L: D        switch(ep0_std_req->bRequest)
* P. f5 r( T  M6 v, x        {
/ X* v" O# u* f4 J4 G6 s! L            case REQ_GET_REPORT: break;
6 @$ o7 \" D$ X) f; E/ ^: j+ w* g- W            case REQ_GET_IDLE:
/ t( {- [8 h1 p6 e* a                USB_PMA[64]=0xfa;   // return 1 byte
( j8 g5 y) U! X3 D3 z                USB_PMA[1]=1;  _/ T# G6 `1 R, Z0 S: ?$ k
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;7 E; |" I- x6 ?
                ep0_state=1;
- s, m$ @4 J4 R* P# p                return 1;
" W9 G# T0 V3 S' q% i9 V7 t                break;
- _% a- y8 o( J$ ^            case REQ_SET_REPORT:. Q5 l! f* b. p6 r' h+ m- S9 |7 ~
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;2 h; n7 ?0 e+ U) [
                ep0_state=2;
' x  G0 w( |& J7 }# m                return 1;& a  E% f3 @. a5 l" m; Y
                break;
  S! p3 l# a& p: R0 \            case REQ_SET_IDLE:6 z% w" B2 j) J
                USB_PMA[1]=0;   // Zero DATA
  I( g, D( P2 l1 K, q3 C1 ?; G6 a                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
; B! e. s7 Y+ X! K! S                ep0_state=4;0 S  O) g- @. Y* i7 u% w
                return 1;
" x5 b! B# a+ U8 v/ y8 v: [                break;3 s$ n7 z- [7 C4 @$ L9 H: G3 ^4 g
        }
% D+ a) a* E) ]4 L: f# o& k! W        return 0;3 E4 S0 d- P% @9 L: K' q
    }
8 P/ d3 H4 L$ p# U! N& V+ h    else    // standard
& F, t7 k) Q! N% a0 v    {  \- n& ]4 P$ G# o0 O+ b9 r
        switch(ep0_std_req->bRequest): v, I5 S# A  W1 s+ e
        {1 {/ `2 g3 f0 A% C/ c
            case REQ_GET_DESCRIPTOR:
3 b' }4 F  G% `' ^0 M. P0 W5 v/ N                return descriptor_service();! g$ B8 J9 p" ]9 L$ L+ A
                break;* Y6 p  v8 Q; P( A5 L# \
            case REQ_SET_ADDRESS:
$ n; @& N" ~7 Q2 w9 X" v                if(ep0_std_req->bmRequestType!=0x00)
& }" ], W0 K% K9 ^4 W                    return 0;' `; ^3 I5 B& m6 p- b
                usb_address=ep0_std_req->wValue;, i' |8 |6 R" {" H5 D* E8 w
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
7 S1 k2 W; [3 o% z# ^                USB_PMA[1]=0;       // Zero length DATA0
+ q# k7 a7 Y1 f                ep0_state=4;    // No Data phase, B& k. k% ?: N4 i! a
                return 1;* r& Z5 _5 ~+ A, a
            case REQ_SET_CONFIGURATION:
- X) Q- T( E% s- i0 A% e. n                if(ep0_std_req->bmRequestType!=0x00); [) @% r$ b! n! R0 G/ F! _9 }
                    return 0;
" t" H. ]. N3 t$ X                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
1 Z& c  T7 }2 N. a; z, u" c7 X                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;( e0 ?2 r- X, Y
                USB_PMA[1]=0;   // Zero DATA
7 r6 \6 x# l' {! H. J4 S/ x6 [                ep0_state=4;    // No DATA phase
2 [: s( _: W. V; T                return 1;
( X& m* f( e- U7 W' c$ b* @            default: return 0;  [" Q3 T- E' d0 j/ k* O& q; [
        }
( `$ V* \# ~2 _1 n9 T+ C; \: {    }- \+ _/ s" k: D4 N# S, h
}
( c! B6 `4 S1 S- v% A  g; J9 u7 ]% [0 U- o4 z! B: |

6 [, o7 ?( g3 s  D0 h, f" |: E1 W2 h6 s* q9 D- P+ Q5 r
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
7 O9 k: A. M) I4 S3 Hchar descriptor_service(void)
. k# |7 Z: c" g$ q{
3 }9 I( _% f7 r2 R  ]) a. ~    switch((ep0_std_req->wValue)>>8)
' W, [% W4 ^# V    {
1 Z, ~$ u( w  p  S* m! g0 K        case DESC_TYPE_DEVICE:
' ~+ I6 {* Y# u# Y            return ep0_preparedata(&DevDesc, sizeof(DevDesc));9 C" k3 {0 a; i, R. J% M% D
            break;
! s/ m" S+ w/ M( d        case DESC_TYPE_CONFIG:
  E0 H4 b$ T( k5 G: H  J            if(sizeof(ConfigDescData)>ep0_std_req->wLength)
7 d3 u  _, N, K' n+ R2 \! }4 M                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
! G6 [2 I  U, _7 v3 Y            else/ W8 _% F/ e' ^* I& A1 V6 x% r; W
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));, y- ^6 @- j2 R5 S
            break;
, U- z9 w4 m9 n0 l        case DESC_TYPE_STRING:! ^- h# l0 ^$ n8 W6 X7 f
            switch(ep0_std_req->wValue &0xff)
5 U$ f5 U4 V% L, {/ H            {( ~2 q1 _* O( a0 n' X( S9 u
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));4 r% `$ X: Z& F8 c+ l* f: a
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
5 g* k: S9 k% g# J; ?+ a                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));* O; e* u0 i/ I! _
                default: return 0;
1 M, p7 s% O/ Y            }  S4 a, T; C  s$ W
            break;
$ Z# T' c% Y# h- Q# z3 y        case REPORT_DESC_TYPE:
4 u  W) `: K; s" o6 _  `            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));1 ^8 M" f' [% M( {" u8 P& F
        default:
9 e. {' P* h8 J- G            return 0;  p- x6 ~( }" D3 \. D: G( D
    }3 e+ y* R! Y0 e6 O) S1 H* A5 o9 P# d
}
! F) U7 ^1 d6 a" j) W下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.1 d: @0 w7 c+ A( F$ f! C" @
void TIM6_DAC_IRQHandler(void)' P4 z+ D: N  w; g& B* L8 X$ j
{
& g7 h4 s) f% Z    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
3 w4 [1 g" b/ F9 d
4 `1 v% {7 w- w$ i8 w
( {) o  ^! P) b& X
    TIM6->SR &= ~TIM_SR_UIF;2 j& C- F; B. _- h! p. d
    prev_key_state[scan_row]=key_state[scan_row];- M% w. S+ f5 q* n
    key_state[scan_row]= *PA_IDR;   // update key states; ]/ C- _% n' B8 ^# t* [1 ^* d/ l3 T
    switch(scan_row)& H& D, K2 P+ }) G2 |2 g
    {
: R! ~) @( ^, s0 x$ ?! C0 i        case 13: // next row PB14' v% @0 u+ j/ @+ A; i
                GPIOC->MODER = GPIOC_DEFAULT;$ z; C- Q5 A# `: I7 }/ |
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
$ r* f) N& g) B  S% Z5 h7 O$ ?                break;6 T9 o7 m$ C" Q" y% L8 A% w' f1 ~7 v5 l
        case  0: // next row PB15
3 `% f  a( Z1 R2 w9 f( ~                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;: g5 @0 P+ W6 y
                break;
+ o' U4 x% g% J1 F        case  1: // next row PB3& Y4 u  l3 V- Y* S, l: W$ ]
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
- J: Z) L2 j% l6 ~                break;
2 E9 S/ F7 k; v7 b7 w        case  2: // next row PB4
2 t% t* n- h! F8 P, G$ x! |                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
' [& s- [# ?! W& K3 W1 S0 b                break;
8 e' s# Z; S& P4 F# l) z7 M        case  3: // next row PB56 a8 h: L0 F$ i/ F; C* Y
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;
) O. B( \) H+ P  ]. z/ A8 y- k                break;2 @& v5 F+ D5 i+ u" C
        case  4: // next row PB6
2 q2 h# E7 r3 I0 ?                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;2 z  s' R$ P) f( T' i
                break;" i1 ^: u$ {% ?" \- W) ?2 o$ T0 n# f# }! H
        case  5: // next row PB7
8 Q3 `  y2 ^- S, d" P                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;2 R% d* Y* n' |2 {8 ?3 S
                break;
' v& ~2 s: ^9 q4 x1 `) _        case  6: // next row PB86 n) j; t1 O7 B
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
9 a- J, v7 I! @5 [                break;) R3 {) u- g4 o
        case  7: // next row PB9
+ L1 G9 s5 d* z7 B& ~: q& O0 D( B                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;
3 R8 B8 D* h+ V& ^5 G, E                break;2 S- Y9 c  T$ P- C
        case  8: // next row PA8
& ]6 w. X% z6 a/ g                GPIOB->MODER = GPIOB_DEFAULT;& Y5 g+ M+ i4 t6 Q
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;
" @7 |) l9 n) N' j                break;
, M" O5 R7 e7 X  g, }" L        case  9: // next row PA9
$ f' x7 Y' q( X5 e  m9 O$ s1 L                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;9 B: k# K* E8 K4 U9 q
                break;
7 f: E& `3 m( Y) O* z0 ?        case 10: // next row PA10
: V2 `- c* k; m* E9 Y! s& w7 K" P                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;0 Y, y7 |# O% d3 p- e" L  T* F
                break;# Z  \" O2 J: g; v& k/ E
        case 11: // next row PA15* ~; o3 g( [3 q. Z' `8 {- o. M
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
8 C3 n8 Z1 O1 t5 u0 y" e4 \( i                break;+ S, p( K5 A+ f' P; ~7 D1 I
        case 12: // next row PC13- G* }7 y2 N3 {" Z9 l
                GPIOA->MODER = GPIOA_DEFAULT;
- h1 N5 v; ^% h6 [# }# ~# g) s6 T                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;( q2 c" }6 Y1 R
                break;7 b  z0 e1 ~% |, ?6 Y) B
    }
4 E" w: S: ~7 V" J" Z; P+ z! s" E# \    if(scan_row<13)
7 {9 x4 k6 w" X1 s  j        scan_row++;
- f. i- r- P1 _. A) b    else2 p: `% Y  p  w
        scan_row=0;
( ~2 s6 ~, ~/ V$ Y, v9 R}
  P. x/ ]: b3 ~, a% G
* z5 F9 U& U  p8 Y6 B$ [: i1 k4 K: z, J$ I
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
: \4 j) ?: O3 q! h* n% b* i6 T# P5 b
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。9 a; N$ E* J" J) F! N8 @
; J( u1 P* N# {2 Y1 X/ p
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:

/ U; ?1 M( Y0 U6 W4 F( K6 K+ J) b5 s. ^) Y) N8 z
  e: _0 _7 p) o5 ^" c4 d( y
const char hid_keymap_qwerty[14][8]={
2 O! p; b: P  S) J$ s    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
/ U: n7 b) y' M1 T, R, ^+ d9 ]7 D    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},9 ]4 Q: m" L; t( [) A1 Y9 Q% K' s
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
6 h5 ^3 j2 k5 s# x' y* ~/ `  R    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
% W' ?1 a6 B% L  b$ K% u    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},. h# ~$ l+ C; q* q& \7 v, ]8 F7 O
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},  A- J- J9 g6 L7 W- X9 ^
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},/ p1 D3 X; r2 i0 K1 }. K6 U
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
3 J- z  I5 ?0 x% N5 V: J    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},( @$ t+ v4 A8 s! O% J0 X  }. c% ?
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
  H* X3 b' N5 }& p8 P    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},! ^4 D0 S" k  u! q
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
- u4 S, ]% j9 _" w$ ^    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},8 C& H( I7 I8 X
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}2 H# V' C1 @/ l" J$ y, ]6 ]- z
};
; s' h- _$ K+ x: i' s
  j2 a" C* {$ p* t& [2 b

. ]3 }0 g; e- H' Aconst char hid_keymap_dvorak[14][8]={
! [0 Z; S$ @; X! d9 t# i    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},2 u* e% W% \. l& m* B: ?4 O3 J
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},/ q( O+ Z% d5 n& ?) c' c& d0 I, E
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
% Z+ ^0 r1 b: U- [9 P, o" t2 h    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
* e/ E8 @# d! n9 m1 k  u0 J    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},* P2 V6 `9 |+ x. n) Q
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
. }1 k3 ~, R. I2 x# _    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},: R" J7 P: f7 M
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},6 I$ @& g/ D  v1 E
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
# B5 \7 P: K* \, A' |    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE}," @# u6 M0 `# D2 S( d$ X: p
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
) }& b! t- \! m! [    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
/ c7 M8 H0 ~& R2 \    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
3 C1 v5 `1 n! [* {! X3 R! R2 O    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
# Q3 t+ M" \7 ^/ H};
( B2 k# z8 D% p: q1 Y" w( @. q3 u
' l! E* b9 f( `. L# F5 q: X, @9 C. e
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。" [, N$ P: g7 H4 n! L( n& ]. z
; i+ a0 _6 B% L4 j( s  Q( w
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

7 g9 V3 W. s# h8 D  W! `3 i0 Y, a3 _7 v4 X2 q
void update_key_matrix(char row, char col, char onoff)
0 l) T# H- R" r5 K{0 z; |( I) d8 I! u9 e7 n" D
    static uint16_t hid_report[4]={0,0,0,0};
9 |" O7 P- X0 T3 P( [3 l+ [7 T    static char (*hid_keymap)[8]=hid_keymap_dvorak;8 d( r% [' _8 o4 i
; e4 s& S5 ]0 ^2 E4 Q; g; ~6 ^

& \8 F4 w6 R" c  |: A- D8 I    unsigned char key=hid_keymap[row][col];
& t% R2 E3 F& J& Y9 v7 |. l  T2 _    unsigned char *report =(unsigned char *)hid_report;. P' Y& H) F7 n& p1 s
    char i;0 i4 }6 Q# s7 k6 h3 f1 A
$ N* j% A" j# @$ j0 S4 t/ J

# s# K0 O- e, H0 U    if(key==HK_MODE)4 C* P: g9 S, w5 V4 g
    {# A( t$ x0 Y6 m" a" K
        if(!onoff)
; ~; Y* Z3 |; f$ U# r/ f; ?        {
/ S* C; a/ u/ Q8 Z            if(hid_keymap==hid_keymap_dvorak)
$ p  F! d( s$ [0 e! @* h/ t8 h7 r8 V            {
$ l6 e$ x5 o9 O                hid_keymap=hid_keymap_qwerty;7 \! G! j& f' O. D8 r( L8 `
                GPIOB->BSRR = (1<<2);
/ V/ ?# r1 A  \! U, |: p8 f1 X' J            }
. j0 |4 y( r9 z. m7 e5 m            else" y1 M9 M4 `$ a; S% D0 U
            {
1 Y' @/ k6 b4 N: o/ w) E, v/ k& @                hid_keymap=hid_keymap_dvorak;. ]$ z0 O4 R( V6 k1 s, D) a7 m
                GPIOB->BRR = (1<<2);4 ^# D. Q# L, i- L& i
            }* ~0 K& k0 g/ f% p$ c: H$ _
        }
9 S0 ^& |" p6 m% g0 [        return;
, Z$ W! F/ s2 B* ~+ ~- M3 S    }: u! p! @9 v% |0 O0 b& r

, d' g. W' E! z, I& V% k7 W3 {; ~2 f# h

4 c- t4 A3 j: X# g    if(key>=0x80)   // Alt, Ctrl, Shift) W. {2 H6 m9 ]8 X  j5 o
    {
0 r2 ^# Z) `; L/ U8 e        uint8_t bitset = 1<<(key&7);
' ^1 u2 T7 I8 c  \) B' h3 l        if(onoff)   // non-zero is key up
4 C0 {+ D+ a, E5 \6 M+ q& b            report[0] &= (~bitset);
4 y9 J; `$ _% W" ?  @) X3 J; G$ n7 j  o        else
2 l) Z' ?1 H: @# Z9 m) N            report[0] |= bitset;
7 E* i7 Q1 W9 e2 h    }6 a/ f: d- c, X! o
    else% p3 G. n3 A- X. r& R; l' a
    {6 D/ W  |! b9 Q  z- ?
        if(onoff)   // non-zero is key up2 h# K: M. F0 C; z9 Y
        {
+ L4 ~" J( L+ _0 i. p/ t- `            for(i=2;i<8;i++)+ }# t& X" T1 N7 X% m$ P, `
            {* Z  D. q5 y7 C! W/ a% ?
                if(report==key)
- \- f" v3 q" ^* F0 T: j                {
- m; i$ ]+ v# u; g& e                    report=0;9 g, ]; Z8 M) j1 W
                    break;$ E( J1 P7 p2 u7 R- T
                }
/ t. {- Y  M. q# E6 T            }
3 }6 e& Y* H4 ~$ j6 e* F  h        }
1 f/ @- u% A4 W1 W) }$ a        else# b. l+ s$ V$ G+ p
        {, v$ N" I  }. K
            for(i=2;i<8;i++)
3 p: I: I! r% X5 [9 f$ C            {' T, q9 t8 m3 H
                if(report==key)3 V1 U) ^$ v3 X( _' j8 a
                    break;
( X' d  l6 ^2 u- v, n                if(report==0)
7 [+ c2 t, ]1 M  k9 G0 N0 A                {( r- E  r; K( X$ |% {) E
                    report=key;
5 A7 z; ~* l" S$ v2 o+ C' C$ h) `                    break;
6 W' i8 o6 y( N1 A/ n% o                }( J: e& @6 b" c1 N! O
            }! `" O7 o# P* _. P! V' U
        }
2 {" V$ J/ G2 N" n7 g& K    }+ |2 `0 j8 W% ]
    for(i=0;i<4;i++), `; k6 T6 b8 `- N5 q# i
        USB_PMA[192+i]=hid_report;
; V  C( ^# R6 z/ M- k. y    USB_PMA[5]=8;   //COUNT1_TX
% j) O( ?  f, x# y7 ]8 \    if(ep1_wait==0)
- }# e) }$ r, D# d    {+ T# I/ o6 t  R! G1 h! p
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;2 ]5 j$ H0 I" M
        ep1_wait=1;  _7 ?* O/ f% m8 ~: S5 B2 V
    }/ m* M: W4 U- L# `
}) ?& b. u1 C* @  ?, C
0 B: n0 T" U4 Q1 p8 |$ }, Y

1 A& o5 [3 T) |9 c完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。& D# Q# e# T% z* Z9 u% [
keyboard.zip (8.7 KB, 下载次数: 6418) " M/ I& v4 r5 K+ |8 N! c

& N# o/ e* l: K& c3 i7 t/ ~  h6 }( h

+ n+ z8 C, ~4 x& t" Y* J' q  N- y' E
0 Z6 o9 q- o8 n0 X: d* `  r
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘! n: h, ~" M& P/ M. e
不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
: B4 _& k0 A9 Z- @刚开始我以为要把打字机改造成电脑键盘
5 a1 W4 o. W3 O$ l  L; C, m不过楼主也很厉害!
& q8 _- a2 a6 @) Z$ r
哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
5 v( K. U2 w4 m& k4 P
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-2-17 01:22 , Processed in 0.175182 second(s), 27 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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