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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

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

8 [! c* i8 I' s6 A% a) ?% L% O

+ m0 z2 K1 G$ q7 _ 235140i3a36qivqzuvmt5q.jpg.thumb.jpg + m6 _4 r' {" b9 |% w2 _: z+ C: r
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。' T- _! _. U  A4 t9 n3 P# r
001734klbyoluenuwz4h4b.png.thumb.jpg ; D# D, X( B; D% L3 u+ b2 y* t, K% m5 D3 K
为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:; L" i2 I& d7 ]8 a5 w
003625r2agx2f5v922cf2f.png.thumb.jpg ! d# A  @  m8 L- N- f" T/ n
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.# \# [8 i0 y3 i$ {1 V, E9 F& _9 ]
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
. L, [8 K: g7 c3 ]0 o+ R
005836yvs0wvovwsssgd3o.png.thumb.jpg
7 ~, r5 j. ~& ^, Q0 i% IDvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。2 }" w1 ?' d# _2 g% H/ d

% @6 d% V; w7 }: g; B5 B7 T1 T" ^1 x我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。2 \6 q; q2 `0 i' j! U
; x, K! _* h( `) ?
到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
* V: k! H8 z+ s/ ?( J
. W. o! @. a' P0 _8 a/ w机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
: h7 a: J& c: d: l) R
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg 2 _: y3 A. f7 J5 y  {' a
----------------------------------------------------  分割线 ----------------------------------------------------------
# M1 s4 r% u9 D5 S! F4 Z9 v" A
3 b% z+ s3 P! u- W- j先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
0 V4 U- @' }7 L. \" M
020011osionbunl4ui44vi.jpg.thumb.jpg
% r- S8 b, I  _. z6 D: x轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
. {5 c' s: Y+ j; R/ c 020017j8ycmnv7788bqv52.jpg.thumb.jpg
( C9 _7 h8 }1 z9 V特写,80C49
2 j# r+ |. ~1 P  O% Y, O: ]4 B8 L 021040oujzuvtut6iujtvz.jpg.thumb.jpg # v3 N, q1 T$ g% U) l5 b
LED部分,使用了一片D触发器锁存指示灯状态.2 Y. x4 L& k0 v0 _1 {" v
022209ixzjfjc9crmrrqjm.jpg.thumb.jpg & h% ]* O' R' a, g; U8 R
暴力破坏,将80C49拆掉8 r7 Y6 ~8 h4 K
021113e48qq98vyohvhzzh.jpg.thumb.jpg 8 K' E# N/ T- Y1 J: c6 F
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。& T1 e: d5 X! X8 u. ^6 m; f
021125nc9az6dj33rlds2r.jpg.thumb.jpg ( O5 v' R' N4 G" Q, r
焊好元件后的板子,准备替换80C49
/ o/ M8 o. X5 i! Q 021146zmmpmh4pav4o7g6a.jpg.thumb.jpg / k" v  L1 e0 e) A6 t
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。) F; w1 B/ M7 c7 I9 l( d
021104shifhnrqbr3o5nlo.jpg.thumb.jpg
. E3 O" y! a/ Z这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.
. C9 H/ ^1 R. |# Z; ?/ ] 022003ym1p9u4ug40280uu.jpg.thumb.jpg
$ ^9 ?$ P0 M$ y- }5 S+ ]开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。
) p/ s) `( }, V% Y: n8 O" g1 E 023313kt141q9qajtol7ma.jpg.thumb.jpg * W  n7 l) m% ^* l
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。8 e2 w" S3 C/ ~, W1 N
023322nt7l5xb3ltttkltt.jpg.thumb.jpg 4 }, I: v) |( Q* e6 u$ x
主键区键帽就位
* U: N6 F" x; @3 t/ V 023331hin88e8wkrwzwikx.jpg.thumb.jpg
  k) ~% F; E# a& M* u% w$ G, R编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。0 S2 m6 @1 x2 h* j* Q1 e" _* O
023336wjzlgopugg1jyy79.jpg.thumb.jpg ) C8 Y3 I: q: l
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。+ p3 o$ ]* u% k* T# r* h/ r" l
023341sffu4j3g2323h6fl.jpg.thumb.jpg : l% f: R0 e" v- ?) \: b7 E
! u$ o+ b  y! d9 k
----------------------------------------------------- 分割线 --------------------------------------------------" O2 d& E7 a8 O9 F! i

7 L; Z; f' d& e- K% _) i9 c
0 p& G, k/ S0 l, A3 q- m& ]
: u! ^: @, G% M" N
5 q4 i  C* o- d6 L

7 ?  g: f% B0 f3 g. \7 I! ]& _3 S

" z$ n. Y5 `5 ^8 h( }, ?  v
' a$ r! i+ e; `# d, e0 z8 Y2 C1 t" l  i: Q7 ^( B
/ ?) |1 o* C/ f0 i( x( N
$ I2 S7 a1 c2 e. ?
, J0 _! j4 [0 b* @" t) V* f5 {
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
. H" d* }  X  @& K80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:& Z" Z# D7 w: w; w% x2 X1 ~
104025nzibm2rmiomhyirm.png.thumb.jpg
8 C; M3 ~9 c$ o5 `: E: R
/ r6 Z: M* X) [2 A/ e
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
1 g4 M1 L# }6 a5 E3 d- l 105004zkrez5houvkkznko.jpg.thumb.jpg 0 u1 l% t. ^# E9 J' d- y

9 _# a6 ^& t% t5 [" F/ o4 h扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。5 D. w# a+ K4 x" G! X
# b* g( F4 i1 e+ A9 r
这是我设计的电路图:" ^3 W$ n+ l0 H7 L( C% ~0 h1 c
110344ej2z2oo2rflo7oe7.png.thumb.jpg
  a6 G; i: {( r$ y3 e2 f
) t- u, b$ \0 Y- M
PCB Layout:
' L0 Z, _5 O( q( X 110847jjbjvt34vwt3v5bb.png.thumb.jpg
/ O; z( ?! n: y0 V- W
0 ?; V4 h5 w' v( m2 o0 e0 {. p
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
- g  I1 H5 l+ l* Q% o4 c4 ?4 T: I8 j$ [; Y  ^8 I

  W  P& k: Z9 ]# k
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑
* ?5 V8 w# Y6 ]& e" v- |) i( t
* f& ]5 y# Y) i: V8 \- E2 K软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
6 d7 q2 J; ^6 r: t& a
' e6 C; k+ Y: G8 D6 y* F总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:
( p9 J) B& _) N) G+ F4 T& Y2 y* b 113818pmrfsb6z0byt6t06.png.thumb.jpg 6 i( V/ I# O- ^  x) h! X1 z! f

4 e) E* D/ w. N! P5 W4 B7 H其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)% l/ i2 a9 Z3 I# h
7 {" L, f( v7 p5 w! R# ^+ |
USB的中断ISR,bare metal哦2 O% D* s) p4 x/ D/ _& [

& {4 z. t: ^, m8 F. Rvoid USB_IRQHandler(void)! x2 x9 C* M/ [0 g* n
{
- Z4 C0 p' U( U2 M; Q    if(USB->ISTR & USB_ISTR_CTR)( E" i6 s0 ?" @$ `7 Y. F
    {
! x; p6 Z2 D2 j        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
3 I) t4 q+ F8 D3 m7 o        {
4 O' w5 H! d' {/ x            switch(ep0_state)$ y  N9 \- Z: y* ?+ {% R
            {0 H% z3 O& l6 L4 |$ l9 o( Y; X) ^
                case 0: ep0_state |= 0x80;5 E) r2 E- L( u  U' C
                        break;
. }% r( y- v, m5 k9 G+ K4 y                case 1: if(USB->EP0R & USB_EP_CTR_TX)
$ g. K- M- O* v+ H% ^1 ]4 J; Z2 ?                        {
# P; s; ~& I+ C$ x8 h5 g! {- W3 m                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
$ [! s$ y, ]' @$ A8 h                            ep0_state=3;: E  u9 h+ r$ W0 R3 H1 }; {/ m, a
                            return;
- u" J* Q$ p7 r: K                        }' W$ o$ I% d* y9 a
                        else
$ @3 n" g: Q( P. E                            ep0_state=0;
- V5 k/ z" l/ }, g7 F) b" k- b3 {# `                        break;+ ]8 Q/ j' b; p0 k! b! d
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet6 [2 A& R: U. |3 S
                            ep0_state |= 0x80;6 m: i6 x6 \: m: c, ]) V5 i3 l& N
                        else" c9 ]# x; J( c+ W4 b
                            ep0_state=0;) X  B: J* N+ I3 {0 B
                        break;
' p0 g9 ~" M  f                case 3: ep0_state=0;
8 ]. X- A1 `2 D                        break;  r( E. r4 d, R' B' q
                case 4: ep0_state=0;" f- p/ k4 |1 X; ?
                        break;
8 a8 d$ z- ^3 Q" m* f. B5 a; E; K                default:ep0_state=0;+ [" S/ `: l% O6 g6 m8 N$ ^
                        break;
# J! ]. v  e+ W  H& C! t; c9 w) d            }) `4 O9 b, K0 N: T! @
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
+ d* ]% h' I7 X+ H$ e. A5 a            return;
5 r7 P3 V* _; |, t/ @- q; i$ I3 t; [        }
% ^5 T1 X" {6 u3 M; |  Q        else    // EP_ID can be 1( Z2 r3 A0 o1 R; d8 y" B" q6 ?
        {; V+ E% J( J/ d" U4 e
            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag6 k% S# e/ T6 c
            ep1_wait=0;
6 J( h5 r. z& Z, G            return;
* E4 }6 U8 u& B/ E, R' J8 [: b        }
6 p8 N& I" h; a, A    }- t! _- J% m) }# Z* [# @) {' l
    if(USB->ISTR & USB_ISTR_PMAOVR)9 ?- H' b$ @4 Z. {( _
    {
* E! e* u/ Q8 h) h        USB->ISTR = ~USB_ISTR_PMAOVR;+ h% R1 x+ L5 F( U1 q
    }2 S% {& i* R" M. P) v
    if(USB->ISTR & USB_ISTR_ERR)/ r- S$ t# i$ ?* M  ~+ N3 }
    {
% w8 J8 w! F5 d$ B; m9 I        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear- M' P9 d" Y* e; y6 S  g1 H" f4 G3 ~
    }# m3 ?. ]( `( q2 \6 F4 F# H; q
    if(USB->ISTR & USB_ISTR_WKUP)
  i$ Y  p" R2 ^4 n" k    {7 H4 Q9 S( @( `. M6 h
        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
3 a6 t5 q9 h* R9 k        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear2 R9 I1 ?5 t- V$ D
    }; `) {* O3 {8 l  K  p5 o! N
    if(USB->ISTR & USB_ISTR_SUSP)- o4 C' i; E& U7 v- e. S: ~
    {
6 C1 o# D# n$ l; _* ?        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend3 G$ q8 ~% [' g( k* I; D2 S/ S7 I. }
        USB->CNTR |= USB_CNTR_LPMODE;   // low power. W' X8 t. r6 O3 N) q/ o
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear; l6 b2 ^& e. I+ C% i0 j; Q2 ^
    }" A+ x+ u; O5 Z3 r+ E8 e- n
    if(USB->ISTR & USB_ISTR_RESET)- {. ]' j5 W0 `; T
    {
# c. a& n+ M1 L- l% X3 L' l        USB->BTABLE = 0;    // buffer table at bottom of PMA
% P4 E, y5 h5 o, ^8 O8 P0 G4 x        USB_PMA[0]=128; //ADDR0_TX
. L# e( R7 |3 c/ ?: G  y        USB_PMA[1]=0;   //COUNT0_TX
/ l' B. D2 d' k6 d) Y* Z7 k  ~2 e- f+ F        USB_PMA[2]=256; //ADDR0_RX  o7 W; S( C& F" c; Z/ `
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
4 ~6 p/ E/ t5 J, K  U        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;. O, f. L8 G( G1 T2 I. {% F, L
        ep0_state=0;
# Y) r' X0 a1 \7 M7 v! b+ G        USB_PMA[4]=384; //ADDR1_TX3 u) X# e6 m7 k
        USB_PMA[5]=0;   //COUNT1_TX  R5 l2 s9 n/ z: ?' x9 s
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
% K- m, m  L3 p, X* G        ep1_wait=0;
2 B! [) g9 n" Z' u        USB->DADDR = USB_DADDR_EF;      // enable function  u+ K% B& V+ a, N; g
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear2 a' Q& j) U3 K0 k$ U
    }
% R  s( B& b0 r  O; H8 ]( H& b    if(USB->ISTR & USB_ISTR_SOF)+ S$ n! ^, r& `
    {
6 ^" W9 _+ X* E5 U8 H- F        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear1 f3 |; \" v9 u# B$ H# R/ q% W, j# V
    }1 T" ~& {  L4 |* T
}! R1 g# d1 ^! E
' b3 J; Q! B( ?9 j
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
2 i7 F* \4 K: k) F
) R# T. K: ~5 A& h5 d) o6 _! B5 e主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。; z& w3 _6 E; D2 d' _

6 y' _7 Y: h+ v- M. f
    while(1), A% @( e9 f/ I3 \2 A3 H0 @+ m' v
    {
6 t; v0 K) c+ g# n        static char row=0;
1 g' e  t5 C9 T& ^7 b- t. _        __WFI();
, J( A* D5 U. U. h        if(ep0_state & 0x80)    // request data processing/ {0 l# z( }! B" p5 A* q: [
        {
/ @$ d: l, }& M; I            if(ep0_state==0x80) // SETUP phase& J7 l8 k6 y( N4 D; C; u. c& U  B. [
            {
9 }/ P' ?  H3 z" Y2 C- \( M& i5 \                if(!setup_packet_service())
& W4 J8 y9 x% K: P; L. U" e' Z                {
- v& X1 T0 s$ K  x9 q                    ep0_state=0;7 K! n3 H5 N9 d0 \7 Y5 u8 k0 A2 n
                    // not supported
1 l0 P" A; _7 x7 i+ N( Z                }
) I5 e6 P3 x1 R, L8 v! J                // ep0_state should be set to 1 or 2, if processed: s, Q. b5 D$ \) b! `
            }
! N3 b7 H8 T- x- Q* T' Q! Y            else    // OUT phase3 ~1 w9 D. z1 Y
            {6 R+ U- J' Q/ d- P: V% h
                // process data
% ~0 R+ l( t# A, o% I: g) Y                show_LED(*(uint8_t *)(USB_PMA+128));
# q9 ^9 X8 R% l                ep0_state=4;  i# \6 i3 n+ F& P6 K2 z" D
                USB_PMA[1]=0;       // Zero length DATA0
- d6 U) V. j  [- ~                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;, |8 {0 @9 f0 c# o8 q9 }' O8 }2 j
            }
% \1 ~# p2 n- [* Y        }' d; g2 J# d/ h; ?7 @" d& a
        else
4 U5 q+ ^& d% K9 m9 k* ?        {
& n& g5 T# I) n            if(usb_address && ep0_state==0)
9 ?7 o) T: [  t            {% N( C- D- g! A: W9 L& f* s7 w/ J
                USB->DADDR = USB_DADDR_EF|usb_address;
7 P/ t& Q7 h4 y9 B4 N% _4 \) ]' ?                usb_address=0;4 n1 P7 M* z$ j! H+ y; Y
            }
- B( `( e- N* e1 t( c8 q; Q$ l, F/ x        }
9 Q; H/ @2 J2 C1 T. s" g, V2 x, I        if(row!=scan_row)   // new scan line' T  b" J: L5 x/ V
        {
; ^( R; i3 x% c! R1 ~3 x            if(key_state[row]!=prev_key_state[row])
6 k2 D& d2 f4 F            {
0 v* q6 k2 t/ I) y; P9 W& t/ s6 d                uint8_t test=0x80;6 g& L8 y2 s; ~8 _0 v
                uint8_t diff=key_state[row]^prev_key_state[row];) ^4 N, h7 N0 Q5 n  w$ Y  d
                for(i=0;i<8;i++)2 R: p9 T8 c& W0 v4 i5 T9 y
                {
# w1 H( Y* M. d( o9 L, U, H                    if(diff & test)9 [+ W' o6 r* R1 T% A& X' m9 f9 W
                        update_key_matrix(row,i,key_state[row]&test);
, r) q+ O; t- X9 q! b7 r8 `7 Z                    test>>=1;
8 N2 |  k4 P. u2 ]$ E$ s5 q$ p                }
& |& m3 w: x. d7 e3 u" I            }
- A& W4 W2 }( _, ^: K0 A            row=scan_row;
/ a; k: U9 n( e" |: e( X        }  g3 D( U1 m9 J7 H. d7 U& V2 l
    }
: j) R4 X1 J2 W  Y$ B2 z* K. ?
. R6 \* p, j: d' E' Q9 N* a9 ?' P* G- T+ e" E, j& z8 c% h+ G
EP0的控制传输,把用到的请求处理一下
8 [: ^" w# q# F3 K% ^. ^+ H+ hchar setup_packet_service(void)( ?) ?$ ]; x8 v
{, Q6 y, h5 @' i
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
9 o  M* Z5 r) a. w    {
& R# y3 _' |  b        switch(ep0_std_req->bRequest)
( b7 C9 I8 G- Y; U5 l        {8 W# x. y: Q8 S+ J" C
            case REQ_GET_REPORT: break;
  ]0 x; H4 Y; K0 n9 D            case REQ_GET_IDLE:6 x5 t% [. e$ l+ {5 k8 ^( |. p
                USB_PMA[64]=0xfa;   // return 1 byte
& `7 Q3 Z$ A0 g8 _( e" s$ _# B                USB_PMA[1]=1;
# o* r9 {% ?4 D& l8 `8 a9 H                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
; d" u6 |1 Q, ?2 h$ }- C. K' a                ep0_state=1;) r( w1 s! X% Z6 u- C7 x
                return 1;1 r+ t, F0 D( W+ p+ L: B; E$ r
                break;5 P$ Y+ `/ D% i& h. d
            case REQ_SET_REPORT:
8 n1 K4 K$ |5 Q5 ?* \                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
5 Q1 E1 g6 O/ b3 }, K                ep0_state=2;
7 d; |8 q" A( ?4 e                return 1;2 A7 K6 `' F4 O, N7 q8 ?- \
                break;
$ r. S" x+ y; E" U            case REQ_SET_IDLE:$ ]' i6 i) G! ~
                USB_PMA[1]=0;   // Zero DATA
3 g, `$ Y$ G9 x5 `2 z) F! u                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
# v$ @* l+ {7 m) x1 |                ep0_state=4;
3 r& A: S" U: l; v" Q. r9 M. X                return 1;0 q. E6 k& n: S4 B9 O
                break;
8 a+ K9 b: B( _        }
3 U, @" M6 r- h! z        return 0;' N3 F& ]- g6 g% B+ o. X3 y
    }
4 f1 U$ }. ^9 E    else    // standard
$ {2 R2 |8 y1 X$ q8 z    {
" r2 L+ [: m1 F) [        switch(ep0_std_req->bRequest)
6 L* y6 _6 \( ]1 A1 _0 a0 ^# j        {
+ U% {" u! H' f) B+ A            case REQ_GET_DESCRIPTOR:# u  [$ i0 m1 D! _4 E0 }* l
                return descriptor_service();
+ ^; M0 l( f. J+ f6 O$ \                break;% X# I" w. p6 }6 b' [/ K
            case REQ_SET_ADDRESS:' \( j$ e- g9 y+ h6 k
                if(ep0_std_req->bmRequestType!=0x00), U" a5 [. @: C( E. b9 U( w
                    return 0;
1 l3 H- o. W$ |# l                usb_address=ep0_std_req->wValue;' U  U+ e" _5 F, e. e6 l
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
2 u4 `' V. [$ X. x& D                USB_PMA[1]=0;       // Zero length DATA0: Y& a5 J& j3 V  ?6 _5 G. f
                ep0_state=4;    // No Data phase
- L) n& H1 y7 Q; _! A7 f                return 1;$ ]! [9 ^" T- a8 a( r0 [
            case REQ_SET_CONFIGURATION:
8 s1 |7 l9 e9 n1 }; }- F                if(ep0_std_req->bmRequestType!=0x00)
& s- {: n5 D9 _                    return 0;, \6 p6 U7 w, w: E4 X3 ~% |' a. G
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
6 X9 X: ^$ b+ a0 [+ Z" \* X/ F                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;0 S  _  b7 D2 \& y% m: S
                USB_PMA[1]=0;   // Zero DATA
* Q! j( |5 X! O                ep0_state=4;    // No DATA phase
9 T$ v" M0 g9 T" o9 p                return 1;9 F& y; |7 A3 b: g
            default: return 0;7 ^8 k% p( i/ P, G5 s( o
        }" O+ M) e- @3 D6 W6 C
    }- p0 x; b$ ~; X/ G- q
}2 n6 C/ ^) M3 ]9 D
; z: T0 d% m4 r( b% u9 R

4 c% b" z$ Q) T# q& S3 g$ P2 {) ?  l! i: R% m7 H. f) |0 {& f, w/ |
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的6 o5 @- y) k1 x" A, k
char descriptor_service(void)7 A! o! H' g/ i) d+ o# H
{
  }% v9 B% B( _% `& |8 J    switch((ep0_std_req->wValue)>>8)* S  y6 G/ _+ A& W6 Q
    {
5 m8 X: \' ]; U* r( D6 \6 w6 h        case DESC_TYPE_DEVICE:; T6 d) {/ u6 R( u5 c" g
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));
) P6 a/ i- I6 L% y8 U- W            break;
9 y! f8 }! O$ p        case DESC_TYPE_CONFIG:
; _8 A! j6 s" z& m, `! ?- E9 S- j            if(sizeof(ConfigDescData)>ep0_std_req->wLength)* v( v" I7 K2 x  ^/ F5 F6 k0 t$ V
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);" O) M2 V0 h1 v. ]5 s( u
            else
( K' e- ^5 D( U7 `) p* h& s. G( ?                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
: }! O, ^/ j6 R7 x            break;7 ^" o' I; J: w$ i! J5 F- o
        case DESC_TYPE_STRING:' E7 `# e8 ?$ E) `
            switch(ep0_std_req->wValue &0xff)7 d# b1 F# r( |1 A; A! \
            {
# E0 e# E& A3 s5 D7 A3 e1 {                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
+ {8 `6 E+ v2 B. w& x* {                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));9 O! r' R) g5 Y! J/ e# P
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
7 S/ v6 \! b  b5 m6 K3 ?% W9 D7 w) W7 y* k% \                default: return 0;9 Q( e; P9 Z0 R1 \
            }+ i, z2 j( v3 l9 L8 I
            break;
0 H$ b( V+ _3 M8 a8 }        case REPORT_DESC_TYPE:1 |+ i7 v; R- e0 `: x
            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));1 D. r  w2 ?' S
        default:8 L  r- f  z8 |6 O  J. F! B
            return 0;
7 o, n- M1 D: x$ x0 _: v9 H* D    }% F, @! k: Y: ]/ H% E
}
& O! w. }" q  |" {% f7 H" x- j: z下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
. \3 i$ S# w  r' E) V- evoid TIM6_DAC_IRQHandler(void): m$ r7 x! X! M0 B
{
7 W  T1 `  E0 m$ U; P& z/ J4 s    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);  n. ]2 w2 w$ o* p4 c
8 C$ p8 R' c: p& F- Y
, k4 O% k1 N- \1 U
    TIM6->SR &= ~TIM_SR_UIF;
+ f4 m6 Z' K; _2 u2 {3 H    prev_key_state[scan_row]=key_state[scan_row];
& i( N2 L, W+ B8 g* N& H& V    key_state[scan_row]= *PA_IDR;   // update key states
* h" R- p9 b# M    switch(scan_row). {! Q6 K* Q/ F! E
    {" O& Q) J% v5 s/ V4 }
        case 13: // next row PB14) B+ {9 W1 j! d1 I: D/ s+ U2 o
                GPIOC->MODER = GPIOC_DEFAULT;5 o7 O3 ~$ C0 b1 K" U; l: K
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;9 f% c- Z: g9 E% j6 E
                break;
: p$ l* `: O4 z: K  L# L        case  0: // next row PB15/ m0 L6 r' X# p9 s+ u0 f; W; t$ j. W! [
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;
* B+ @( L1 E" e: m$ j7 D; ~6 U. h                break;
! A$ E7 x4 J. g% p3 ^" \        case  1: // next row PB3
; n+ j5 @7 r& r                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;; N2 `# \9 w' R. s$ c
                break;, {) Y4 V; T% o$ Q& L( a
        case  2: // next row PB4
* z6 G4 ?5 {* S) Q                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
4 X8 Y" c8 ~. p; x  y                break;
/ _  `) ?2 L& s* ^        case  3: // next row PB59 N( [/ @+ \) z* i5 W
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;' y7 [% l$ J! W: T/ Q
                break;
. c! x/ n3 X3 e  r: U3 ~0 m        case  4: // next row PB6
" I# o3 I% c9 {+ d9 C- a1 X; p: d                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;' E, d% t( ?' V, u
                break;% G3 O$ t2 E! k9 e
        case  5: // next row PB7
7 K$ G- G& ]! _. v1 Y# V                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
1 `! v7 x; f) u2 h                break;
  C; Z; q) t! v- o; f        case  6: // next row PB8
- Z# S- N5 E1 J8 l# d* p. W                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
% W, C% l1 K& U/ l, q- ?  `                break;
( V" w+ |. g. ~+ m9 v1 x        case  7: // next row PB9
! P4 d# E% ]" z9 s6 H5 v                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;
3 ?9 F! b# q1 J' ^* S                break;
& p2 T& q6 d% H: z' T" Y        case  8: // next row PA8
3 H7 ]1 Q; d  L' v/ k" a                GPIOB->MODER = GPIOB_DEFAULT;
' G# ~6 R6 u/ z9 Z3 `1 ~                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;
: W, k0 H( m. `$ `. v                break;
: W+ B  R2 f8 _) ?4 y        case  9: // next row PA94 g1 e- E0 k+ O! Z
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
7 d4 e0 Q6 y) v                break;5 y; V6 L5 K* Y2 q; k( l5 M- U' l
        case 10: // next row PA10
) ~, b  r: o8 ~# D5 @                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
9 {$ I6 k& R( p3 t1 c                break;% ^& i4 r( {8 k5 L  G
        case 11: // next row PA155 g# ^; Q+ u0 U0 Q  b* [
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;* ?9 z$ X4 [1 M* L# E# N
                break;
7 o' A: |5 Z. |; ]        case 12: // next row PC13
" v1 M/ u7 t( X5 o/ |/ h' J                GPIOA->MODER = GPIOA_DEFAULT;
8 n3 p6 h' D) r8 M, B; g                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;) Y, ^3 i) p! R! S
                break;+ v) {3 m4 S2 L
    }/ g3 Z7 ^  Y6 n/ L
    if(scan_row<13)( R7 k6 d: ~5 N! l
        scan_row++;
/ Z6 `: [; {6 j; T0 d    else
4 N# h& s. Q) t0 k$ E: `: K        scan_row=0;
4 j' E( g6 o1 }7 E- ]2 f}& n* W7 {5 L2 g( n! n
' I+ M) A' q4 I$ N% H3 O

: Y) `0 V, V9 L  p7 G1 v
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 ' L0 _2 K. r4 _, s) ?: q" k

8 q; {7 m: i8 a( U2 y扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。; c6 J- H, B+ {1 C2 @

' R2 ?0 y6 W* |. c0 B* X/ f- F要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:

2 {; O- X( j! e  d) j7 d# |  o! z1 ]$ J. K3 a

. U0 M- G. I; K( l2 W8 T5 ?const char hid_keymap_qwerty[14][8]={
( z1 V% T) H" @: e; m- f' G    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},2 s7 Y" I- i: V
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},% S0 O1 A: y1 T% r1 k( W
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},1 |4 q5 n$ b% K- u  B
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
/ \% ?0 F+ Y, ^( {& z. S$ A    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
- P1 U; V. s2 I    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
; _/ J: ?% R2 u    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},9 H7 W; k9 v% v3 Q, u  }8 J
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
$ D8 `+ p: @- i* {# v    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
6 P% E( q1 W+ K: P3 Y% u6 e' {    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},6 D+ p: u7 S, m3 W, R
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
% v* q) ^+ e7 l, q# F3 \' S  [, G    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},0 F/ w$ J' b- s4 ?: ]- u
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
) h9 D+ [; ?9 I) t    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}3 x# |7 m3 U9 R4 ^8 ^; y, F
};7 e( Y1 B" c, G1 X! ]6 t
8 s; h; c5 F8 p$ g, v" L- p

: X. A8 J& _4 Xconst char hid_keymap_dvorak[14][8]={
9 N' N/ w9 a% L$ p1 u1 S    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},4 d6 N8 C0 i1 c
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},, S# m1 \6 ~. E+ u' L2 a/ o5 f( g
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},6 @8 m3 ]5 L$ n6 }
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
) u9 x' R. B2 b( }2 I    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},7 Q- ]5 x% t) R  p& @) {$ I
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},, a, V2 v% g. k: x' m
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},8 u9 M) ^  l* u: ]% n/ u
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
- Q, u0 m' k2 J6 ~0 h/ r4 l) Z    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},8 x* P  ?% F) b7 U
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},# t% j& h' \' V) |7 K0 }+ E
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE}," V+ N8 w7 d7 N8 ^3 y
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
4 J* m' I% @8 E8 x; e+ L+ R4 E    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},: U9 l1 z. s' p6 m+ w
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
+ [+ n, _3 D6 d: y( _; @; E6 J9 T};' l2 r, L. F4 Z3 F) G
3 ^' X6 H5 F* t' ?

5 P% J$ j# X4 z2 N- X上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
. y; e1 U% o) ?% q9 M+ U9 ?6 b8 A% P* Q
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

3 b" C( B/ U4 m, T/ c. ~$ b5 {+ P1 v* i9 @+ Z9 ^
void update_key_matrix(char row, char col, char onoff)) H8 ]  F( ~9 w2 L7 k/ y; F
{
" k* @( \2 u. l& r8 X    static uint16_t hid_report[4]={0,0,0,0};
0 }) }2 S* N2 [! |3 @    static char (*hid_keymap)[8]=hid_keymap_dvorak;! [6 G$ p3 H; S- B0 [6 s; }

, D3 s- g7 ~% O+ ?; L/ k

; W: y# w2 K' J- c& K& @( J    unsigned char key=hid_keymap[row][col];5 ^0 H0 W1 z+ F! J2 l  q% g) R
    unsigned char *report =(unsigned char *)hid_report;
5 E' G9 N) l' B. C* W    char i;: E& z1 Z$ f1 ?3 s6 p" X* H. F

, O+ f: D' H$ k3 i5 o
8 j0 G( `* {4 @% W$ n
    if(key==HK_MODE), t7 x% B5 D* y  W
    {
( L, S" ?0 z' V0 }$ ?) b        if(!onoff): D. B2 }" q, c
        {
: o) L  x( }- r- @' _3 ?* n            if(hid_keymap==hid_keymap_dvorak)
- k0 l4 L. G6 |' R! i            {& l4 l0 I/ v$ K
                hid_keymap=hid_keymap_qwerty;
5 J5 t- H; \0 R: |( I1 v                GPIOB->BSRR = (1<<2);
9 _. K' P" }% o9 A% Y6 p, C! G2 z            }' I$ a1 L4 ]6 J' L  s+ E
            else
0 O  W; e& R4 [8 L6 a            {
! {8 @, H. K4 Q5 m! N                hid_keymap=hid_keymap_dvorak;, j- c- A: B3 P4 j' H1 R5 J4 ]: ?3 x
                GPIOB->BRR = (1<<2);7 U- q2 A3 S/ t& Q) _* w
            }
5 G8 y/ ]2 n  E5 h4 M) u) ^8 d( x% o        }
) m5 L( |. E0 F6 v: t7 k        return;
8 O6 Q# [) o9 `5 s: V% g    }
; T, j0 Z) r% o* t0 ?3 ~# Z
' E3 J9 C" l5 ^% r4 q) N, R. m

, s6 W. K5 R# |7 }8 r    if(key>=0x80)   // Alt, Ctrl, Shift9 [$ H& r: {( k" S
    {
+ T2 W% h; Z2 X% @0 \: J        uint8_t bitset = 1<<(key&7);
8 r/ F1 z9 [2 L$ Y! U1 {* u; x$ [        if(onoff)   // non-zero is key up
& `4 x6 C' a3 o( M7 O            report[0] &= (~bitset);- I8 W; Q8 j' Q: \: e% x) Z
        else
0 a) G0 O8 g) [$ B# `            report[0] |= bitset;- C2 |' v+ A" w8 {) L
    }
' T- H) o. e0 `5 ^  C( s    else3 V! r. m$ R3 J  q. m2 K
    {+ m) H6 H( J& t
        if(onoff)   // non-zero is key up2 Q0 n6 `( I) K7 t
        {: L, C6 L: u) R% G5 z% S
            for(i=2;i<8;i++)
% m9 h! U; [2 }4 X8 |9 ]7 d2 P, o            {" ~" Q( y/ ^0 R! Q1 Z
                if(report==key)
. `( `6 t2 H0 D' i9 z4 W5 P! h                {: {% V, |' f# q
                    report=0;
0 j/ Y* q7 j# t* I                    break;( L( N+ r% R2 [* }' ], b
                }( D7 ^) Q" H+ y; H% b- P
            }# i7 D. O. g; j. C
        }, [4 \6 F& U" l" p: e* u7 g
        else
! U! L1 I1 ^1 p5 a' y        {
' Q& e- ^0 Y4 n6 ?7 l+ b- d            for(i=2;i<8;i++)- T3 z! P3 h5 B( a6 t- \; p) I$ N
            {
1 ?. }1 F) d; H: a7 {                if(report==key): [9 x2 G# {+ {3 u$ W! n
                    break;3 Q7 ~6 W' e/ W# g$ i
                if(report==0)* C7 p1 ~* p) Q3 \
                {, G/ L" G6 W# s7 @; v& W
                    report=key;
4 P) m; T; Q5 B9 o4 g1 ~% Y                    break;
! w$ ^+ S5 k1 f; ]2 ]                }9 B" Y; J' J( s' f) V. @
            }. {$ g' T3 V' G5 ~. k7 j
        }
6 t  H* i, K. M& k2 z; a% p6 c    }. [4 y: t& f% L' c/ Y4 G$ J: i  g
    for(i=0;i<4;i++)6 D6 `1 V4 n2 J+ K; t: D8 k, t
        USB_PMA[192+i]=hid_report;/ J/ z1 ?; K/ _; z, Q* ^
    USB_PMA[5]=8;   //COUNT1_TX# ^; s) c3 |& Z
    if(ep1_wait==0)% @6 j  w# t+ q: S8 B! `! [% I) M1 Y: W
    {
9 u* V! H6 N, F) [        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;8 ]4 [0 M! O3 z+ E4 ?
        ep1_wait=1;
+ M  t' T) Q0 r; ^  G& E    }% U/ f0 Q6 Z5 M! l8 r
}( `+ ~4 a' F, `& l
* t# ]% l! H4 K% w! @  h+ l3 t

! Q8 e( Z# w% p2 c" P% @" t2 p完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。) w0 a& O9 I& h, O; s' }( D. j
keyboard.zip (8.7 KB, 下载次数: 6653)
2 m4 s9 R( R1 t/ b+ Z" j
3 K& |, D2 F. v! q: ~  ^! A
( r8 |, J, h% H& i% X
9 ~+ T% r: V/ W+ X( e
4 F9 q9 v% H, C9 C! B1 V$ J
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘3 h/ C% P& A5 k
不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
! Z% D1 ]& v$ ^) a0 w  r) V刚开始我以为要把打字机改造成电脑键盘
3 ^: U6 v% k- ?& w0 r不过楼主也很厉害!

8 c* ~9 s" K6 |5 m$ ~哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
- F# ?) P5 A! c. h5 T9 q
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

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

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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