找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
楼主: miaozhuang

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑 ) m7 {2 T" J( B  z4 R  a

$ j* I0 r/ p7 o: l' A, shttp://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
0 f+ z2 i5 \5 G3 o& W5 i) r这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。- b8 {% \  j: d
1 v+ I5 I) u# e6 f3 _* E
在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。

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

  g  T- C$ P) P 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
$ d; w1 C( b% B& l----------------------------------------------------  分割线 ----------------------------------------------------------
3 m5 K/ @6 h% u3 g: J1 a  X
, O: U) M+ {  e. O% V' v) H先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
& e; Z6 ]& C+ ~# N, a/ }
020011osionbunl4ui44vi.jpg.thumb.jpg 6 G7 \' ~2 f$ q3 B% M" y% v
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
. I- D: s9 V2 f+ m. `- A' ^+ [ 020017j8ycmnv7788bqv52.jpg.thumb.jpg
4 b5 e2 g  }0 q/ z; m特写,80C49
  I+ N& e; u6 k5 o7 B 021040oujzuvtut6iujtvz.jpg.thumb.jpg   n# |* W( i0 M' k
LED部分,使用了一片D触发器锁存指示灯状态.0 w  f7 ~, f/ ~) s
022209ixzjfjc9crmrrqjm.jpg.thumb.jpg # j% R' b" e& D4 a5 w0 v
暴力破坏,将80C49拆掉6 U" a' d  b3 W
021113e48qq98vyohvhzzh.jpg.thumb.jpg
- x) [1 V- b1 s$ b5 K2 q拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
& m  ~1 C8 E$ V- Q9 o 021125nc9az6dj33rlds2r.jpg.thumb.jpg ) _# ?8 c3 X) p7 |1 N% W# p
焊好元件后的板子,准备替换80C49' I" K  C+ M7 b! ~! b) u( ^
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg 1 c7 k) d+ [+ \' E2 l3 u& ?! K
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。3 a$ f2 q! Y0 h
021104shifhnrqbr3o5nlo.jpg.thumb.jpg
  _& ~9 d* o" v! `* c5 T2 l9 r0 ]3 |这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.
) O/ F# i% s1 i0 R8 [ 022003ym1p9u4ug40280uu.jpg.thumb.jpg $ R! J  W# r& _* |7 a# ?4 R- [
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。3 f3 \+ w, |) u7 F( i
023313kt141q9qajtol7ma.jpg.thumb.jpg % m- w& n1 X; l2 S( x! Q
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。2 S4 l0 \8 Y$ T, e2 q% }3 A
023322nt7l5xb3ltttkltt.jpg.thumb.jpg % p" `- G9 [& o( o. I
主键区键帽就位
# d2 V7 U/ h0 {( x 023331hin88e8wkrwzwikx.jpg.thumb.jpg
  L4 Y  X7 _0 k' ]" Q% C$ o# w编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
) w; j2 _/ p! S/ B& ]" q 023336wjzlgopugg1jyy79.jpg.thumb.jpg $ H6 ]4 f# ^, c( ^! v) O
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。, r2 H- U, G# a* a
023341sffu4j3g2323h6fl.jpg.thumb.jpg ) O+ Z, m- Q8 w$ T0 |" A4 T

. w3 m$ t/ S9 N" W----------------------------------------------------- 分割线 --------------------------------------------------
# A! Z& W( M/ e7 F; B! F

- n  A% Y" z% k* u! V* J
6 O; r3 k) V& |5 D& t& x

7 V' M: j  x' w: e: l* p5 |! b8 y9 A: C) B9 B# a6 s4 B
. c5 }6 a% n  K+ U

4 R1 k$ I6 k) r' t# R  d
+ ]; v) G1 g: J& ]6 a3 O6 i3 T6 X& w
% l6 R# w6 E2 z" F' F
* X4 Y3 g* F  H, t' a

" c( S. ~; l) ^0 X8 C$ `9 X" g
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
( P+ w% k) j7 _* O. K* P  p80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:2 b$ {6 q0 i8 j: V% i) W+ L* h6 w6 O* J
104025nzibm2rmiomhyirm.png.thumb.jpg
- c, `1 j4 a% D0 a$ f5 c0 u

" i# Z; X2 G5 l" `  T其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)$ ?5 ~% O; T' b8 T( u6 ]
105004zkrez5houvkkznko.jpg.thumb.jpg
8 V; L1 e$ m4 p  x8 Z7 B- e+ I

0 W& ^7 }8 z5 U' M5 L, S" n- {2 H扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
! A6 [3 s6 G. C, |7 r" T+ T9 G/ t$ ?% \2 ]% l$ _( E! o. i& [
这是我设计的电路图:
4 J) e% E0 E3 q) `9 n0 y 110344ej2z2oo2rflo7oe7.png.thumb.jpg , p% E, i9 N) j& U7 Q

' [) ~4 G! v& `8 q# d0 r9 r4 Z# y' yPCB Layout:
0 |2 S3 p( W9 ^0 P  J3 U 110847jjbjvt34vwt3v5bb.png.thumb.jpg 0 ]& p" Y# \5 R% @

5 X( \. G) e: c: e$ A4 j不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.   E1 }" W7 ^# N% w3 ]% g  {! u
% z7 D9 \$ e: o* S9 `5 {
2 h, i% n" G6 a: t' M5 n. w- U
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

7 I6 H, ^$ u: u% m" H其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
  A3 e5 p2 [  M# @& e" Y
6 x' S% e5 R4 f" }USB的中断ISR,bare metal哦
% `8 m% t+ F% V6 c; Z$ ~4 g
7 G+ G" n+ O. `- B! g
void USB_IRQHandler(void)
, H8 h1 o6 _; P{
8 \4 C/ o) @% i: t7 _( i    if(USB->ISTR & USB_ISTR_CTR)$ ?5 _; c8 u! x+ b: P
    {* h2 A, K" @+ }+ l% B4 p
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
* a* ]: L. B. Z        {
6 U; z; s' X1 Q/ i1 E! p            switch(ep0_state)
' x4 b1 U* S! B. V6 g" e6 O' j( k            {
. q7 z8 {3 I7 Z% [                case 0: ep0_state |= 0x80;
8 g; b- F* Y% K( g# D1 t* ?                        break;/ i1 Y3 S4 j" W& E  x
                case 1: if(USB->EP0R & USB_EP_CTR_TX)
6 T' J) D5 ^, {6 ?0 h$ W" F- C1 P                        {6 X1 O4 ?' F1 d" V
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;( j: k" G% q4 f' w6 `: T. m
                            ep0_state=3;8 H9 a: V) D' x2 K) [5 [
                            return;
9 [% G( q' B; N                        }
% N/ P/ D1 g+ y3 |4 Y                        else
2 T0 e8 J, m" J0 k) k  F) o* d                            ep0_state=0;
. w: T3 \7 Q" t! s' W, T/ g2 z                        break;
4 f! Z' {! O$ x2 P' I$ C                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet+ }  c$ {. z; J: q
                            ep0_state |= 0x80;
% y+ j0 H, f$ q: H                        else! w7 P  b* ?- t5 I; l) ?
                            ep0_state=0;1 c: W6 P8 s1 S7 U7 V
                        break;
% z3 t$ ~9 {! O+ J4 v0 E                case 3: ep0_state=0;6 p2 h# d! o. m0 S: x
                        break;. A1 B# k" A% [5 J' ?9 i
                case 4: ep0_state=0;
$ B) i6 i+ P3 c, S  h8 J3 t8 m                        break;7 D) E/ U/ w* L9 Q: T4 l8 E
                default:ep0_state=0;
/ z: w# [  m9 F8 ?/ f/ `                        break;% h6 M: d3 F! T3 ~- O  c6 r" H
            }; C$ Z. c6 w6 a
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
$ U4 [  A. H4 G% q3 [            return;" f2 U, @+ W+ |6 c' h9 x
        }
. i: O3 q* }  m: t% I7 _        else    // EP_ID can be 10 I6 B" t3 T3 B. r& z! i, S
        {
/ I" M2 J( I5 @            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
  U7 x! t# `0 r. x- N! w7 Z4 f            ep1_wait=0;
. h) t  Q( W+ x            return;
+ E4 M' J0 }* ~$ {        }! s; u. d; Q, ]
    }" M$ s% B2 A; O2 ]4 x* ~1 O* m7 K
    if(USB->ISTR & USB_ISTR_PMAOVR)
: S  K4 \+ U$ X# }' E, E    {) W$ o* i3 d- M$ W
        USB->ISTR = ~USB_ISTR_PMAOVR;& ^3 Q2 |' h4 |: L
    }
& \1 H7 `, _2 A- \    if(USB->ISTR & USB_ISTR_ERR)2 m0 d8 W  D# ]/ p3 r: F5 Z
    {
1 ~( [# o+ x) d8 f! E        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
8 t. o$ t: C7 l6 b* w    }0 P) _4 `0 \) Z6 e) R1 Y
    if(USB->ISTR & USB_ISTR_WKUP)& U6 n' E) Z3 L
    {! A( b3 u& n! {/ `# l
        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
) m# G* O1 g8 W( E        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
1 K# k( v; F! i9 L" V5 ?+ i& n+ M/ h' n    }
, o, i( Y! H9 [3 B    if(USB->ISTR & USB_ISTR_SUSP)4 c+ N, B  s8 ]+ M0 |$ T( O8 w
    {% m: A- g7 r! ~, M* K0 n. q9 j3 Q, u
        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
0 m2 D% E; y- R) C& N2 W        USB->CNTR |= USB_CNTR_LPMODE;   // low power
, N! W( b, G) S        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
# J3 D9 N% t8 R- u    }/ D% i- s) k+ e& Q, E/ \0 o
    if(USB->ISTR & USB_ISTR_RESET)
& n1 P( E4 F# p9 i    {
5 f% t# F& _! j% t        USB->BTABLE = 0;    // buffer table at bottom of PMA# b0 P  q9 D0 o6 H) B$ M1 P# }
        USB_PMA[0]=128; //ADDR0_TX  \' x0 u3 A. c
        USB_PMA[1]=0;   //COUNT0_TX/ h. G" H: C, ^9 p- y: S
        USB_PMA[2]=256; //ADDR0_RX
3 A8 b, G' r5 Z" b/ P, a% C4 e. M# [* u        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
5 L+ z  z. ?* v9 A2 d        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
/ r$ u1 K5 [( _% E# n3 h  |        ep0_state=0;
- F& f6 B( C4 A- w( C        USB_PMA[4]=384; //ADDR1_TX
9 @6 U% e0 W" p; C        USB_PMA[5]=0;   //COUNT1_TX+ L2 Z# k) P8 T
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type$ V$ O2 X, G( O! n
        ep1_wait=0;
* x; C3 E# s- |5 v        USB->DADDR = USB_DADDR_EF;      // enable function" s8 j2 g9 W# N, j6 @
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear) C8 {9 P1 C" Z/ W. X( D. L: _0 U8 l
    }
; m0 d" A0 V8 R$ @4 J2 q+ o, v    if(USB->ISTR & USB_ISTR_SOF)
" H" `+ d7 _. H+ m# ]    {# j; l% U' h" E
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear" |+ F/ G# s8 `+ |! Y( O. _
    }+ a5 _  p' u( Y; U: b2 h- }1 g* N$ y
}& a$ h4 F  C2 s5 T2 |; X
8 [$ N# O# h2 G/ S
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
  k7 q% u* J, n: ~. o  y# P: S6 C; J, t9 _# q  q' ~' Y' Y7 p
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
) u, L0 k. I3 ^8 ~3 C
7 J# U$ p4 p1 i5 z& i" R$ I
    while(1)
3 O8 c0 c4 ^" C    {& d" E. r4 d7 |$ Z
        static char row=0;
% f9 f' a% G' q) d; z        __WFI();
" k0 {; L4 s2 \. r" B        if(ep0_state & 0x80)    // request data processing! U, Z! I: y0 q! [. [/ ^; O% S
        {6 f; p& I  G+ F
            if(ep0_state==0x80) // SETUP phase
% d  |! m# F$ L! C9 q            {# C/ o3 l$ m4 x3 ~* s1 [
                if(!setup_packet_service())0 d8 y' ~% K# h5 p
                {0 ^) I3 Q& ^3 W4 x0 e% [
                    ep0_state=0;
4 A" w7 [8 n* a7 l7 L                    // not supported
2 `7 C. k3 F' A7 ?                }1 L2 Z5 Y/ c, N! H( f0 c
                // ep0_state should be set to 1 or 2, if processed/ h! h  u& ^4 ~4 l! L4 M
            }4 u- L& L4 D1 U) p, C
            else    // OUT phase
( X7 ]( v6 A) {* p6 A            {  }) P/ [7 k8 d* e2 H! `; T9 |2 u
                // process data
) z( F$ l; Z" i2 w: w- G7 V& @                show_LED(*(uint8_t *)(USB_PMA+128));; X5 H9 I% {/ C
                ep0_state=4;
* R5 B/ ~6 |( z" I6 c. t* b- ]                USB_PMA[1]=0;       // Zero length DATA0
& V  {; C* W: z) Y+ X                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;) k1 W' ~! ^+ o% @4 [% c: y' |# d
            }3 c2 _3 S; n* w2 ~; b& H' h/ G+ `
        }
$ ]# _, N" V% n        else
! ]8 h/ z( S0 g+ h        {, B3 R9 o; J9 l1 p* E4 }. {
            if(usb_address && ep0_state==0)
, E& M# @/ V8 J# d# N            {
4 K$ V) w9 V& v5 ]1 r                USB->DADDR = USB_DADDR_EF|usb_address;. l% Y8 x- F9 w; l" d" n1 y  V
                usb_address=0;
4 T8 ]0 Q3 ~( ?5 ?            }
& j+ \+ B7 [" v: G' o; Z/ v" ^        }9 ]  t3 G! x0 B* m! Q
        if(row!=scan_row)   // new scan line
6 L- d& T" n! b2 B  V- A) `+ }5 i        {
2 R: L, G: U& i; L3 a' a            if(key_state[row]!=prev_key_state[row])
8 ~4 Z# g- z/ ?7 t            {
9 i: o! ]/ v3 h                uint8_t test=0x80;: z! ?5 O9 I/ i: R
                uint8_t diff=key_state[row]^prev_key_state[row];
3 H! S5 a9 l: t                for(i=0;i<8;i++)
: c7 J! p& e7 P2 v3 Q                {
* N. r# U5 E! }                    if(diff & test), B- a. k! K) p: r, D0 F& I
                        update_key_matrix(row,i,key_state[row]&test);7 f# F2 T& `5 @* u0 V9 {
                    test>>=1;3 J9 A; N0 [, x9 s; V( c3 y2 D
                }! ?3 {7 J* \5 k
            }& x: J+ G6 C4 E% h" X; V
            row=scan_row;
6 r! n& F/ X- M  B5 d        }! p  U9 J; l6 O& w0 k
    }
5 P# S: c3 u  }, n: `+ {4 M
2 P/ o- d: k4 e* t4 t
  g) y2 c' c* d% c. e7 rEP0的控制传输,把用到的请求处理一下7 W! ~% E" `- T) V; T4 K# J
char setup_packet_service(void)! I3 K+ c* _* Z" o+ N- V
{
" Q# p, x7 [/ q5 B8 o9 N& ~    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
! {) |* v1 ]8 i8 r4 p0 f    {- w& F, Y, @7 ?' S) R
        switch(ep0_std_req->bRequest)6 S. s' a9 p4 S
        {) @7 e  E( P2 X
            case REQ_GET_REPORT: break;( t4 l2 a# Z% i
            case REQ_GET_IDLE:! O# H5 b+ V5 r) W! H, a' ?) s
                USB_PMA[64]=0xfa;   // return 1 byte, w3 {# m) r2 O1 O6 h
                USB_PMA[1]=1;
  e, {8 G2 S( L# W. }                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
/ W  N& k; s; j( o! {6 t% V                ep0_state=1;! _: d4 Q; @' s$ o
                return 1;
, L/ D' S* `  ]1 S                break;
  I3 [3 K* L: y7 I            case REQ_SET_REPORT:, d* S& Y! y6 y: J/ D
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;5 s- L( K, L+ O) L. p5 g
                ep0_state=2;6 j" Q6 i0 `5 w4 v
                return 1;, ~: X; I% ~$ [( i" b; U7 b
                break;7 p* D% z6 V/ p6 L
            case REQ_SET_IDLE:
  e9 m; s. x& _9 u0 k( T/ q                USB_PMA[1]=0;   // Zero DATA; j2 Z( I  y2 b$ J  Q, ~8 ?
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
1 E4 P! B" D7 M7 j2 \5 a& @                ep0_state=4;5 G- `* }9 Q. C1 d* T
                return 1;
/ G, R& @! C/ \                break;8 I0 {( R; K3 p8 P* [
        }4 H; s) |3 ]; x0 g- H; J! N- g
        return 0;
& U; D( T) y# J: P; [' Z, a    }+ Z) a8 z( y) C1 Z$ f. T* U
    else    // standard
# F" [9 I) f# ?" r3 }3 c2 ?    {
# r$ E  m0 `9 W' `, [        switch(ep0_std_req->bRequest)( @4 A7 U) p1 H, G- }, \
        {
6 J0 u% B$ E2 o! _5 W            case REQ_GET_DESCRIPTOR:/ Y! t: b" a0 h- J) ?
                return descriptor_service();
' l6 q5 O# D1 N) ]2 l                break;
: h0 G2 N; f( R6 G! ^            case REQ_SET_ADDRESS:
# Q* _4 D: u$ ?2 l1 G                if(ep0_std_req->bmRequestType!=0x00)- S2 w8 C3 n7 p; O6 x- X
                    return 0;, ]  N- }! X! y( J1 d
                usb_address=ep0_std_req->wValue;
$ G. Z( z2 W1 T( {* @                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;$ v  e9 ^' h" F9 M* I
                USB_PMA[1]=0;       // Zero length DATA0  B6 ]$ s( }! W2 @( t7 U7 I# e6 F
                ep0_state=4;    // No Data phase- S& i4 R$ ^8 X
                return 1;: k5 K8 Z% j5 M( x2 \, T
            case REQ_SET_CONFIGURATION:8 p3 H9 r* j" ~! x
                if(ep0_std_req->bmRequestType!=0x00)
, ^- s- o+ Z! E2 W+ w                    return 0;
" x' [4 @! K1 h( C7 c                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;/ u* t- U/ O, H1 v* I% Z
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
7 I$ `+ C" A+ |                USB_PMA[1]=0;   // Zero DATA
; z$ \+ E1 r7 x* Q' \; i0 P/ b& S. y                ep0_state=4;    // No DATA phase2 r% D0 B# Z. m/ |) ?
                return 1;0 p- \" X+ J- g: x
            default: return 0;6 ~# U  v9 U, |0 @5 }
        }
+ s/ |( G3 c) O, x0 I# X    }
, D- R& y# `! Y1 a) |' _. k/ Q}9 N6 y% e  r( h! [" H

: Y& }. k$ f1 `$ C
5 F) D, m7 S$ A
  T/ `! ~: `# y
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的& W) x0 z: V/ k/ s
char descriptor_service(void)
  Q( n9 f) c7 U1 c' }{
1 R1 I$ |4 S1 J0 x: R    switch((ep0_std_req->wValue)>>8)
! m$ @! Y5 y& w# n; e    {
/ K6 ^6 K* n5 O8 k" f% f) R        case DESC_TYPE_DEVICE:, Y& p! {1 Y3 ~$ d
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));- j& l9 X4 T( T) m3 C" k$ b  L
            break;' d( Q0 D1 E6 N$ s; v% h! D- L
        case DESC_TYPE_CONFIG:
1 @  N  F  k- T* F6 e            if(sizeof(ConfigDescData)>ep0_std_req->wLength)9 o- L7 E) C  K- X( }, e1 I
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);) d$ ]4 P3 {9 O; }8 D
            else
! B+ k( x& a+ K; Q) K6 r, R                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));, j2 {  L- z$ o, x" c. o8 U4 g& i4 V
            break;
0 ?1 I4 X3 ?" l8 O1 @7 h' G, `        case DESC_TYPE_STRING:
3 T6 z! S' e4 I# q9 I            switch(ep0_std_req->wValue &0xff)7 B7 C) G* v& r- j
            {
& J$ X8 K) {- x3 G( b- Z                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
2 j0 E- E" P6 E" A1 O# s                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
& W# ?6 r( ^7 N2 L1 _( r" G. m, o                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
7 ^+ P2 \( O! T. p$ r7 |                default: return 0;
$ }) h$ j/ ]% s            }
7 n+ Z! t+ z" S, E: Z' _            break;
3 y9 @& b6 @: K( v* I8 L4 Z1 [        case REPORT_DESC_TYPE:# X7 Z" p2 {. Y- u& S) _1 u: `
            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));9 X5 v0 w) G" U7 p; {* {
        default:# j4 I1 I) l( k* l
            return 0;
. X, Q& X; @6 k/ M& f    }
$ L, ^( [4 |; V% I6 {9 ^/ v# c! U}
) E: Y$ U, c9 A/ X2 C下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.  u; ]6 e9 V, x( D- [
void TIM6_DAC_IRQHandler(void), i3 t' ^% h1 w/ M& Z' S& O! X0 Q
{8 W( j# R5 z1 }" S* U) C) A7 u+ k
    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
1 r6 b" Y3 ^. k6 `! V3 _5 g' d8 f5 w0 v8 }4 M( \/ g: y
0 \# H% A. \0 |9 N- I; o
    TIM6->SR &= ~TIM_SR_UIF;6 W, X$ n+ C  [( l
    prev_key_state[scan_row]=key_state[scan_row];
. X' S8 X( {6 h( b  X# t7 s* Q* g2 z    key_state[scan_row]= *PA_IDR;   // update key states2 l! x+ n5 |6 z/ w& t
    switch(scan_row)0 P& W- W6 Q( E( N% I
    {, U+ o+ i) M: A$ t* n5 g
        case 13: // next row PB14
) t2 X1 G6 k  n) _) q                GPIOC->MODER = GPIOC_DEFAULT;
, z* V, M- O: X                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
9 p1 y% D: X# k4 ~2 f% J8 b                break;
  J: M$ ^, \- x- Z' K6 q" `        case  0: // next row PB15. H6 C* v9 k% \8 M- w) _
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;
7 \! \2 o) o& q9 m4 ~                break;4 E4 i2 F+ q# C4 B3 X0 E$ H
        case  1: // next row PB3$ |2 w% N0 P+ z% g% r& O
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;0 _& T6 l" N0 ?- k
                break;
( @" ?: F7 g' K2 p, z        case  2: // next row PB4
$ I( M- y, u( l2 R7 R7 k: O5 z( a                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;+ _2 v% p% O2 P! p: A' ]8 P
                break;
8 F/ b& p  _# l4 o( S% u5 m        case  3: // next row PB5
- y8 [* ?' V( r( d, ^                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;4 v$ c4 T, _% Q
                break;  j" X! v6 [# t* W2 v7 X' R
        case  4: // next row PB6
. Z: a, f$ h8 y( z' C                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
  j. q) x6 c0 |) B                break;
( b; G3 d2 l, @& Q) e7 _        case  5: // next row PB7* e& b) a+ a  x' `
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;: K0 c0 ?+ Q0 x
                break;
) ?9 o4 v5 K, [3 D4 M* {) ^9 _        case  6: // next row PB8* J+ F$ v& u& S" f2 Q, V7 L
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;, r: q1 q7 j1 f6 s1 \! D3 q
                break;
# S, b" A1 \; g        case  7: // next row PB99 ^. _# z' V; u& B- Y' |
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;
6 O. ?1 E3 K7 v5 @- U9 `/ Z2 a6 |0 u; w                break;
. c$ Y. X9 ]1 C        case  8: // next row PA8; I6 @7 F' j& w+ ?) j( {
                GPIOB->MODER = GPIOB_DEFAULT;
2 b( t( C4 W& M0 m& m# U                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;
. F& ?6 |. W' J1 u) g5 }                break;
, x$ z! `* c; \( Q& j        case  9: // next row PA9! }" @+ Y2 l, ~
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;$ z) s6 V: B# y6 v
                break;
9 k0 d1 s8 @3 B; P+ {' a0 X* ?3 w        case 10: // next row PA10/ R8 _) Y7 |* e5 P4 E7 I
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;; c9 U. ?$ @8 E8 E+ F+ c% k
                break;
4 W1 u: o/ Y: d# T0 g. _        case 11: // next row PA15
' i* ^8 F8 z1 `- F. n                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
1 D8 a3 q5 S2 p2 u7 @# n* @% ^                break;7 r- e* f6 i& ^
        case 12: // next row PC13
7 X  i8 O% i, [1 i( y! }                GPIOA->MODER = GPIOA_DEFAULT;- U/ h& v6 S* U! d9 D
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;& a7 l9 |9 p" k6 w% m: R
                break;
. X2 Q5 H, Z( v8 T( n& Y9 f8 r$ ^: ]    }
9 B8 e! g; k) T    if(scan_row<13)- z8 k4 Y' w1 ^$ _
        scan_row++;
& f  q; C: \) ^' |5 C* H0 m' [    else" a/ x* V( W8 @* C2 c4 k
        scan_row=0;
6 M3 O0 @/ {, ?/ E2 ?, w- K}& I0 b5 s2 L% g5 l* q7 P1 \4 N
, O  F' f1 s6 u, P) `4 \
  i/ a1 J5 Z, V  ^, t/ N$ d7 ?; {
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 7 k$ G" y% F) h4 \3 K0 }

8 ]0 J; W' _! {0 d) }扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
( l. @9 j  V; _! \" [1 D' R: A8 l* W# o4 V. P5 M) }
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
; L7 z# a; R  h- Q

( o) I$ l% ^1 j& \- n1 @' F/ F8 s5 Q! ~6 z) Q) }
const char hid_keymap_qwerty[14][8]={
( {# o: @9 C; o" B    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
, w( L$ C% J0 E( T    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
. v! k/ H+ ]8 G, H+ {! L/ G. Z    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},2 q* l- a. f3 e
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},# T% S5 {' ~  F. y- c# r
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
3 y( d- h8 c) r4 b0 X* `    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},4 H# S0 |8 T: \' h+ x# U4 P
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
- R0 \1 s% K. k: ]) X0 |, B3 a    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
( s1 c9 W9 ~( }  p$ U    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
, H7 u/ C1 X# C, x8 m    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},% Y0 j* W' v5 l' o/ X( C+ E
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
5 o/ K' K1 ~* X    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
' _: o; x. Y) m8 W5 E( R0 w% U4 @    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
8 i6 U* J: l" t1 o    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}& L1 f0 A0 p$ R; ^
};! F: w2 C0 H  |9 O& A; O5 `
# x$ J) j$ |9 X3 S9 X
. a% C% ]5 \/ _: Q/ h. V6 P$ ]# |
const char hid_keymap_dvorak[14][8]={
9 j1 u) p6 O4 v% [$ Y    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},  J: Z: ]8 J. V* o
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
" P: e* r  t- S, |    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
2 p- W- D: J9 M    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
" W1 S& k: g. i: ?    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},  {7 U) X" q2 A1 @
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
* T' `" `$ }" N3 D- w4 p. H: T    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},1 }' F. c4 P% h9 F; Z) p
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
5 J& V+ I& v* Y    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
, h& ^. a- X% f    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
$ ]& F8 R0 Q) }' U# U    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},3 K" t; _- }7 L6 n
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},+ u. o, t' x4 V; M1 M& r5 I
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},$ O; A' n; r1 @  F9 O) q
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
6 b9 {% @& X+ r* h};
6 M; {+ j+ o+ `% S$ b9 Y) t( F0 @
1 W' X) t- i- d+ L/ [) @1 ?  L
( N) l# E# P# e& v3 n- K- B: H上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。# t0 L1 r, N+ x, J/ @  x7 b4 U# [
% G, Q+ ^# ~+ S( J2 n! b
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

& m8 e2 c. ^7 I
  |9 D& c7 U( A1 zvoid update_key_matrix(char row, char col, char onoff)
% f* R* @" J+ o0 F8 u{
  z+ {% Q( p# P9 O    static uint16_t hid_report[4]={0,0,0,0};
) U: X# {) L. O/ h6 R( B    static char (*hid_keymap)[8]=hid_keymap_dvorak;
; F. t$ `, S. {. t" S6 N/ X1 f% }; k6 J) K9 y. Y0 r) |

+ F; o/ V1 r, N( Z# T    unsigned char key=hid_keymap[row][col];5 c3 W1 s+ b% Q+ D
    unsigned char *report =(unsigned char *)hid_report;1 d6 n$ t: }5 H) L
    char i;+ k( v) i+ W' a6 C6 {; `5 U* T

* x; I" c. {0 A* [' L( L+ v2 }  R
; K: q1 {3 ^8 }' f6 i) }5 f* I
    if(key==HK_MODE)
5 W; F0 U. R, W# T    {
( S* q0 o+ \  }- z! R) L. r        if(!onoff)* o$ e" P5 }+ W8 S: L1 |
        {+ g- N" }3 ]+ @5 |. m7 w) e
            if(hid_keymap==hid_keymap_dvorak)
: ^7 L; I+ w( u0 x1 f9 v, o6 P  M            {0 c, U$ l4 N2 t% M1 T9 o8 ^
                hid_keymap=hid_keymap_qwerty;
1 Q6 p, r3 X" _                GPIOB->BSRR = (1<<2);
& u1 B8 M, e, \/ N) z# O5 ?6 e2 s            }
1 j- g7 p6 R( o! K( P" f7 N+ r            else
) }% J$ l; B* N  k. I. W, \            {
. \' A$ ?5 p% }" S                hid_keymap=hid_keymap_dvorak;
: F/ r& Z) b  h( ^% T                GPIOB->BRR = (1<<2);
! N! G: ~3 E- y& }7 B, Y& e            }
" B3 v# u3 A6 y9 p        }
% \1 A1 i7 [4 p5 Z5 C        return;
6 w+ D) y% L5 A& x. D    }
( ?0 Z& b5 U2 L+ z  M! |) _
, |1 I& P+ k/ [/ U; |; @$ s+ o* ]

4 V+ p  X5 H  E8 k    if(key>=0x80)   // Alt, Ctrl, Shift
7 m2 v6 u& {1 j. _, P. _, R# V    {
* v9 l3 k8 N) W; v7 |7 s        uint8_t bitset = 1<<(key&7);$ U( R& W# P' a/ E% j6 |- M4 J
        if(onoff)   // non-zero is key up
$ c3 W9 |7 J4 ~! y' Z: l            report[0] &= (~bitset);
# P1 X, L$ x1 ~! }3 z9 N        else
6 K$ X( k) Y6 ~* ^" V% ~: D1 X8 z            report[0] |= bitset;4 C  M2 e5 U6 d8 v" p) A; Z5 S, f
    }. K8 c" o" z: Y1 Y2 _! X" H$ b5 N
    else
9 G% M  J' g! e  Y% _) m0 p0 t/ n    {
6 ]  A  `" X5 |2 {6 ?$ N, n( g5 R        if(onoff)   // non-zero is key up% c; |5 x: m  ~" n- R* [
        {
& p0 N& K6 H: r2 r            for(i=2;i<8;i++)- N/ d2 r, j* g/ O" k
            {/ ?9 F2 ]- S5 s$ d* a. V5 G4 T
                if(report==key)
$ o1 w7 g8 g$ n( J+ {+ j" D                {# M! I4 k, O* L; N
                    report=0;
: r, f! @& Y7 {+ w                    break;
) ~  J2 T( Q9 R                }8 k7 \2 [# a+ i4 v/ o& z
            }
/ E" m% ]+ v- O: O        }
/ W% Q! @2 S8 Q8 s        else
% n6 c5 f* ?1 f2 a+ M8 G0 }' E        {2 k, H* y! I" @9 q
            for(i=2;i<8;i++)
. `, R) N2 L8 _( E. M            {
, ~& t/ Z8 u# Q# X& _! K$ F                if(report==key)/ X; v  E6 z. L  K& o' y7 _
                    break;. J7 d4 A; j) U; {( U
                if(report==0)/ e9 a8 }8 y5 G. _
                {. }) [. v7 x6 S9 K, w7 H3 j
                    report=key;
/ ~9 |2 x% Q9 ~0 }                    break;
) O6 [9 F' G4 S                }
# N, v; l) z+ ~7 E9 R7 D            }
# m; C& L( {5 N        }
# }- W% K! K; B9 b4 m9 X    }; m, g% @, T! n+ w0 ?% O7 G$ ]
    for(i=0;i<4;i++)
- g. D, i& p% C* V. M% ]  P) F2 t) K! {        USB_PMA[192+i]=hid_report;
8 L0 a+ c9 o% a* ?; Q/ Q2 b    USB_PMA[5]=8;   //COUNT1_TX
( W1 r9 m! k, V/ e" k    if(ep1_wait==0)
7 V6 f3 x% ]( R$ G/ t1 G    {
: Q& Z+ z1 h& @        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;+ @, I; Q& G: C; Q! R; x
        ep1_wait=1;$ e3 j$ {5 ~0 y4 l6 r6 y4 t# M
    }
' @& `' u2 b* w( W+ |; ]}4 j+ o+ m3 [8 @) h, l2 L

* l3 ]* `$ x/ A0 V! F# d  x, N7 C& y6 g  Y/ Q% K
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
/ V( N+ h/ S- J0 ^9 B keyboard.zip (8.7 KB, 下载次数: 6378) 0 \" K0 T3 q1 X- J) S" T4 @& x

/ I3 g5 M+ |* G2 g% M# g1 ^/ @3 c+ J

, A; C! D& o: p( [2 I0 C
2 j2 ]: L* W# |6 j. o. {
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。. L( g# Z; a; Q; e3 i: M7 B
回复

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
4 ?' I+ C- O5 V6 S  f( ?0 v不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
- Y4 s8 @) L' p8 v* l8 ~% _$ R刚开始我以为要把打字机改造成电脑键盘
1 a5 @8 W2 L* n7 N5 x9 k1 C不过楼主也很厉害!

, s2 h5 h. M/ [2 e哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
7 @8 R1 v- ?0 h9 g" \
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-12-31 08:56 , Processed in 0.158676 second(s), 24 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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