请选择 进入手机版 | 继续访问电脑版

MiniDSO产品技术交流  迷你示波器-袖珍示波器-示波器探头-

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

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

[复制链接]

24

主题

63

帖子

288

积分

vip

Rank: 9Rank: 9Rank: 9

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑 2 V/ b4 E( p/ O( M( ]1 n; n
. X  J  V# v3 V1 l
http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1$ y1 m9 P: u- T  x0 ?% q
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。
% r: Q1 E* s' g$ G% O7 u) R4 {" T7 Q7 k9 f/ ?
在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。
- k3 |9 E2 M6 s5 M

7 w, E$ ]+ O' g4 `0 ~  \# a 235140i3a36qivqzuvmt5q.jpg.thumb.jpg $ n" E4 J  v' y% o( V5 z# j" o
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。( o- f# w  u% e+ Y* b) f3 r3 f4 j
001734klbyoluenuwz4h4b.png.thumb.jpg
. r; k' K7 j* A; P  Z: h2 C5 }为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:
+ _( `. p& O; K# F6 w+ u& M7 o 003625r2agx2f5v922cf2f.png.thumb.jpg
' S& h& v; |* |: O; _其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.
7 p; w. y7 O# E, D% }4 ADvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
9 }: n4 X4 m) I0 L9 B: X/ G
005836yvs0wvovwsssgd3o.png.thumb.jpg 4 O$ @6 e8 d! f/ _; M
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
5 t1 y1 I0 [, H; g+ [+ Y( ^, n! d" `5 ?- I; F# c% j
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
( P5 E$ u2 z; }* J2 r) `* M+ N, ^5 M) F
到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。# G" Q" @7 y5 o( P2 [  v

2 k/ b! ?8 D/ A0 G机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:

, z3 P( m$ A- s( d: d( P 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg 0 k7 o: }  k0 Y4 L* Z
----------------------------------------------------  分割线 ----------------------------------------------------------0 n, ~1 u# t9 z+ D4 N

3 v( ]% t/ S& k( ?先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
3 J: }8 E, r$ E* W& S/ V3 l9 Z
020011osionbunl4ui44vi.jpg.thumb.jpg
1 D! A6 i( M5 U! g9 e% |轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。( d; ^% H, o3 n8 X7 i
020017j8ycmnv7788bqv52.jpg.thumb.jpg ( J1 Q; `9 ^" @3 S( \8 h
特写,80C49
  f% M( U( i* ~ 021040oujzuvtut6iujtvz.jpg.thumb.jpg / r! S5 @' ?3 Z+ d$ U+ N) a
LED部分,使用了一片D触发器锁存指示灯状态.6 W+ d1 x: E& s7 |5 N$ A1 c
022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
3 o' P. D/ `$ {9 k& p2 S9 ~1 b暴力破坏,将80C49拆掉
$ ]" E4 h- O- [, |/ y- O4 g4 f7 A 021113e48qq98vyohvhzzh.jpg.thumb.jpg
' ^0 }! ~5 D& N/ a6 i拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
: l# @# l/ ^8 i, v& { 021125nc9az6dj33rlds2r.jpg.thumb.jpg ! F7 S! u+ T: X# V% i) W* X
焊好元件后的板子,准备替换80C49( s- |1 G1 H  q& ~" V
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg ; I/ M4 B4 a! }: p2 L& Q, L8 m1 i
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。, C$ G9 e1 i; t/ G( `8 V) e! ^5 ^% x
021104shifhnrqbr3o5nlo.jpg.thumb.jpg
, i. D2 f$ t9 K# g5 c( v这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.! R$ ~/ Z# C1 t  Z$ K* h/ V
022003ym1p9u4ug40280uu.jpg.thumb.jpg
3 x, F) `0 R7 j6 b! }开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。" d" m9 `8 D8 R
023313kt141q9qajtol7ma.jpg.thumb.jpg + D4 K# `4 _+ w
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。% f0 t" i6 z2 M6 `9 P! r1 u/ g) ?8 W
023322nt7l5xb3ltttkltt.jpg.thumb.jpg " k+ K7 z2 j9 b8 l$ b( p9 O0 H
主键区键帽就位5 `3 {, G/ w- A; z' d3 c. L
023331hin88e8wkrwzwikx.jpg.thumb.jpg 5 G+ Q) I/ C& _$ u! V
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
7 ]! v2 o* E3 \5 o: D 023336wjzlgopugg1jyy79.jpg.thumb.jpg . i5 B( Z8 r+ h1 ^  ]1 V; }
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。
7 T8 I* L9 O4 `: R3 G% V 023341sffu4j3g2323h6fl.jpg.thumb.jpg
) s4 `2 T# g5 x$ |6 @

8 ]( w, m" \$ j----------------------------------------------------- 分割线 --------------------------------------------------$ h* P5 B; h* d# X
0 z7 V. u+ ?  q6 X, h% A% ^
4 U9 A0 C+ m0 _' Q6 d$ U
" x7 }6 M3 z0 ~% h! `! m( \

+ W2 B) u% s$ p* W! B6 \8 ?7 F4 T7 v8 ?" B5 V, \9 W

* b$ a1 J/ u. ~' n& ?, X* @
( l5 U: U4 M* n- V
- ^/ S. O5 Z& g5 R% Y
! {( o7 u) f, ?! F: a4 t
+ `/ g) H+ w3 x- v2 D5 [8 ?- F8 ?2 ~5 l& r
7 U; _' d4 ~6 M
回复

使用道具 举报

24

主题

63

帖子

288

积分

vip

Rank: 9Rank: 9Rank: 9

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。5 ^! [7 h% u/ b; g# P. Z8 m( Q5 O" T
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:1 I  I$ @  u4 M. x6 E) k$ e
104025nzibm2rmiomhyirm.png.thumb.jpg
& p0 t, Y( S+ d6 V
1 h. P- E9 |. K* L* D4 l
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号): d- B) C5 X  s3 o
105004zkrez5houvkkznko.jpg.thumb.jpg - b2 @( k) r/ N

8 x7 r/ A' F5 ]2 Y2 M6 [0 j扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。+ J1 h5 H+ S4 T% r+ y

2 z1 V/ e' a$ f" e2 J8 N/ d+ {( Z这是我设计的电路图:
7 ~( s7 l9 w) A# s' s 110344ej2z2oo2rflo7oe7.png.thumb.jpg
% q4 r0 P1 p( p
# U% u  K# K! G; ]  X
PCB Layout:" ~1 Z$ D* W* v- }$ q; F
110847jjbjvt34vwt3v5bb.png.thumb.jpg 9 f) B; F: x* S4 o( q
" m5 c* b0 T- s/ T. A7 ^8 a
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. ; J( g+ S0 }0 @

# o1 O1 X9 J8 E- Z5 s

; h5 b; J. A! A/ ~' G& T
回复 支持 反对

使用道具 举报

24

主题

63

帖子

288

积分

vip

Rank: 9Rank: 9Rank: 9

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑
7 {* {5 R& d, L# l! ]. F7 {; r' A# a$ ^- g2 ?! |7 \
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
1 }( f* V. F+ n. \. n
" Y- X& ~' `: {总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:, Q' y" H! C% ?( v7 V  G! P/ i
113818pmrfsb6z0byt6t06.png.thumb.jpg
+ Y" u3 e( {6 j/ m8 k$ u; C- ]3 l

5 u( |: T6 d. P  G( a7 R其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
) ^' I0 b+ v& w% J
2 ?$ j7 r0 f. d% S8 Y8 q9 ?6 vUSB的中断ISR,bare metal哦
# j4 a5 J2 T( P6 x: P

- y* {) e. T0 f2 g& svoid USB_IRQHandler(void)
) N, A; w' M% O  x{; f- z! \. C  r2 p0 x: e
    if(USB->ISTR & USB_ISTR_CTR)" n" \" p6 w7 N7 Y
    {
; M  V, A+ f& I4 t        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
/ X; Y5 R/ b( N# C( p/ _        {
/ r0 C  Z0 B7 M) j/ ?            switch(ep0_state)
( E3 V) C! w3 Y# Y% y* T            {5 y1 ^7 l# r3 R% N5 `" B% Q9 Y
                case 0: ep0_state |= 0x80;7 C, O. ~6 H2 H" L
                        break;
( Y" a  T- I7 D4 Z, B2 r                case 1: if(USB->EP0R & USB_EP_CTR_TX)9 Y. S% }, |9 n+ n& _' \  j
                        {1 r" c0 B: q  M1 L
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
- V+ ?+ Y% P3 O- j1 u                            ep0_state=3;5 X+ Y9 I. s+ }3 d6 |
                            return;" W% @+ q; M( h0 y# o! p, x# z
                        }2 c7 ?' W% `  P  A8 [8 \
                        else+ j. p% i5 |! l
                            ep0_state=0;
9 k% b8 f: C; K3 t1 l                        break;2 ]3 n5 o& S( \& @6 P- F
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
5 p9 A7 V1 f; a1 }) f$ F                            ep0_state |= 0x80;
( J0 `7 P7 ^) N. G2 [9 _( f) Y* z                        else
$ u, f) I& H5 g/ a                            ep0_state=0;
& U" t- ~; p2 u9 V                        break;
5 E8 B3 k" o; W                case 3: ep0_state=0;9 C) A6 H6 k2 s2 h0 b# {
                        break;5 p1 N% T  [: k0 g7 n( `2 c1 t) g/ C
                case 4: ep0_state=0;0 q1 K: C! `5 z' |
                        break;# c+ w, e8 Y' R
                default:ep0_state=0;1 V. ]6 ~) T6 C+ Q1 j9 y" |
                        break;
" g) E# X$ o5 n4 [& e3 T  M            }
( K" E/ x' G# U& [$ n5 l            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag9 Y! E1 M: J: Q2 N' D' S, ~
            return;
  Y+ E8 X# a* T        }
! ^* K0 ~; w* `8 z% ?! ]        else    // EP_ID can be 1
  P& e# v) ~7 B        {
) V# B: B* a0 B: V. K7 m) g, A            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
8 b: E$ M. u# m# G" w& G$ J            ep1_wait=0;
# C2 j- k2 _% ^: o% H            return;0 |, D, m' G  Y6 F0 M; C
        }) y# q! y0 S5 O6 I  u& q8 H9 P7 u
    }
7 y1 o4 E; l' A: e1 B2 s    if(USB->ISTR & USB_ISTR_PMAOVR); Z( e6 l# o2 y- L. [4 w4 {4 y$ d
    {
1 P3 b5 a: I3 K3 ~+ m. e        USB->ISTR = ~USB_ISTR_PMAOVR;1 ^( ^9 a5 u4 [
    }3 k. m$ \; T+ k+ Q' Q/ _: j1 `4 Q
    if(USB->ISTR & USB_ISTR_ERR)
1 X' I5 q( y; J5 P6 a9 O    {
  Z9 ]9 x- J. N        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
- L. C% ~3 V. U    }! k) _$ u& a3 U) s! S3 L
    if(USB->ISTR & USB_ISTR_WKUP): H2 F1 B8 ^* O: }5 |- f3 W: U$ P
    {
+ J5 ~8 m1 h1 |% n# ?        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend4 g+ I3 O  O# ~, l, p
        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
0 B4 ~4 N. o" m" P" v  g# L$ i' k    }2 r, ~, P4 ^4 ?  ]% R3 o2 V9 M( H
    if(USB->ISTR & USB_ISTR_SUSP)
9 |$ X# ~0 g2 ?3 \* l$ }% S    {- J. o2 c' e) i  h
        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend/ O4 ], h" e. O1 [2 q7 _8 B% b
        USB->CNTR |= USB_CNTR_LPMODE;   // low power
* ?/ Q" J" [) h$ J' L        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear2 b# W) d3 i8 q& g9 g+ d4 r
    }2 V/ C2 j9 _8 o' b6 e( \
    if(USB->ISTR & USB_ISTR_RESET)
( s& G7 f: c5 w# b/ d    {
; j8 V9 G/ V- y, n" H        USB->BTABLE = 0;    // buffer table at bottom of PMA
! T' j- {; C: h& L- A        USB_PMA[0]=128; //ADDR0_TX
( g5 U2 w1 B3 k        USB_PMA[1]=0;   //COUNT0_TX% R7 l! r$ G8 C/ O6 p- }% E
        USB_PMA[2]=256; //ADDR0_RX
" N) j' a- L0 ?0 f        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
$ S# M7 B- I6 L4 p3 x+ r; q; `6 R/ o        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
+ W; B, t* h# @. v$ d. f  f        ep0_state=0;
0 P( P4 b8 l8 K) u" ?        USB_PMA[4]=384; //ADDR1_TX8 U! n+ }2 ^# v) H: [% L* P: e2 m
        USB_PMA[5]=0;   //COUNT1_TX
; a0 E% T- H5 o, a: b        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
- b, T3 t9 W5 W; o4 E4 a3 g        ep1_wait=0;4 F; `. a0 A9 R3 |# j, C
        USB->DADDR = USB_DADDR_EF;      // enable function! k, {  R; u  q2 ?
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear" h( l; W4 [! I) k" y# r
    }4 X+ ]6 r- ?$ S4 N+ z8 _8 @
    if(USB->ISTR & USB_ISTR_SOF)
0 C" {5 v2 C  u" t8 R    {3 i; W% M. E) h  Q0 D
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear& u, N4 K4 s6 A6 p  |, j( S
    }
1 w4 a  K" R- s$ ]6 t1 `}* [, C, K4 b' i  k
% Z3 D  g& E. w+ ^1 v7 G8 E
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
6 A+ v. m% O; j7 o( b0 [
/ @# ]5 u% y( H5 p2 B' @* i6 i主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。' s4 D! `/ ^. f' B9 H

) e, G0 T. t9 f& j1 y, n6 r9 K
    while(1)3 T0 X! c7 Z# x9 t* A1 F/ B
    {
4 s9 r) o3 R9 h: d- ^        static char row=0;
2 S$ q  e9 {3 R        __WFI();1 g& t7 L. g! T, b& K9 y
        if(ep0_state & 0x80)    // request data processing4 O$ `& g8 q" |( X
        {
: q3 j8 n& I5 n# U4 G2 W            if(ep0_state==0x80) // SETUP phase7 S  @8 B  O: P5 Q9 {* i
            {
& g8 H3 C0 Q* ?+ B. g) n: N. k. A                if(!setup_packet_service())+ O$ a" Y- V# V$ a# l
                {* W6 Q) O5 Z  q. W7 b/ ?
                    ep0_state=0;
7 e" O) _3 Z. p( D" j- B4 x                    // not supported
9 {3 K1 f8 u0 a                }
' O! r1 t, w4 ^8 s/ |6 t                // ep0_state should be set to 1 or 2, if processed
+ s# v9 _( l& U& Y* `2 y) X            }/ b  I( \/ ?$ J+ w" Z
            else    // OUT phase
; T- Y2 z, k% h8 @# N& x            {
6 R0 _" I9 R5 z1 p3 s/ f                // process data4 Y2 a. c, t& e7 k( }' M
                show_LED(*(uint8_t *)(USB_PMA+128));7 f; C" I' r9 X. F. ?2 L) J. K
                ep0_state=4;
$ ]. x  q2 Z; r7 f8 y- Z% v                USB_PMA[1]=0;       // Zero length DATA0, h2 u6 m' _3 w% h
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;2 a# o/ `7 T% G) }
            }
; T; i* L$ _1 x5 h, ^. U        }
/ q, y, [. S$ J8 u/ |0 _        else# |$ N* a6 d9 x" N3 B0 R- r$ |
        {' H, i0 a! `% p, p. ?
            if(usb_address && ep0_state==0), Y) Q2 d5 V; h; D1 M
            {+ u+ n% U1 Y  P% _7 a+ T
                USB->DADDR = USB_DADDR_EF|usb_address;/ h& A4 X- ~- p! A
                usb_address=0;
# ~; x2 [+ I' O; X3 `            }
9 v' T9 I- Z, M6 z, T        }; ^+ }% d  p& L3 ?7 U1 w, h
        if(row!=scan_row)   // new scan line- p0 d! e7 t# ]
        {: \+ I6 M2 z/ i5 m2 \
            if(key_state[row]!=prev_key_state[row]), O5 M/ S1 J, B9 E  G5 a! F. e" r
            {
3 C3 C: f0 L( g# u" x4 r% [                uint8_t test=0x80;; l1 ?* B2 G- W2 k+ r5 y, v
                uint8_t diff=key_state[row]^prev_key_state[row];
% B% s" m' U) O, S5 J  v) H                for(i=0;i<8;i++)
2 {5 H% _( u7 C& z" {9 b0 {                {
+ E2 u+ J" D4 |% Y                    if(diff & test)
5 h8 r: b, }7 Z5 b0 M. Z# n                        update_key_matrix(row,i,key_state[row]&test);
, Z. z4 @' T" c9 `2 m; m: ]. F                    test>>=1;9 w% L0 r: y0 _; }& h0 w
                }
/ A& b. L4 x+ p2 p. r- u6 x            }
5 H+ v- G" l5 O- n            row=scan_row;8 [( q& h) Z0 w9 k* N6 E
        }) I7 [* W/ n: k1 G% A3 s: ]
    }- ?1 V- ~# p2 `: P6 i1 Y7 n

/ Y/ i2 j2 g0 d6 ~4 r: F
1 {0 Z  Z- J# F( Z/ S0 v- eEP0的控制传输,把用到的请求处理一下+ e  \/ \' h, _0 _5 @& a
char setup_packet_service(void)2 H( r- v2 c! h4 {% d0 T
{
& n0 G1 F3 p  \$ D    if(ep0_std_req->bmRequestType & 0x20)   // class-specific2 O0 i  L& |8 c  `
    {
, t5 @% y; q1 ^' o4 X+ q" ]        switch(ep0_std_req->bRequest)2 e2 l1 m0 N+ q
        {
0 ]5 |8 ^) n, z& z# U7 q            case REQ_GET_REPORT: break;, b5 h" T7 k& J  p2 k$ _
            case REQ_GET_IDLE:
/ ~, G4 T5 ^' |8 H/ O: t                USB_PMA[64]=0xfa;   // return 1 byte  h* F0 }) Y1 i- j' T- y" C
                USB_PMA[1]=1;- O& ~( ?9 r: J3 U
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;# ~" z" s" i$ a2 j
                ep0_state=1;0 T2 F8 N2 u( a% A% V
                return 1;" ]* k$ R- i( A
                break;
( T7 H8 W) j- y0 Z& ~3 Z            case REQ_SET_REPORT:
0 G) f) d+ t, m. r1 i4 n                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
! c) \* ?9 S5 \7 X# \+ A                ep0_state=2;
2 H+ I9 f( Z4 L2 y! f3 g5 ?                return 1;1 |+ \* `- t4 y0 S1 D
                break;
4 L: v) q. b+ `: \            case REQ_SET_IDLE:6 `; o# G& r+ v# m9 E5 {& y/ v* w
                USB_PMA[1]=0;   // Zero DATA
7 l7 U4 S. C# `& ]3 v                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
/ s- i% P: l; k( \* `                ep0_state=4;
; q3 N3 s# N7 D                return 1;* _3 L, ]: M5 f
                break;# k* b6 g" P9 D2 r% o$ m
        }/ k8 C7 P: P( y4 T
        return 0;) l* V' d, v, |$ H; M* o) v
    }
3 H# R! g( k' r! Q# C    else    // standard
  r6 W$ w: E4 i: ]$ o! g( x3 w. ~    {
! e' r0 b5 \& S. P& m        switch(ep0_std_req->bRequest): _: k/ S' A; G5 G$ ]7 ^! G( ^& S
        {
, ~% ]2 e7 z, I- `            case REQ_GET_DESCRIPTOR:
; h$ O) T& @7 u, t3 ]6 o3 i                return descriptor_service();
6 X) O1 t* l8 i5 f/ ?1 d% K/ k8 K/ `                break;
2 \; @  o* J, k8 s8 ?* V) T4 N  X            case REQ_SET_ADDRESS:  x3 T8 y( E6 ^- W
                if(ep0_std_req->bmRequestType!=0x00)6 ?: G: B) k" Q7 }. }6 Q( x
                    return 0;- p( V3 R& H+ L
                usb_address=ep0_std_req->wValue;4 s$ r; u9 S9 Y/ g. ]
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;: _( e# @7 M9 g- e
                USB_PMA[1]=0;       // Zero length DATA0
  b6 W' W5 E, k& L3 B                ep0_state=4;    // No Data phase
# Q$ N- n$ R) p6 F1 t( P9 |                return 1;8 @) w; K% V$ U% G0 w# Q
            case REQ_SET_CONFIGURATION:
5 G& F& }$ C% W" P+ \                if(ep0_std_req->bmRequestType!=0x00)8 N* Q) L2 I8 ~6 U4 }
                    return 0;
) j. q. @; n; u3 P9 {                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
) O( _+ z- f& `! I8 r7 I                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;7 U& P: v5 T+ l0 N: G8 F
                USB_PMA[1]=0;   // Zero DATA2 K; s" z7 X7 s
                ep0_state=4;    // No DATA phase
7 C; L1 v1 _2 f' r3 h                return 1;
3 f$ Z% ]4 d# o' Z            default: return 0;: ]+ q6 U1 B  ^$ l/ M
        }4 C- V0 H2 Y; m  M( e
    }
; h5 X& d& i6 ~' r5 O}- ]) w' e8 l, W: d& @
( E7 U1 B, M3 |) v1 {
' y& N4 `+ I$ h* |
. P0 [% `0 Z* y: D2 i% w# B
回复 支持 反对

使用道具 举报

24

主题

63

帖子

288

积分

vip

Rank: 9Rank: 9Rank: 9

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
# K8 y2 U& B) {2 a7 K$ L. r3 echar descriptor_service(void)
8 `4 Q: g0 }; w, ^) j{
' k1 D' I! ~$ |. L& A: N    switch((ep0_std_req->wValue)>>8)& l, h9 p% Y9 ~+ b1 ]
    {. T8 o2 Z, T/ o5 o
        case DESC_TYPE_DEVICE:
  E6 [# F4 Y7 E6 E" m            return ep0_preparedata(&DevDesc, sizeof(DevDesc));' ~# O* x# N0 \" Q/ H
            break;  J) o1 W. Q2 g) W* m4 E5 i
        case DESC_TYPE_CONFIG:4 C& r! w) y* C( X' _: v
            if(sizeof(ConfigDescData)>ep0_std_req->wLength)( S& K% c- N" E, [
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
# N( z8 Y" S3 ~/ N( N! b            else& j/ {3 u0 r' G" z8 ?4 u
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));% A7 M; l. m3 c! a3 y* p
            break;" j7 j* a  Z# U- t( Y' ]9 E5 \
        case DESC_TYPE_STRING:
$ s$ g+ p6 h9 I/ [. B* X& k& x2 Y" c            switch(ep0_std_req->wValue &0xff)0 b3 }6 D4 d$ }# }* j% s0 @4 w3 ~# R
            {
4 }) M- D. M( U7 E* i                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));+ E; O5 c+ Q% r7 n- s
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
6 j, j" o; q, t                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));! n. A0 s+ P  S. Q" G
                default: return 0;
: h6 T3 f. s; K+ v) J            }9 I7 n1 Y! B( T" Y5 p
            break;  u! [. f4 n" s, i  I* R, V
        case REPORT_DESC_TYPE:
! s+ s' i* H9 h! H" }' y4 _            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
: y  O6 H% ?% y+ f5 l' w+ G        default:: ^2 r4 U5 Q  J/ f& g: T
            return 0;
8 ?2 x& A7 ~: U% r' P5 A% h    }
  d1 Y, f3 H2 t}% S& B' Q  v! R3 F3 l
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
7 t- d$ j6 G. d" e$ s* e5 F0 ivoid TIM6_DAC_IRQHandler(void)$ ^  s9 ~' Q$ l( o- ]
{* o4 ]! f. a6 U, o! ^
    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);( O. G6 [' j2 j3 `& y
7 r9 s: j0 ]; p
, a/ p, V3 `7 j/ r
    TIM6->SR &= ~TIM_SR_UIF;0 {# m% x7 I  {% ?4 C% b
    prev_key_state[scan_row]=key_state[scan_row];6 d: G9 d) g  @3 ^
    key_state[scan_row]= *PA_IDR;   // update key states% l$ G3 x' B$ b1 I
    switch(scan_row)
6 `: I) U7 i( T9 ]    {3 ?% }' @+ z9 U% M. M. z; i, l3 f) @9 v
        case 13: // next row PB14
" F# H5 a/ [8 K                GPIOC->MODER = GPIOC_DEFAULT;
; s& C" J8 T9 h/ O                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;! ~4 D' `& f% a9 u0 F. B; V
                break;
- {. ~4 y- O* j1 [. n        case  0: // next row PB15
7 Z, u7 [( r* u8 v+ R                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;5 g% S! ?) v8 [
                break;
6 E; Y* ?: q. }" G% n+ @/ p# T& J        case  1: // next row PB3" w4 z1 K5 z1 z  t' t
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;% J5 V: L/ R/ x* W8 i( o7 V
                break;. s4 u* _0 a2 K) L$ z6 {; e$ B3 T- ?
        case  2: // next row PB4% M3 a) ]  ~& l. \( p
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
5 P$ [7 C1 Q+ J# p- t. s                break;, `/ G% U# A# j4 M- Y; ]
        case  3: // next row PB5, h: d2 B2 N6 v+ R" Q0 u( ^
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;3 ?7 P+ @4 s, Z9 }
                break;
* ~$ B+ \; [7 L# P3 ~  T        case  4: // next row PB6
7 b. a8 ^" T! I  C2 z                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
7 c' J& B4 Y- C6 M: e# F1 n                break;9 ~8 |2 S/ i" a7 K5 t) I
        case  5: // next row PB7) U& S' e8 U" w7 D2 e. Q
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;( R! @* a3 q! {  f9 e/ ^/ J
                break;
( X3 i2 B8 g% k2 ^        case  6: // next row PB8) x- Y: I7 y5 k2 p+ T! H
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;, O9 x& ]% J) w: r8 D+ b% c' L
                break;
# r) j4 k, B* \2 E8 N        case  7: // next row PB9( x. d! p7 a+ G* v/ Y4 O7 b# K9 {
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;# F2 w0 p- _1 {5 h$ \& b  U' R
                break;
& S6 [8 B2 F& B! D- _  W$ e5 ]        case  8: // next row PA8
6 N" o8 G, P) l: q                GPIOB->MODER = GPIOB_DEFAULT;
6 ?0 R7 `( `! S2 l                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;( |% D, }6 _0 ?4 ]; D* u, X
                break;! h" v! N9 O- e6 s  u8 i6 Z
        case  9: // next row PA9
: P- c2 V# H( x2 z                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
& T0 B0 I  }* o+ {* P) P2 Y, s                break;; M& i$ o' W+ D# b
        case 10: // next row PA10  {& N3 I& [  C1 j5 X
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
) T) G, I' H5 Z( i                break;
9 {" U8 l: b& W+ k        case 11: // next row PA155 u3 {9 v* W2 N7 V/ a5 X
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;4 U( _% R" i# \, d
                break;
3 m( a2 c4 M, |9 x0 F. a+ _        case 12: // next row PC13
' t8 T1 D& j1 a2 O2 ?( [4 e                GPIOA->MODER = GPIOA_DEFAULT;
2 h( D' a1 B% o1 ?- x  x. w1 q8 q                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
& w3 @5 L2 X( V& J: H# L                break;
( [8 P1 L& v, _! a    }
5 u. X8 `  u! ]" r9 B( {4 s7 S    if(scan_row<13)7 v# W9 M. f( t+ P3 T
        scan_row++;. N( h7 Y6 T( s
    else# F- |3 O. |. I
        scan_row=0;, `7 ^/ p1 `  T
}
! l; D1 ~2 ~! I8 d7 O
' F2 d$ r, N* z  F
) c# L5 R' j- b, u
回复 支持 反对

使用道具 举报

24

主题

63

帖子

288

积分

vip

Rank: 9Rank: 9Rank: 9

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
4 C1 R' w$ m- w5 @9 K2 S; g' ]7 x1 z
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。9 f1 j: n& ^$ V7 x
: |7 D! B; J0 c! J% E
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:

# m8 b0 Q. `7 x- H2 ]& U$ V
9 q* p7 @, ^7 @" Y+ l* G, w. m+ O5 m, A" C% B  q. j0 [
const char hid_keymap_qwerty[14][8]={
9 o  B0 d1 D' u4 c    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
' X% X. B& `- G5 o8 c; R    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
" D) k8 S3 Y; e7 R, T6 r. x. w& t    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
, B' O9 L* R/ n, |- w8 d    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
! a& M# q1 i7 O6 R    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
4 O6 k- v% e$ q- Z    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},' x" `" j0 ]& e3 T' O
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
( X; f$ J  q7 a    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
. c% T' r, I* O- ?    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},- i8 L! e4 Q. \0 l# a% i( h, u
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},, i9 m" @! {) Y0 F1 K
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},1 y( {: x, K; v' _& c0 Q
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},+ U8 h0 d$ E1 w" G
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},' m( X- h+ Y1 a# R& q$ ^/ b; Y
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
) ?/ h" t8 j' m( h2 t+ X};
1 s7 U0 I$ \( L9 m: W$ ^( M2 I: r0 [( H! m3 G" w
! |; r2 r1 V0 q- D
const char hid_keymap_dvorak[14][8]={! n, h, {. `, s! [5 e( O
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},% r" Z  ?. O5 ^
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE}," M! s. O4 t: e: z" V; J
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},$ K8 f$ e+ Q8 f' ]9 n$ Z' ]4 p
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},# C# f7 y8 b$ r- y; H2 b
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
: v% ]$ ~: E  ^" ^8 t    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},- }' V  ~( G2 O8 g# y
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
/ e$ a# `9 ?8 W  x$ Y! f7 I; F    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
/ k' r6 v9 s2 [* ]# F( @+ i' i# V& J    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},: t/ b" x3 H1 y& b
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},- [1 ]" ^$ d7 \" w: x
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},; d% _+ [8 s% y' _  L4 c- Z. q9 m
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
2 {: E& @2 S4 Y5 ?3 t3 R9 }1 ]    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},. ]* k$ G! t4 ]' E) K0 J
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
4 D; E6 s: |0 H};5 U; |, m0 w- C1 _3 R) l

7 P5 Q+ a3 }* f# |0 O
0 T% i3 Z. U; [7 Y0 W$ U, U5 g上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
; B$ X9 f4 v; ]4 U: G$ I5 M% x+ ^
8 \6 L" U) T' BHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
! a7 e. D: t9 c4 k+ U8 c

: q; c) H  l; h) {( B% pvoid update_key_matrix(char row, char col, char onoff), a8 ?2 y+ N4 ^) ^$ P
{
5 A4 W1 U! F" F! d# ^    static uint16_t hid_report[4]={0,0,0,0};& o# g+ J/ |/ |+ d3 X; X
    static char (*hid_keymap)[8]=hid_keymap_dvorak;
) R) O: b8 G2 a, D/ E  j$ Z+ _
' Z  p/ Z, w5 G2 x# Y1 u
; y4 W. \$ d) e: M/ D& \- G& E
    unsigned char key=hid_keymap[row][col];; `6 x  B( i; P$ U8 `
    unsigned char *report =(unsigned char *)hid_report;
' Z4 Y) @7 E* `% E    char i;
$ M8 H1 K& K/ |+ B
( @5 F$ Z! \# N# A+ q! D: X2 l
- P1 m* g# f* u: b2 e. ?8 `& ~0 Y
    if(key==HK_MODE)
, H& H3 [/ h0 r" W/ d( x    {
# a) W( G; R, }5 H5 M( f        if(!onoff)
, T9 k/ |2 x: I$ l! t. H9 r% I# `        {/ b/ m1 P9 c7 h9 ~2 g1 Y
            if(hid_keymap==hid_keymap_dvorak)
, T/ }1 x- g# M; J            {
2 Z; m- a$ L- r2 ?$ J8 s                hid_keymap=hid_keymap_qwerty;# d* r0 P3 M5 ?3 H: o
                GPIOB->BSRR = (1<<2);
4 P3 _0 _$ ^* f$ j9 q, C            }" M$ x2 r* m0 ^* k# }6 _
            else
3 I: n( @" e% r' v; t            {
2 y% p  Z2 b- O) Q6 r/ l5 r                hid_keymap=hid_keymap_dvorak;2 W" I6 }% y" o6 [
                GPIOB->BRR = (1<<2);( J5 r5 k( G# |( A
            }( v3 d6 C0 ?  K6 j% {
        }
( C+ Y! o1 }% X' p. X  v+ Q        return;! t% ^0 L6 o& c" b
    }
2 n" j9 o  q$ y6 l9 R7 {/ @( }2 j. M) H& Z6 S

- W, ?. B0 v0 y    if(key>=0x80)   // Alt, Ctrl, Shift! t5 q- {' w- g6 V8 k6 [
    {
: S  }" I1 S/ k" r$ q2 F: _        uint8_t bitset = 1<<(key&7);/ H* b2 G( c5 F4 ?! K; D
        if(onoff)   // non-zero is key up3 d$ M- z% ^9 U; [
            report[0] &= (~bitset);
/ O7 E% T$ C( i, ?1 o' D* D! }        else9 j7 ]: B# X! B9 O- Y
            report[0] |= bitset;
' W) Z; v- w! v8 z6 |( `7 I4 x1 v    }0 H  n7 C% d# f
    else$ [# B  }1 b2 V4 z
    {
9 r: W7 z1 h+ M& H* x& o- s" y        if(onoff)   // non-zero is key up
; F+ u  v" N+ Q: m1 Z        {8 a- Z3 s* v) c# h. ^
            for(i=2;i<8;i++)/ G- x8 G4 H% F9 O* D
            {* `8 F2 a0 S6 q/ A( e# w
                if(report==key)) S% }% T5 `9 \1 ]/ P* O
                {9 w! C2 Z+ n# P/ P- {( l& [' P8 [
                    report=0;
4 B3 a" H8 h6 m8 K1 C7 [# A                    break;
! m6 {( `( T- W! y' H                }
# k# s8 [# J$ [+ H$ A& ?            }; _" _; q2 p2 c: |, ?
        }) N0 D8 O& r+ g( v) O
        else
9 Z: R; z5 B3 ]" }( Q        {
, T" ~1 I4 e, \$ D& Y            for(i=2;i<8;i++)# P7 ^* }7 R$ l
            {
4 p3 X9 I3 N& [. m$ u$ W& e                if(report==key)+ b. [+ n' @& L2 d
                    break;
5 `  L+ J) V- i( a                if(report==0)  }) E. [* I+ x/ m6 a: j" b4 W! J' J
                {
! v& P( A6 n& V                    report=key;" k& L6 }0 f$ l$ i3 R: o+ L
                    break;
7 {) }" F4 ]9 R/ I, d* M$ A                }
1 U6 u# y5 u0 Z& ?* C* ]            }; [# g5 q6 P: S, m7 I
        }
- f/ }  P8 q- {0 r$ V9 i, T    }2 ]4 m1 x1 d% _( U- q- F
    for(i=0;i<4;i++)
) c) Y! ?3 l2 s5 t9 V: @( h# n/ _        USB_PMA[192+i]=hid_report;2 A! Q/ T7 v6 F. i
    USB_PMA[5]=8;   //COUNT1_TX
# L' K9 |& k2 B5 F    if(ep1_wait==0)# y' s) s/ k8 L- L. _
    {
* R' l3 n; B% U5 Q2 Y5 h2 n$ ^        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;+ }9 G9 L9 h+ E" Z; ~. q: k- T- M
        ep1_wait=1;( z& Y" J8 x8 v( y/ e' N
    }: k1 o1 H4 b% k5 X
}
$ k# e7 i4 |/ B/ H4 d* A: c/ E5 O1 t+ k  U8 L. Q$ k# h% I
0 g' q  _# L0 F  u& e
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。; z/ _5 C, w0 |$ g, Q
keyboard.zip (8.7 KB, 下载次数: 5966)
回复 支持 反对

使用道具 举报

24

主题

63

帖子

288

积分

vip

Rank: 9Rank: 9Rank: 9

积分
288
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。5 {0 H8 w0 ~/ n; `, x# P
回复 支持 反对

使用道具 举报

8

主题

151

帖子

217

积分

中级会员

Rank: 3Rank: 3

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘# s/ Q1 k5 r6 q  E9 k! z
不过楼主也很厉害!
回复 支持 反对

使用道具 举报

24

主题

63

帖子

288

积分

vip

Rank: 9Rank: 9Rank: 9

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
7 m/ C: f, P" z* ?. C刚开始我以为要把打字机改造成电脑键盘# k' |# _5 m$ G. o8 S
不过楼主也很厉害!
0 J1 l) a. z& Y' v5 c, U3 O
哈哈  是有点像奥
回复 支持 反对

使用道具 举报

0

主题

3

帖子

4

积分

新手上路

Rank: 1

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害8 n& `6 X! }, h0 v. p+ o( u
回复

使用道具 举报

0

主题

2

帖子

2

积分

新手上路

Rank: 1

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

使用道具 举报

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

本版积分规则

Archiver|手机版|小黑屋|MiniDSO产品技术交流 迷你示波器-袖珍示波器-示波器探头- ( 粤ICP备07030012号-1 )

GMT+8, 2024-3-4 12:05 , Processed in 0.206753 second(s), 27 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

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