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

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

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

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

[复制链接]

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
" H" {6 [% l7 N7 C% n9 M. n' L4 r+ R! I2 U8 f
http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
9 g, @7 z" i7 W这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。+ g7 q! T, K. p; [6 n9 F  X' B- _

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

! S) @% C5 p8 n+ o" C0 y

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

1 P! O+ w" l$ s# {4 T 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg ; b3 B$ C" m8 E- ]; |* y2 i, h
----------------------------------------------------  分割线 ----------------------------------------------------------  M: j2 F3 i5 K( B* y( p

5 f2 z! s" b' Z1 Y5 a" _# y! Q) P先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

. e5 e- V9 {5 g1 I( |) r5 C# P 020011osionbunl4ui44vi.jpg.thumb.jpg 7 l, U5 k+ o  F2 g0 O" @, U" S0 x% r
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
# s6 k: b1 c8 W* V$ E 020017j8ycmnv7788bqv52.jpg.thumb.jpg + L+ P8 j$ j  N* y/ Z" t
特写,80C49
0 n; P- o  x7 O* y2 X* C4 x8 b 021040oujzuvtut6iujtvz.jpg.thumb.jpg
' g2 |7 y2 S4 K* XLED部分,使用了一片D触发器锁存指示灯状态.
2 r9 d, R  o0 f2 o. @# K6 @3 Z) i 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg 9 W' o+ Z& \# ~  f8 z7 @' n& d3 F
暴力破坏,将80C49拆掉
1 v& c2 N' j$ w, ^ 021113e48qq98vyohvhzzh.jpg.thumb.jpg
# f/ @) P  E' `7 |" Q拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。5 v/ W$ D5 a! \% n6 q2 O  M0 e) a
021125nc9az6dj33rlds2r.jpg.thumb.jpg % M- b( n$ b2 A3 ^  \6 f# ^- r
焊好元件后的板子,准备替换80C49
. Z6 A/ Z) H; C. [, ` 021146zmmpmh4pav4o7g6a.jpg.thumb.jpg : H# Y5 s6 D9 F* s
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。
! Q) J& Q7 ~8 g0 q- K 021104shifhnrqbr3o5nlo.jpg.thumb.jpg $ I' f9 a/ q. O
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.6 A3 d! R, B5 o$ p8 q1 E
022003ym1p9u4ug40280uu.jpg.thumb.jpg
  p7 P6 d# ~4 A开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。& B  ^6 q" v& r. a
023313kt141q9qajtol7ma.jpg.thumb.jpg 6 E; i7 C# B& i1 R# {+ n
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。. O$ f6 r$ [; p. u4 k8 W
023322nt7l5xb3ltttkltt.jpg.thumb.jpg
# K4 }& S' U3 h/ y* K
主键区键帽就位
' P# J; H% }3 S0 x5 \& l) o 023331hin88e8wkrwzwikx.jpg.thumb.jpg ) }( H! L2 P& D2 `
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
: p/ {/ C2 G& i& T0 }9 P& v 023336wjzlgopugg1jyy79.jpg.thumb.jpg
. h; h- a) L; G9 |. I% L8 N最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。
0 g' D" k, N7 r9 | 023341sffu4j3g2323h6fl.jpg.thumb.jpg / V* d& H# Q' [+ ]# y: u& L, K

; G3 `3 H6 Q4 m3 V* g9 U----------------------------------------------------- 分割线 --------------------------------------------------. ]# v& S6 v8 C- f* `5 n- y# q3 l
+ {* S1 Z9 t0 d" P+ f. u( x8 c
9 S- d( [8 v% \# R# x' d- k$ M

! {( z( q/ r+ c4 Z9 l1 u
- w  E; d$ P- {) M
$ V  L! i. s4 ~8 s' i
2 d! i! E2 [4 O; C. N3 ]
; P* B" v9 w. }1 N

- h/ G9 _! n. N$ N1 x: f" w: L  n9 g$ O3 b2 c8 F' \
; M% n# r1 q1 ]7 d+ u, h

# e5 G; Y. x1 S) ?) I& p* K( L9 @
回复

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。0 \4 t$ P; T0 u7 r8 Q; M6 h
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
6 w/ A$ u  B6 x- O9 D 104025nzibm2rmiomhyirm.png.thumb.jpg
7 }( k; u, j- l6 |% i, Z* s* l

. X$ M- ~" e- s; y% ?其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)& D! x" b) H) I5 H( Q( V) M% r
105004zkrez5houvkkznko.jpg.thumb.jpg . f0 @) ?+ l. s( w9 F5 m; m

; ~6 F; x2 w, a- i: \. ?! d& ~扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
% \6 X+ a  D: i, B9 T6 ^$ [7 z
; w% M4 I1 N3 ?4 t* F. w6 `这是我设计的电路图:3 _' J' v6 e$ [* {
110344ej2z2oo2rflo7oe7.png.thumb.jpg
9 D- Q* j( O, _4 t, G
0 l/ Q# _1 O1 c( a9 m% k% L
PCB Layout:
) x6 L" S. g) N0 S3 j* v5 D5 t8 t 110847jjbjvt34vwt3v5bb.png.thumb.jpg / }' b- W1 d6 v# s9 K6 b

) d4 C" {5 g8 |* O8 S( `; m不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
4 `: j% i, g$ `1 P" z2 u  i" Z6 y- t, W' \8 ]6 j6 z0 t

( ]1 A, d7 d9 B- C6 m' O
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 ; Q/ p* G$ W3 |& Z

. o' `7 Z# M( r' g+ T, p软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
5 u+ h' Q" G# @% k' a$ O; {+ Y5 x5 Z! F$ H6 m* W0 x
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:
! N8 t) S' ^2 `8 j 113818pmrfsb6z0byt6t06.png.thumb.jpg
8 @1 Z9 l' X% y1 g" v) Y% ]

. O5 t2 d; d) c- g其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)- S* u, H5 K. c" c0 s
' ^# U8 J) s8 @7 K
USB的中断ISR,bare metal哦& w* t: p0 a, O. T
. O) Z$ A$ o) F
void USB_IRQHandler(void)5 M+ K0 T# @$ ?5 }
{, R1 s8 F+ n& V" u0 P
    if(USB->ISTR & USB_ISTR_CTR)
1 n4 `; _3 e0 g% U* n( c* U    {
$ w7 o/ b9 y5 a0 n2 ~( X        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
) ~1 Y' C8 r. p5 t, U7 a        {
% S# K8 A6 t& ?' N& ^6 t) D            switch(ep0_state)
7 Z0 J  S( x2 t            {
  g2 A% a3 p: j6 Q" _8 F1 M                case 0: ep0_state |= 0x80;
4 r3 B& B0 l' Y4 A3 U9 m# r                        break;4 R5 |2 T1 V  w% r- ^
                case 1: if(USB->EP0R & USB_EP_CTR_TX)
* ?7 o9 {7 z* ^1 U* _8 q                        {8 B- q+ f# j8 X0 f2 e8 a# w* E
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
$ l" [# F" F* i9 `0 L+ d                            ep0_state=3;
# t5 J' z, O; R                            return;
# M( y3 k, @6 x4 U4 N                        }( N% x3 j9 ?& u& u
                        else
( C. ~; v1 d% ~. a  ?                            ep0_state=0;; F' \8 F+ B6 @& n8 e: o
                        break;4 Q# \  _9 l7 e( e. q( \
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
$ y! v' A1 u" _6 F1 Y: O; O                            ep0_state |= 0x80;
6 j" k& F% D; R: `                        else
4 h0 ?- ~  K) J" K# X+ b                            ep0_state=0;9 {" h7 b3 R5 J& g- a: k4 E
                        break;
$ @+ @& ~8 t: ]9 C7 B+ N                case 3: ep0_state=0;
3 G' @! V$ f7 s; P2 o4 O                        break;- a( b& q# u+ {  h6 A
                case 4: ep0_state=0;
* I5 G9 _+ |# H& ^& `( M3 P                        break;
3 O; v! S  ?. A/ r1 S/ L+ Z5 x                default:ep0_state=0;: I6 i; t9 p0 z' r" K" F
                        break;3 c2 X, a% N: A8 w) j5 x" O
            }
. L3 r% u7 x8 X7 ?: ]$ b  \            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag, r+ g9 t) T# _) {) O  }
            return;2 l+ Y5 C1 I5 w. `1 s# O, V
        }& A8 @7 J8 f) Y9 |2 j
        else    // EP_ID can be 1
1 o& R* |, Z' g/ x        {: y" H' M7 M) w
            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
! |4 Q4 D  D0 {            ep1_wait=0;
# A6 d5 k. ]- P4 l/ y, x            return;; }  I# t+ u: Y
        }( }% i7 |5 ?2 E+ \  p7 ]9 E
    }
% s1 T, v  x$ W" t' l3 l, e* D  l    if(USB->ISTR & USB_ISTR_PMAOVR)
3 E0 \1 @1 \/ {# A; }; {0 c    {
2 A4 h: s( B& N. ^& @        USB->ISTR = ~USB_ISTR_PMAOVR;
' u  y: u( O* ]8 m1 J    }
; \2 {2 A5 R" J    if(USB->ISTR & USB_ISTR_ERR)
- `9 L; [9 l! l) B+ `+ ^    {
+ Z4 I- j# a' `) j        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
0 ]4 J, S6 J8 c+ \; m8 ?" T# p    }
$ M. S% _9 G+ [+ l! u8 C. W    if(USB->ISTR & USB_ISTR_WKUP)! _) M5 R1 Q3 r% `2 P. r
    {+ b+ d$ s, r3 p- X
        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend, C( [+ C. R; k, p9 j- E
        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear( T  @9 _2 i4 y1 S
    }
+ k* i( U5 d+ l4 p6 @" s2 K    if(USB->ISTR & USB_ISTR_SUSP): v8 J! y( a- C# b7 ?( E" |1 \3 @
    {
  F" a$ O& W8 @        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
" E% |- t; t4 K5 V        USB->CNTR |= USB_CNTR_LPMODE;   // low power. Q% t  l1 r+ t  L( }
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear* S+ O! w' u/ o# ]  V
    }2 x: K& g5 v' r
    if(USB->ISTR & USB_ISTR_RESET)/ R# M# Z8 e2 [* f; O+ j
    {% d2 G) \0 U7 R) }  B7 x8 m
        USB->BTABLE = 0;    // buffer table at bottom of PMA/ W- {, o. b1 p' [5 i8 j1 [
        USB_PMA[0]=128; //ADDR0_TX6 j# E# w$ H3 t" E$ b& w' e
        USB_PMA[1]=0;   //COUNT0_TX& d, x1 D- o( H2 v& q/ }2 h5 E
        USB_PMA[2]=256; //ADDR0_RX
9 j! @& V6 w: Z: c        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
4 K! r4 Z: d- N6 \        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;7 G3 I* E4 l4 O+ I# R! ?
        ep0_state=0;7 ~$ R/ S5 D+ W3 I
        USB_PMA[4]=384; //ADDR1_TX% V: J6 r/ j- U/ ]4 \/ U7 P
        USB_PMA[5]=0;   //COUNT1_TX
! `' a  f! S2 `        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
! z/ b' `3 _$ |  ^$ C% |        ep1_wait=0;
/ J' X: V& O2 @0 [- B  G4 H        USB->DADDR = USB_DADDR_EF;      // enable function! M# `. _6 q# |4 V% M
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear4 s/ A' Y1 ~/ {* v
    }
  y! d( g, _/ \    if(USB->ISTR & USB_ISTR_SOF)
+ T! H# j; x/ j* M) D' q; X7 P    {( w" k# l) ^: e9 P4 x
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear, W- d  K1 W4 Q( p! E+ i
    }
* ?, v+ H! G$ c) U3 E' M}: G9 W" X' Y. j: u
  W/ f" v) h9 j0 l
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
* N5 s4 i. w- b! v2 U  Z
  i4 }+ }2 ]; F; L& Z主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。5 M. U6 V1 Y& e; M! g# y. X' t& g3 m
1 B% U, J* o# o* M& b
    while(1)
+ T# O2 A+ Y0 F; P  G    {4 q$ [+ k1 @. {' B& T: k! T
        static char row=0;- @3 z9 {5 W# Q. Y0 ^
        __WFI();
+ f; {$ _" V4 A2 l, E        if(ep0_state & 0x80)    // request data processing+ U* ]2 y) g: i0 c
        {) u( `2 \" C5 y9 G' X2 F
            if(ep0_state==0x80) // SETUP phase
" [; P5 n& |  P2 e3 U$ H            {/ I; t( T) F+ t6 e# g3 K# J
                if(!setup_packet_service())3 U. Z$ U/ V" _4 C% O
                {
: t( s7 J" ?/ s; A- p- ^' y7 i# B                    ep0_state=0;+ C% z: z: v2 S) r4 x) L( }4 ~
                    // not supported! l9 \& k4 I# ?: U. k: O" E9 O
                }
* r6 C4 |+ W/ A                // ep0_state should be set to 1 or 2, if processed
1 ?! u' L9 h  I4 j9 a# y6 \" c1 Z            }$ e/ }& ], y* `7 u7 y' j
            else    // OUT phase
/ q+ Q3 S3 {+ p0 A            {
( j2 Q5 H" S1 K( Q" G* s                // process data
9 e; }9 @: c+ K5 i; B                show_LED(*(uint8_t *)(USB_PMA+128));
! ^! N3 R( s% ^- G- W- ^                ep0_state=4;6 A4 F7 [3 K  f0 x& i1 X
                USB_PMA[1]=0;       // Zero length DATA0
" r" I! C" C* i# u* J                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;5 U! J4 R/ w) I5 L0 r/ W
            }$ I8 o9 [4 }# m
        }
+ n. Y1 p, ]+ s" t        else
1 N" [; A9 v, \& ]        {
- }/ O5 c3 ?; T6 t/ X; a            if(usb_address && ep0_state==0)& M0 j- A0 z0 t& q& ]: ^. @) D
            {
; l+ Q$ W9 A( Y                USB->DADDR = USB_DADDR_EF|usb_address;
4 B- V( H  ^  q9 G# y9 L                usb_address=0;
" E0 t" E: f5 L$ ^: z! t/ y            }4 v1 X& r! s5 B
        }
3 _8 n9 z0 Q& Y# t        if(row!=scan_row)   // new scan line
# k' }. \) g7 ^, M2 F* Z! g        {
9 F! H" ~' \- d0 R! X3 v: N            if(key_state[row]!=prev_key_state[row])2 C+ X  v' }% w5 a$ @$ u
            {! D; C- p' o! U7 r2 ]+ e
                uint8_t test=0x80;
0 [7 o/ [! e- y/ G" P                uint8_t diff=key_state[row]^prev_key_state[row];
- z1 O1 L6 C) i4 |                for(i=0;i<8;i++)9 z* {5 @6 h9 _' K2 m/ H
                {. W0 m. i: O4 z) K
                    if(diff & test)
/ R6 a2 _( Z1 k6 g- }' r* V                        update_key_matrix(row,i,key_state[row]&test);, B/ K! Y+ c( U! e5 V% c/ D
                    test>>=1;+ X/ O" o, U5 Y. [% q' k  s# Y
                }
: u: g! t6 H3 d( o" I/ P            }
) ~- k" d; f2 }% l8 ?            row=scan_row;
: u# L. T* }. N% B8 ?3 o        }
0 X8 Y7 H" x& l5 J! O1 d* y! v    }
" N0 r# K4 A+ X( a: @* I3 Y2 E  \! l# w" a! @: I& \

0 D, ~2 ]* M; l! C" xEP0的控制传输,把用到的请求处理一下! h. ]6 i! H7 X! o) E
char setup_packet_service(void)  q6 a/ D5 ?2 U. ~/ S$ d
{
& t7 n* J! M3 I8 \    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
/ S+ p! R* k' p. ?8 f' q4 e% v    {
! @' b! l5 L( E9 F3 N        switch(ep0_std_req->bRequest)" Y  e; [( i  d/ L* [# h" O- k6 T
        {# j! m  E( u  |5 m+ z: G
            case REQ_GET_REPORT: break;
! R5 L) ~; X: F$ g  T            case REQ_GET_IDLE:
" y  p1 F6 h; Q. t  Q5 Z: ~                USB_PMA[64]=0xfa;   // return 1 byte
, @  O" R% Z6 I5 |9 I) ~# w' d3 ], S                USB_PMA[1]=1;
. Q) }  e$ T% F: i* N                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
0 F- M- G0 C* p6 H+ p2 M  I$ W) w- c0 C                ep0_state=1;* F) X7 w0 ^/ u7 o* U8 z  S. D! ?, y( [
                return 1;4 y% @$ ^* i* f+ U
                break;
; I8 k+ _9 f2 s2 f& E            case REQ_SET_REPORT:
7 I% v2 _0 P, z8 y6 [) x                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;0 ]% [5 U$ e. s  v
                ep0_state=2;
2 x$ e7 D+ C6 f                return 1;2 P: h8 [' M) o* G* R
                break;
- u' r. s1 Z2 e8 e- \5 ~            case REQ_SET_IDLE:
- E4 [5 X- l7 |' D5 X' a                USB_PMA[1]=0;   // Zero DATA
6 k3 t/ ^5 d9 m                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
* U; o0 V% t: d- k                ep0_state=4;7 `' M6 ^5 q8 C) Z
                return 1;/ G3 ?- W: r3 k5 W+ g
                break;' Q( a: D! @3 x8 O- E7 a1 F
        }$ J1 Q5 A6 e3 E& x
        return 0;
6 M; R6 u1 Z/ i+ Y3 i! W$ `+ @    }
& T4 j+ ?. s  U) z% K/ n& ]    else    // standard( t( {* p; u+ d
    {! w+ L" h9 M. v# z& I9 {) A
        switch(ep0_std_req->bRequest)
8 x- M0 J' d% U! J0 J( l        {
5 L! ~: |' s+ T0 V+ u0 |1 c            case REQ_GET_DESCRIPTOR:/ j( J/ a$ ]4 A, J9 P0 P3 Q& E% z
                return descriptor_service();* e' o7 Q& p! L' m, u
                break;
2 Q6 g1 k2 G2 e6 U, Y            case REQ_SET_ADDRESS:
! m  h6 v* X7 c- f                if(ep0_std_req->bmRequestType!=0x00)! p# k: w2 ?' Y0 r: n* u5 i
                    return 0;
1 e" b! ^) E% f; e, s2 U8 G, Y                usb_address=ep0_std_req->wValue;, F" V' A$ I$ b2 _0 d0 a
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
* w! g+ ?# P$ F3 z                USB_PMA[1]=0;       // Zero length DATA07 ^3 _4 {+ @8 A0 b
                ep0_state=4;    // No Data phase; ^  s5 o9 V2 l, w* @/ z5 \$ }, `! c, x
                return 1;) h( i) s( I9 n* t" s/ G
            case REQ_SET_CONFIGURATION:+ W& Q& ~0 G4 V2 \+ k2 l, Y9 t% R" Z/ a" }
                if(ep0_std_req->bmRequestType!=0x00)4 ]3 o: J! ^. G. q9 f
                    return 0;- J" V2 Z6 G7 ~/ R7 j; G" g1 Q: ?
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;: X7 G1 v- W# w6 k0 Q+ H
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
! C$ ?5 |' A# t                USB_PMA[1]=0;   // Zero DATA
7 a1 y( G; c$ K, M3 V                ep0_state=4;    // No DATA phase7 @  R) Z* u! B( A
                return 1;, p/ r7 n! K  \) K+ i) ?
            default: return 0;
4 {( e" g. P3 n9 P6 x1 d        }2 [( \! l6 D, {" L; x
    }
* R0 }, y6 Q! `2 `}  E5 V7 |# M5 A- Y5 q$ t# D

: w0 n3 {% K, b% A+ c4 m6 s. B4 z4 [; U

& ~7 R1 {. o: H5 Y# S
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的0 V" o. q0 {2 ]% G  n& Z$ e4 V
char descriptor_service(void)
9 e" J/ ~+ A9 N) c2 i9 f) y{
1 V# ^; X* }+ y" J+ b$ _    switch((ep0_std_req->wValue)>>8)$ t. Q9 V) l6 v' `/ Y0 ^. W
    {
- |2 a( e; N# ]% Q6 K+ F        case DESC_TYPE_DEVICE:0 r4 ?+ ^* U% d! g/ q9 y  M0 k6 M
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));4 E9 e! f5 U. Y3 p* i
            break;
, v& j0 t" l, o2 \  _% X7 d+ F        case DESC_TYPE_CONFIG:
8 F+ ?" |( J1 b  C            if(sizeof(ConfigDescData)>ep0_std_req->wLength)
8 ]$ d+ x4 ?6 p: W                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
+ y! s) N& Q6 t$ [8 A            else) `0 q2 m' N5 L: K
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));5 U, {; g) _+ i4 P; u6 M9 ~# f
            break;, S- t' L- W: t, Z
        case DESC_TYPE_STRING:
1 f5 f% A+ L0 o0 Z% J. N  o4 M6 I            switch(ep0_std_req->wValue &0xff)7 l0 ]2 j. G3 j: E
            {
$ L" ]. H9 Q5 k1 O) g                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
/ j0 S% t# A5 l3 u$ z                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
. u8 [' V' [5 V. [                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
1 b* l" ~& Z% J8 Z5 j8 q; C! T                default: return 0;
9 Z0 H3 t9 N2 |  H. M$ g3 n            }
+ a9 O  I, w# ?8 _6 S; Z7 x5 {9 s; y            break;6 O# p* \& e4 t7 C# q/ Z
        case REPORT_DESC_TYPE:
* e4 I6 h7 }6 a  b- P            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));4 ~5 F0 I) b2 F8 X* l6 a
        default:
  P) Q$ @8 H, y  l8 j2 n) ^# `0 g            return 0;9 V: Z5 r  {6 z) g' M
    }
( e  a" t3 d: j* P7 h: }" a}
% @, k( a' j  p# G6 E' r下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.6 A, a& C4 C3 w; v$ e3 e, Q
void TIM6_DAC_IRQHandler(void)8 Z3 ]7 u- S* S# p
{. i* z# P9 |% x5 U
    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
! m; Q& d- H* H* Q+ |! H
2 l3 N; i/ n2 W% j* X. x
% J) d; [3 q* G( A# L7 p
    TIM6->SR &= ~TIM_SR_UIF;+ x3 Z5 r7 ^+ W$ _3 f" ~
    prev_key_state[scan_row]=key_state[scan_row];& J- q9 C8 ^, {- D3 C# V0 T
    key_state[scan_row]= *PA_IDR;   // update key states) M# ?! F# k# x7 }& D0 C6 L
    switch(scan_row)
8 @7 W9 l0 b1 Z* R    {: X! ^% Z2 d. J  i
        case 13: // next row PB14
3 s- b# L* S; |9 @9 I& y                GPIOC->MODER = GPIOC_DEFAULT;: P8 W) Q" I' O! z! d
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
: ~) ]* j1 a5 }; M- J                break;
9 N/ G& p8 ^9 q6 N        case  0: // next row PB151 D3 H; f8 ~8 ^6 p
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;
0 i* }0 Q7 e2 x( a                break;# I- y* Q2 z- L+ }! y7 U+ o6 h. o
        case  1: // next row PB30 d; M( W6 \) j* Q
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;# `# x3 @0 z5 }4 g
                break;& K; p0 |/ U/ D# E/ f. U9 ~6 j
        case  2: // next row PB4
7 K" P8 D$ ~2 g, x                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
: G# b1 v# x+ s1 ^- T                break;
8 r! p+ W! v& e3 H1 o% s. T7 H3 B; c8 R6 }        case  3: // next row PB5
! n. x4 m8 Z( }" w; Y                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;5 x8 I" [1 z% W2 y% e( L- z, H
                break;% z, Z; k% s, j; A3 d* S
        case  4: // next row PB6
9 L; a% k: _# ^6 L                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;8 q0 p; K/ C/ Z
                break;
' l" P5 }% s- i        case  5: // next row PB72 O4 X: }  @8 e
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
' a" |) ]' t) c& e0 E/ y+ \! o                break;8 m( {" g$ _  N# J( ?
        case  6: // next row PB8
# }  ^& d* U' b( ], v                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
& R  ^/ _* l( Q( H. `                break;
# B) @6 a8 P" G1 m# y: S% Q5 }  @6 F7 x        case  7: // next row PB92 h" ^5 M9 Y" \% p
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;% O/ j2 M& U, t) r
                break;
6 p$ {  @4 ]* y9 C; f- a' ~& b& i3 a        case  8: // next row PA8
2 v/ Z8 d) F2 @9 n                GPIOB->MODER = GPIOB_DEFAULT;8 {4 Q1 d% z8 E
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;: I$ U+ t) M- m, p1 {# L
                break;
- L' I' @1 s; u$ W  K7 C, s        case  9: // next row PA9" f; {+ D; O# |
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;2 N2 ]  `6 c+ f4 u9 ?6 i2 j4 i  O
                break;
1 |1 q2 b- S; b% y' p        case 10: // next row PA10! A) Q$ ^/ {& Z+ ?) [
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
3 h3 u  }: B# M. m/ s$ b" n                break;5 K* ?1 l3 C! k; G
        case 11: // next row PA15( [8 D" R9 N8 x/ @: ^" F
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
  S, u$ D! w$ k  G% P                break;- R- N3 m$ f7 v% ^; D$ c
        case 12: // next row PC13
' R" q6 X2 P6 [9 M/ E0 @, I                GPIOA->MODER = GPIOA_DEFAULT;8 c, h- u2 C3 ~2 B) \
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;# g: v( V5 r1 e* x4 q" D2 [
                break;
8 c1 i. i* \# z$ Y1 }    }
: |' o/ Z  D) |- M    if(scan_row<13)8 S3 q& z: ]9 B3 X
        scan_row++;
" W) j# s$ D; X, i; @7 J+ J; v    else5 I! F4 H" ]/ b) S
        scan_row=0;2 x, X( q! d; G6 }) \) s
}
- S9 e) b, ?" Y/ o- f9 ]7 v  e: V6 C) }$ y2 C4 R

" {% @" p9 r& [5 [; p( S/ ?; ]8 ^
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 ' m( r, J- Z9 x: r6 }$ f

7 i- Q6 D$ w% D扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。- O. k/ g. Y: n
" v. ~7 K7 h: u( K" \! u, l) E' i
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
) P$ ^7 n! I- o- k4 S6 n* E

9 S$ R+ e* |& X4 L: T3 H' X
" w$ M1 U# F7 {" Tconst char hid_keymap_qwerty[14][8]={. f. Q1 g  b- P+ |* h/ r) n
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},4 V0 {3 }$ c3 h2 w7 a0 C# [
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
# g2 D2 J" ?( ]4 a( Y) q1 G7 K# f# O    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
8 {9 U  s+ g: p/ g6 @8 C    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
) o- T! n$ s- X8 f: p" w8 R8 ^    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},7 g7 j- H0 L1 l; `7 j* P" n
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},9 ^( G/ N  d% n" H$ B& O1 {& f; g: b
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},6 @2 G6 {* {6 T+ y
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
% f8 z5 p6 r- O& P7 d    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
( r+ \) x/ B% z. e: m0 i$ \) ]    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},2 b+ r: M. @5 r( I- p" S' V
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},) T1 L/ b: |. H4 S, R
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},9 t: X, p- g& I' @$ D! c) n
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},$ C  V6 @  T( h4 u1 w( B
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
- s4 u% ?) [! f2 B7 U};1 j3 P2 c. T4 q* U; l0 v

- ?. Z- v9 Y6 w7 Q. u3 Y

5 p, g/ H7 g0 G' D& K0 rconst char hid_keymap_dvorak[14][8]={
/ u0 U6 v, V' q( r$ S    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
* z3 P/ \% h2 y3 G) C( w0 f6 L    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},$ |: A' ]/ D6 j1 u8 t9 o7 {- y0 I
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
* A; N$ t* _* m8 {: y9 J& K    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},6 V- y) a  w  U+ I2 H
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
/ `  w8 \9 y8 g0 ^0 L9 ~    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},# _8 M0 I. q2 v* I. r, j1 P
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},' j5 X1 ~( x3 x% S
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
( v/ D& M3 q5 B7 |" s& x    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
( @" a$ E7 K8 g6 B+ `  a! {    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},4 [1 d+ o) d2 h0 N; c: J  R% l+ ^
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE}," o4 h5 e8 L. T* I6 C$ i; Y
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},7 x  S; s/ K. c( X
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
- o- L% w2 q+ m- ]    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
! e& Q+ p8 Z# |. J  u};# o- r: ?7 c/ L3 y, J1 e5 ?

" C3 u9 `/ j0 f+ H6 n8 G
5 j7 K) H. \; i上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。0 Q  p0 ^* i% p6 Z, D  `

. b  q3 s' K: t3 |# v5 p/ Q+ ]2 KHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
; ^) \- ?. R9 a" e
/ ~: j& q1 c) z7 g( A2 d
void update_key_matrix(char row, char col, char onoff)
  N) q! J+ E& Q& U* P{% U/ i7 M8 x4 N2 ?+ Q9 n- m! ~
    static uint16_t hid_report[4]={0,0,0,0};$ w; ]) a1 X9 d, F: O
    static char (*hid_keymap)[8]=hid_keymap_dvorak;' Z% z% V5 N; V& ]' J5 c
% H7 d  A( U" F$ d5 i4 J- e

" n! P% H1 k  X& _: a    unsigned char key=hid_keymap[row][col];. `. f1 X2 U7 m% |6 ?3 p
    unsigned char *report =(unsigned char *)hid_report;
: {8 i- Z6 a6 a2 ~% p. u. ^  P2 _    char i;* C$ B9 F8 }7 Q+ \. L

" c+ R) @  ^+ B) P) W2 |3 [

  N1 L/ v1 u: T9 l- }    if(key==HK_MODE)5 h& }0 P/ L  T  R
    {
: k% \0 f" X! j9 B' O        if(!onoff)  {+ _5 N; k- {' i7 d7 D' I$ q9 R
        {" L3 J1 j. I4 W" D: Y. T
            if(hid_keymap==hid_keymap_dvorak)3 R$ b: @8 z0 y. |- k' B
            {* ]+ n" b: U% W! y
                hid_keymap=hid_keymap_qwerty;2 H3 }1 y' h  _1 d- q9 ?$ |
                GPIOB->BSRR = (1<<2);
& k* X& [$ J3 g) `            }
1 n$ L& q" w( E  O- t+ r+ @            else/ m' V% _8 F7 c
            {
2 C" u7 l7 _3 b, l+ b1 [                hid_keymap=hid_keymap_dvorak;9 ]# g; o% s. w. J- U: z, }6 h; s9 n
                GPIOB->BRR = (1<<2);
& ~+ Q0 H- N! b5 M            }& G0 H" [, ^+ D9 Y* W& t2 l) R
        }! K9 y, N+ @. L- \) z0 ~
        return;
4 p8 @( Z) B! a9 A: Q    }% a6 i; i! H/ \4 |0 \% _9 z/ O  W

0 m2 t% ^2 {( C0 M  p: i9 q

( S1 O& r* v  J& X0 ]    if(key>=0x80)   // Alt, Ctrl, Shift! X2 f; F. M& c2 t; {
    {
1 l8 N+ h% \6 K2 [3 f        uint8_t bitset = 1<<(key&7);) e6 b' [% {" ]3 o/ V% }% b3 R  H4 L* X
        if(onoff)   // non-zero is key up
8 e. S) e5 n  I& E7 r            report[0] &= (~bitset);
) S+ w; @* |  H8 Y" Z4 I        else; f8 l! N$ h' g9 N, K; n. R
            report[0] |= bitset;
% F0 \9 {( I9 _6 d  P! o    }
9 T+ N. V. j4 K; A    else
( i, c& D; f5 X, v8 i, m% Z    {
0 ?. t$ J9 g1 \. K, F. `  I. |        if(onoff)   // non-zero is key up
9 O' E. X( @6 h$ Z; y8 k3 B2 i        {6 O" h- G4 I7 C) t
            for(i=2;i<8;i++)
" V$ {/ v( |+ K$ v) R0 M            {3 W& u+ e' j5 l7 _1 z: W7 l
                if(report==key)7 d5 J7 s# W3 Q! Z. Z
                {
: o3 t/ T/ }& i: R2 [                    report=0;( m( S2 [# ~* X) b: n
                    break;
& X* e3 N# Y. X* h                }' h( e& c2 S6 y1 X6 V
            }
- A7 K' v9 X' S& F( x" Q" ]5 R        }) f4 }% V& p0 t6 l! \
        else6 G( d+ ]  }6 F' u& }) S
        {; v" O5 F. O4 n
            for(i=2;i<8;i++)4 m! Q& D/ A# l
            {2 }2 z6 b* G6 q( o' m- t% w
                if(report==key)
8 W- s8 Z: {3 i! {) |8 w3 I                    break;8 c$ H1 _  P9 A: X7 b; v
                if(report==0)( f* V! R  W/ L6 O
                {
( N' ?" L, `7 F+ c) Q                    report=key;
8 M2 @/ J. L( R1 M                    break;
% |7 k5 u% C) d- U8 X# R: w1 E" Y# r                }
9 D( m, i: p# t) @1 L% K            }, F) X7 F: ]  @
        }4 s. w4 f; v0 b( F/ Z* F) d# A
    }
; w1 v+ }& {, }    for(i=0;i<4;i++)
5 ~* x# u$ l# C, N        USB_PMA[192+i]=hid_report;
( J3 D3 Y& C! i    USB_PMA[5]=8;   //COUNT1_TX
& _& r& |* A. K    if(ep1_wait==0)& p3 y4 x4 b3 v& y0 ], K
    {# I# P5 Z" T8 D9 E
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;) O1 b& q  u  y9 g  F5 I, U* r
        ep1_wait=1;
+ y3 i$ ~& V) U$ r    }
/ Z8 ^- ^9 f$ `2 t3 x}
, {! Q' e+ n7 k0 o. O2 h7 V" ?8 W4 m  s$ G5 i4 _9 [/ f, u

! j) {, @2 ~8 O" L完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
2 E! \) |6 _) ?' ]* S+ B keyboard.zip (8.7 KB, 下载次数: 2775)
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。
# y' y1 U' ^3 X4 i6 j
回复 支持 反对

使用道具 举报

8

主题

149

帖子

209

积分

中级会员

Rank: 3Rank: 3

积分
209

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
; b) `* ^$ ~8 l; j# d9 f( d不过楼主也很厉害!
回复 支持 反对

使用道具 举报

24

主题

63

帖子

280

积分

vip

Rank: 9Rank: 9Rank: 9

积分
280
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
4 F" K. ]$ }" a& S刚开始我以为要把打字机改造成电脑键盘+ ]1 ]3 l5 g* e7 {' A( P1 G
不过楼主也很厉害!

9 a$ a1 ~& @* L' F( Q3 g哈哈  是有点像奥
回复 支持 反对

使用道具 举报

0

主题

3

帖子

4

积分

新手上路

Rank: 1

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
& W: l7 l0 U, D, \7 {# I6 j1 u' V
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2017-12-15 23:53 , Processed in 0.174960 second(s), 28 queries .

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

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