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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
2 s7 w% r3 F# x  u$ _! \: a- {  V4 Q  l, G& R3 q
http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
2 |% }6 k$ T5 o! a' {0 L/ J这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。( `; `( Z4 w: T5 N: s, j" v/ e4 D" [0 g

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

1 ~. p" J6 l3 C$ [

4 p1 T$ v- B& x8 }+ N 235140i3a36qivqzuvmt5q.jpg.thumb.jpg
  g3 x# f7 ?3 M7 `
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。
. P  \# J0 ]; m$ Y7 u2 C8 g" W5 m+ R 001734klbyoluenuwz4h4b.png.thumb.jpg . }  p# Y% G+ D( G: Y, x- [
为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:
9 x- a) g, g" t* A( C! D- d 003625r2agx2f5v922cf2f.png.thumb.jpg
% E( B, J, G; g6 O) m- D. Q) X( |; c其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.: e3 I. K* h$ \5 [0 a/ T
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:

2 n9 S# f6 O' x0 t: @' s 005836yvs0wvovwsssgd3o.png.thumb.jpg ! D( F/ u7 N% q2 L  S! k
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。1 d: H6 @5 n' \( w; L1 g1 ]8 [! n! P
# O/ ]. g! q5 V; g
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。! W+ y& h) s" f* K. i8 z& ?

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

; R$ a% T4 T$ q3 K6 L! z5 s 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
6 B* p  s* s8 |. u9 f+ i----------------------------------------------------  分割线 ----------------------------------------------------------
5 c5 ?( B: h+ t4 `7 K% o1 K4 e1 i7 G1 z8 `$ f
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

: I" r; ^# B4 R) {8 F 020011osionbunl4ui44vi.jpg.thumb.jpg
3 O& w2 D- D2 f0 u, [0 I轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
- p! ^+ `3 A2 c* H6 U 020017j8ycmnv7788bqv52.jpg.thumb.jpg * b, }5 U& ?9 j1 D$ z8 r" s
特写,80C49
- ~0 l# ?1 o, c1 ?2 g 021040oujzuvtut6iujtvz.jpg.thumb.jpg
; ^8 X8 ^4 U( p- F4 H5 a- FLED部分,使用了一片D触发器锁存指示灯状态.+ {! ?# O; t( _: G
022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
" l- Q5 _: X# E' e+ N# G暴力破坏,将80C49拆掉/ R% S7 q* F7 J7 v& Z
021113e48qq98vyohvhzzh.jpg.thumb.jpg 4 ]& Y! o$ L4 y# y+ X0 q- I
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。; c' s% I; |) W% X3 K
021125nc9az6dj33rlds2r.jpg.thumb.jpg
; k! {; G, {$ r' a. V
焊好元件后的板子,准备替换80C49
6 W: s: ^1 s" O3 o; _ 021146zmmpmh4pav4o7g6a.jpg.thumb.jpg " @: W9 S+ L0 m0 G
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。  m, ^: A# u" G( p
021104shifhnrqbr3o5nlo.jpg.thumb.jpg
$ F0 @8 q& o8 G* W1 V$ \这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.& g" g! s+ Q8 V0 [' h( G: u' ]
022003ym1p9u4ug40280uu.jpg.thumb.jpg
! z2 e: Z# Q4 w3 E+ ^' e$ a开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。
. @( p9 U; _  O; Q0 C 023313kt141q9qajtol7ma.jpg.thumb.jpg , b; Z( T; |" y( M, S; z& C& b" x3 `
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
8 t6 }4 D( Z9 M2 K; N0 j" s 023322nt7l5xb3ltttkltt.jpg.thumb.jpg
6 ]. `) g+ i% i2 A6 \! [
主键区键帽就位& O% H9 P) E' H! C& P2 ]
023331hin88e8wkrwzwikx.jpg.thumb.jpg " r* K- x; I1 X" o, C2 s) Z/ m
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
0 {$ t1 R5 G/ O+ A% U 023336wjzlgopugg1jyy79.jpg.thumb.jpg
2 @- i+ D: L6 p+ g- x( ^' j& O最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。1 m" v* a3 ^6 K2 L5 y9 L' \/ Q
023341sffu4j3g2323h6fl.jpg.thumb.jpg 7 L/ V& h- U$ m. }4 s# G
& i2 c" ?! G0 s" p7 t! p
----------------------------------------------------- 分割线 --------------------------------------------------  o) w* w5 \- j9 M. c# f$ K
5 T+ z4 e; {' k9 ^

9 n* [( R& Q- t4 t8 \- X" i: ~- `3 P7 C% C" r8 x5 u* ~9 N

; ^1 F+ h" S! k5 D8 m+ J9 Z  x
; K% b9 {: z; A( `

, Z1 j" B8 ]: u/ X  v
# \: o' |# A4 f% k
! l, Y0 b8 [- M, Y7 \  L5 j7 Q/ @; Z0 n9 C+ {! m0 D$ t' [8 A

6 j7 D# K* N+ ~6 w) \2 E; P7 z9 E! U
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。$ |! ^. W8 c8 I1 k
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
; ^" e, u, L$ q* t$ K- U' h 104025nzibm2rmiomhyirm.png.thumb.jpg
5 m6 @1 @6 x$ p+ Q+ l2 t

: R8 o3 G% o3 H3 S% ]其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)+ }8 ~7 a9 b! N
105004zkrez5houvkkznko.jpg.thumb.jpg " ?* ^" K% L4 u4 b$ |  K1 P" [- Y' \
# \" R0 H2 }! O- F( e' z; p+ V
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。  F" v, B' m7 [; C! c- _  r, a
+ T. D) x1 j1 P7 o5 ?  B
这是我设计的电路图:
. W: l, Q- U, g9 V; |) r 110344ej2z2oo2rflo7oe7.png.thumb.jpg + J+ I( Q  v5 Q" H% t' X. k9 O
& D6 m- f* ^5 _, j$ F) U8 c9 C( e
PCB Layout:; ~2 ]# _5 e- e
110847jjbjvt34vwt3v5bb.png.thumb.jpg ' k1 u+ B) Y9 Z2 V- ]9 u

4 ~1 P6 ^0 M! E0 O- ~不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. ) L4 S) z+ r' j& O+ b! s' }
4 {- [. H0 {+ t

- i" |' I) w0 K
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 & G5 }$ i+ y% v) a5 u+ r' C8 `

7 r% J+ ]6 n  T) e  X4 x软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。- l4 L; Z' v/ {4 _
9 k. v% g; ^# ~8 t, D( N. g
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:5 [' z8 ?; ]; N& q/ Y) Q+ g6 i
113818pmrfsb6z0byt6t06.png.thumb.jpg 3 s% T& p5 f( d7 V1 z1 F* W

  ?3 j+ [- v% q+ E: Q: p) |! N8 ^其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛): x  r2 f+ T7 q4 D, ]! m
0 g  Q% h: ~- w
USB的中断ISR,bare metal哦
% ^3 l6 G1 m5 p4 \/ S- U" \" X. e) S
6 a$ N0 b2 W* G5 z- K
void USB_IRQHandler(void)" E* ^: e) X% G9 m
{5 A" L$ l5 e: l$ X8 @* R
    if(USB->ISTR & USB_ISTR_CTR)$ W2 n5 K  ~1 k) [
    {
- W2 Z8 @" H! Y9 P        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
" a* P) A2 F. @' ^# a        {" E5 k* c7 c) X
            switch(ep0_state)
( [4 e: V; Z7 g( }            {6 [* |5 {8 L/ l) }1 J/ W
                case 0: ep0_state |= 0x80;+ p% h& i. `" ]9 H
                        break;3 t0 T: O( |* Q
                case 1: if(USB->EP0R & USB_EP_CTR_TX)7 @: l7 J4 S  @+ j1 n
                        {
9 x8 X4 M. Y) {                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
% A/ u, }, H9 }% [' v                            ep0_state=3;
& O* J; L3 Y7 C1 M  \; B                            return;
( _! S% O/ Y  z- _4 g3 P7 G                        }" m8 [, E6 @! \- w9 m$ H" p5 X
                        else
- _8 d1 N/ @0 n/ l; u                            ep0_state=0;
/ ~$ A( J( v/ ^2 Q& C; J4 d6 W$ I                        break;
1 y1 Z) I, h. u& [9 Q5 L6 O                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet1 x6 n3 x$ N. X% w+ s
                            ep0_state |= 0x80;
7 H3 W, Y4 R3 I$ n; X                        else8 p, A- C' v' V
                            ep0_state=0;
' z8 s* L/ _  @" {5 S1 n) p                        break;
6 }6 J/ F0 z- e' |& l9 i4 \/ m6 o  ?                case 3: ep0_state=0;) P1 y7 O% _6 q( {' I+ ~
                        break;
' e0 b" k, F% J% ?( @                case 4: ep0_state=0;
$ k. D+ y1 E; @) F2 K7 G* R                        break;
7 `. Y; I4 c3 u9 P& |                default:ep0_state=0;
5 ?: @' j! z, }1 ]" ?                        break;
5 b8 A0 n3 L1 {            }
  o7 @* f2 N: C4 ]  G3 C2 Z4 X! D& ^            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
9 ?& A: H5 o; |) D% o+ p; x            return;
/ _6 W2 m) S, A0 _1 W( W7 }        }, q7 ^6 Y: ~8 [+ {# z6 j' k9 M
        else    // EP_ID can be 1* b5 W2 r+ O! \8 U) z
        {
) L' |% l# i5 Z6 @2 b/ U" N            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
8 M# L) T+ s0 n% U! R  z9 [            ep1_wait=0;. F4 t0 r+ A9 v& Y7 \6 a. y: I  b
            return;( C5 Y% m9 D+ d! B8 M8 k
        }
* @6 V) X! R+ w0 a3 V# [    }% T/ e! o5 u; n# k8 g8 T
    if(USB->ISTR & USB_ISTR_PMAOVR)* `3 p1 t, u" W
    {7 C# ?+ o8 V2 b# u, t
        USB->ISTR = ~USB_ISTR_PMAOVR;0 B, p' v. @9 ~1 @3 I8 o% A
    }6 N, z# J) m8 a2 ~
    if(USB->ISTR & USB_ISTR_ERR)
2 `% @; _! g% w/ d* O- e( X; c' E# d    {& |+ m! O2 I+ U" Y' I+ w
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear0 O& v; u4 s. Z+ D; n" x& o) A( p5 w
    }
& i4 f5 i; |) Q    if(USB->ISTR & USB_ISTR_WKUP)
1 a! G9 O+ h0 i5 c1 z9 P    {
$ L% N5 U, P9 R+ l5 w+ i        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
' L. @$ o/ k5 H  x        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
, n8 l/ j' I$ M: U    }
7 J! C! Z1 e$ n! s6 p+ f0 L    if(USB->ISTR & USB_ISTR_SUSP)9 e: K3 ?7 T/ i) L# U
    {
: w* h1 ^# u- l4 m$ C        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend. J$ n% o0 K! ^0 l
        USB->CNTR |= USB_CNTR_LPMODE;   // low power" w3 t- j1 S: U3 m- r4 ]
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
  H. B7 |- v, v1 L1 d  m' G3 T1 C    }! H8 d: Z2 J! c& Q, S- o
    if(USB->ISTR & USB_ISTR_RESET)
: C8 t* H' T$ A! I) V6 |2 w    {
9 {5 i$ h. [$ D5 C, [) _' L        USB->BTABLE = 0;    // buffer table at bottom of PMA; ?" e* U; h8 H4 a) a7 x  S  t9 n. P# I
        USB_PMA[0]=128; //ADDR0_TX( e) `5 M3 `4 j
        USB_PMA[1]=0;   //COUNT0_TX# U" u. Z$ X1 a8 R
        USB_PMA[2]=256; //ADDR0_RX
# E7 K" m, E  F: J" o        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes( E& v4 A* U. [8 K( d7 @1 U
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
0 s! h4 @6 f  P  B+ S* q        ep0_state=0;9 s3 L$ w. Z( D1 f
        USB_PMA[4]=384; //ADDR1_TX1 C" g5 ~  X2 w; f9 X, U7 h
        USB_PMA[5]=0;   //COUNT1_TX& Y+ [3 k) E  k, K% o
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
# O% k, i& y6 \4 t2 R        ep1_wait=0;
" u* m% D4 f) A' H5 V8 }2 d4 s8 S        USB->DADDR = USB_DADDR_EF;      // enable function' N5 \/ ^1 ^- f% i
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear- s! Z0 F# h5 R- L9 N( R# i! M
    }
% Q2 {# F% ]8 Z& O    if(USB->ISTR & USB_ISTR_SOF): L9 q! ~1 R. a# n
    {% i9 T, k) R# j7 V6 W* _; o6 _
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear( G. R( K: u% @6 Y! m1 y8 Q
    }4 C; s2 D$ \* G. J: g9 J& f
}
3 d. Q( G9 I" e' `; |4 C
, i" y! _8 K, s1 X+ W( c& d# j
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。  [; i, H% N, i& `# U* ]% ~4 U3 B" d

( C3 g) m  z' b" |主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。' W/ G/ X. X4 c: d: u( Z! r' \

+ M/ n6 b9 W2 a7 @& \! Y
    while(1)
) @) z! h9 x( ^1 ?$ t/ Z. A    {7 g% y9 u2 x( u; o$ D8 D% Z* |
        static char row=0;. o. P0 O/ x3 m: f
        __WFI();$ ~9 {- V% h. ^( L# L
        if(ep0_state & 0x80)    // request data processing
9 W0 v* B6 j8 l) K$ [        {% C& K# w- ]& Q  C1 ?
            if(ep0_state==0x80) // SETUP phase
2 c2 K0 k8 r4 T: g4 a            {1 q. o3 |% T% i0 r
                if(!setup_packet_service())
, I( e5 \& ]9 c$ i, [7 t                {
: x$ g% Y3 \, h7 c                    ep0_state=0;
) j' q2 s) k& E* z                    // not supported" T, N3 M( o) Z3 ~% V3 w' u
                }& X( n4 r6 c8 T* r7 b$ X( V
                // ep0_state should be set to 1 or 2, if processed- @6 J8 g$ y9 m
            }  d  q% |7 N0 q8 D
            else    // OUT phase: ^. |2 W: Z; I8 M. S, ~
            {
0 ^6 O# h' ~2 Z8 f                // process data) ~; p, h* F/ Q# J/ ~0 E7 L
                show_LED(*(uint8_t *)(USB_PMA+128));
# l' Z# ?1 U& q! z$ |  W                ep0_state=4;: T7 l/ |- l) Q+ b; ^' D6 P0 [+ e
                USB_PMA[1]=0;       // Zero length DATA0
' N3 ?9 L/ N4 L/ m                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
* K$ P2 Y$ X; w4 b5 r8 m            }
/ o! ?: p  K* A% o6 ?9 X. W        }# X8 `0 p" K  Q/ e$ o7 k1 M
        else
4 S) `& @) b! F! Y        {
* r% h  v$ u$ ~            if(usb_address && ep0_state==0)
6 [6 v2 F6 V0 W& L; T            {
. B' R7 f. F0 f/ R: @                USB->DADDR = USB_DADDR_EF|usb_address;
9 j) q: h6 W  q) c/ _0 G$ i/ o                usb_address=0;0 `; G$ {7 |0 [3 [5 B
            }' g9 w. y, }; F7 l" X: i; R/ p
        }
9 D, T& Z8 Y8 M+ S, \        if(row!=scan_row)   // new scan line
8 |3 V1 f0 K: g! p) r  X        {
$ Q! d% r' d1 ]; Z            if(key_state[row]!=prev_key_state[row])
! N& U) q% {) `: w) w            {
) z& B! f' n& H' @                uint8_t test=0x80;4 j! l. C* }- J5 X
                uint8_t diff=key_state[row]^prev_key_state[row];
" q, g7 b8 j2 `  i: `# ]0 y                for(i=0;i<8;i++)
) Z1 H- n: p  u- y& v' a6 X                {
' ~) U# p6 t+ h4 C( Y- b                    if(diff & test)3 _, x- U7 a" U0 @& i+ m
                        update_key_matrix(row,i,key_state[row]&test);- r+ e# [& W! V. r: ?
                    test>>=1;
4 A) ?# L& u. r# |! y0 ?$ _# ^! R' Q                }+ B' y" d  C& I' F% i# ?; d
            }8 e0 V3 J) X$ m+ W8 f1 k7 i
            row=scan_row;- k+ p1 B, m; @2 L* e" O
        }, B1 H' \$ W* q7 T
    }/ Y6 {. G$ E9 e6 B" \% T* n9 X

. k# O5 S' V. _4 K5 i7 ?4 R' \6 f" I4 p+ U
EP0的控制传输,把用到的请求处理一下9 N# K  |  E, k  ]- Z  @% q
char setup_packet_service(void)6 G2 G" \7 z3 j; g' z4 ~
{
3 O1 c  o! ]/ Y, Y) j- l    if(ep0_std_req->bmRequestType & 0x20)   // class-specific: ^' Z; o/ F7 L
    {/ P5 w5 J* O+ h0 m% C# Y/ [- ?
        switch(ep0_std_req->bRequest)- A  T. {) e: @% z* O
        {
' a! H- B4 q5 Q% Q! N, @+ \            case REQ_GET_REPORT: break;
  i* Y; F( g( u( T) s            case REQ_GET_IDLE:  c' k; N4 i9 H7 s# F) a
                USB_PMA[64]=0xfa;   // return 1 byte
1 H$ d2 {" |6 m. J+ k                USB_PMA[1]=1;4 q$ Q- b7 v" Q% U5 W  o
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
0 f& j  g4 t# ^# Y! f- M8 ^                ep0_state=1;" a2 b# p+ _0 M# w2 f4 m
                return 1;
" t, U* s" |  c. x  i                break;
* ~9 a: }3 \* |- ^9 U" m            case REQ_SET_REPORT:. g5 `# K, Z9 I5 n0 f! G
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
3 f# N" a$ S5 Q+ x% Z* ^                ep0_state=2;
, F, h/ G+ V6 f( G                return 1;% j- V. T- I5 s9 [$ C0 [+ y, {
                break;
( ~9 L% H0 B, f6 \5 l1 c0 B            case REQ_SET_IDLE:
+ |( M5 M" P# t; E5 W                USB_PMA[1]=0;   // Zero DATA
' H0 M( G3 ^. `9 t: ^1 \                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;/ \$ z3 G, F5 g  \! j
                ep0_state=4;: s0 l: H6 o+ Z  s  a# Q
                return 1;1 X' w$ K- Q% z8 |
                break;5 I, T) A6 E% g$ \
        }  o7 N/ q2 Y$ n# N) [1 L
        return 0;2 ^. D6 _- ^7 E5 f3 L5 M
    }4 u8 F, \4 b" s1 I
    else    // standard: l% o, u, C# I9 u, Y$ i
    {
9 y/ h, b0 M0 k+ s7 s        switch(ep0_std_req->bRequest)
' n% f1 f9 \, U7 R! c! m6 H5 x        {
8 R( ?$ o/ w( Z: B' K+ v" e, }. q            case REQ_GET_DESCRIPTOR:
7 S6 T$ h" q0 Q                return descriptor_service();
: n, [3 _2 ~( T" E- K) k                break;
) G5 y( g8 P! \            case REQ_SET_ADDRESS:
# u4 f* D$ W* j. J/ Q                if(ep0_std_req->bmRequestType!=0x00)
6 D% R/ B; q& ~* j# E3 t' Y                    return 0;6 t) N2 z5 J& V+ f
                usb_address=ep0_std_req->wValue;& d) Z) Z* w( @6 e) S3 o0 p
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
( g8 k# g$ _7 q; L% S                USB_PMA[1]=0;       // Zero length DATA0
/ b' Q- N! F$ E8 m: L( p. L                ep0_state=4;    // No Data phase
0 o) B: m, Z. {  z; K. `. S                return 1;
. D- c$ P2 o5 g) ]2 O7 X. N            case REQ_SET_CONFIGURATION:
! m8 |4 o# P' |                if(ep0_std_req->bmRequestType!=0x00)
( ~  G2 u5 o7 _' t. |  \                    return 0;2 F# p9 q  r6 B
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;- O) D/ s* |: i# V$ y& O
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
1 i+ i8 C2 O, [' F* O  D% k                USB_PMA[1]=0;   // Zero DATA* r0 `* Q& l, T7 Y, a: }0 b
                ep0_state=4;    // No DATA phase
' X0 t6 X& o, h2 V, T                return 1;
# ^4 a7 }& x) U3 L            default: return 0;* f" h6 S( ]& X
        }# |* l( m9 f4 W, E4 {& b3 p3 B' v
    }
4 y5 T6 M# [$ O$ e- S- g. p/ z}
; w1 `$ M; v. g8 ?/ }) Q; I# D+ [3 O/ F6 X
% ~& h) Y) G- E- K" r

# P4 e3 W+ z& j8 m* l4 ]
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
5 i" T) a, c( {char descriptor_service(void)
; Q$ W8 e: J% j8 Q: ~1 n{
. s2 F' M8 F7 z) f    switch((ep0_std_req->wValue)>>8); G0 h. z" s2 Q' [- W: H9 i- P  ~
    {1 M+ `1 z  h% B3 f9 V- w" N
        case DESC_TYPE_DEVICE:7 }4 T% D9 {  s' Z; {
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));
: F/ e( f1 u( V; a) v& [+ C( b- ?- y  U            break;2 X! q8 E* ?' t1 y1 f4 S/ f
        case DESC_TYPE_CONFIG:2 N1 w5 s: p% H8 T0 `
            if(sizeof(ConfigDescData)>ep0_std_req->wLength)/ P4 k4 V6 ?9 v( q3 V/ K
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
5 e3 U, G0 @" n$ H7 L) |- n& R& m1 Z            else
- ?" p) B/ E8 b9 T6 K6 A3 F                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
3 L0 d  K& o" e            break;: T5 `! k# R" j4 {
        case DESC_TYPE_STRING:1 ~2 c2 ?$ p+ ]$ h6 x
            switch(ep0_std_req->wValue &0xff)1 I6 P- @) j9 d* Z4 H( A  ]: z
            {/ p3 a3 c$ G" U  q! I; k8 G
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));) s# j& o) M6 O1 x; Q0 ]
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
" E( [! u7 i1 t$ {) n% K9 k: u                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));4 a: _  K1 k$ ?* @( B& H
                default: return 0;
2 s7 a. z2 Q+ Z$ k            }3 O: C2 ~; d# @+ o/ v( J
            break;  {  Q+ ~( J" S' K6 _6 m0 ?
        case REPORT_DESC_TYPE:
! o( s, c; w1 w) R  G            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
) v! y& Y6 O9 u        default:3 n4 u3 S4 Z8 m. R
            return 0;' w* r2 p$ d# B& N; H( n* n8 V
    }
" Z; n: k6 h1 d: m& t}
+ m  ~# E# H  Q- o下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms./ |3 ^2 \# A+ K2 m
void TIM6_DAC_IRQHandler(void)
) b( f) @- q; M. d) Q{& t( j5 `) @$ p; A4 c
    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
- b; n$ o! L& O. P
+ }1 b$ Z1 X: P  `9 ~

; x/ ~, q  l8 ]8 }; B$ A6 m( u5 E    TIM6->SR &= ~TIM_SR_UIF;
8 t2 w. N* P3 Z3 |    prev_key_state[scan_row]=key_state[scan_row];
0 [1 f% {1 f3 |% @    key_state[scan_row]= *PA_IDR;   // update key states' ]- H9 z- C/ q0 O- q8 D
    switch(scan_row)3 q0 c6 r& A) Z) r- h$ s, I: M
    {" w1 L2 L; _6 P5 a/ y! F+ ]
        case 13: // next row PB14$ C# ?4 Q. Z* _6 Q0 O4 A8 z
                GPIOC->MODER = GPIOC_DEFAULT;
6 Y7 j  a, a. {$ z1 I                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;* ]: ~% P9 a! u
                break;/ ^- M: |' A7 P/ x' V
        case  0: // next row PB15
2 F  q" z0 O( q4 T& h                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;6 o1 p- ~' m, Y% L/ h  I
                break;% U- J+ g0 O( K
        case  1: // next row PB3
5 E* Z+ L2 y4 e  g$ B5 O                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;8 A4 M$ ^, X9 D; u
                break;
7 i8 m- D4 d& i: \" D) h$ H8 Q        case  2: // next row PB4
  J. i; u7 v& Q) u& D# ?                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;) ]2 n6 L8 B- R3 A4 Z
                break;- q* I' G6 z2 P. X4 Z( X
        case  3: // next row PB5
0 I+ N, }5 j; o) r                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;: S/ v: Q4 k" J7 R- {8 k" n5 [$ ]( @+ P
                break;
: G8 B" y) v! [        case  4: // next row PB6
: i; r  P- A9 h. {- H                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
, j& F) ~* X8 L+ u& k0 ]                break;8 M7 ~1 f( b  _  H; A( Q
        case  5: // next row PB79 f: C" c1 A7 [
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;$ E7 O5 v2 X" J+ M
                break;* w* a3 y3 m. A. K$ r9 k
        case  6: // next row PB8
5 r' R0 K/ r. \  K/ k% @                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
: w+ a8 L5 E$ b0 Q- g' I/ D                break;) ~2 @+ }8 c' G: \; R+ K% ?
        case  7: // next row PB9
, s! I" S. H1 B                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;8 i: F$ O9 y) \& l
                break;
- \/ T# k" f. v# J        case  8: // next row PA8' Q  P5 l) ]% `, J4 {3 v
                GPIOB->MODER = GPIOB_DEFAULT;
$ G; k7 h1 ]5 V9 N& K" O                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;# x! R! s; @, g0 D; A0 q
                break;$ i( `. o+ d; ]; Q
        case  9: // next row PA9
. \: Q& T! f3 O: ~                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;7 e1 F# w. n) v- H5 a, J/ `
                break;
5 k- \0 I1 n8 P2 H( p8 K$ N0 e) h- [# ]        case 10: // next row PA103 ?% e2 j# n6 G' E& H+ g4 V
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
; K% H5 r6 e8 w3 ^                break;  N8 a* H; ^  B' @2 l+ L9 C
        case 11: // next row PA156 e2 s* j5 N2 q2 ~& h9 `  ~& S; T
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
* U" k- {: S- S7 _' C0 A1 M) j, }                break;
3 t. @/ [* Q& O" b$ o        case 12: // next row PC13/ F$ f% o' L. Q' l
                GPIOA->MODER = GPIOA_DEFAULT;- r# E2 e$ I9 v$ z" E+ u' y
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
- a, ]8 ]( n. X1 D                break;
5 E& |- x# p' z1 A% x1 Q    }
0 K/ K" u4 L- v/ L    if(scan_row<13)& Z6 g2 A1 N4 w# G! M0 b
        scan_row++;4 C, N/ G8 v4 g/ d% `
    else& E4 J" W6 ?" l% s' o
        scan_row=0;
: p! a4 a5 ]0 U& p}' I7 k, n. j+ {

; o! U1 {& t6 p& z( K7 k. S% q/ L# B3 [& v' K6 {- W) ]5 Q
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 # p0 i7 I4 l2 B8 o+ z, d* J

  H6 L$ A0 u9 S) g1 [- u扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
' v! j! }  A$ ^5 @4 G4 Z3 a8 h( p# r1 \: F6 Y# M5 j
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
1 U* M5 j' L+ n# \

  |" ?/ X# x9 `% w
" V* |: k1 N$ Y6 @1 w) J3 fconst char hid_keymap_qwerty[14][8]={( x3 c7 C8 `, ]4 [; y
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
/ q( g9 p. T6 ?) p: w0 |( e    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},3 p# {* |% _$ Q& w% C
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},* H* [+ {5 D# I% L: J% g8 s
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
+ I, I, Z) B- h- X    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
& ?2 d+ c0 O8 X4 h- `; W, D% E    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
% u7 Z, Z- Q' j, q. v    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},: A( W6 R8 n. i, P! b5 V" L7 A
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
9 h5 L- H6 p$ K    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},. q! v6 B, l; c/ L6 f
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},! k+ f( [8 Z0 |1 n
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},5 F4 j% b0 u+ l9 s' l! a
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
+ x: H# C- u& _3 l) S5 L- [4 C    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},, F6 @: Z- M; u6 J
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
5 e9 f8 p( O8 ^2 B8 ?};
, z. S7 d$ @2 `' B* A4 U' p4 |# j8 b/ G3 ?1 x4 r2 q$ V

, x' s4 p1 v4 N8 @3 l% vconst char hid_keymap_dvorak[14][8]={  q1 Y0 `6 X* t7 E  j6 q$ J% G% x- H, r
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
# q* f- j& ~; u# P6 g    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},7 P; l5 h; _) `, w) S* ^0 L) F
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
8 d& G" d6 R- p; ^+ M4 |  n    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
0 U; R: k- \- V# P# F  O2 }' i6 w    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},* Z7 |5 j, l7 A
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},7 J, v5 a8 C2 U, ^
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
  p. x: p: I& p. K/ Y    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},7 K* p: s8 g' y. E4 H1 y) U" R
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},6 J4 s8 N4 k* R/ X' \
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE}," y+ F4 X- r# U# q
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},$ p* L/ Q0 {/ V" ^( m4 b
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
+ h) c5 O) a) a3 h    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},$ t1 J  n6 F" Q
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}+ l/ J  j: q" h7 _3 I9 R0 G
};. h! g7 e( [: S* n2 P; T, V$ P
1 u& ?# X; A. l4 k' e( X' Y

/ [4 @' M! [+ Q  k8 z6 r上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
1 ^5 n- m; h9 g- [) j: b  N: N* `. B! `* c8 I! G2 F8 [
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

' Q' E& Y9 r1 `2 N* Q+ o$ P1 |! H5 Y2 R" {) b, J+ m  a& u7 Y/ X5 v; w+ Q
void update_key_matrix(char row, char col, char onoff)1 N3 [; j  `, J* o" h
{
# s9 B  B* G6 d0 m2 E. S    static uint16_t hid_report[4]={0,0,0,0};1 M. ]) \. [) I" I% {% d7 }
    static char (*hid_keymap)[8]=hid_keymap_dvorak;
  m$ ?3 c: d, C, }6 O; L7 g5 e, d: g4 N5 g
+ |% m# @. i, q1 V
    unsigned char key=hid_keymap[row][col];
9 p' n5 W$ s: R% i: O+ \    unsigned char *report =(unsigned char *)hid_report;; G& i& ^  f7 A" x
    char i;7 ?* S' C. B. @

" Z' C+ J+ U, |* g- W( n

& l* j7 D/ V& m    if(key==HK_MODE)
9 Q* y% |$ c8 X* O    {
$ v7 N! A  U( K3 `6 ?        if(!onoff)
  y8 {: z& g) S0 P        {
/ g5 o. |. o. Q" Q/ {+ t            if(hid_keymap==hid_keymap_dvorak)
/ C1 S9 D5 D  G# M            {5 k( v5 l6 t7 {
                hid_keymap=hid_keymap_qwerty;5 l1 L# X( J! _: I: P. ?0 K5 L
                GPIOB->BSRR = (1<<2);
. e( p6 y1 a' r            }# ~' O4 l: `. W+ [2 r# ~/ u3 d6 `
            else& P) j) u2 L' x1 z$ d
            {
: O) m3 v, y9 X                hid_keymap=hid_keymap_dvorak;, D; D" ]5 Y4 O; A/ L) ?, o
                GPIOB->BRR = (1<<2);
4 _# h( f* r* \1 ?0 P  F# n4 l/ W% q            }
/ L2 Q/ Y/ e% q        }
* F' Q  X: R* p4 F: w$ }% H        return;
3 ~, }2 E; ?8 O$ A5 ]8 G    }( ^, p0 X9 S& f. D7 z3 [6 w
6 K6 x3 j- {& k' @; ]
/ r" }9 k" Y2 U- l
    if(key>=0x80)   // Alt, Ctrl, Shift+ s, S1 }& W8 M1 }+ o& V- T, L) s. d9 t
    {% U  N; _% V  s# R" W
        uint8_t bitset = 1<<(key&7);  G+ d* Z) A" R; f+ l; w
        if(onoff)   // non-zero is key up# [* G' G+ h- E/ Y8 c' l% s
            report[0] &= (~bitset);5 y5 ~1 K& H( y  H" K" @( S
        else; A1 E7 o5 G' A4 K3 x5 g1 d; I
            report[0] |= bitset;
0 p, B, i4 h2 `4 i4 ^    }
. |; O$ p' P2 _  x3 h- \3 z    else; [1 L+ h' h% n! |. |& u
    {
1 d. S  }9 ^3 ^5 z8 B. s3 A        if(onoff)   // non-zero is key up
1 }' X3 F* H6 ]; ^        {
4 b/ ~% D/ A- u* ?5 [" j# `            for(i=2;i<8;i++)3 i% T1 T+ f1 r! e) Y6 j
            {
0 P% Z, [" w; L2 Y                if(report==key)- ^9 F  s5 q  Y; p2 v. q$ i* T+ ~
                {/ ^. B; u* F, u
                    report=0;; U+ m2 ^/ e8 B- S
                    break;
' i4 F9 [4 l* |9 p                }
! c+ F0 Y, F0 O2 K; j0 c7 o: t            }
$ F$ x3 |, V6 j        }
* I5 [5 [- Z  M: g        else
4 F( w" X4 I: t/ Q+ H7 D! R        {5 ~2 F3 V$ u2 H$ l- @; R
            for(i=2;i<8;i++)- |5 x& ~. l* b0 j2 C1 E) _
            {
1 r  r/ N3 d: q8 q8 @# \  w                if(report==key)
( p8 ~; @. X) |0 _3 i4 H                    break;
  v7 o9 c/ \5 E2 C/ L& ]3 V8 ~                if(report==0)
% a1 g6 y9 T2 @2 I( B) N                {
+ J- u. F2 T6 f1 E7 C4 b/ @/ a                    report=key;
6 ?9 A& D; }; K" |- H4 {                    break;
& l7 G9 d3 }* o4 b3 u% G, t  V                }0 C/ w3 ~8 F$ J+ `- q9 w
            }
  Z/ A( t4 N& B. h! w2 D" Y( e4 J        }& w( `$ M( F) Z( A
    }
9 y8 ]+ _% Q& w4 S/ D: m/ V    for(i=0;i<4;i++)$ e5 B3 B2 K( S4 t: x
        USB_PMA[192+i]=hid_report;
& ^% r( U0 d, ?3 M: l' l. p    USB_PMA[5]=8;   //COUNT1_TX
' C: f% Q3 `0 U# _0 k" _    if(ep1_wait==0)1 I1 N- _7 M* D8 M9 z% ]7 M% x; I
    {* G/ U% |3 F) ]
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
) w4 a% t$ k. v; n* C' o4 s' D        ep1_wait=1;
, d: |& ^' P8 w7 `: Q: E    }
' [7 ~) I& |& ]: D}
+ O! w, l  l! L' p3 {) Q2 e
- y2 M! @- b1 {
4 \1 I- v# n, X  x完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
( P; B7 a9 ^1 p$ L8 | keyboard.zip (8.7 KB, 下载次数: 6653) ( E8 W$ u9 d  _& k8 p

8 K) {, L& d# z# H/ y- f3 t: y0 L. T; M
3 ?1 ~' |& g1 s4 T
) H3 ^7 `5 J: p/ _, ~
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
# L5 a8 n5 u6 X# P8 y* l不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
- H& N  _! b. o# A3 k% [' R刚开始我以为要把打字机改造成电脑键盘# s1 ]" T# R  q- y  G
不过楼主也很厉害!
% Q7 a  @! u2 P. G
哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
! m$ `. r& g; l! T% t6 {
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-8-20 02:44 , Processed in 0.173821 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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