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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
3 Y+ S5 X; W3 u3 S0 T# O: K* M* @
5 j: [6 z$ z" m: [( h3 b. T# yhttp://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
( X+ b% y% W' N' g这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。
( a3 V3 C+ B: h/ y, t% m" ~: B. @4 r
在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。

) s7 J% p/ F: c- h7 G) d3 E" T

; B3 i1 ]+ |# o( {) w. C7 p) H 235140i3a36qivqzuvmt5q.jpg.thumb.jpg & U" s9 ^! \" G: ?7 _8 _: c
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。* X# H; r3 d1 c4 b& k- _) ~6 L4 P
001734klbyoluenuwz4h4b.png.thumb.jpg " R# ~0 C5 a: F9 E# Q
为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:$ Y9 _, }* r, z( M2 [
003625r2agx2f5v922cf2f.png.thumb.jpg * y: m2 [; z& J5 ^! P! S
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.3 |0 ^# H1 _; c6 c6 X, Q* s( B: y
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
% X% a+ R8 m& P' I
005836yvs0wvovwsssgd3o.png.thumb.jpg " i$ V& Q' t- p
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。1 V# h, z% F. m6 }. c. y& l. l- e
3 H7 b! x5 V0 v2 B3 T
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
- \! `: J# |( |
) j. d- N! E' ~* R到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
: H8 e- b  c: N+ t3 m2 W
- q" o' `: O( K" [0 c/ b( g机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:

, E8 c; h5 _' {. k4 Z) N 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg - H/ V6 C& J: v
----------------------------------------------------  分割线 ----------------------------------------------------------/ N: D$ w2 Z$ i, d* _

( P8 ?: Q7 t, o8 C3 O2 B# X先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
# a8 B9 N( f8 Y
020011osionbunl4ui44vi.jpg.thumb.jpg
& r8 F) _( U6 Y. {6 J轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
7 z* N( P* Q. w: \ 020017j8ycmnv7788bqv52.jpg.thumb.jpg
" ?" _7 y) x& O特写,80C49$ j' e  A6 K, `( H9 w' r
021040oujzuvtut6iujtvz.jpg.thumb.jpg
. j! ~0 F  C; X% }3 @2 T5 }LED部分,使用了一片D触发器锁存指示灯状态.  |% k0 V9 s8 `: H3 v# R& O
022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
2 {& v9 ^: Z  `4 A: M- F暴力破坏,将80C49拆掉8 q% n7 r3 O: {" j3 r7 O
021113e48qq98vyohvhzzh.jpg.thumb.jpg
, R( H9 K* S) K% b拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
9 j" n8 f. l7 o! a 021125nc9az6dj33rlds2r.jpg.thumb.jpg 3 n% N% o  A& j3 ~6 D3 Y7 h
焊好元件后的板子,准备替换80C49
0 s3 C2 {9 N' q$ t 021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
6 B1 C0 ~. U. X- U) z# C0 B  a用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。
2 z" h0 b' i% T( |/ w& N 021104shifhnrqbr3o5nlo.jpg.thumb.jpg
( p$ ^  T0 b; @6 R. }3 w# e这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.
# i, O. p9 o5 \& L: ?1 h' E 022003ym1p9u4ug40280uu.jpg.thumb.jpg , s3 I! Y5 k- V9 @0 _7 H
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。, ]9 B0 Q( y- t
023313kt141q9qajtol7ma.jpg.thumb.jpg 0 b/ i' T, _# _2 d# a
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。6 j0 w+ W3 j: U
023322nt7l5xb3ltttkltt.jpg.thumb.jpg - M- L% u' P0 q# U8 K: D1 J
主键区键帽就位
9 `. ]+ V+ O! O6 _# |* j; h9 j( t 023331hin88e8wkrwzwikx.jpg.thumb.jpg
, g# w) a, Q- C( D) N编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。" c# M/ l6 g! ?
023336wjzlgopugg1jyy79.jpg.thumb.jpg
, x6 I: M$ e% a( h! }3 b1 k: v  z最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。9 s& z! j/ x8 a% }% E
023341sffu4j3g2323h6fl.jpg.thumb.jpg ! z+ x& A+ |6 v- w: A
7 p- n5 a. r. s/ o2 k; F
----------------------------------------------------- 分割线 --------------------------------------------------
2 ~9 q! b3 K* A* k* c% Y0 ?

3 R2 y5 Z8 N6 e

' L  ~8 S) ?2 E/ ]6 u1 D5 Z3 A% `: G; f* p; m8 M1 R6 \

* Y+ q5 [9 M. d( }! L/ J8 U9 J- M0 j, i; v' k' Q5 A6 C- B' E
  S5 R! }9 P6 ?7 U4 a. X! R9 l5 H

+ ~$ X+ s  I* J4 `; v" D" }6 F( a" q) Z- V: g

' A! {( W% j1 I$ W+ O5 ?$ b- `! \( J  }: D$ Q4 O

' |6 R1 x& [5 S" B/ u& I
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
2 C* b/ z1 |0 E3 M3 U7 y80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
4 p9 w  f& o1 l$ U% d' C% j( ~3 D 104025nzibm2rmiomhyirm.png.thumb.jpg 0 D  i- z: u( e$ ^' W
6 `# @% m% R; T* G) z" U! n" Z8 P, f
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
) K8 {: x' N: W+ x2 j2 z: t9 V 105004zkrez5houvkkznko.jpg.thumb.jpg
  D1 ?4 i% `+ Y9 }+ |! i1 F

/ x  `7 s/ }! C( Q扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
0 _: ?; C5 C/ j5 u
* m$ [/ D0 J! i2 E这是我设计的电路图:
4 p/ g  E" P- w0 ]& _: u 110344ej2z2oo2rflo7oe7.png.thumb.jpg
( [7 X+ E: N: B/ E, x, N
2 \) y  T$ C/ k. q
PCB Layout:) H/ l8 o9 F# P5 U9 Q4 Z( H6 P
110847jjbjvt34vwt3v5bb.png.thumb.jpg
- N$ u" [2 l0 E7 I- [6 _

# C  a0 w  j) w不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. 6 T: S7 ?- R5 q1 G5 O/ E

+ W) `' t+ ~* |) W' Z% h# B
, t8 V/ G; j  V1 r3 x8 W
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 : C& j* f/ p2 _. N- d1 d+ k
; z/ K( Y, O+ M5 {& ~* ]
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
9 n+ L5 K8 a3 N1 ?, p- |
. W0 i* E0 g% M! K- f; m总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:
( p" k1 B/ \$ i! R6 ~ 113818pmrfsb6z0byt6t06.png.thumb.jpg
! o0 D2 f  ~0 ], R0 H
$ d% @+ d! S  `7 }* s5 e8 [
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)3 M& n# {; T  |& d# b9 h

  P+ g9 f- P% L& R4 \6 i( t: k2 sUSB的中断ISR,bare metal哦
; W* [! A4 ]4 l+ a* H5 q& P
' A& n, i; o# n: ?& ?9 \$ p
void USB_IRQHandler(void)6 e" n3 X% _7 C1 D
{% K; a6 T* Z$ Z& w& j  s
    if(USB->ISTR & USB_ISTR_CTR)+ G/ h) n& d4 b  t$ H' f$ Q3 m. m
    {
2 F) Q0 d; n8 @, Z$ o3 B* l# g$ H        if((USB->ISTR & 0x0f)==0)   // EP_ID==05 f8 H8 ?; i) I: J3 u# S. O
        {
. J' ~, b  S: t% g. h, J. C            switch(ep0_state)
- a) Y1 p2 \  |) R; q" {$ W3 @( l            {& }& z$ u8 T- b5 W9 W. d
                case 0: ep0_state |= 0x80;( \6 k* {1 I. P$ i
                        break;
+ ?1 d4 v* \$ w* T9 r; W3 ?. B                case 1: if(USB->EP0R & USB_EP_CTR_TX)  T0 c$ c7 ], Q2 W- U0 E+ c8 g& ~" S
                        {& G4 a5 l  N* P
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
# N9 u3 e+ L1 d% {1 F$ y- m                            ep0_state=3;9 ^# G& T+ C0 ]1 v
                            return;3 X$ w8 a0 a5 c; e" E; ^9 |/ T8 [
                        }
7 \1 k% c, o7 |+ m* |: }4 N) D                        else
# ?2 D* W* p1 p9 I                            ep0_state=0;
& d& h) I8 l4 p                        break;) a  C5 N7 \; ?5 [9 i
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet& b# R+ s3 Z) R: v
                            ep0_state |= 0x80;% l* d, I+ Z$ f+ I, }
                        else/ C8 ^( ^3 x+ P3 [
                            ep0_state=0;, p: @% C- ^. k& _* R; D4 O
                        break;
1 g7 E; |: F$ z4 m                case 3: ep0_state=0;
9 `/ Q1 T" x+ [5 p7 q; }' W                        break;
. T* e. y* R  n1 W                case 4: ep0_state=0;
0 q& J5 y! f6 e" y                        break;- D* E6 q4 v& `
                default:ep0_state=0;
/ q4 A3 ^8 g7 e; c4 x                        break;6 C' A+ Q) Q  Q/ |! z
            }
, B: X% r0 b9 r3 S) s            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
' l( U7 V( b4 G5 f            return;. q# Z8 _  ^3 t6 t/ Y% s
        }# x2 j* I% G! F8 x5 a9 S
        else    // EP_ID can be 10 m: t4 C5 q: M2 w4 M
        {8 M+ |8 C. n- F
            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
, j7 i2 V* ~7 D: ]            ep1_wait=0;- c/ c1 d; u  u. o
            return;' }+ M. M, C; l* T- n8 `/ v" I
        }" K' H! [3 q( l- \. U( t9 H  }- c( {
    }4 j* s6 ~7 Z8 Q6 p6 f0 u
    if(USB->ISTR & USB_ISTR_PMAOVR)
, e( E3 P  L% a+ Y: }. E- `( y4 P    {
5 h: E6 H/ P$ H3 W) n3 G+ M2 z/ e2 t        USB->ISTR = ~USB_ISTR_PMAOVR;
! C1 Z' ?' H# n1 e* |    }
2 R  Y) E9 {' p% j    if(USB->ISTR & USB_ISTR_ERR). h) R8 c! @3 R( q
    {
/ g* G; W0 ]# `9 _3 O% F        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear6 U, c3 @1 K: y% l4 \# a# C
    }
! [. ]! w, ?' @: v+ N6 T    if(USB->ISTR & USB_ISTR_WKUP)( [  W5 A) b, u. K; x: t
    {
3 K9 g  J/ q& U  y8 S        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
# r' c* ~) T/ V        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
* w9 ~/ i. n5 Z  r/ v0 y    }
: l& l% F( ^" z% c2 z6 b/ y3 E    if(USB->ISTR & USB_ISTR_SUSP)
8 T$ U9 P2 I2 {7 v    {3 J7 G% B) [3 K7 l3 P; B- j
        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
8 y5 Q0 \' E8 O5 d" L        USB->CNTR |= USB_CNTR_LPMODE;   // low power# h1 q2 n" O- y
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
, A+ {' ~: n/ b( e' l* R2 D    }5 i2 n7 Z5 e/ h9 t1 {
    if(USB->ISTR & USB_ISTR_RESET)
- U  Q# g+ u% X    {) N+ N$ B. {$ S. V1 l- o  y* I  M
        USB->BTABLE = 0;    // buffer table at bottom of PMA
1 z+ w% \& |& B3 G0 z        USB_PMA[0]=128; //ADDR0_TX
# e- A- l) o, Y6 {; n8 B        USB_PMA[1]=0;   //COUNT0_TX9 j) Y6 m! Z6 ~9 h7 m9 M" H
        USB_PMA[2]=256; //ADDR0_RX% g( k: D1 G4 N1 y, {
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
. e+ @- B( x& ?  z' C) d, O        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
1 ]0 Y% Y2 s. `8 \5 g        ep0_state=0;
" W+ r$ o8 g' R! S- o  S- X        USB_PMA[4]=384; //ADDR1_TX
) |% e. X9 k$ l6 _; A        USB_PMA[5]=0;   //COUNT1_TX
& B- I& Z$ G1 |9 M+ U4 K% d4 F" I        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
3 A3 W/ E6 _/ F0 q. ]        ep1_wait=0;
2 C1 J4 R) v. |6 ?        USB->DADDR = USB_DADDR_EF;      // enable function
7 X: O" z9 \/ f% g. e( |: P# l        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear6 [4 T5 b2 @7 j9 H# v4 Q* k/ T+ m
    }& G" f* [( G5 ~8 V
    if(USB->ISTR & USB_ISTR_SOF)
, y& l- p; f. G" q6 l: S    {+ y7 ]& J8 n" ^) n
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear
0 [6 G2 w- R0 _* l" ~+ J# G0 a0 Y    }5 F, r- `( g/ H2 L. O) I9 p
}
) h7 D4 I+ G. [6 ?$ O3 e3 {" Q; ]7 k9 M. l, T
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。  q, `" d$ w' o
5 `3 B( q" v/ p5 @( o
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。5 S0 e1 [! H3 Q  b7 B
, h9 a' g& E+ M2 K6 F& P' B/ x# Y3 P
    while(1)1 c  G9 o. l/ n. t
    {
/ R& Q9 O9 l3 n        static char row=0;5 ]; B4 p9 b+ `( }8 {7 V5 U
        __WFI();
! n2 h  I: n* p! J1 d/ ]6 z' {        if(ep0_state & 0x80)    // request data processing6 v8 x5 W# M7 ]4 F$ H
        {
$ a* Y7 i: c0 u# }% h            if(ep0_state==0x80) // SETUP phase, M# l5 H* r% o$ J
            {. C- R8 L$ g& B
                if(!setup_packet_service()), a+ q( r+ i' e' _3 Q4 I3 C% h
                {" ~* d$ A8 N+ [' m# w# o' E+ Y
                    ep0_state=0;
7 n6 k: u- S  ]9 q( S7 i                    // not supported: l. M: K6 h0 o: u
                }
% N/ E+ V2 v) i: i  e+ ~                // ep0_state should be set to 1 or 2, if processed+ \& n" f5 n/ ]; K, G) J3 A
            }% p! }* Y; @# @- A$ q4 h3 h
            else    // OUT phase& x8 V3 q" J/ ?7 Y8 s) z" e$ l
            {
8 }: k) G  L2 r5 h! a. _/ G! }                // process data
  j5 L8 ^) l4 j& C% B                show_LED(*(uint8_t *)(USB_PMA+128));
3 S9 \( }% z# ]- R                ep0_state=4;
2 _  F( Q4 R8 H& k% a- ^) x                USB_PMA[1]=0;       // Zero length DATA0* x/ n1 Y& z4 ~9 ?2 B) |
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;+ j% K! G! Y2 \1 D& o/ T
            }& F6 V8 g7 }" h& Z4 o0 }
        }
! s8 n8 W, z5 Q+ C& ^        else6 k4 _7 Z! ]# R" B- X
        {
2 ^( U. U. e- {; N1 i7 {2 u            if(usb_address && ep0_state==0)% \* `* ]* B* y/ c% J5 k2 {* q3 X2 k
            {2 w  x8 `% z3 w
                USB->DADDR = USB_DADDR_EF|usb_address;
8 B/ Y# X3 b) m. H, K                usb_address=0;' w- b2 Z9 z8 t0 g
            }( a/ ~6 R! V0 i' L6 p8 e% N
        }
8 J( \. {) O$ {        if(row!=scan_row)   // new scan line
- F( _4 j- q7 Q5 v( N0 Q4 d        {; y; W" z- r1 P* ]6 {3 i  J) L
            if(key_state[row]!=prev_key_state[row])
$ w/ l4 p% g" H' r$ e2 |4 b% O            {7 o' E* T/ Q. i  `' }
                uint8_t test=0x80;
, U6 W1 z- s6 K                uint8_t diff=key_state[row]^prev_key_state[row];, T0 w. g0 k/ O" L1 B' g
                for(i=0;i<8;i++)
; s0 x7 z. X5 ]/ K, V                {  c/ }6 G0 U/ E4 \
                    if(diff & test)
5 w7 O- x. K% e1 E+ z) S6 g7 j                        update_key_matrix(row,i,key_state[row]&test);
- S! d9 M/ r" Y4 r* |                    test>>=1;  Y1 |! L4 c# P" D% b# M
                }0 }1 I3 h7 v5 c% w! D' f: ^! v
            }$ ?7 F1 h  ^; M% ]
            row=scan_row;
* B: v! t2 V& e2 {+ H        }
+ U4 w( y" k3 X( F( v; _    }! o6 ~* {) u6 J3 c; B' u" V) j
- |3 c: ?9 p7 ?! l- }7 ~8 f

& ~! F: _7 W0 I/ p: zEP0的控制传输,把用到的请求处理一下
5 s5 A% S' o# G2 Ochar setup_packet_service(void)
- A  H+ Z& w( W# y+ L1 y{
/ L2 B* e3 C# X! ]# ~+ p3 G0 l0 s" B    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
0 V+ Z8 X$ R1 ~, G. q* M    {
9 y/ p( ~4 U8 l* x+ g7 q        switch(ep0_std_req->bRequest)
3 x5 q+ v  T2 Q$ [' }; _% I" Z4 ~        {
& u& n, D9 O( M1 ^+ _            case REQ_GET_REPORT: break;
" J7 e7 E/ R7 U5 Z% y- U% d            case REQ_GET_IDLE:! J6 F9 V# n% ^' H2 o. c; `
                USB_PMA[64]=0xfa;   // return 1 byte
5 s/ c9 [& Y+ k4 R9 ?+ I/ O                USB_PMA[1]=1;
1 i* t0 ~# b: k2 H) a# E6 M                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
- ]6 q8 E5 J# C8 y                ep0_state=1;1 _$ ^0 x4 a3 s4 S
                return 1;
2 v2 I. Y5 T$ e                break;: }5 l) b% a8 L
            case REQ_SET_REPORT:  u: w5 f! b0 Y5 u. l- x
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;% l) H( l: S6 n7 A7 C, z
                ep0_state=2;
" p: P1 {% _+ j6 I                return 1;9 M5 e1 q6 W. k1 e5 M: L+ j$ c
                break;3 W4 v5 A  f, s+ j, c0 Q
            case REQ_SET_IDLE:) E4 y2 w- L, k8 d% x; ~% ?* w2 X
                USB_PMA[1]=0;   // Zero DATA& F/ Y! I, v- K1 b, p9 s8 e+ ]
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
6 S4 \* o( N8 w$ u$ [& H- I: V+ n                ep0_state=4;
8 C& N, A( V" ]$ h                return 1;9 R6 m" i( d3 _: z
                break;
2 o$ |0 A/ ], U        }
9 v2 h/ D& Q3 v% h! P8 K        return 0;
5 w6 H- i, q6 Z% u    }8 Y6 x2 J& w+ y3 m) P
    else    // standard8 \2 S" R0 l2 Q
    {& }" U  F. R" ^8 R2 \8 z
        switch(ep0_std_req->bRequest)
/ |, c  C2 H" m  [        {7 s' u2 P/ S1 o3 ?: v9 B) J
            case REQ_GET_DESCRIPTOR:
9 M: q. y+ M# k6 U. [5 |  h                return descriptor_service();
7 I. x( k. F: J1 I                break;
; j4 |" i* _9 l' H8 g1 s: Y            case REQ_SET_ADDRESS:
1 q( h! {6 D$ o; w- i                if(ep0_std_req->bmRequestType!=0x00)
# x4 B3 s/ o, o# `: J1 ^$ G/ ^! _                    return 0;0 P$ Q' U( I) {7 \1 w$ K; q
                usb_address=ep0_std_req->wValue;% f& F0 c$ ^# ^: J, K/ o3 r
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
  u. _. A4 k/ ?                USB_PMA[1]=0;       // Zero length DATA0" c( q+ u# w7 ?0 h; ]
                ep0_state=4;    // No Data phase
# s  }  Y5 A+ ~0 ]1 V. f: q) J/ M                return 1;
1 E7 ^! S6 B$ {6 w( O            case REQ_SET_CONFIGURATION:% c, U8 }8 Y& q9 _4 L9 r
                if(ep0_std_req->bmRequestType!=0x00)
% q, y) k3 J; H1 y8 |1 ]3 T3 N                    return 0;! e' p, V( O, [7 c
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;' U5 J% y3 s& T! B2 O2 e' V
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
" e1 G" A% g4 g9 n9 T5 k7 z                USB_PMA[1]=0;   // Zero DATA
8 {# S' L% \, R# j  T2 p                ep0_state=4;    // No DATA phase3 ]$ c) i# B" P( t8 c; }! \
                return 1;8 v: W  n: e3 R0 N# u/ o
            default: return 0;
0 n3 s8 b( W0 e# i9 J! D        }
0 e- z5 Z2 d# J& Z& [    }- L; @, \4 U5 m7 p
}
& n$ x2 C) z5 v. e" Q
3 X, A) j* M4 R2 Z) I
* j& {( ~7 L) Z1 I0 _4 A0 e: i# h! o% N+ }
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
) [( C. M& g6 m; l( W& ]/ Y# }char descriptor_service(void)
2 C, b3 p6 w4 N8 E) ^$ T{* e1 ]3 d) n# k/ F8 a( M/ k9 e( `
    switch((ep0_std_req->wValue)>>8)
' ~9 H# R2 I6 X- d# f  v    {: _3 l/ L+ o7 H$ e
        case DESC_TYPE_DEVICE:1 v3 h- @' \1 W/ a' z; E8 P4 N
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));
6 D+ A' K0 h" j4 G7 X" v# R4 t* I            break;/ {# I# \( f# b' h5 h
        case DESC_TYPE_CONFIG:
; L7 k6 {; X' W5 P* R! m- C3 d            if(sizeof(ConfigDescData)>ep0_std_req->wLength)- b' a6 n' O( w8 n6 A' ~
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
: n7 [# q* i7 B7 \7 f# n1 Q            else2 r! d% `2 _$ r
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));4 ?8 t% ~: X) n6 O4 H
            break;! A9 @9 c- U1 B5 K' e) [! ^
        case DESC_TYPE_STRING:
$ [# M5 Q/ y$ [" X% @- g) N+ [            switch(ep0_std_req->wValue &0xff); C, w5 N/ _# o) j6 L% G5 z3 {, e7 k
            {
8 w; L4 V8 M: q6 \                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
7 F) a' Q9 a3 S5 m                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));( d* c5 z9 B- b+ X
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));  Z$ Q8 E7 g' b/ ~7 N0 u0 v
                default: return 0;! m5 l" z# D! f3 j5 t
            }
$ i9 N& [% l) n/ w% r            break;
: g$ H/ a. O- y. `        case REPORT_DESC_TYPE:
& V) ~0 ^* p" U# O7 W            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));& I5 W/ x; r/ t/ g" b
        default:
5 v& b' e4 z" t8 I  F  q            return 0;5 T$ y# P" p, c" Q
    }
4 S6 G5 B; n* A( }- h}: C4 ^. Y0 Z6 D" w) w' J
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.+ s& u; E  a  t
void TIM6_DAC_IRQHandler(void)
- D3 t+ Q2 ?6 ]8 V( c! c+ D4 z{
) A' ?& g2 s7 C7 J9 a3 O. d4 r    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);3 H; p4 [, h- B8 B& [" ?
6 E/ |) f* B# i0 w

8 w, c/ z, B& \" ^4 v0 y! x+ W    TIM6->SR &= ~TIM_SR_UIF;( R1 @, Q% Y, f& f$ l+ f
    prev_key_state[scan_row]=key_state[scan_row];: a/ [8 d5 X, J# F; P0 C* ~
    key_state[scan_row]= *PA_IDR;   // update key states% e4 g3 l5 i- ?1 C3 f  h9 K
    switch(scan_row)
- }% Q* p7 q( O% N& G  j& y- b! V    {
5 m% z$ p! Q3 j( Q9 i5 a( M        case 13: // next row PB143 A- T# Z( I  n) i9 O
                GPIOC->MODER = GPIOC_DEFAULT;
7 i  [) n' E' M2 t8 |3 ^& F3 m                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;9 ?7 A% o% \$ E% r
                break;9 T; S" J& a; u" p# e% m; ?7 F
        case  0: // next row PB15
. v. {7 b; a5 p! }! b1 l                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;$ m5 u. ^$ l. W( n' E
                break;8 A5 c1 R4 K0 r4 a
        case  1: // next row PB3$ d, `7 c% }1 b- ~
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;& x1 y/ J8 _! T2 K0 J. r) P
                break;
# G$ F0 L. h' x' A! ~& q3 g) z& I        case  2: // next row PB4
1 w; ~  ~2 ?4 E  i- h: Q                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;% {! j/ a8 r% U- l" O
                break;
' t: M+ p' V4 M0 ^        case  3: // next row PB5, n5 Z0 s* h5 |8 p
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;4 l6 S' |4 _. e% E* D- p" ^6 R
                break;
4 h: h; E7 n2 k# I, S8 m1 U        case  4: // next row PB6! A* V. ~" B1 T( b; c3 t' e( p* ~
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
8 s. l2 o& v! x                break;
' i5 |& ]) Y7 z5 |        case  5: // next row PB7( W5 B3 Z$ \* b) a$ T7 u* s6 k
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
* b/ w+ ]( {+ E, I, m  d                break;/ c+ S0 Z% D! i) V
        case  6: // next row PB8
( B  b! b3 @6 _- x' y4 f- I$ E. Y                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
) K8 K! E& o7 C$ Y9 g                break;# r( K& O; t! g2 d# I! S' C, G' j& j
        case  7: // next row PB9
) P; N8 H. a5 |/ _1 z+ s$ ?                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;& c& o3 L5 W2 z1 S, P
                break;
' j2 d7 ]; ?3 _# n3 r        case  8: // next row PA8
, \7 L4 `; T( r) `( B4 D* e' a                GPIOB->MODER = GPIOB_DEFAULT;7 \5 v) L6 x; c% x( g; N
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;# r5 {# |8 X- f# U3 g4 t  L! C
                break;+ E2 h( ]8 m$ l0 i  _# T2 E
        case  9: // next row PA9& ~1 b8 `2 \! n# F% `, I
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
3 M8 Z) T& x, D( M                break;
/ f+ O# _5 o, F& Y        case 10: // next row PA10
& C- p; I( {) P0 Q                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;, U: }1 q8 i, j9 Q+ w. F$ x8 z  P+ ?3 u
                break;
' |" d, i5 x  ]3 c% W  H* {$ N        case 11: // next row PA15
3 X* ?& Q4 D+ @' e                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
6 V" A! L& l, P- A3 Y6 R4 p# U3 F                break;+ {9 l0 W$ V8 p, n
        case 12: // next row PC13
/ j; d  z  I1 A, i% U% Y                GPIOA->MODER = GPIOA_DEFAULT;
" K0 z! K4 a7 K) i                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
) s# E* v' {' _+ k9 m! E$ ~                break;/ B0 l4 p/ D# w" E6 k# `) C
    }4 h- V5 v8 `2 S2 m# v- ]
    if(scan_row<13)
2 i7 H" `" d1 Y, T, g/ P! ~) N        scan_row++;
9 c8 R* v% m* |    else
, H7 j/ v9 P- x        scan_row=0;
, x/ @7 Z3 M4 H! U- h9 Z}
) _8 q- U: Y. G% R7 \: F- {
: u. x; k9 y; E+ z8 M: B6 P
, m2 ?" o2 C- X& M( `7 }* W7 l
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 1 z4 H$ r$ m0 u  l0 E& y
5 `0 ^* j7 M4 O4 i9 O
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
- P. X$ f/ z2 V& J, }# v* D, |& ]" P- O6 V1 E6 n0 {  ]& Q+ v
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:

2 O3 ^; M/ V0 j' ^6 t3 Z- h5 ?' z* q$ t' _  {1 j
/ X' w( n9 {' `  |
const char hid_keymap_qwerty[14][8]={
" T  {9 L7 \: q1 g    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
6 ^* R# H# {9 J1 m! g    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},3 K0 i9 _' L0 a( j$ Y+ y
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},: f: _' B$ [- Z: M5 x0 m( j
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},) B0 s  L9 N% P& \7 Q1 v% D! X0 Y
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},0 b1 w) V* ~6 t( R
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},  ?1 J; \2 E/ s! w6 K
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},$ a  ^# d7 s' a' G7 Z7 ]# ^
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},8 f, J6 {0 N1 w4 q) u# e+ t" @: J
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
' B5 I8 V) s# l, I9 ?) ]    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},& a9 ^- H# @  J3 \. |$ Q
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},4 r3 ^+ h% E1 |+ G
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
- t. F8 h' l5 O% v- I    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},8 c& ~7 P" v- E: a: i; S
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}! ]7 Q! H  j1 |3 m3 M: g. v
};/ H0 f+ ~% B4 x

3 `) p6 C& g( s) b" O
9 X# K; v* Z# Y
const char hid_keymap_dvorak[14][8]={5 d7 P/ G: {1 A- x- P* D
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},. O" y1 }% b/ ]# R0 R. u6 {0 ~' d. ~
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
  S9 L+ O3 {2 u( H3 M: J" D    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
3 b% v$ j" n0 K9 M$ P    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},4 P* r" ^  L# F% N+ ]
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
( B0 U: S2 D' {; `- M9 `2 @    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},& v1 m  _- d: |: }% P+ p
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
& o; E% f. m) i: c6 s% g    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},+ g4 O8 t4 {6 V! g1 x6 k
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
1 |0 h* W; X  o  ~% u    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
: `8 ?+ _! W/ H; d  D    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},$ Y) Q8 G1 ~" [8 d9 g  |
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
/ R) U/ C/ ?8 n0 B/ k! |$ z    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
- Y; p. m7 v) |. V. V. U    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}" ]0 y) g9 u) U; a, `" W. r6 {0 }( a5 Y
};" l- b6 ?7 H0 I2 @6 z2 r& ^$ D, r, d
2 X7 T9 m8 o, Z: w
7 T$ p+ P: M0 L* f: Q& ?+ K9 R
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。* e5 e3 p: Y: R. C3 ~( _$ b! T

( L  E- N# _1 n8 m; zHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

- _$ z* l; Z, u( j. U! a6 H+ D; X; y' J% [
void update_key_matrix(char row, char col, char onoff)) c* H9 w2 R# w  m3 W
{
9 {2 l9 ^* t9 ?8 P    static uint16_t hid_report[4]={0,0,0,0};& s# V3 [5 f+ s! A$ [
    static char (*hid_keymap)[8]=hid_keymap_dvorak;
  t5 p! Q% ^- U& i2 l$ F) e- y4 X
3 M  f* V: Y1 @# L

% z) d3 r$ z3 ^% w& `4 m% V& b    unsigned char key=hid_keymap[row][col];2 \' v" {+ A7 F1 |/ A0 e5 Y' I. V/ |
    unsigned char *report =(unsigned char *)hid_report;  F8 K% k; y0 b
    char i;. W. G) N" Y- q  J  |7 O
0 `" r% ^7 L: X7 u) A! ?9 k
, _3 o; X) `% ~* {2 B
    if(key==HK_MODE)0 H6 v0 F& f6 F( U
    {0 R7 a# k1 H! u- p3 t
        if(!onoff), @$ ]3 I8 v: }+ C; e# f4 ]
        {
4 B" g1 L# }9 K# K            if(hid_keymap==hid_keymap_dvorak)9 J; O) V, f6 `/ K
            {
- u7 ?8 \: \' b: K$ L4 v6 [8 P* c/ |  j  z                hid_keymap=hid_keymap_qwerty;
7 y4 l, Z/ j9 l: @1 c: z                GPIOB->BSRR = (1<<2);9 C" s( E, l/ T9 `. d
            }" w9 |2 l' S7 y/ n. |) g
            else: @/ g+ }/ l% A, Q2 ^: f
            {
; h/ C. w0 L/ y. W                hid_keymap=hid_keymap_dvorak;) z8 w. e) ?- O% d% o
                GPIOB->BRR = (1<<2);
4 F: @' k1 U) @1 `            }# G8 W/ g) I5 l
        }* m$ j  H0 c& Z1 \: b6 m6 [
        return;
7 P! ?: ?3 v3 o8 z. X    }" f, J: \7 s( X" W! Z
* i2 ]+ ~3 B  ~" {; P2 G# P

6 Q. s: @0 y7 [; M/ I8 `7 f0 z    if(key>=0x80)   // Alt, Ctrl, Shift; U5 d* S: o* e# f' I
    {
& e. {0 P) b2 t& P1 T! s        uint8_t bitset = 1<<(key&7);
% {+ B+ I5 a$ z% Y5 [& B        if(onoff)   // non-zero is key up- T& I: |( Y" l/ s$ a/ t$ ^- {1 c
            report[0] &= (~bitset);) U4 J' D: Z7 @$ w4 H  Y
        else
" E+ v7 q; I' z$ B& l, K            report[0] |= bitset;$ i+ `# K  Z) i
    }
( T% s0 S, B/ Q* v  k# F6 W    else
- P" B* C2 W/ F& `9 U2 Z4 C; D    {
1 Y4 N* F$ z- z        if(onoff)   // non-zero is key up
* H6 w7 z( {$ S8 n# `' m  G, c        {) s# ~% R9 J* m! r1 ?' b
            for(i=2;i<8;i++)* Z+ Q+ v' ?/ j; k3 f
            {* H+ P) p8 @( b% z1 K( t/ E) w
                if(report==key)9 I2 Y) r( n/ Q, F
                {+ R$ e3 L8 @0 ~# y  i+ Z8 I; z
                    report=0;. ]4 i2 F' _7 p3 E: A
                    break;
, X9 q- z! H7 f% m( g' n                }' i* x+ q; j/ o2 K& e
            }
5 u8 G, j3 }! E& `6 w6 Q        }. l6 Z( w0 n) F* V: i& r- f# R! o
        else
/ G; m7 `/ ~: x* s/ j        {
$ |7 K6 _) ?* z) o" K            for(i=2;i<8;i++)
. ^6 P8 s$ k) F4 _* X            {* Z  |: e* D6 ]! a: b
                if(report==key)9 x$ K3 E1 q: G' n  T$ O
                    break;
7 U; W: Q9 E0 q+ {! c                if(report==0)1 ~) p5 v: o, d7 L6 Y. W& b( e& R
                {
6 U8 K' V) B1 j" ?  o) ]: t$ j; d8 O                    report=key;
4 _: l7 `  ]0 I5 g2 g                    break;
( b- V$ G7 u/ G" u; m                }
0 W; I3 A" Q5 D' X; B! |            }
: R- t' }2 A9 a        }# l2 S1 D/ Z8 z4 k6 f: {
    }
# @' A2 w# z) D1 Y8 k. A2 k: T    for(i=0;i<4;i++)- M: j3 A% m6 _: N% c$ V9 o- ^) m( e
        USB_PMA[192+i]=hid_report;8 S: b9 q) J+ y3 j8 E8 r! `2 X
    USB_PMA[5]=8;   //COUNT1_TX
! k; L! Y. a: m! L* H    if(ep1_wait==0)) S- D. ^/ q( I! a# Q
    {
" P) ~2 j% B! s. U        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
, J8 M  b# z, e        ep1_wait=1;: ]% R/ L$ A* w& `& m- x' Q
    }
) c0 |7 F* l, z0 v; b}
4 B9 m8 X1 A. \$ ?* u# m- p
! g$ U7 J8 B. {' W9 q( |: h, N
9 o6 K3 Z' d( p4 V$ I完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。. S9 b: ~* A7 n0 E7 p# a  \; |4 |
keyboard.zip (8.7 KB, 下载次数: 6653) 9 j4 Y, }3 d4 ?: }3 i

0 z7 ?0 i% E9 _
2 A- r+ N3 Q6 x/ v: [4 T* a7 M- m! I' g2 F3 `% `
/ F; G8 s, a+ b- i  P
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
' p: T, J/ m+ T; B不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48* t9 W- P8 ?, J$ L2 P. s
刚开始我以为要把打字机改造成电脑键盘- i  `9 T& @& S8 Q
不过楼主也很厉害!

' B) o+ R# K5 N6 D5 Z# z哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
" C3 X3 C; N. ?" P( L' g
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-8-20 01:50 , Processed in 0.226240 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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