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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
9 M: ?" t5 Z  s$ r* p3 g% r1 o2 J1 N" z* J- r
http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1. o- v9 g- u6 g9 D
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。# _/ @8 f' G3 P# A

' S1 ?& Q- @* V" X2 @, J* |$ g在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。
; h% Z1 ^% w+ }0 N3 @! D" D8 d

- K/ y) M! e7 ^ 235140i3a36qivqzuvmt5q.jpg.thumb.jpg
3 M: w, g* {$ s' m  a$ g) z
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。
0 z' }1 ~7 V6 s0 u: n& `% Q' y1 ] 001734klbyoluenuwz4h4b.png.thumb.jpg   l6 u. l2 _! x  Q' b5 R
为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:+ ]9 I( O! i5 p& n) M8 [" F
003625r2agx2f5v922cf2f.png.thumb.jpg ; J2 Y4 }0 Z5 S. V- ^
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.$ j3 Z$ B1 K0 }! I% V! y
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
2 R2 n* I1 O! q2 e# _  a$ Y
005836yvs0wvovwsssgd3o.png.thumb.jpg
+ x2 q$ V0 \, @Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
# y' m1 x9 X$ C" n$ p' X: l! ]7 o7 ?9 B. H/ {
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。! k0 x. o* ?" b

5 U. s* r5 r% f; \3 U  O+ ~到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。6 b8 a2 {2 j7 l4 i' B- ~
1 `3 A# ]( y3 n. K) X2 h
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
3 y6 X2 F) F0 f
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
5 P  H% f; V& {( a----------------------------------------------------  分割线 ----------------------------------------------------------5 Z$ [5 R/ N, n0 G

0 M: f/ k; D6 a$ z, R& H7 [先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

" T& D' s. c# B0 [& Q3 \ 020011osionbunl4ui44vi.jpg.thumb.jpg
2 m" h$ t0 b  b5 R9 _! u. D轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
. M* I+ w% a: J 020017j8ycmnv7788bqv52.jpg.thumb.jpg
7 ^) I1 {4 J; e& z特写,80C49; ~, u& ^; l  p- f) r) Z5 G4 s+ R- k
021040oujzuvtut6iujtvz.jpg.thumb.jpg - h. Y0 T, c  b
LED部分,使用了一片D触发器锁存指示灯状态.) t( `8 p/ }2 A# N: a* }
022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
: _8 L% z* X$ t  o" d5 A, L暴力破坏,将80C49拆掉
- A( }) ]) H1 @  R6 y5 u 021113e48qq98vyohvhzzh.jpg.thumb.jpg 8 V( e8 n! S5 ~
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。) ]  [9 Q, K" c/ A& v: z8 z9 U
021125nc9az6dj33rlds2r.jpg.thumb.jpg
& J0 ?  G7 Z% }6 j
焊好元件后的板子,准备替换80C49: r, [  F& \0 h5 P: w" r( b# i
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg 1 X- j6 ?( B) }# o5 p, a  C
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。( E6 S7 L  \/ `2 P3 s+ H- t
021104shifhnrqbr3o5nlo.jpg.thumb.jpg 1 p* j+ F' H' q# n
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.
; m& _. p) I. P  i# B6 P, r 022003ym1p9u4ug40280uu.jpg.thumb.jpg 6 \* c; ^: N; g6 k5 o
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。9 {% D: Q: Y0 l6 a! f3 {
023313kt141q9qajtol7ma.jpg.thumb.jpg , Q9 \6 c) ?  ]/ e9 I+ S
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
) h0 f& ]5 o0 a) x 023322nt7l5xb3ltttkltt.jpg.thumb.jpg
( m/ v! ], `7 T6 b
主键区键帽就位
( b' p2 {/ {0 g 023331hin88e8wkrwzwikx.jpg.thumb.jpg
  u$ o! U* g8 w' S) F6 ^编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。. G5 }) o/ L6 E
023336wjzlgopugg1jyy79.jpg.thumb.jpg
, g5 P% u1 v: b3 z最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。+ c9 O2 P. k5 y0 o9 U8 s
023341sffu4j3g2323h6fl.jpg.thumb.jpg
! O$ S2 A& Q9 \! c" ?6 d

& f* Q( u' y; [, [: U) F3 ^$ ^% t----------------------------------------------------- 分割线 --------------------------------------------------( i8 [6 U! v3 D% {0 u0 t& O
- D6 X( {. D# f) I; Q4 \
' T9 {, V3 d5 O0 v

7 H) V# O" u' z6 R- ~1 e' E7 p
% R4 J! m: I2 O: d/ p+ N5 j2 X6 b6 u& o7 L; [# \3 H4 T
; i" [1 o% y) A0 P3 j

' @5 A3 K' z8 k: L& M8 O: N  t8 F9 `5 U% M% d
: {- C) E, e" y: M- S7 `( W, U' Q
- P, ^& _" {7 C: [% K; O  y) M

  Z3 P4 M# i  n: T& D
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
/ J% I7 l! k! v6 D# T0 G80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
0 N) [2 ^. J+ O/ b, M 104025nzibm2rmiomhyirm.png.thumb.jpg
) ?8 F0 J/ J) V) P
8 M- y* f4 o; ]$ l
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
, |" s1 r' p2 A6 }: r 105004zkrez5houvkkznko.jpg.thumb.jpg * `% L$ W% b0 Y; Y. W4 p
# B: M7 u9 Q% y5 g. Z1 G
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。# P" m( k( J- R# h1 f- T

5 r- F, x2 ~5 R* Z" T. X这是我设计的电路图:. s$ B5 \$ s0 K2 m
110344ej2z2oo2rflo7oe7.png.thumb.jpg ; g1 w1 C6 {8 W* z/ m; |% F- g+ j

0 f6 m* |5 r  YPCB Layout:3 H$ R6 t  P+ F
110847jjbjvt34vwt3v5bb.png.thumb.jpg
$ Q+ q0 e6 a: r$ x0 o
  n4 x' M7 U) \* N, U
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
. E6 v6 {0 n% a9 y, g
7 ?* j& g2 W- I  Z
& a5 M3 M( [& \7 |2 ~( e
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 " B4 ?9 x8 E; S6 E
0 B1 z* G5 S9 I% t+ i: c' b7 y
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。8 s3 f6 L0 y/ h0 v
% l- [& p5 U7 B1 d
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:
0 i+ F! J& \' f9 z4 s. w9 { 113818pmrfsb6z0byt6t06.png.thumb.jpg
8 d9 t7 i/ y4 u# u) X3 D

0 k/ @' V: }. X5 N) p% X其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
- e  V" n0 C8 [$ T& c) M6 K/ u. a) G' O* X0 l# W
USB的中断ISR,bare metal哦
. w! g; N) J# z3 Y' v
, ]! v# W0 b$ o
void USB_IRQHandler(void)2 [1 D  X: F- P3 G- `3 {
{
2 u: t  [% c2 y5 g4 ^; C9 l    if(USB->ISTR & USB_ISTR_CTR)
: _) t3 K- I! B$ K9 ?    {
/ P( [' @! K1 l( Z2 i        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
- f0 t% J9 v/ w9 U: h% [        {4 {! M  C% }& w: |$ J, ^8 G
            switch(ep0_state)% k2 D  [  Z$ |9 U/ \# K
            {
: w* M9 S+ M7 u2 }+ e: N& `- X                case 0: ep0_state |= 0x80;
" Y9 v1 ?  L/ h. m& r                        break;
( c. ^9 q9 i8 |" m; R! v9 p                case 1: if(USB->EP0R & USB_EP_CTR_TX)6 z) w, s3 T. h3 Q$ s8 M: ~9 k4 `
                        {# P- g& b& c% S5 m+ w
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
8 e' W9 G% ^9 a0 f! \( d- o                            ep0_state=3;- L8 |6 O3 w9 T  r  m9 T$ }& g
                            return;
4 e7 d) q6 m: c! [1 i! g                        }$ B6 J1 V( M( W8 c7 j% x
                        else
, _! Y: L8 c% p+ s" u  A! x/ X                            ep0_state=0;
, T5 i6 N  C  j9 L- q8 [% V                        break;
; A! V! W( u9 J5 ]; C6 [' @                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
' t: e, w5 `0 g, e: Q4 Y9 B8 X                            ep0_state |= 0x80;, V5 C4 L5 `" |; C# P$ U) X
                        else
' ^6 m- [3 ]: E0 Z! Z- A  X                            ep0_state=0;' q2 v. q) |) C' C( \7 d
                        break;1 `0 X0 ]+ f) v( F8 q# J
                case 3: ep0_state=0;5 {8 |% W6 [/ L+ R1 i& G
                        break;
5 g. E! @/ @3 i                case 4: ep0_state=0;
0 h$ w- K4 b8 H6 F                        break;
8 j% X+ }5 n, ^                default:ep0_state=0;$ @3 J: {% J9 a. K: R" y# v
                        break;0 D$ b0 Q. c+ A& s4 Q% n
            }
; u6 U( Y6 r1 d( I            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
$ z; \) n! Z8 l8 E5 ?            return;
: o0 F4 M$ k& K/ t        }
; r  u( p* ?5 E        else    // EP_ID can be 1
  L, z/ v6 _3 k3 V2 f4 h/ s8 n& F        {
, W. _3 K. O) ^# A# l: m            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag, D) g; w0 F' U
            ep1_wait=0;
+ j0 B8 z& l4 p  M; Z: _  S            return;0 s, i7 \) K2 y5 w7 o, [5 O" G
        }
) h8 X( }0 Z) q: F; T) d6 i    }
) z3 D( I# o3 ^0 @" E9 f    if(USB->ISTR & USB_ISTR_PMAOVR)
7 V# j( w5 A5 T% l: T    {: M7 q8 q' J, P% {8 I
        USB->ISTR = ~USB_ISTR_PMAOVR;
) e5 ^' \% N3 k; H: ~    }
) W- K* s# P4 M1 a    if(USB->ISTR & USB_ISTR_ERR)  {) o. z2 H; L9 K
    {' t% P* z7 g3 F+ @" l
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear' z* O3 M: h0 ^' c
    }
3 `% Z* e4 `5 E    if(USB->ISTR & USB_ISTR_WKUP)
( Y7 t7 K  S. l. w$ i0 y6 N    {- ^; X2 ?& Q1 O" a  k/ H
        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend! [) ?3 k5 h* ~. C7 S
        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
2 j; i* @" e% x4 V8 @0 t    }
6 j. ~" x6 u+ F( D    if(USB->ISTR & USB_ISTR_SUSP)' W  ?* r% @# n$ A
    {
3 ~1 P. l1 M. b9 u) Q        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
9 u) h1 e9 D" H8 ?  |! H' R) m        USB->CNTR |= USB_CNTR_LPMODE;   // low power% I3 k8 `9 v" D- K5 C
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
# i! i$ w, v4 m# F3 m( w- Z2 z    }3 D! N( d- K( `: Z
    if(USB->ISTR & USB_ISTR_RESET)
9 Z$ C+ ?' |, c- F" e8 \; e    {
/ q% Z" S+ s' r9 s# ~8 x2 Y        USB->BTABLE = 0;    // buffer table at bottom of PMA
  A% M+ E# F8 @6 V        USB_PMA[0]=128; //ADDR0_TX6 w7 |, \  c. ~; r* ?% L9 |
        USB_PMA[1]=0;   //COUNT0_TX
/ `; Z5 l/ R" v1 n9 A        USB_PMA[2]=256; //ADDR0_RX4 M7 ^' M. J( \" q! Q1 t
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes4 z7 T" d; Y9 p; y. F& p+ ~/ K$ O
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
. \8 I' C  U' z+ s/ z0 m4 H        ep0_state=0;
6 f2 w5 l& y2 _) d6 G$ S* _( O3 t        USB_PMA[4]=384; //ADDR1_TX% B' _. R, H& f. ~$ |$ R
        USB_PMA[5]=0;   //COUNT1_TX
0 Q9 i" r0 K" Y$ n/ ], A% B        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type3 m/ U; E- f& g+ V1 T
        ep1_wait=0;
, I+ A+ y7 t6 z, i# I0 N* S        USB->DADDR = USB_DADDR_EF;      // enable function1 K: M5 x# ?: H0 W, y
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear) p* n7 F# {: l
    }
- X' m0 Z. B+ E    if(USB->ISTR & USB_ISTR_SOF)
3 ]+ G# f) ?  M7 D) i& W    {
+ f& q' j% r5 c1 C8 p        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear# v) X% y$ A! B2 i2 `6 l0 U. u4 a
    }
! V4 w; x1 i/ U3 ^- j}$ j, ^, M4 d( Y! t9 A/ F+ _

/ j7 g& @6 j1 a
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。1 [# j( }+ i6 W

8 s& l% H7 C& W/ l" y* I0 T! X8 B# H主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。5 G) X" ~0 N  ?/ q: \& a( {
+ {* [4 B( o; i1 T5 x; N
    while(1)2 z0 F) h+ l, F$ M& R+ q& U# ^! ?
    {# f9 M6 D; @+ o. f0 E+ S* j
        static char row=0;
' n: e7 X: f9 v        __WFI();
) N. ]" t3 v( \) |8 B" q        if(ep0_state & 0x80)    // request data processing8 T% W/ T+ l, @# G* l/ O
        {
7 `: s* y) ?6 S& B3 D% i( X: p1 e            if(ep0_state==0x80) // SETUP phase9 z9 D7 H8 U( w9 s! M" s6 m2 t, p, G
            {' s: f1 n5 o" S8 H/ s8 A$ Y3 K& X
                if(!setup_packet_service())
* d. S2 v  {* }* x" z2 O0 k                {9 v3 R. V% _  N0 p; k- i0 {
                    ep0_state=0;
, s  l+ s6 |/ }  |7 L9 x                    // not supported
9 m% g  w1 F6 c' w  w9 B3 ^: v                }0 K) v9 E& Q/ `6 X# R
                // ep0_state should be set to 1 or 2, if processed
3 C1 D: [# s' A6 t            }6 R- z, C! W7 d" {
            else    // OUT phase6 J6 [/ P! N9 w' E2 [7 q- k
            {; p8 ~2 m$ r/ t& A
                // process data
9 l$ w7 B5 j$ G* k! J                show_LED(*(uint8_t *)(USB_PMA+128));3 `6 E, F* n. o7 E7 N
                ep0_state=4;
* K( ?$ w1 U1 ]- ]: J8 z3 i                USB_PMA[1]=0;       // Zero length DATA0
% @: K! y+ ]7 a- K9 L. g+ x4 p                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;' L" _7 C! J  r/ j" R0 c
            }
8 {+ z' e" E( q, G        }
2 r0 t, l* p5 W        else* K- D8 m4 L  I' J1 f
        {& m( V# ]# X+ S4 z
            if(usb_address && ep0_state==0)
' B4 w7 ~$ d7 t  n4 |            {
. X( K0 r4 r; t" W                USB->DADDR = USB_DADDR_EF|usb_address;
+ {: F' N! ~  k/ x& P& y3 v                usb_address=0;/ \6 w2 z: C0 C4 v/ F( F$ y% d8 u
            }0 h: }7 `) Q/ |6 ^
        }
. p8 a8 a$ |9 g" y/ Y! c        if(row!=scan_row)   // new scan line
0 ?: J  C% ?9 p, z+ W        {7 [* `1 g. ~# _0 N; U5 R
            if(key_state[row]!=prev_key_state[row])% e. O, o8 s- M" Y" `
            {4 O; I8 d7 V2 g' [* Z
                uint8_t test=0x80;
' v6 W# k/ H+ D% \; t! I; A& m; U& i                uint8_t diff=key_state[row]^prev_key_state[row];: B% U* L" \( Y  W  \
                for(i=0;i<8;i++)7 {: L% w  B* \. ^  L, p( Y
                {
0 g) B- y# a. o$ }9 j! g0 @( u                    if(diff & test)9 K4 u4 v0 `! c: }* n2 E
                        update_key_matrix(row,i,key_state[row]&test);
4 ^  ]! `3 U) ~                    test>>=1;9 d+ @, C: {) s$ I( M
                }
% L, j  U2 R$ y0 ?4 ]$ O$ M" S            }
3 _8 H. H1 N$ W$ a* Z! l3 z            row=scan_row;
0 ~6 X! M2 E: j" c% j. v        }
' @  M! t$ K4 M/ _0 _3 a9 n    }/ n5 K" {. m( k2 E: E" R: \6 C

$ j: V6 u" _7 C6 T3 y( T% g3 G$ C/ X- A+ t; `9 H
EP0的控制传输,把用到的请求处理一下
; m6 }) d* ]6 ?char setup_packet_service(void)
) _5 y: W$ u% A5 I9 d{+ W! C- v) ^. }; i3 j& m
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
- V# H$ w$ e" R% e    {! }* w" R9 M5 p1 o7 P! |
        switch(ep0_std_req->bRequest)
7 w# i; f- V$ P7 r! G2 x8 V  W1 p/ j        {* l" ^/ y6 H& T
            case REQ_GET_REPORT: break;7 ]# e4 ^0 ]- h+ k; U
            case REQ_GET_IDLE:  ~8 q$ I6 z! e# [
                USB_PMA[64]=0xfa;   // return 1 byte$ N6 n8 Y5 g% }* e7 L; v' e+ Z! L/ ~
                USB_PMA[1]=1;3 {5 s( V# K& n6 b! q" }
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
! `7 N1 z3 ?. r/ ]9 e: b                ep0_state=1;
# F0 d+ z+ g2 ]. O1 D+ J5 P                return 1;2 c! r# g+ y( y6 X0 S
                break;& S( R! y5 k" C5 d
            case REQ_SET_REPORT:2 O: L  m1 E# R& X, v$ X
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
" D2 u  S0 S: r( A8 W: C+ v                ep0_state=2;
9 j9 w: o, J' B4 y9 `                return 1;- [% V2 ^2 ^& c
                break;4 L) g1 E/ M0 f: p
            case REQ_SET_IDLE:
' D7 B' a; |2 ]5 E( n8 K; Y                USB_PMA[1]=0;   // Zero DATA
; s% P; T- H) N7 Q$ {% X                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;* W7 q0 s" {, A. F  ~
                ep0_state=4;
  X+ ^2 t" W' B                return 1;5 |% O, }$ T0 W  R
                break;
* G! K0 @  h+ @- k" P3 B  y) |% n        }
/ g8 N8 ^! k1 B/ }1 a6 y9 r        return 0;
. `0 X# `* p+ ]; G3 h1 U$ O    }
4 D9 n- N0 L1 P7 l# w    else    // standard
7 e, j) K2 e' |" j) m0 _    {
' [! l& N* a6 w7 u        switch(ep0_std_req->bRequest)
% r6 P% ], Y/ H) \        {
4 w3 p/ ~  J' T- R4 |; B* m            case REQ_GET_DESCRIPTOR:
3 Z' y$ }' Z6 T5 {. a                return descriptor_service();
5 k' f4 M( Q( h; z4 I. J  `                break;
" c# ?& c9 u$ E; b2 |6 Z            case REQ_SET_ADDRESS:
7 \" V% q- D8 ?+ q/ h                if(ep0_std_req->bmRequestType!=0x00)3 [7 E( H9 j9 y" P# R2 ^6 P2 Y
                    return 0;
" x. T8 P* c' l; K* h* C; [                usb_address=ep0_std_req->wValue;
7 L/ A/ s1 b6 l5 U, W; B2 v: k                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
' \6 K! y* H/ }1 o( Z! E! ^                USB_PMA[1]=0;       // Zero length DATA02 m' }  C6 U( f3 |2 C: c
                ep0_state=4;    // No Data phase
! }6 ?) I6 \$ i" c( \0 c  {( c                return 1;
/ L) v) R" [, g, s: M            case REQ_SET_CONFIGURATION:/ G2 n! N4 R/ }8 F
                if(ep0_std_req->bmRequestType!=0x00)  |/ z! @) q& P& l
                    return 0;
% c6 M& E7 U2 r1 C! W# F2 q                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
) \( s' m2 J) ]. J/ d4 t$ Z9 l" v                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
# V; v/ \" M/ B4 t! N4 B' e9 o                USB_PMA[1]=0;   // Zero DATA; m) G5 f! N; ~8 T
                ep0_state=4;    // No DATA phase
! G/ I$ |- I. o) M, N                return 1;
1 I* F7 O, |1 Q% c& a1 \            default: return 0;6 f) z; Q; T% c  `+ l( k3 A
        }
, O& A; c3 @) X' B' d7 j6 `    }$ B. I- }; }9 {5 T$ i$ o" |# U
}) R5 d# A1 }% _
' J2 t2 {, j; B7 v; B: U

. l0 r, `1 ^" ^
: F" ?! {1 m% r/ v- f
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
& o% [6 K. c6 d2 Tchar descriptor_service(void)
6 y0 l5 C; n$ L{5 f: E$ A, U& [0 I) E% F6 z
    switch((ep0_std_req->wValue)>>8)
" M- D/ Y1 K* h; l6 _: Y) z    {
+ ?6 q+ ]- ~* [* d$ J        case DESC_TYPE_DEVICE:
. n  E% m1 F: e. N7 g5 u6 Y            return ep0_preparedata(&DevDesc, sizeof(DevDesc));$ @0 j2 q0 }" k; S: S( T
            break;  ]. j$ K; f# g  w7 P/ }
        case DESC_TYPE_CONFIG:6 ~+ j( D" Z/ J) e2 W; e
            if(sizeof(ConfigDescData)>ep0_std_req->wLength)$ r% y1 E9 v; U/ r. O) t
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);. l9 u, m/ H( E1 W' S3 I
            else
: ~. Y* i4 y2 U( ^& Z% |                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
9 X4 U% c' t1 Z$ g" c* r            break;
/ ]1 s6 r! C* _, @3 t$ ~        case DESC_TYPE_STRING:
2 h; y% D. a0 K7 C; D- F            switch(ep0_std_req->wValue &0xff)
- [3 ^: O2 g2 V( d4 C7 Y9 y            {" Q, |& L2 D! k& n3 j& q
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
( M  r  r) d8 s% \                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));% d: U' W% V  k' }+ Q6 R5 X
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
0 T- V# K- o0 ]2 ^                default: return 0;! M1 K7 D  u7 }" c6 D
            }
& L" ^& k8 {3 P% f$ \            break;. }0 d; f2 D1 G  k
        case REPORT_DESC_TYPE:
' H9 x/ {  X7 w, l3 N, F* [5 f            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
/ s* @7 z. b' L+ ~6 |" q+ M        default:
; ~+ U' X( ]1 C            return 0;/ C7 w( i, A: Z
    }* g: `  h9 ?2 V: {3 o
}% I- l5 x: O1 d% @
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
$ P( o. o! d* c" K; i4 gvoid TIM6_DAC_IRQHandler(void)6 {( ?/ O  K! h: f+ l4 v1 M& X
{, j9 z# Z9 {8 c. n: ^0 X
    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
9 ]# Q# S* K+ {2 B6 P/ m
4 L% e9 ~. f  x7 k$ R& R" a# L

4 A' F/ C9 X, ?    TIM6->SR &= ~TIM_SR_UIF;
" N" }3 p  ^1 b' g6 h    prev_key_state[scan_row]=key_state[scan_row];
  f& e/ F. r8 D; s) E    key_state[scan_row]= *PA_IDR;   // update key states0 z" r" ?4 y3 p# ~7 g2 r9 j& B
    switch(scan_row)1 e  C, J4 t  U; o3 N1 k  p
    {
) c# I$ {1 {3 e/ K        case 13: // next row PB14( x6 ~( E% k# e9 y8 e! y
                GPIOC->MODER = GPIOC_DEFAULT;
; M* M; ?, D. b                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;2 |% \2 }- N- x. P1 t$ D# e
                break;. Z" r( k1 l* h2 D( r) J/ {
        case  0: // next row PB15( X* F: t9 A% N# d
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;( V- p) f1 u8 ?+ @
                break;: P7 }+ _% V9 d# {! {, Q) e
        case  1: // next row PB3( I+ }) H5 ~9 h) ^& ~
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
' D7 _8 K8 y0 t                break;
1 D8 U. T' T# L5 M4 E        case  2: // next row PB4+ h+ ?) M8 S" {
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;0 R% _& ]2 x9 G/ i6 w5 G9 g
                break;
; M3 T  B& W- k; n7 C7 s        case  3: // next row PB5
9 f6 R) U6 F6 A                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;
9 c/ O; [" \0 p0 B5 O- B                break;
! x; b+ U; t" L        case  4: // next row PB6
# b: R9 k- ~7 d0 |$ W2 Z                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
. K/ c; V# S4 A# v; b' M                break;/ `1 v: _& C9 |
        case  5: // next row PB7" C1 l6 q" |* L( V
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;3 H1 J1 I  x( X, S$ r: e# j
                break;8 ]* b# ^. [; j6 H3 f+ d$ b- |1 U- u
        case  6: // next row PB8$ n# D5 {4 b1 K
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
* p  D4 K: p* M3 [                break;
( @! m& T7 ~* |. Y$ M! A* \& K        case  7: // next row PB9/ ?. w0 q% B+ C: c) M/ r
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;
2 q0 e4 M# E4 k  Q, c                break;& v7 c0 `; F  T4 b4 p% R. a# H
        case  8: // next row PA87 p9 `; K7 f: G/ e' u/ N, ^
                GPIOB->MODER = GPIOB_DEFAULT;  O) `) o4 _0 o
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;" z5 F, o$ C3 L$ P1 `8 U
                break;
" f+ @! [; v! p9 |/ X" [        case  9: // next row PA9$ N% M% M! L! n) F$ b( \
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
8 L# \: V. U- O9 y8 C                break;, ^" h8 v, o5 Y0 {1 s- H& D$ f5 Q8 L, k
        case 10: // next row PA10
2 p, P6 L, F, y! S! E                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;5 F( w' `/ O/ l, h, I% x  Z# ^
                break;: n0 Z* R& T8 K
        case 11: // next row PA15) \: b* R! B# J0 G/ e5 B% N7 a4 z
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
3 }- G0 m$ W; m. s0 L" W                break;8 o7 y0 `' g+ x$ B3 U$ o+ \# I5 D
        case 12: // next row PC13
% T: P/ |- {! {$ n; }* V# ?5 X6 k                GPIOA->MODER = GPIOA_DEFAULT;
$ S) o" Y9 t7 o/ @) l( P7 q. V5 v6 J2 D                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
7 p/ B) b9 _' n/ U                break;2 _* G; t8 w" C
    }
8 o, H4 k% a, k5 N* J    if(scan_row<13): R. ]5 _+ r- M4 a1 f. E/ Y
        scan_row++;3 r. J! \1 ]; e, C6 r0 S3 Y5 E  h
    else5 w6 c5 Y& g* p! Q8 J
        scan_row=0;4 `& G! ]$ t$ y
}& d0 j6 M3 l# h4 ~3 y. [; Q0 n

! I. w" Y' S9 g& J! E  Q- ?- }: I3 x% S+ ^6 I) n
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
+ u, K' {8 @6 ^
! T4 Y# J, J  J- L# q9 Y5 _扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。) n3 v) [& M* i
/ H5 w4 [- r* l. h+ j# C
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
' U; l. q6 {3 |3 ]
1 K& }2 Y) l4 n7 D) D

6 x( v% h4 {* i: N& p$ D+ i2 Y/ Aconst char hid_keymap_qwerty[14][8]={
' y) J+ E9 ^+ e. }5 Y1 U% a/ j# }    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},; v* i9 f! J+ S" p8 I' V
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},/ U& Q% ~& ?. t% [" }1 U( Z% U; k/ }
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
6 ^4 r, o) A( P    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},4 t# o, w! i8 o( D4 x% E) X
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
/ g8 H; v! Y* W: O4 S: Q8 J    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},2 E# s* I" G+ }4 k- B# r8 ^
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},# ?7 x6 e; |( A  ?, K, N
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
- s& q- D2 \% Z    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},8 w+ p8 B5 i5 _
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
4 A9 Y" L. P# d( Y    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},$ k# I0 d$ _1 i+ [+ S/ ?
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},( L& X, @) P9 K: v3 _" |
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
7 Z$ n! d4 `+ m! [, {9 y    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
7 X" p* M  r4 f% r};
4 s/ }3 t- }( q, T+ i) K& {8 G% O3 l8 G& R% ]
- X0 Z& c: D* j$ O9 Q
const char hid_keymap_dvorak[14][8]={
7 _/ r+ K/ s. [    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
0 x6 s7 @# P  G7 Q2 o    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},2 c, C7 \" @2 J9 ^- `) r
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
& m; W) F! h3 W5 R    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},& F7 P9 S" N, Q5 m
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},- ?# h% f( L' W! v7 }. L
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},1 t3 G# i0 R: I: V  y
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
# a3 e5 |$ l. K- ^# e: `    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},. w7 A5 j1 X7 |( J- x+ T0 D
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},/ X; E- C: v8 _
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
6 @* [  l3 n8 v+ N% {    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},5 _+ `( C4 a/ p7 P6 Q. @4 Q$ U
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},2 `5 q) X) e1 `9 Y/ X) d" f
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
/ v8 w6 N6 v2 ^2 b% Z0 {  z; }    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
, H- ~% \4 D9 y% U6 M0 U};
" @2 [( Y2 r3 h& S7 k
+ x: @& H- |) |
8 w0 d4 y6 D# E/ I上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
5 a* `# E2 B/ \" D& s. @0 f0 S6 V0 V3 U: P2 [
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

9 b) l0 Z( }/ Y' a& ~. x7 z+ `% [7 W2 [* I- s' G" p) O' Z
void update_key_matrix(char row, char col, char onoff)
5 J, G2 ?8 a+ j- V6 C{" F, B: L% v  _4 l8 T0 j
    static uint16_t hid_report[4]={0,0,0,0};
! T! `, {+ R+ }. t2 c3 Y    static char (*hid_keymap)[8]=hid_keymap_dvorak;
7 e( [' p/ ^6 Z# e5 d: J+ d: t) x  [- u+ B% u6 g

& K8 m* M- k1 L# B+ k' m, o    unsigned char key=hid_keymap[row][col];5 b6 T4 J$ x! ]
    unsigned char *report =(unsigned char *)hid_report;
% C. ~6 P* k5 r1 J3 z# s) o    char i;
) S* m0 i5 [$ U2 E1 w% P" Q: ?5 {; }; S: s2 f% b. a
3 b4 b0 `$ C0 W" ~7 a
    if(key==HK_MODE)
) k" ~; q' O3 V6 U3 }5 V1 |* q    {& y0 L+ C$ n, N' h
        if(!onoff)% ~. h2 @9 c) g9 A* t
        {
+ g3 R5 O5 e4 ~+ d9 q9 G            if(hid_keymap==hid_keymap_dvorak)" {: |  ]9 I% A5 a% K$ y: A
            {3 U% B( W; }% P
                hid_keymap=hid_keymap_qwerty;
4 X4 A/ z5 F2 ~& U. S                GPIOB->BSRR = (1<<2);
0 p* g; ?" M5 u6 C/ k# x3 E+ G  A            }" A! C0 {) e7 Q0 t8 M& t! I
            else$ W+ y  {+ U6 }; m6 o
            {
7 H, |3 D* A9 ~6 G9 v                hid_keymap=hid_keymap_dvorak;
: M1 @8 ~' v, F% A  K9 \                GPIOB->BRR = (1<<2);
# p: f7 E" o8 M; p( d, u            }
9 h) e# p5 u- S- |% P        }
7 S6 f3 Y" E% n        return;
. ], q1 u! `( h/ ~6 v    }
$ w+ V- ^) \/ p$ u/ S/ N5 M* P9 i( F4 {9 j# f
' S! {4 d( D% D+ _1 G5 {
    if(key>=0x80)   // Alt, Ctrl, Shift5 [% j% y, ]3 K2 z' z: n
    {7 G. Q* f. x4 X8 s, k# h2 y3 d) e
        uint8_t bitset = 1<<(key&7);
5 b* c# j7 o3 O2 j5 T. w        if(onoff)   // non-zero is key up
, q0 m5 g4 F) @7 y2 n5 }* J            report[0] &= (~bitset);
( M* M. m- k: ~8 v        else
* y9 Q+ ]! f. z: v* k3 c            report[0] |= bitset;
: L" O# [& q. l8 `7 Y; C    }0 u: U9 s* V4 Z& y% n
    else
% X- i% i- K0 @2 @  e- N- O8 `    {
; u0 J9 _: u4 L! z. ?- F# Z        if(onoff)   // non-zero is key up
4 O& R+ I  d( p( ^& A( Z( @4 m        {! S# g0 a. _  ~
            for(i=2;i<8;i++)) V! M8 H6 u& l. G( X- K
            {) ?$ I1 N% R, k1 Y8 J' y- [" n
                if(report==key)
# L5 k: T5 R/ N, _( E. N% T                {
3 n0 j7 D2 l# P                    report=0;
  A! Q7 T! Z7 T, A% M- I* g/ z! D                    break;. B) e. t' M& i6 H
                }
6 d# x; U  B! V" o" X! o            }
' Q- S  a9 ^. t, j, C% h        }5 [/ i1 Q- D" u) e! z
        else; j. ]( [1 H0 c. m$ X% \7 n
        {
6 k, N2 \; t, p) Z$ `; B            for(i=2;i<8;i++)( a" H. \* I, _1 ]$ p
            {( M* H' f! g$ g3 U& ?4 z( y4 A
                if(report==key). ]3 ^! }) x9 r3 n
                    break;
* s( B8 X) i" m' y# D* b" W, b                if(report==0)
9 m: j. e! t. @                {. T' y3 F1 |3 U- ^' a" s$ Y* L* m5 }! u
                    report=key;
5 l9 p) \2 ~' q1 L6 K* J  v                    break;
. H# m6 f7 C; M! E                }
" ^3 N8 S8 F4 N5 o: z            }
  e% n* I% B  U  i        }+ x& u( I% d6 J" e5 ^5 u
    }
4 ~- o4 z" G& d8 l- q    for(i=0;i<4;i++)
% o6 l7 ]- d" @# B        USB_PMA[192+i]=hid_report;# M% \2 N! ], E; i, V
    USB_PMA[5]=8;   //COUNT1_TX  p1 O/ P# \. w& [0 r
    if(ep1_wait==0)+ D; ?# b& V, F
    {
: Q8 w/ J: a. x( E6 P9 @) z+ W) a( Q        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
9 R0 I9 j4 C9 r* S, K$ g' X        ep1_wait=1;
( f% D9 Z( C$ L/ n8 t* s+ R    }
7 Z( W0 K: e7 P; {% `}
, ~1 I' q1 n' T- c4 H  E4 K! \* x4 x  F, b/ Y

+ k( D" n1 A* B( A2 F# Y% K# R1 o完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。) q2 e& x2 u2 d1 [' a  N, b
keyboard.zip (8.7 KB, 下载次数: 6064) ' j- g/ T. \8 a! q

, `* }" L. J; Q4 F: H& q9 Z) Q9 }
* U. h$ s0 z5 C$ P; x3 m3 r- V
% ^/ O1 M+ t* z& M1 p! D
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘& L' s9 g- G  w: u  x
不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48: s/ ~9 q( i; [
刚开始我以为要把打字机改造成电脑键盘/ f+ Z7 q% L% B+ j( b3 h. e2 N
不过楼主也很厉害!
, Q' h  u8 v2 F- }( _
哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
' [5 l; D! x% W
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-12-22 11:09 , Processed in 0.188143 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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