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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
1 n$ D% ?" p1 ~9 l3 z5 y, R$ a2 S1 `3 c9 q2 D3 a
http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
( |& P2 B/ D4 d5 u这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。: Z( W. |& Z% I; f3 C( O7 |* z

, I0 N( K- U# z& ~在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。

- K5 W$ _& n# ^; n/ M/ Y

2 Z8 V& g" M  f7 y- R 235140i3a36qivqzuvmt5q.jpg.thumb.jpg
$ v+ R6 ?$ J$ ?/ g, G  \( ?4 X
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。
$ a- D+ l! X  o* @ 001734klbyoluenuwz4h4b.png.thumb.jpg
( I3 `( F$ Y3 o" F; x6 j' ~  d为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:- a7 K0 K  l4 \% Z( O" d& L
003625r2agx2f5v922cf2f.png.thumb.jpg 8 L! [3 f! B/ U* {; a' L0 L
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.; L" I+ D' c' b, c' l) y
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
  z: U* I% ?/ A1 ?
005836yvs0wvovwsssgd3o.png.thumb.jpg
0 }+ y* s: W9 e1 I9 u0 _Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
4 j/ B' g3 |* C2 N
( I. |0 V% u) u- M我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。2 k" E+ u3 u, \* Z! B/ ~
6 V" U3 N" |1 f9 M; j
到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。6 }1 j8 ~& P3 c
; z+ f) j6 g( v
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
  ~. e" X. A% U/ e2 A- Q
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg - g, h; }3 j. R/ l
----------------------------------------------------  分割线 ----------------------------------------------------------
: D! h& L. q3 _5 c% q$ N/ E# Z: G/ b! d3 _1 X
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
$ {$ j) W& n+ B7 ^
020011osionbunl4ui44vi.jpg.thumb.jpg - w0 G& H$ s* G9 ^8 e# g6 k
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。6 p( h: e! J9 {2 p* S
020017j8ycmnv7788bqv52.jpg.thumb.jpg
6 O; ?( e6 E7 A% |/ |& J3 v5 S7 j特写,80C49, q+ [: C* a* t/ U
021040oujzuvtut6iujtvz.jpg.thumb.jpg 8 K- }$ B: _5 d, ]/ S1 b6 }
LED部分,使用了一片D触发器锁存指示灯状态.% s; i" l2 {1 K5 m
022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
+ ?4 T% ~" Q% J) m# o( a暴力破坏,将80C49拆掉$ b- v: A4 b2 P& ?4 A2 X+ ~$ n
021113e48qq98vyohvhzzh.jpg.thumb.jpg 9 A1 o7 Y1 g8 R6 v, G- T8 w2 F
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
5 h* E6 d. D! Z" ? 021125nc9az6dj33rlds2r.jpg.thumb.jpg
; Y. ^' A8 [: y) r3 T
焊好元件后的板子,准备替换80C49
5 N$ C3 M& S9 B8 l7 d 021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
1 v1 e4 [& Q. ]0 \用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。5 \/ _7 K; _# G# G
021104shifhnrqbr3o5nlo.jpg.thumb.jpg 7 O$ B6 z; Z1 a: F' R
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.  }1 T# p. ?7 P& L7 k: y+ v
022003ym1p9u4ug40280uu.jpg.thumb.jpg & d5 V1 i0 x+ @; |
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。- u4 [) S1 @) s1 P3 F% K
023313kt141q9qajtol7ma.jpg.thumb.jpg , L% }4 C# \5 X5 |
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。1 \: a# |9 Q( D& @
023322nt7l5xb3ltttkltt.jpg.thumb.jpg
. }0 G1 \% S, f) d0 Q9 h( K  d
主键区键帽就位! T1 P1 C" I1 s8 y! z
023331hin88e8wkrwzwikx.jpg.thumb.jpg % u) c1 S& Z6 Q; W
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
$ F+ n3 V$ w" o; S 023336wjzlgopugg1jyy79.jpg.thumb.jpg
6 Q7 o) F$ x& j. C最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。
1 i8 Q5 Q7 Z- g 023341sffu4j3g2323h6fl.jpg.thumb.jpg - \, s! a/ h- _  \3 o1 C
: D8 `% y& \2 C- D4 K+ |
----------------------------------------------------- 分割线 --------------------------------------------------
3 M1 W" n- h' _1 K1 z7 A
2 E3 H+ {5 `! q$ v" h
3 c$ N4 t# c. R9 K

; c6 A6 z  B- l6 J, b( |) i' L/ ?5 o- @, B
% g8 U; Z; b4 H, c
% ~! T" B$ N; [9 j& I: u/ w

+ U6 p  U0 u5 u- r. g  g
+ W3 L% [, u' I" ^+ ^( H- K5 Q6 w7 y* {5 |

; x) E4 ^) R$ n- B; M2 _
3 ?* E% o7 D3 P9 J0 ^
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。" [0 ]  Y8 G( W
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
- ~2 p2 d5 S! ~5 `: B! O* q. u9 b 104025nzibm2rmiomhyirm.png.thumb.jpg
3 Q: z' w; d3 X7 S( W

9 A( R( {$ b  s( f  C* c$ G其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)% H; V6 l/ k! K
105004zkrez5houvkkznko.jpg.thumb.jpg - o7 ~; G6 R# O  l0 ^, e9 i9 R. d, e7 m

8 l: {: N8 M! ^$ d- l' k$ a扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。& A' j0 e9 X% f6 i; v
* B/ x5 B  y( A$ G4 R( ^$ g0 `# |+ Z
这是我设计的电路图:
0 N( J. G( G3 Z" ~% |7 @( ` 110344ej2z2oo2rflo7oe7.png.thumb.jpg 3 x4 d+ N* c/ _+ e- z; X2 w$ s/ D
# ]. j3 J$ e& ]5 L: _" O
PCB Layout:
) j% i6 m" O( l/ y4 R5 h: M4 l 110847jjbjvt34vwt3v5bb.png.thumb.jpg
6 Q. [) P! e6 ^# C

8 m) _" n3 g- @  \; ]$ F+ }不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
! ^+ C6 N' V+ R5 i/ s
& ]1 A% M$ s9 ~
8 h: i8 z' e$ d
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 3 B, j; V3 k* v( o+ `$ G
  y* v9 h# N2 e% B3 O$ X
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。/ _( ]' E# a) C4 i7 N9 R

; c' i9 {! I3 ?6 N8 S0 W总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:/ q/ a+ K# M) m2 u/ F( Z: Z
113818pmrfsb6z0byt6t06.png.thumb.jpg " ]& J3 b: A2 g1 {, |) @

$ Z6 V  g; ~/ b% _8 E. O其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
9 i6 x( |* U# j* v8 R2 ^# k% y' N$ N
! B8 s, X9 Y& h* n2 NUSB的中断ISR,bare metal哦
& z1 D* I7 b# y+ d
4 R! y+ G- d3 X- ?0 v3 V
void USB_IRQHandler(void)
; F( G% v' k/ Y4 c* z% x{7 |$ C/ a) z; q8 t4 Q! j
    if(USB->ISTR & USB_ISTR_CTR)6 J: @  b4 G& K2 q6 I9 m4 a- W
    {3 n+ w% V# _+ c* Y6 e
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
& h" I  T% G- q# i  M        {
7 p" `( z' G! x# O' u& V            switch(ep0_state)
) m8 _3 _1 D2 i& [0 b' W            {( N; t2 v  ?; c  B
                case 0: ep0_state |= 0x80;
; x+ g+ v# H, I& b2 H) \                        break;4 G6 x1 |5 f% e% T# H9 n
                case 1: if(USB->EP0R & USB_EP_CTR_TX)
( ]+ P# I4 g' p/ }, \5 C4 x- ^+ j2 p3 L                        {2 G' [* W! f* ?8 l& V" j. h
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
9 N0 s, t6 P4 ]# J                            ep0_state=3;6 v+ ^* ?) z6 G5 O# @+ L! r
                            return;
# X8 d! _& Z8 \( I2 n5 U                        }
- f& p& A5 @: d7 d                        else4 V* g5 f" h, _
                            ep0_state=0;
! |8 \+ Q  y" I# k) Y; ^& a, [+ v4 F                        break;
% t* i7 S) v; e  p- m  ?                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
! t5 C- N1 h" z$ F5 @- u                            ep0_state |= 0x80;- m  k( w2 r; N& p: A
                        else
6 U5 V6 @* [- N, E* x4 B                            ep0_state=0;" P, z' x8 [% M2 `% z* K
                        break;! _9 s2 f, n/ e% C8 c& V
                case 3: ep0_state=0;
5 j0 b$ t6 u3 S2 ^5 {& o; \/ A                        break;
' u% Q% n+ E" ~+ [                case 4: ep0_state=0;# |, _9 |  o: W) O3 D. N4 ^
                        break;
; {# m4 y! E5 R6 ?# b# T9 u4 @                default:ep0_state=0;- M" F& |( y8 T  N5 D$ E8 f
                        break;# ]1 h: J4 U8 v- M7 A. t) y
            }- u" R$ g- D6 \8 A6 M$ p* v+ ?0 e
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
8 ]6 c6 }2 A" w( h6 ]            return;( r8 ~7 |% r7 u/ b% G
        }& k: g- o6 J4 m: C- l" l
        else    // EP_ID can be 15 Q) E  o: s# Z+ ^
        {
9 \2 G' [4 O( E4 C            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
9 F1 u. {6 y( L! M! N) E( f  D2 r            ep1_wait=0;' p3 F+ R# r' X2 w+ z
            return;( x7 _8 `$ J1 c' A
        }
/ q/ N# t! Z- ]$ |    }: y$ d" g; N, V# \3 {8 U9 L7 j* V" k# _
    if(USB->ISTR & USB_ISTR_PMAOVR)
  l7 M8 _5 f! h% u" ]/ W; `! {% p    {
+ B% R7 T6 x- P& ~& s2 b! ^+ D        USB->ISTR = ~USB_ISTR_PMAOVR;
3 Q5 R, ?1 Z( ~8 _    }: F# w7 @5 U; G6 q
    if(USB->ISTR & USB_ISTR_ERR)
8 K- X; n; a8 n2 G1 m    {$ P7 ]1 N% |5 g" K3 T. Y5 Z( a! K
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear8 s; H( G$ T- d. z0 U. ?! O- M
    }
. V: K( N: q1 z* _, W) ~    if(USB->ISTR & USB_ISTR_WKUP)
% B9 C- E  I1 O" n" Y( D    {
+ }0 t. Z& \; K7 u# q; v( y        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
9 k( Y) c5 m6 _        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
+ ?0 E5 K* b) U  V3 G  t    }/ [4 ^* C' {5 ]: i- E' q
    if(USB->ISTR & USB_ISTR_SUSP)
" m& n1 z1 {6 q7 V, k3 L6 _    {
* V3 U! u! c1 Q$ [- l) H% Z        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend. e. l0 E8 Z5 x3 J  L7 p9 r
        USB->CNTR |= USB_CNTR_LPMODE;   // low power
" {" E$ I+ \' i& @: N; a        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear9 k+ J" l9 G; i/ S
    }
! s6 Q( F, B5 q; u( t! [" e    if(USB->ISTR & USB_ISTR_RESET). p# p1 y- j" \, _7 m! v6 f, [
    {
6 ?; ^; F; ?7 e        USB->BTABLE = 0;    // buffer table at bottom of PMA
6 n% h) a! N& p5 p        USB_PMA[0]=128; //ADDR0_TX
7 I, ~% ~/ |5 [  T8 H3 k3 ]# d3 r        USB_PMA[1]=0;   //COUNT0_TX
9 K5 ^! \( K4 q# R; b9 |        USB_PMA[2]=256; //ADDR0_RX% f& O% i/ H4 A  n5 s
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes" [6 R9 Y$ Q8 @" Z
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;( L; N9 z6 U) t7 \9 E
        ep0_state=0;
2 p( ?% M2 C# F0 N        USB_PMA[4]=384; //ADDR1_TX
/ {) o/ L% u: j8 ]9 c        USB_PMA[5]=0;   //COUNT1_TX
9 M$ Y* O3 l0 F% z  _" ]( b        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
5 v) ^2 n- [& R6 m6 m" W5 {: H        ep1_wait=0;
1 Q3 E2 e1 g9 G$ F        USB->DADDR = USB_DADDR_EF;      // enable function
3 o0 i6 E1 g# d% T        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
9 X5 e9 B4 \  Z% O3 H    }
& w4 C0 m( [0 Y! t- [    if(USB->ISTR & USB_ISTR_SOF)+ M! d+ y1 g0 d( g: B1 o. p8 \/ R
    {
! h$ U7 z) ]7 i. y5 O        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear: b2 o0 Y2 y/ v+ _! f
    }+ k8 X# }1 E# ?$ I9 h* v9 I& V% v
}
( W3 [2 {9 Y& _# i* @6 t+ C0 R8 J
6 p$ M; f$ b( D0 b
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。: @# s# `4 n2 b6 Z3 x1 c+ i, J
$ ~9 U* L# U1 ~
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。5 @7 y0 h: o4 Y# u

/ L9 L7 s' F: ^8 \. ?
    while(1)+ c! z' M  |3 H* I. i) ~
    {" @# k( ?8 i& |, ]
        static char row=0;1 N1 y+ j5 Z9 K& ?8 r0 q
        __WFI();( R. ]' o+ i7 t7 T
        if(ep0_state & 0x80)    // request data processing
( L, r  b% m, Z* i; K/ m- F        {; \+ e2 u  B/ b8 T
            if(ep0_state==0x80) // SETUP phase( Z5 I4 h# G# B: U% P) g
            {
% o% J  k5 O( Q7 l' o; b                if(!setup_packet_service())1 |) j5 ?& z, \: I6 J
                {: I- v( T3 @5 H( k0 [1 p# q) @7 M3 n
                    ep0_state=0;! Q- G8 A" w! ]) K4 [3 x; k1 D. ?
                    // not supported
5 E2 Z( i- i6 H7 y$ U                }1 C/ l7 M3 n! ~( m
                // ep0_state should be set to 1 or 2, if processed; [8 d/ K. C" U2 P% n
            }
9 R  V6 Q( M: e; V  }3 n, }3 B            else    // OUT phase
$ Z: T7 n( x. O, T9 A$ c            {0 f( B8 e, }3 f3 C( L* F" A' V6 o2 M
                // process data) C+ s, \% j* @
                show_LED(*(uint8_t *)(USB_PMA+128));
  p. n9 A4 X  k                ep0_state=4;' ~7 `3 K6 c6 {1 [* P$ N
                USB_PMA[1]=0;       // Zero length DATA0
$ a/ R! i# r! L/ K                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;9 ?. y) y( z' |$ a# w6 L
            }
, w& n' }2 V$ x' `        }
8 O! A! n2 D9 D# Z. S        else
* p% k' f6 ?7 M- f9 {+ [1 G        {2 _# R4 e7 _- L$ m
            if(usb_address && ep0_state==0)1 M- O* J6 P5 P) X
            {! T4 ]5 C( l$ K  f& V* w
                USB->DADDR = USB_DADDR_EF|usb_address;
" u# @, q0 D/ e; s6 v                usb_address=0;0 L* `2 Y3 y- l9 m
            }
# Y3 K( `. l3 n        }
& C3 H+ |. q. Y! m. J) N        if(row!=scan_row)   // new scan line
; U5 d* G/ _  g        {
" H4 Z2 p* W$ w; F5 u            if(key_state[row]!=prev_key_state[row])
, V* k6 J6 I# H- T1 U- H            {9 X3 ]- Z, e% Z6 v4 V" a5 V
                uint8_t test=0x80;6 h& x/ H5 n6 C; `4 Y
                uint8_t diff=key_state[row]^prev_key_state[row];/ z. Q# J1 V' H& f! a) [5 K2 e/ d7 I
                for(i=0;i<8;i++)
6 i7 x; U  k4 M! F+ r7 @- Z6 y                {
" N4 N  c, c, G& f6 U% @                    if(diff & test)$ i5 Z; x! g9 o
                        update_key_matrix(row,i,key_state[row]&test);
( t" F# Z# {* E$ v( \                    test>>=1;! ?8 m2 O$ A) s$ b/ t' {  x* Y* L
                }" h* H0 w6 a2 Y# J2 a% [! N
            }
# `. [2 m. \# R! \$ Y) G            row=scan_row;
' z/ A3 |  h  u& f, L# M, F9 ~7 g1 Q        }
/ r* @+ C  @7 C" B* w1 k4 c    }
: L7 M- g. \+ w$ E* i1 ?, a0 Y+ v  G$ [* B9 O( A! t
1 P+ g/ O" ?+ X$ ]$ ]
EP0的控制传输,把用到的请求处理一下& X1 ^& e, p( W4 d. y1 B8 ~! x
char setup_packet_service(void)0 H6 N# @# `0 F# [- \, K3 f
{
9 k7 a. ]( T- h6 m0 w9 j2 l    if(ep0_std_req->bmRequestType & 0x20)   // class-specific. i) O# |( D; }5 n' B3 M
    {
& }- E6 g# C7 f        switch(ep0_std_req->bRequest)
0 H- y6 X: R1 t( @; ]+ ^5 N% t        {" v, U, G! C4 E
            case REQ_GET_REPORT: break;
( r8 c  e  N2 Y  j$ X* O8 W: x            case REQ_GET_IDLE:4 M! o- N, T4 E: e) h* @
                USB_PMA[64]=0xfa;   // return 1 byte
. G; \. }0 }# G! |                USB_PMA[1]=1;9 T# ?) h. N9 m: ^
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;" q5 f; L$ v) Q  {& _/ q! G9 P
                ep0_state=1;
6 X7 L6 g% }' O9 ?- \5 M/ \                return 1;) {2 J! O4 N, l# K) S; j
                break;6 _8 a1 O( r+ K/ C  @5 X
            case REQ_SET_REPORT:# T8 s* J. {. y1 R
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;. b4 K2 t; K; p
                ep0_state=2;
0 Z! w# q# C( N% D1 n% b                return 1;& Q; v  j, s3 Q
                break;8 M! {# G7 j( B/ u, `9 P
            case REQ_SET_IDLE:
9 y: G( M/ k2 z. V$ I                USB_PMA[1]=0;   // Zero DATA: a& p6 M2 U" |, `/ _, {! F
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;* t0 W/ ~# a4 r5 _" S  l
                ep0_state=4;6 d. k0 ^; J$ {: y
                return 1;6 V$ j6 d: Z* t+ w5 c% s. A
                break;. w& E1 o) l2 }) [2 D$ j
        }
8 C4 B9 F! U$ B        return 0;3 u6 `3 v1 y7 U, N7 f7 w; l
    }
) m  Y: Q+ K) h( ?( k8 H3 s, R    else    // standard* {$ Z  A# Q& r+ A) _8 W; }; G
    {' A9 x2 P( g- Q  V9 w$ m$ I! v
        switch(ep0_std_req->bRequest)
- F/ y1 n/ m2 N- a& V9 R6 b        {
2 N# E3 e/ e( T- x0 ]            case REQ_GET_DESCRIPTOR:' u  o- V5 b% A
                return descriptor_service();
; R) o+ M! D: l# P1 H                break;
" M8 B; t' ?' p& k6 r            case REQ_SET_ADDRESS:
4 \+ y3 K, y; K1 m: }                if(ep0_std_req->bmRequestType!=0x00)$ c7 ?+ ?2 d; O- R  w7 b
                    return 0;. t* ?1 {/ W. n; A. p
                usb_address=ep0_std_req->wValue;
( N3 x8 U$ e" i& |: q! d- r                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
( U4 H8 X' e8 n* J' O( y                USB_PMA[1]=0;       // Zero length DATA0
/ }# O( L% e5 \: h                ep0_state=4;    // No Data phase/ c! O5 ~! B3 _' _$ B
                return 1;5 L3 x4 ?2 e3 `; ]: A
            case REQ_SET_CONFIGURATION:
- o5 i( ]3 Q& |; M                if(ep0_std_req->bmRequestType!=0x00)
. ]8 w2 `5 C9 }# Q; K5 S                    return 0;
( g5 O* e7 g, _& x                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
5 O% V: J7 [: q% C. G0 ^1 L                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
7 o& t8 m3 ]" Y& [                USB_PMA[1]=0;   // Zero DATA
* z( U" l. _8 B) \# i% ?5 J' G( ~                ep0_state=4;    // No DATA phase( J2 F- u( L& T' a( u
                return 1;5 w& [2 P: q9 |  \8 P6 W6 c
            default: return 0;
& o6 \9 [. i! M8 T        }% k) D' l+ g" K% w8 J
    }) b. M( o) a, f9 S  c
}9 @0 x4 H, z- X1 d/ a9 D' Z/ X* @

8 m4 Q& N: ?; D2 o4 a& R2 Z7 G  F( C* I+ N- X, j  o3 B! w! v6 T

: d/ Z/ A7 a; }$ A" X
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的) `& X( ]5 p/ B# p- o% m. X$ |$ E
char descriptor_service(void)% W% u, ^2 @. u, }; Z, t
{
7 N$ m5 d5 |2 n( z" \    switch((ep0_std_req->wValue)>>8)
! V! M# F/ m% x" G! }; c/ S* A' n: Z    {1 ]6 l. l; e; i* W7 |
        case DESC_TYPE_DEVICE:
+ ]/ M* L" A  b- Z3 r            return ep0_preparedata(&DevDesc, sizeof(DevDesc));
0 ?. k2 S) r4 z: ]            break;
  h  G  ~" `2 \* S  ]        case DESC_TYPE_CONFIG:. ~& X$ F7 l7 z- O  c
            if(sizeof(ConfigDescData)>ep0_std_req->wLength)0 O/ L. [4 k( u6 Q, |
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);+ L! W  `9 N. S0 x3 f8 J5 b7 k5 t
            else/ E5 J7 z( ~5 {/ |
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
0 Z4 L0 U7 y3 a9 \            break;/ l0 x1 T/ z+ M
        case DESC_TYPE_STRING:
5 z! F8 F7 K/ y  W3 W& d- p& Z: e            switch(ep0_std_req->wValue &0xff)
& {9 @. E- a% R9 m. N4 U$ @0 q' J; q# ]            {8 J* `  }$ Y$ D- Z' e, ~2 W6 S
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
2 f2 K7 g$ S0 [, S                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
$ L1 S( ~. }" Y  g' I* Z                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));3 f* E2 Q% \, b3 h5 O0 ?+ B7 n4 \; N! O
                default: return 0;. I5 W+ e# T& p5 G/ N' e  O) l
            }* a* V5 A! A- }
            break;
8 N' }* i8 \( ~% M( o: }        case REPORT_DESC_TYPE:
6 T, _% x: }: v2 d            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
: P  i0 u8 c' h% O! m* e        default:
- f& I7 H8 U" `' g            return 0;( @& I2 q0 x0 |9 @
    }
3 ^/ N& e% c* ]2 ?) B: T9 U5 S: ~}0 @; M7 m) ~( T  X% J2 V
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
( e4 p' o7 Z8 S  o4 {5 Cvoid TIM6_DAC_IRQHandler(void)3 g- w* h' m1 B
{! o! r4 V5 G. C& D) c9 j* q" g  _
    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
- `5 _0 _9 o# Z" S
& e. y; ?$ o# h$ j

$ ?3 S6 P/ h8 ]. N    TIM6->SR &= ~TIM_SR_UIF;8 N7 h" |. U0 d' T2 s9 v: W" g8 C* m. |
    prev_key_state[scan_row]=key_state[scan_row];8 }* D) e+ [  i" [: @5 A# w
    key_state[scan_row]= *PA_IDR;   // update key states
1 ?7 d/ C6 h1 i    switch(scan_row)
) R1 {7 ]: u; b+ r7 X8 j    {
+ P; a" c" D% t        case 13: // next row PB14
7 N/ U6 k3 {/ X1 Y6 ^                GPIOC->MODER = GPIOC_DEFAULT;( p/ g. j% M, |
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
- Z8 D- b; t7 J# U) p# p                break;2 y; A% _/ v& |" e& b7 z* J: g
        case  0: // next row PB15
+ U0 S/ Y- ^7 m5 p- o2 H1 @7 G/ w                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;( C+ m! t9 G+ Q$ `
                break;
1 ~) \2 g2 {7 n0 d( c( X        case  1: // next row PB3
& M- |/ V  s; U0 e4 i                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
7 {1 r) w7 O4 I  Q8 W* c3 L                break;( M& x( y9 `* e8 i- W
        case  2: // next row PB4% Y0 T) r7 ^4 z2 c! l% v( g
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;9 q$ N6 R/ N8 v2 x
                break;, j: K$ A0 a( l* H
        case  3: // next row PB5( ?& r: Z/ b3 E& p/ Q7 V) t, _5 q% T
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;3 r2 C# D$ \. S: E
                break;
% n' m3 d) x; C$ b8 r- m/ f        case  4: // next row PB6
  ?3 i9 P& M' R9 Z: d                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;6 f6 ^- Q1 C9 W( `; F
                break;, _* l0 h6 F! j/ \0 x0 i
        case  5: // next row PB7$ Y" Z& D* `3 s4 h; J, }+ Q
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;" |! r" V& A1 b9 H3 n
                break;, Q* a/ h0 {# Z. K2 h* O) g
        case  6: // next row PB8
3 ?; c" V* |5 j  m  w9 ], M                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;  j  K* N7 ?- \: K
                break;2 w' d2 g) K  Z6 V- [
        case  7: // next row PB9
( c' K; V0 J) t7 N3 p0 i8 d6 U6 T: X1 P                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;. Z  {! N; Z9 o; u' i, K! B+ R) v1 r
                break;, k+ y, D5 H+ T  b. N
        case  8: // next row PA88 W* R0 k! v6 F; q5 S! w
                GPIOB->MODER = GPIOB_DEFAULT;
% |  N2 e  R5 q0 }: K                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;$ _& r6 Z' G) u) R
                break;
3 b/ _1 S5 U' y: d0 D# L        case  9: // next row PA9
+ o5 h! m8 k( N/ Q3 |) `                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
* w/ q2 U1 o8 {/ B# }4 ?( ?" r' `' E                break;
5 k& E$ D# w) v9 S: ^        case 10: // next row PA10' O* y$ |4 v  ?9 V1 m9 ]
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;+ E$ m& m- L7 A* e$ H& V
                break;( Z- ]$ _  \, S) x) N$ D& `
        case 11: // next row PA15( q/ ?% c( n: Z6 k# [
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
$ }) w* z4 b1 D, `  I9 g                break;
( o9 D, V% |- J& u+ P& ?        case 12: // next row PC13' f- X* F- |; X3 H2 w$ Q+ d' e
                GPIOA->MODER = GPIOA_DEFAULT;4 }7 m. F2 P3 M3 k
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
2 C4 X  `8 T' G  J3 }- e                break;
( d& C: m- c9 {6 s- N5 M. A8 V    }
% ~7 O9 Q- @/ a    if(scan_row<13)
$ w6 b) Y; b4 {; J2 R        scan_row++;
6 ~) t0 v& Q+ w4 i! B9 t8 q" o    else
' \; F6 i; J7 ]8 z0 r. z        scan_row=0;9 j# [/ h* K& n$ U( k( E  O
}
! @) D* p; H8 Z8 }9 d/ m
3 E6 @, R& `  |: M& R2 J3 i6 |" L* _4 q6 x! i
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
1 d5 Y6 W+ `6 |) u7 W6 x
' U# v# ~& P5 I- r扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
4 b) Y- j" n+ d; t9 h% @6 c+ K) u# @& I' z6 R8 v7 _
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
. ?* {5 n% ^! V

; C7 C8 P9 G  a$ s8 v
% E: \7 Q7 |" j6 \* Mconst char hid_keymap_qwerty[14][8]={
7 [+ n% c$ F! y! }7 Y& T; j7 @    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},0 s, r' g3 E+ s/ V
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
8 s! L% N& U+ }3 ?    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},, s8 q" \" m, e+ b  n' Q
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},- f" |) }/ |3 \4 }. }5 N
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},3 P% e6 T! X4 D* V4 a( \8 U
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},2 P; `5 E" C( ]4 q
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
7 G8 @% a! w8 w; ?% |    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
9 s( J, J6 Y/ m6 a; }8 u    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
* _" W( s& |4 `! N" B    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},* i! P+ e; x& G
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
% m5 M8 i; F  c5 a+ q% R+ L    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},- I* N4 L( m7 s
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
; T" i2 x& B$ p4 C2 g3 l0 l+ a    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
, C5 R4 u9 h& q$ |2 r0 A};5 M6 o( S8 i( a3 ]2 S

8 q# v/ u( u* Y7 j# S& u

, z: T: Y3 z/ m: D; _, W9 pconst char hid_keymap_dvorak[14][8]={
+ j. c5 j% q$ p% G    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
3 C4 y& x% U0 J- t4 |3 n0 q    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},, C; K; ?" A" v: o
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
) \" ?6 t) \* N  B. G$ ~8 O  P    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},9 I/ w, R" U. x; i% Y! O
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
3 k5 Y# R5 r9 E* P" Z    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
' h4 L8 b( @! X/ Y+ B# }% y    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},. K+ |; l  U; X8 S
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
2 P% l' d4 {  \6 C0 g9 z; Q( j% X5 n( c    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},7 m% w: ~* S% L
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
+ q2 r7 d" `5 `$ m7 p    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},* O: s* k( }! w/ p7 C- X# T
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},) H4 T! E5 T$ u$ Z7 w
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},4 j# g& I% M- x) c" r( T' n8 T
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}8 y; S; B! l: Y  Z
};& i' K: V. {% V; E" ]4 m

+ N1 C. X2 h3 Z7 g
8 o! b1 |' C8 S. I上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。# e7 U0 _) u4 S! |

- h7 t3 C1 s  c/ m* rHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
: U# Q' {" z1 l8 r# B

* j( B5 }3 n, N+ s) p- R) yvoid update_key_matrix(char row, char col, char onoff)) V' [' B2 F& v. e
{7 G! [0 W/ m& q6 O9 z6 E6 i
    static uint16_t hid_report[4]={0,0,0,0};
" ~. U- b9 Z6 v    static char (*hid_keymap)[8]=hid_keymap_dvorak;
7 K8 u$ J) `6 E6 Z+ W( V: j& A( f: u; o
$ N" |+ d1 b0 q) ^# n0 H+ X
    unsigned char key=hid_keymap[row][col];. P2 y; N9 @! Q. i( m3 X" }
    unsigned char *report =(unsigned char *)hid_report;
2 w1 c  N- G/ M4 `3 y( _& H- g) }. f    char i;
4 I" g0 k" _. ^: ^0 n
9 f6 S% e% r7 L; \

4 G# ^' Z  N* D  c    if(key==HK_MODE)
$ b; A, s  V' P8 H% X    {3 s, O. k/ z. N( s
        if(!onoff)' e: q5 G8 ~0 x8 X: o! b- M, J: ]; H
        {$ v; z5 F6 D3 e- U& V) b
            if(hid_keymap==hid_keymap_dvorak)1 M% V. e7 `- P1 S" a' x
            {
, U: k& |8 X5 p                hid_keymap=hid_keymap_qwerty;
* y4 ~: d2 N( h8 _( k. c                GPIOB->BSRR = (1<<2);/ r3 U$ a2 X* ^6 K7 ~; |# u
            }0 t0 \) k) }- T& o' N2 ]+ U4 N" x5 h
            else' u8 F; V. k  |' S  S% u
            {) z. s2 k" N5 p
                hid_keymap=hid_keymap_dvorak;
1 y# ~. \: }; b0 ^6 x& K                GPIOB->BRR = (1<<2);
" ]6 r, F- E* k7 ^$ g; s            }1 h* p3 X. Y" d5 c; i9 Z# t9 a$ c: e
        }
7 l9 T6 S2 L- q        return;
( x; _3 J( r2 a- ^  {3 @    }4 Y3 i( ^# C( Y+ y1 D8 B
' U* _% n5 }, H1 y

8 l1 m5 @8 i# l4 M$ H( P    if(key>=0x80)   // Alt, Ctrl, Shift" C; b+ P8 O0 `6 {- ]4 f! `4 ^  P
    {" U/ L1 ?, p& w( Q* I+ |
        uint8_t bitset = 1<<(key&7);
( L6 n1 E. u2 `7 j2 ?2 m5 |        if(onoff)   // non-zero is key up$ B8 \  \. _6 B: i4 c
            report[0] &= (~bitset);  i4 @1 G5 B# Y% ^2 T
        else
; y0 @9 F8 c" H2 f& Y) n1 ^) K            report[0] |= bitset;6 I- A3 K) @  U  D* Q, L, T$ o7 O
    }
/ [. C# x  H" l! S+ \- \' a    else3 `8 o5 k, Y; E% j+ U
    {
' ?) t( l& e8 n        if(onoff)   // non-zero is key up/ G0 `0 r: {3 I7 T
        {
) [8 C- q& ~& l( G: f% f            for(i=2;i<8;i++)
! l% H+ E% Q+ {5 Y            {
' |: ?8 u0 B  H1 b7 C, y                if(report==key)" M3 b( k5 d4 a# U, Y6 J  K2 t. J
                {6 ^0 L. Q: _5 Z6 J
                    report=0;6 `+ v8 k7 l8 A
                    break;" ~% x9 p& u5 @% ~) W6 ?2 s6 \
                }! U$ U/ G: j+ [" u( @
            }( c; a( m2 T: H$ r& f  T
        }
) }7 \; U; m! q/ h* R1 P/ A        else7 y7 T: Q; ^0 v5 H, j* g
        {
( }0 P& D; q) [; ]            for(i=2;i<8;i++)3 y5 p: H4 j/ Z
            {
: G- t) ^' x2 R$ K5 Q/ k( G- m  \                if(report==key)
! ^0 O, J) T: W/ U; K# I" `                    break;$ S! R# b5 Q0 Y9 z& o/ _4 _* i) e
                if(report==0)1 V2 q1 X1 N. O1 L6 |
                {0 \! |3 C& |9 A1 S/ _7 D1 i% P4 y
                    report=key;+ f4 v% a! L5 a0 i1 Q' e
                    break;7 ?+ Q, {: G  {+ r. T2 J
                }
1 n, K: L. a0 y/ Z* {1 i            }
7 M7 I$ c/ K$ L' v) c4 B        }
1 `, w7 w) B! S8 K4 v! B8 ]    }
2 B" a3 E/ i9 o/ L; x! X; L    for(i=0;i<4;i++)  b0 a. H9 q% m; Y1 O
        USB_PMA[192+i]=hid_report;
3 e  {0 r& ^8 s, {. \$ v1 @# A    USB_PMA[5]=8;   //COUNT1_TX
- Q) N# g. D. L0 u3 J    if(ep1_wait==0)
- b  o* y" A) W6 Z: `! P    {- l! B( h; M& D2 p- D
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;; S; n& f0 p; _! B. J
        ep1_wait=1;( ]* T; e$ b# {9 ?: P
    }
. g9 E; s" n, n3 X3 o}4 ^3 a6 S* t' p( K6 B% H5 s

! h( H  r; K% K& Q6 C0 A1 ~: K; Q2 D/ n% e
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。2 K3 S4 A4 a) H
keyboard.zip (8.7 KB, 下载次数: 6346)
. M8 {0 t  e& [& y' U
$ M2 {. L/ ?& B7 F( C
* y) j# _* _8 }$ O* }
5 Q/ ]1 p) I" w0 M3 h5 g

* d& t( T5 v! `
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。1 B) K, L; D* v; y4 `9 w& X
回复

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘- x. T1 S2 @; j% {8 l
不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
9 T9 ?* `) ~8 J. z* G刚开始我以为要把打字机改造成电脑键盘" X' e( c: o' P; C
不过楼主也很厉害!
$ |$ @5 Y/ [4 R9 R: ?3 v4 b+ F
哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
( j- s  l2 s3 ^9 n1 [" i
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-11-8 00:29 , Processed in 0.204012 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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