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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

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

0 O8 t- D2 `, A& M; G6 I5 m4 L8 i

- R  j1 \' ~' X* {  Y 235140i3a36qivqzuvmt5q.jpg.thumb.jpg
# d1 g5 A/ x$ r* a& P6 a  m8 \
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。
- r$ I& ~) r& z: {) `3 q! ~ 001734klbyoluenuwz4h4b.png.thumb.jpg   E/ n5 q1 |+ I6 b7 t
为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:" V, k  W: D4 v; C. r% u
003625r2agx2f5v922cf2f.png.thumb.jpg + C" L: R  P; i. t6 n$ D2 L
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.2 y8 r- c0 D: v) {+ b1 c% E8 j& M
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:

& w1 D: K$ q0 A3 l: } 005836yvs0wvovwsssgd3o.png.thumb.jpg 6 V6 y' n7 Z6 K' e
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
1 F, p  I# k- ^+ I
* D) Z8 o  m' _. N  W我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
( y* E0 A) W: X. s# _) x8 @1 t
6 X! e# M; A# Y# X, G/ m到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。* F: N  E% `: s9 c) x
9 @$ @& s& |6 L+ s! U. m. r, N
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
7 }( N. X" y, C5 u& _! Q: Q9 {
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
/ Z* s; ~9 s& L# J2 Y$ I----------------------------------------------------  分割线 ----------------------------------------------------------
9 K' w/ @2 l, h, r3 P9 A! u; R9 o
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
* o$ w) D' q7 q  O* O9 Y
020011osionbunl4ui44vi.jpg.thumb.jpg
& C. E& {5 z( v3 d5 |3 d轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
/ z9 Y, N. t) K0 X 020017j8ycmnv7788bqv52.jpg.thumb.jpg
& M* }' ~$ f8 w+ U, h6 d6 S特写,80C49
7 w1 p9 Y: \" P+ {# Q6 m1 W# M, l 021040oujzuvtut6iujtvz.jpg.thumb.jpg
; n3 P. R. T, \* `; fLED部分,使用了一片D触发器锁存指示灯状态.
0 W9 a& X/ n# b# m1 A! m 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
9 \1 e& b  V8 W2 U, _暴力破坏,将80C49拆掉# s  i8 s  ]6 Z
021113e48qq98vyohvhzzh.jpg.thumb.jpg
% ~' _1 h. f9 C( U1 Z拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。9 t: v1 I% C& x3 e
021125nc9az6dj33rlds2r.jpg.thumb.jpg   Y2 o1 {3 C4 d, _) D+ K- V; n: _$ z+ x: O
焊好元件后的板子,准备替换80C49
& C5 g3 O' n  d, v( t* ^  e 021146zmmpmh4pav4o7g6a.jpg.thumb.jpg % S4 Z6 n- x' Q. u8 W2 Y. f
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。0 T3 d% Y* r+ U! B5 P: F$ c% i
021104shifhnrqbr3o5nlo.jpg.thumb.jpg 9 B% W5 u& \' ]
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.# X' O; q2 I- J7 @! E) L& c# N5 [( N
022003ym1p9u4ug40280uu.jpg.thumb.jpg
* [' @9 z6 l/ ~; B8 [# J+ k' `0 n开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。
: t! N9 w4 t2 U7 G1 A9 P 023313kt141q9qajtol7ma.jpg.thumb.jpg
* P% I/ ^2 s1 o; _! _$ @; b: N我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
" v( B, Z7 \8 m7 b 023322nt7l5xb3ltttkltt.jpg.thumb.jpg : F, G: q; i) E: G; S. u
主键区键帽就位% Z% y% @/ {( @" I
023331hin88e8wkrwzwikx.jpg.thumb.jpg ( y* C' |3 F) r" d5 e, {1 C
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
' ~' {! s7 B" o1 L! S; g% D' {. y) u 023336wjzlgopugg1jyy79.jpg.thumb.jpg
3 J0 o# I7 G! i9 q( y5 h: U! l最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。" h) u, `  b: U; Y& E$ q+ K  ~
023341sffu4j3g2323h6fl.jpg.thumb.jpg   W6 ?1 g4 B& p6 z0 C/ n

0 T* W! _6 {4 a----------------------------------------------------- 分割线 --------------------------------------------------
  ^7 ~. _2 T9 L
/ |, h( B) n5 ^  R" C$ k

% g- [2 U) d* Y
) R) e+ i2 r+ e% k% z. p5 e
0 x8 V! z/ ^6 F2 y& Z% i. `+ q6 L8 U$ p3 @/ A; x
! Y( B0 N; Y6 Q# H

( Y+ ]! B* b% P) W9 O9 O; {% F5 {: |. G, j

5 d/ E& |% m1 x. ], U2 ]- g
$ L5 t. A& ^/ I1 n5 F- J* [; Q
0 `$ I3 g/ {; f
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。9 t6 Z- E0 F+ |2 {, [3 B1 T% K
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
8 ]/ t# z! d' O* @) ~6 `5 G/ a 104025nzibm2rmiomhyirm.png.thumb.jpg % v5 r9 F6 S- q9 e8 j; R7 m3 L
* K1 {3 h5 v2 s# d% T$ q. g1 b/ j. ^  X9 b
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)' h: M+ q- D4 v
105004zkrez5houvkkznko.jpg.thumb.jpg
9 y$ F) w! j$ [- @

: @- c; h2 N5 }$ t+ }扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。0 b& S* H$ K. s- [

8 x" @, w  [! p, L% n: L, y( I这是我设计的电路图:
8 j% J/ K9 r/ h4 A/ S 110344ej2z2oo2rflo7oe7.png.thumb.jpg * X! C: \; E# ]

( {5 S9 R; n* [+ ?; k3 ?8 hPCB Layout:
# X) f& s! B  M& [. s) n3 |/ E 110847jjbjvt34vwt3v5bb.png.thumb.jpg - D; c: t! g8 i) |; Y; P; g

: g( _; o0 ~6 P不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. ; O  C1 r' G4 H: j

  D+ j0 W2 `6 @! L8 h7 Q

. i. j' F$ V5 g* n4 k8 Y
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 $ x2 i' L: k; U% r) f2 E" H6 \
! R' c* a1 E1 w" U5 D% [
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
( |% b# l/ {* \9 z9 R1 d/ i* i) e, |) g8 \# F5 P) ~0 T
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:( e( U) E, j' u& ?# e0 s7 Q8 h
113818pmrfsb6z0byt6t06.png.thumb.jpg & l/ u2 p; ?- J9 ^. R% i0 R3 S
0 a. M0 p- q4 G8 n" z  W/ r$ i) G
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
# O3 y) Y# C( m: a" ~  H6 G, N. y' B$ c8 }* _8 @4 l
USB的中断ISR,bare metal哦+ c( ^2 u# ^9 R9 T& |
0 ]* J* n* K2 F5 N1 X
void USB_IRQHandler(void)6 R  K5 \2 \% X( b) S1 g* C. i
{9 W- q0 R) G. w% e% p
    if(USB->ISTR & USB_ISTR_CTR); P$ K; H+ M& c& g$ D0 R' [$ x
    {
3 L" K* l: K7 w4 ]8 ?; m        if((USB->ISTR & 0x0f)==0)   // EP_ID==00 @) m+ b/ X$ A9 Z$ P
        {  ~1 x, {6 e; R. i: v" U
            switch(ep0_state)
' Q) M6 p3 X8 N& d            {% k0 y  p# @! [  `2 Y
                case 0: ep0_state |= 0x80;; b% l. P9 u/ ?; J
                        break;  L1 ^* e/ R& P( s
                case 1: if(USB->EP0R & USB_EP_CTR_TX)
4 u5 Y0 T4 j6 e) R" {% _3 a                        {
" h" r/ M* n3 `1 V) m                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
7 e3 a% s. P- _" ^+ X* R% T                            ep0_state=3;6 X. x! ^2 g6 Z  V
                            return;
$ J, M& c0 g- ~* U/ f                        }
$ P/ S1 k. g6 r7 N( U4 \: q; [+ _3 g% [; A                        else
9 ^% k( W  }/ b7 y4 [/ c                            ep0_state=0;- A2 u* v! D. p( }" |9 |2 O
                        break;
" X& [  j7 Q* c0 n# |1 L- |8 B7 L                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet4 V) C" ?) \* Y! \. {5 h0 E8 Z
                            ep0_state |= 0x80;! z' K" b5 O! H: n& i1 b) _
                        else
: l, H* X( |" s4 I                            ep0_state=0;
+ t" ^8 U% b8 \2 K8 ~: y, b, b8 g                        break;7 z9 |/ @5 b6 U' _  V9 K3 i. L
                case 3: ep0_state=0;* t$ ^0 m0 h- r" k% K
                        break;6 A0 k! s! D$ b0 x" F! K
                case 4: ep0_state=0;
" t4 h  J6 g3 I$ A                        break;/ ~! e+ n% ^4 |- t2 W
                default:ep0_state=0;
5 x1 ^5 K2 h: q+ m                        break;1 r6 N1 D9 n! `
            }# X, k8 V) Z. \1 ?* `6 T0 H
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag/ d% V1 w8 l! c( \) x3 P
            return;- I' ^' S9 p( z" I
        }! b7 n0 B" S: s, b# n- e$ B
        else    // EP_ID can be 1
+ O8 H$ K  H* `# X# B4 S        {7 A& i4 {/ C3 B, I& Z+ p& s! d
            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
1 T4 Z! V$ {- o" l: L. B& C2 ~            ep1_wait=0;' O, X/ ]: v4 E' j  C
            return;
$ W) ]* o/ Z  s' L% L+ U8 S        }
# [3 B5 D" I( E. I. v$ W- l    }$ v1 V& q( g* m) @3 ]; @. Q
    if(USB->ISTR & USB_ISTR_PMAOVR)
% }4 e3 L0 q' T. y  p+ S( I* o    {
9 b: ]  n7 Y- \6 Q9 k- i3 Z7 n        USB->ISTR = ~USB_ISTR_PMAOVR;
7 `8 t2 j6 v$ E7 i. N    }* B* h7 B. k8 ?! r' \4 ~
    if(USB->ISTR & USB_ISTR_ERR). H7 j2 t8 U6 Y# E6 w; m) H
    {1 V( h* }& m5 m5 l% e
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
  F- [; L2 Z# ~( e% k6 R6 K0 n    }
$ O% v# R. }/ j* c1 v    if(USB->ISTR & USB_ISTR_WKUP)
9 [, A. w6 Y) l. V% ^/ \    {" U  d  ^8 z! D8 g9 R2 _% s
        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
5 O& v# j9 @$ Z8 k+ ?        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
6 f4 b/ h2 A+ Q6 V  R" f8 J# ]* b    }
1 H0 N6 v8 p& c% s6 w5 Z- G    if(USB->ISTR & USB_ISTR_SUSP)
, D8 B) ^+ G+ L) S6 O9 q) d    {
) V. {. V! Z4 V; x6 A! p9 q        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend4 x! y, J  L1 X9 S: X
        USB->CNTR |= USB_CNTR_LPMODE;   // low power0 W0 L  d+ g# W& \, k! F  X$ q# u% R
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
# c& Z' }1 f+ Q9 M' J3 p8 x7 ~, a    }
2 K4 }3 b1 D! _    if(USB->ISTR & USB_ISTR_RESET)- z1 E, V6 }: H) _; l( P
    {% h! r/ z) r, j- f
        USB->BTABLE = 0;    // buffer table at bottom of PMA
! a) A( K( M* M! J* S        USB_PMA[0]=128; //ADDR0_TX- `0 G9 g7 [7 _' W' r& q5 m% V/ A
        USB_PMA[1]=0;   //COUNT0_TX& c* z9 n3 G+ V. B
        USB_PMA[2]=256; //ADDR0_RX; Z; }6 w% j' S. b7 w
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
/ h! `9 \. n3 y        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;4 i6 w  ]' k' j/ ~% V% }
        ep0_state=0;! p. Q0 \% {' Y. @# t0 p/ j
        USB_PMA[4]=384; //ADDR1_TX
5 J' k' h( b1 x3 b        USB_PMA[5]=0;   //COUNT1_TX0 q8 t3 {  }. T+ }; b; d8 Q& t
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
  I# {* }3 F3 v- Z! b        ep1_wait=0;3 @6 @0 B( C, X) P% ]
        USB->DADDR = USB_DADDR_EF;      // enable function
0 P" r2 u1 E- ~- |: Z: o5 I        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear- E) _+ [4 v2 C- F, N
    }2 Q" l& X$ h) F. ]- v
    if(USB->ISTR & USB_ISTR_SOF)
9 L; s5 n* Q3 O9 T; g+ }$ X    {  c/ n2 ?0 h6 C
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear
9 u) z9 q5 H# [    }( o& L& ]& v9 U* b2 I& l% H
}
8 Y9 U+ d& B5 U  X/ b' j5 S2 H/ l( B; h0 g
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。$ D" H! t; n( b) r
. P  n9 x  Q' d: ^9 Y
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
) q* o  X" ~* w8 e+ O' ]8 n1 o' \7 P0 Y6 s, {) z# T
    while(1)8 S) D/ D' P) o" z5 z
    {- j3 f! H7 z+ x# @
        static char row=0;
3 Q& X/ j6 R) F1 e$ k9 @        __WFI();
9 k' X; O; R: T1 G4 U+ a6 R        if(ep0_state & 0x80)    // request data processing
* e# V& x4 ]& n& w        {
/ O7 O0 ^6 H! T            if(ep0_state==0x80) // SETUP phase" i( s9 R/ F7 h/ O& G
            {0 H7 h+ G/ M' e3 G7 R0 z1 a6 q
                if(!setup_packet_service())( y& z4 |+ g+ U( `( p; t
                {
5 [, [# ~# `, z5 f" N/ I5 H5 W                    ep0_state=0;5 G3 w" c! ?0 @$ H4 B$ W5 A
                    // not supported
1 |* w1 I  l7 c' k                }
4 g: Q; J! t% j& p: I4 _) J: o                // ep0_state should be set to 1 or 2, if processed
0 T# h1 j8 w- [: f8 |            }
: w# B* o# p+ {- ?+ c( @* N            else    // OUT phase
' N- d( n  q6 }/ ]/ p2 c- ^5 h6 k            {
* U5 F5 ~- F0 `3 J                // process data7 U- {3 y! m, ?& n/ e" n  i$ V8 f
                show_LED(*(uint8_t *)(USB_PMA+128));8 {! |& F; ?0 I9 P7 L0 g
                ep0_state=4;
. ]$ s/ m8 s: j$ P! V  e                USB_PMA[1]=0;       // Zero length DATA0
7 ]( R0 s" D! d% g2 o1 {4 A8 v                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
7 O/ V6 B" ]4 E6 X/ f( I: O7 }            }6 V( y. x; I, M9 z8 z  B4 J
        }" X4 s' }' T" A/ F+ f' }
        else2 X: l& f5 d/ ~$ s9 t3 E
        {$ X3 V7 K( {! N3 J8 K
            if(usb_address && ep0_state==0)
- T5 ~" Z) f! @" e; c0 ?            {
/ _3 I, [4 z! p$ e5 W% `8 a                USB->DADDR = USB_DADDR_EF|usb_address;
3 d# v. C9 G9 H3 g' p                usb_address=0;
/ j9 f6 e) j; e$ F            }
7 D# n; K2 b3 h: L2 E        }
$ |* M* P3 ^/ S4 ~$ F2 [3 ^* P        if(row!=scan_row)   // new scan line" S% c; X$ ~% t% _8 Y8 a7 Y
        {
' z3 b* e6 W! v            if(key_state[row]!=prev_key_state[row])
& G1 F% ?, x4 u- K            {9 q, p( R+ k/ E' L! |4 a$ }& {
                uint8_t test=0x80;6 _; Q  m) K/ E" E( Z3 \
                uint8_t diff=key_state[row]^prev_key_state[row];
$ s, z) N8 d0 ~1 N$ ?  G                for(i=0;i<8;i++): S3 u* ]8 K! L4 t# c
                {2 x) A9 F3 R& b: L$ |% Q# Y- K
                    if(diff & test)
* ?( a# O2 ~0 E; r6 H                        update_key_matrix(row,i,key_state[row]&test);
) v) U, Q' F, |% }: C2 N" C                    test>>=1;' k$ D' {0 c5 c4 I
                }
, L5 Q( _8 l( s5 q7 @; n1 D; T            }. p6 c* h8 z( i& U& f  b
            row=scan_row;
' L1 O8 F- E2 F0 ]3 m        }4 i2 C% K* @1 C  Z' p
    }
" F4 a4 }# v, ?% F2 T  a% G& {3 w$ A
  l+ N. b: ^5 }. B5 f& I
EP0的控制传输,把用到的请求处理一下
( {) H4 v  o/ V/ {7 H, R( e. wchar setup_packet_service(void)# \" J1 v& P9 ~
{* ]+ w# u. o/ S  D. z
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
. N5 V# n, y  h+ x3 B& g5 Y' p2 q    {
1 l1 t  r9 r, g- i+ w- ]        switch(ep0_std_req->bRequest)
) M* N8 K( q; Q, `        {0 ?2 F$ [0 r& ?) i3 s( ?  Z: W
            case REQ_GET_REPORT: break;
. P& k4 ^4 w; k- u, P4 W            case REQ_GET_IDLE:
' I, J% e  \- a                USB_PMA[64]=0xfa;   // return 1 byte
9 c. @" ^1 t# k/ T3 y8 X, {( R/ w                USB_PMA[1]=1;
( H$ Z6 ^  A! Z" y! U1 t9 D" ~$ ?                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
2 }2 R9 |' R4 a/ J6 v                ep0_state=1;
) k4 y$ H3 b9 L6 C7 b# h( H                return 1;: R. K: p, M( ?- _% t9 N) B
                break;
2 [8 r1 ]4 H# k2 {  h8 M& _% F            case REQ_SET_REPORT:" U1 `5 i7 D" D7 d
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
! P5 F) c9 D3 x& D0 Q# _7 B                ep0_state=2;# q! v2 r# ?1 v6 i! C, R
                return 1;
' o- b: \& G' S, v3 n0 E' T0 _                break;
! e+ V+ {' `/ e; e* I' o! D            case REQ_SET_IDLE:! b3 G' s- C' J8 S: A% C8 N2 A
                USB_PMA[1]=0;   // Zero DATA: D1 e" [- a; v
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;: W0 M2 g3 A1 w& x. [
                ep0_state=4;0 ^' @1 Z9 Y5 U$ A7 G2 D& Q
                return 1;
3 P" T' W- ~, v; q  s, R% k/ Z                break;+ I% a) H5 I8 i7 m$ t; Z
        }
0 Q1 g4 l$ e2 v        return 0;
  i; s* Z3 ?# a' Y" q  e    }8 ?* D0 i0 E: ~, U: e
    else    // standard
3 f' J5 b/ S% u    {( f$ ]4 `$ `+ e, o4 N
        switch(ep0_std_req->bRequest)
- [( J# V0 q+ o) h6 B3 u4 T3 y. P% z        {
: O8 P+ q2 P" f1 R            case REQ_GET_DESCRIPTOR:8 }8 ?- Q- ]" j" b
                return descriptor_service();! ~5 u: R) ~+ {3 x+ ^
                break;3 B' B0 H) y3 r( q- z# m9 ?
            case REQ_SET_ADDRESS:, @8 [8 M# m& s+ i9 R0 l
                if(ep0_std_req->bmRequestType!=0x00)
; T7 x$ p) H4 s  b4 g                    return 0;" _9 P' E) e8 Z9 }& S' D" i
                usb_address=ep0_std_req->wValue;5 }, P$ z  Q# f4 x
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
' D+ g7 z" u. u9 ^# R$ ^                USB_PMA[1]=0;       // Zero length DATA0
: r3 L, s3 s9 Y                ep0_state=4;    // No Data phase
1 H4 _4 p4 [% }6 x3 l) c                return 1;
1 b8 ?. K! w4 M' C: C* I/ f            case REQ_SET_CONFIGURATION:
3 j' z! }; j8 Q, N7 F                if(ep0_std_req->bmRequestType!=0x00)
4 u; M/ P8 ?5 a+ T% v                    return 0;
( a0 l3 h5 q  x# v" b                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
0 c: i0 f- L& s% o1 l: H                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
2 a5 i$ B/ N) h# x; I  R+ p' l, i                USB_PMA[1]=0;   // Zero DATA
5 @4 v/ v8 k) H7 L                ep0_state=4;    // No DATA phase* m  v- z/ d( p" d+ J
                return 1;8 ]* S5 \$ ]/ ~
            default: return 0;
% ^$ w6 @7 ^& d" ]3 q, ]- W# d        }
2 Y0 d- U) c0 W. j    }
' D3 K6 h) G: @1 ]. F}+ z: z0 `$ p0 T) u0 @; w6 |

5 I, |; V5 Y- a9 o1 T1 C, s
( A4 w% }; @. f8 A( a( @5 H9 {9 L  X& D1 r' E3 ?
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的  }# E% s; `' y
char descriptor_service(void)
$ G: d. [! R7 j) j0 R* w{- ^# W7 Q8 T& T/ U- L
    switch((ep0_std_req->wValue)>>8)$ F) H9 V; L) C2 s. D0 c% Z
    {
8 W* f) C0 R5 a8 ~6 v. }$ e        case DESC_TYPE_DEVICE:
" J- d) U+ l- C. ]9 D" K" f' c            return ep0_preparedata(&DevDesc, sizeof(DevDesc));; H! f* Y2 X' `' G: X
            break;
8 m: v5 s! `5 |; `        case DESC_TYPE_CONFIG:$ F8 E$ i, j  }# ^7 g/ d
            if(sizeof(ConfigDescData)>ep0_std_req->wLength)
8 D1 L* o6 N$ W4 n8 \, m8 M                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);; h& Q. V' r3 s; @' I; r
            else) B3 ^* r/ p( x# O; O* W
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));7 o) G+ }; K/ W/ k  d* C, _
            break;" f. A$ M- [/ M, d2 e! k
        case DESC_TYPE_STRING:
9 N! {. }5 k; F9 _* x1 u3 S            switch(ep0_std_req->wValue &0xff)& C! P  [. l- P# Q. W$ z2 o3 M
            {
1 T) B( ^+ X, E' H( a+ M6 h                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
: Q! F5 y9 [# ?' b" ?( P# q$ Y                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
/ M* a3 D, z' A  t- d9 r3 |                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
2 E: \# r: G. N3 d                default: return 0;) Z5 w$ l6 N; u
            }9 X! G& a5 w* a  v
            break;; D! b1 Y# k" ^2 M+ W& O
        case REPORT_DESC_TYPE:
4 T. i+ }1 R; f1 z. w( j9 N            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
3 R4 {6 n. o& b: V+ n        default:4 ?: O6 s* e! Z! h+ ?$ I7 R
            return 0;
$ S$ R8 S# H) @7 u    }
% k) q6 G2 ~" t6 H6 ?* t6 n}
7 e, v$ P& J( P. K& Z下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.# C, F' l1 K3 L) n& S& O" e  F
void TIM6_DAC_IRQHandler(void)
- \* i/ T5 u. z; M, S% ^" U{
' `5 N' Z% u+ {. R" y3 |    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
' l* Z. i: s9 b9 K4 s3 H4 z& Z
% W% ?& z9 G, J% S! P. G0 S
6 b% a8 _/ E, Y9 }# f# Y2 H. g# |
    TIM6->SR &= ~TIM_SR_UIF;" K$ l) r% {; ^' z/ P" @2 U) _9 O/ L
    prev_key_state[scan_row]=key_state[scan_row];
- h; Y, V, W4 u  p+ S( f0 A    key_state[scan_row]= *PA_IDR;   // update key states
- t; @8 W0 G* R: G/ E2 Q4 D0 S    switch(scan_row)
: a# c7 L$ X+ u7 s# H9 L1 `    {+ {- K2 K/ i, C7 A
        case 13: // next row PB14, P) v  Q, \$ N# _1 v
                GPIOC->MODER = GPIOC_DEFAULT;
" v, E3 z$ G0 J. _8 @5 Z  P( a                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;- `$ H- z% B; c) W" j0 p$ G
                break;
9 S1 h6 Y( ?5 q: y/ F) V        case  0: // next row PB15
; k( x; n' p, M  ~7 I                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;) R, P1 F0 d- b8 V0 k/ K# }  u
                break;  X) s, Q! l, |  Q5 m1 `+ o
        case  1: // next row PB3
7 }$ i. w; A3 S3 W. R) I                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
( w  o; N0 ^- a8 D) j/ ^! h                break;& L4 u6 k, F. t
        case  2: // next row PB4$ n0 q9 c, C+ b$ F
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;' A4 i# X: @* R% y
                break;3 `4 H# b5 }9 S: C1 W1 w8 K
        case  3: // next row PB5
- O! d1 E$ q* f8 V" `7 s                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;4 f9 y" a# U  y6 y8 G& W
                break;* s! Y: G  ]: f, v- h
        case  4: // next row PB6/ t$ N6 U. p% J6 j& v8 }$ s
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;% G9 ^2 v2 C/ ], z: Y
                break;
1 O5 H9 j: S9 v& N& ?        case  5: // next row PB7
; p2 F6 _! A: @! K) k) g1 C                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;5 L( j5 ?" ^4 ]/ d, z. B! [
                break;' }# y: L" o! Q) p9 ^6 f
        case  6: // next row PB8
% T: J  B+ [) ?  P0 [9 s6 s" }                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
! i9 @' r- Y6 v) y3 Q: D3 d                break;
, b/ J9 X# j+ w% A+ O        case  7: // next row PB9* O8 W  b/ U- @/ O# c5 U& Q  W
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;% B! `2 B3 j/ [) g2 J8 ?: W/ r% j
                break;' \3 Y+ |6 V. L
        case  8: // next row PA8
& L# A# e! V5 v6 h                GPIOB->MODER = GPIOB_DEFAULT;
# _8 F$ |# p1 u  S6 d. n2 u0 V) X                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;
1 Z3 C! r; R/ Z& G1 l                break;: g& t: U+ {4 ^' K9 Q
        case  9: // next row PA9
. W) V% t" @5 T: E- ^  ]3 J                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;1 [) R, O9 J+ }& M+ g4 d; `
                break;; V4 r8 l3 o6 S2 H4 B% S  C' v
        case 10: // next row PA10
+ |- [$ ^, R* t  u) S7 @, f. C) E                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;, c8 q' a* G5 q0 S
                break;
* O; u& |7 d- @7 G        case 11: // next row PA15
7 }1 h8 e( V+ D+ S$ K/ R( W3 N                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;/ L% Z$ h& m7 M& k$ T5 m
                break;
/ c' n! i; H% s        case 12: // next row PC13+ [9 d' r' Q+ Z; W* D- z: x
                GPIOA->MODER = GPIOA_DEFAULT;
+ f4 g" |- Q  M                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
% H$ P/ }& ?4 z5 O; H8 f                break;
' B$ _; L+ i5 [5 h    }0 k/ `. `1 i$ o) v$ G
    if(scan_row<13)2 `" Z* F! q4 |; B4 Q% g  ~# i7 V
        scan_row++;# q! t$ t9 o; f- ~
    else
/ t% _2 ?! a. |$ u0 O' v        scan_row=0;
6 H9 f+ X  k5 H% d- S5 J}
6 I0 C5 e! X9 C
% R3 @" c* Z) Y; v  b( E  Z- Q0 U0 I" M& i3 B
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
# p8 D4 Q% e% |2 f' h4 ~8 {; n+ U' |
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
6 W: y1 s) ^7 t* E3 ^+ r; ?1 i
5 k$ K' |# }3 _  Q7 c* D. F# @要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
. t7 i- ^. e2 q# P% \: x. k

4 b: |! C: Q6 W  f
7 V* N0 E3 Q/ b/ ~! Sconst char hid_keymap_qwerty[14][8]={- e, N% x8 Q3 L
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
7 l/ r" n* p& k0 p8 _2 V6 a( x    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},/ Y+ Y( F& T, I5 S2 b. V! _
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},/ f+ \& [8 b" [2 z* i# T( N
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
/ p  T) S  K) F, k) H+ I: o3 O    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
+ h0 {: @2 ^' L3 @( Z    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
3 v8 Q& E, p( ?. V& n    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},+ A0 R) d# i* v7 Z' ]
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
- V- Q, J& e3 X  [+ `2 n    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},' H. U6 {3 ?+ X6 `
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},) b' I- H( b  @9 k6 _9 d/ m5 _* {# L
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},8 [5 [( h  w8 ]$ F6 E
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
. X" ~% F1 j% p! v! ^+ x" N; Z3 N    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
' f& u: f7 \. m- w3 C" f! r    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}  S6 z" X7 p* U$ B' H1 M
};
6 ]2 C  ?1 @/ |$ f/ q# N) l) T! I( T4 q$ r) k
* |/ R  ~$ F  V
const char hid_keymap_dvorak[14][8]={1 Q* ^. p5 p3 N6 {1 f& }/ C5 O3 f
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},0 e1 I+ f7 o3 d) ]2 |
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},) n4 t3 G9 @& q- T- y
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},6 y& L! X9 @! ^* e# i& u
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
' H/ F$ g- N& ]4 p' e5 [4 Z- \3 K    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},- E! M: {) N2 X* P; [8 t
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
, r9 g- A) `; R/ o2 U    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},% ^, G. G5 l5 X' e3 o; X
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
- w0 f& d  |& K  V! t! ?    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
' ^/ O9 z, R* h' r& |! k% H    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
3 l/ H: F9 S& K9 B) F    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},/ ^) x; ?5 T$ D3 w4 s6 o1 {1 P1 r
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8}," i3 n$ q2 U: ?# M$ {$ g
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
0 R8 r/ P9 n0 [+ Y5 ]- U# h    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
0 Y/ o; N: [$ E2 ]6 U};
5 F) z& Y; {- ~3 U( T) R% ~# D
2 A3 V% C! l: r" R/ f1 @5 v
1 S/ G6 s( L1 Z! D上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。- g( k& q; h# B0 f8 Z* k

+ [$ _- V$ P0 f5 SHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

4 M4 u$ E( t3 y7 @8 x/ d) e2 ?% M0 v. v/ y
void update_key_matrix(char row, char col, char onoff)
4 x' B& E" P; D& `( V0 k{8 E0 v1 H* b' n' [* D) k3 p
    static uint16_t hid_report[4]={0,0,0,0};/ K" ~* F& \6 j4 w$ Y
    static char (*hid_keymap)[8]=hid_keymap_dvorak;
8 S7 k. C7 m2 C2 f% ?: e2 U! }( Q+ K0 S; f6 \+ e( j; m
/ h  `9 N  n$ l% L! `1 H1 g
    unsigned char key=hid_keymap[row][col];7 l. I0 }" O- L( R
    unsigned char *report =(unsigned char *)hid_report;0 g8 P5 T5 _9 F. p7 t5 E3 j
    char i;
; p" D$ \& m$ p/ B* v2 h
* d* k+ U9 V% }8 A* f
+ ?( T& Y6 K6 Q" ~4 ]/ }
    if(key==HK_MODE)2 K5 o6 ~) Q5 ]" i; `; y* a1 u
    {% u( R( G0 s1 |3 O  {7 A% [0 C
        if(!onoff)
" ~% A4 m$ A4 G. C: g+ B$ [# C        {$ d5 L) `  k# k' i& u1 T& _
            if(hid_keymap==hid_keymap_dvorak)
& Y. z- r' X5 F4 d4 H9 M. u6 `            {
$ ^( I8 T+ C0 j6 _6 z2 s                hid_keymap=hid_keymap_qwerty;4 S" a( ?4 R$ O  o6 [( s1 j
                GPIOB->BSRR = (1<<2);# I( ]7 @) x& {! f* R
            }/ f$ o1 ^( F7 Q; u. }2 x1 O
            else8 p; }- S* c: w. j; {
            {
* L- `" t1 s, p: z( ~                hid_keymap=hid_keymap_dvorak;: Q, F) ], @: J& [; R$ S6 r; m
                GPIOB->BRR = (1<<2);0 c( t/ A! d( t- H" y) X" a
            }. o- a% L, d6 v% P! u2 D# }' G
        }- s" B+ u" i5 g' Y: t
        return;) z- G) z+ J: y9 o4 G: g+ d# s5 ?
    }
$ z+ o* H% r) k) I, R- c% E! G" z  n" v
  |# x* w+ ?5 ?) M! ]
    if(key>=0x80)   // Alt, Ctrl, Shift8 {; L3 e' b* @# K- B5 J3 y6 O
    {
( n/ Y# L6 E2 n        uint8_t bitset = 1<<(key&7);
2 u: @& R2 @8 O& P7 b$ U& @        if(onoff)   // non-zero is key up% n% A2 x) d( m. v( s% R  [* |. h
            report[0] &= (~bitset);
" f/ ?5 T: K# P1 H; {0 w: a* [        else- a" K1 s6 D1 K8 |3 k( N
            report[0] |= bitset;
* L0 p1 N5 r- e$ A) p7 K    }( x! H/ Y# S( ?+ @# C, e
    else# ?5 J% [1 W1 a# [: I1 a" w
    {+ X+ d4 v* T! n+ W3 u) y
        if(onoff)   // non-zero is key up
1 G, ?; ]/ L  y) t% J        {
+ m4 w- A/ D4 B' a2 n            for(i=2;i<8;i++)
% M* \' _! g6 s& Z) N; _, }            {
: B" h$ P+ ?- r5 ]* k, B; L- C9 q                if(report==key)) \' D  Q) H! k# K& T, m6 c
                {
3 \  v% l2 e8 J                    report=0;  A( `# x. q: D0 H5 i1 c
                    break;# m- Y- T' P0 V3 A
                }# T+ z, Z; r/ a, R8 U# Z
            }7 v" Q' e& r" F  R2 n. F& [  Y
        }1 ]1 g+ i' D* ~- R2 a. v9 s
        else9 ]; [8 q$ {, S$ @
        {
- h: @% p7 b& J- x9 \1 @# p3 w" u( q3 z            for(i=2;i<8;i++)) h. Y, A. ^$ T: ^6 D6 k# F
            {$ Y1 \4 I, l7 I, ?- u8 m6 B0 L
                if(report==key)( o9 ]& V6 t4 N( z; n8 D0 b
                    break;
# M& S  C& i+ }                if(report==0)' \  X' v( Q2 w( ~
                {2 ~& u- X" y" A4 Q
                    report=key;
+ e: P7 ?- ]" {                    break;
% u: ?- {9 E. J$ o3 @% ]                }
/ q' t2 y7 `) K2 f# R+ g            }, _+ v. \# k* V3 A6 X7 K
        }
. D1 g$ u* H5 j    }
8 j2 P4 n# H1 w5 W$ P' y    for(i=0;i<4;i++); V) k' C) B* m! K
        USB_PMA[192+i]=hid_report;
7 U% H* o/ R% ^; X9 P" V    USB_PMA[5]=8;   //COUNT1_TX
9 w' z1 O; L* p9 X    if(ep1_wait==0)/ P8 J. E: E' _& C
    {
0 I1 e- a8 {( \- t+ r( B        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
: m# Z; Y( ]. C1 f' c        ep1_wait=1;4 o- V8 [. B0 R# i
    }$ \7 M5 c# z; h5 `
}
" r. h7 {, s2 `" e' I8 Y3 V# ]  F: @) b
* Y$ G5 {% f, D1 r& d  T
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
  V% w' w) ]2 D6 A* i; [  T keyboard.zip (8.7 KB, 下载次数: 6064) 1 e% D: H8 s' F/ d( }4 x

7 I$ d+ w' E" v6 _: c1 l7 t
+ D& p( n" [# Z$ g! v5 M2 P# b  i4 q" z. B  ^3 B' t
, w& Y9 o) O& ~3 C. D! b
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘4 c: n) x6 R7 L
不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
  }/ o4 E! |6 V' V' f刚开始我以为要把打字机改造成电脑键盘% y, G7 E( B, @) F; N
不过楼主也很厉害!

1 y3 [3 O  m: w: `3 ?" {哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害0 P- t8 _+ f. z6 h+ p$ v  a) f
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-12-22 00:22 , Processed in 0.191196 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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