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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
6 A1 R1 P& u* O3 S2 U% F1 P0 n8 i+ z# Q
http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
# \& O4 V, A( H- n( B( e这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。8 D# A. c; X! o( D
3 n. I; x  i1 w2 }7 c% p) O/ B+ P
在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。
  F; v& U( W. D$ y+ i% V+ r
3 ^+ g% ]( m* |1 ^- ^5 M0 t
235140i3a36qivqzuvmt5q.jpg.thumb.jpg
) z& n; e1 s9 d% J
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。. b! ~/ t+ e: q" W
001734klbyoluenuwz4h4b.png.thumb.jpg
. V; N% V* O; Q- _9 D为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:
8 b7 q. v* _4 j1 P9 K1 w 003625r2agx2f5v922cf2f.png.thumb.jpg
# g, v6 x$ L# b( c" f其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.
+ ?9 O2 E. _1 ]; s  @9 I7 t3 t. i9 j4 L, GDvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
- Q5 z2 [: Y$ I
005836yvs0wvovwsssgd3o.png.thumb.jpg ) x% a' p, n& C( m
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。6 v" ~5 x+ _3 i, F
  |3 ~2 _+ s- l5 |" W
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
( P. f# s3 T' \$ v! o
% F6 s4 w! Y' H: ]( [到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
. s& k% `) s" `0 `- @/ N. q2 x* V4 a' b  U! p9 e* m
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:

$ A# {6 w  d, d. c 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
2 p5 X& H( E1 H/ p9 h5 W0 f4 n----------------------------------------------------  分割线 ----------------------------------------------------------) \6 c9 ^0 Q' S; p& \( I* g
5 B2 @! \) c9 u- X
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
- u3 K0 p# E. |+ N. ~! v" d
020011osionbunl4ui44vi.jpg.thumb.jpg 0 X2 B: g! \! J$ Z, I, [: C
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
9 u1 i# `' Q6 V7 u* y5 { 020017j8ycmnv7788bqv52.jpg.thumb.jpg
2 w" G- M: Q7 T' D. q( Z8 S* }; ?特写,80C49( o) v3 P# h0 s6 s: D
021040oujzuvtut6iujtvz.jpg.thumb.jpg
; M. u& t4 r9 h' q9 bLED部分,使用了一片D触发器锁存指示灯状态.
' {' r, y+ w0 d% I- A, }6 v9 g 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg $ I' |, G) y" i5 P+ N1 V
暴力破坏,将80C49拆掉& Q$ u" u4 q% f/ x- v; z
021113e48qq98vyohvhzzh.jpg.thumb.jpg
7 Q0 X7 b0 J0 ]" `拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
& S* l3 |6 U! f0 `% N$ Z 021125nc9az6dj33rlds2r.jpg.thumb.jpg ( L2 @! h3 S, b" Z# ?2 `6 g9 _& C
焊好元件后的板子,准备替换80C49" Q1 Z- f  J2 M7 Z
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg , i+ ~$ K$ O- z5 W: b0 c
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。
. {9 a9 e4 J8 Q1 t( ^$ G- ` 021104shifhnrqbr3o5nlo.jpg.thumb.jpg + b5 r; U3 B7 T& g  x! p# L4 M
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.( X; l, R; D% {; _2 W
022003ym1p9u4ug40280uu.jpg.thumb.jpg 6 I! v! m) Z. i+ F( z& n
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。8 o3 B6 \& v% y$ [4 o% S
023313kt141q9qajtol7ma.jpg.thumb.jpg ) _5 @- D( O& A0 L% G( Q% l
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
$ Z3 @* ^% r6 z, {' U! b2 O 023322nt7l5xb3ltttkltt.jpg.thumb.jpg
1 @. s% L- R2 B& q0 P: h$ J# ~4 F8 c
主键区键帽就位. T. E7 H6 p8 v; C, M6 i: F
023331hin88e8wkrwzwikx.jpg.thumb.jpg
1 j+ w; A; h9 n  \9 }+ p( _编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。0 R9 z: C1 `* M/ h: i- C
023336wjzlgopugg1jyy79.jpg.thumb.jpg
# R4 d; n3 E+ }- a* x最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。: Z$ m8 D% x4 ~! K: d' [
023341sffu4j3g2323h6fl.jpg.thumb.jpg / t* z  Y4 _2 e. H) E5 n
" z( y9 n$ ~4 O
----------------------------------------------------- 分割线 --------------------------------------------------
  D/ e+ ?2 |$ `3 B3 U; t3 q; k
& {! a& U9 p5 {; I( ^
, }' M; x% E  @  j  [
+ L4 V! d9 L; a# W
; E3 T7 h) J+ y: r5 j: F. H

- E' L* Z2 e( `# t- v1 C
  x  N: u& p7 M, K& M; F. [9 V
, e+ J6 I5 z* u; h! L4 k* ~
4 V  R' ~3 R* ?% v. [) E( p8 w
& C, e, `) @2 M4 D  _0 h
' N! x& A" n8 h4 B: u4 A
) j, y8 o$ n+ i, D
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
4 l, F4 ]9 h0 u9 M6 Q80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:1 T1 M8 X  ?9 n9 Q+ {. ?  Q
104025nzibm2rmiomhyirm.png.thumb.jpg
) l7 |. g! Q( R. k
  o4 I6 f4 S9 h" V( q
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
! _7 P, {5 u" S  }2 ~7 I; Z 105004zkrez5houvkkznko.jpg.thumb.jpg
6 x7 v0 G0 f" d  C' A7 }8 ?

0 t1 g9 y0 q: B+ p9 K扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
5 D, x) |" g6 i: F) h" v
& i( p7 E9 S/ K% B7 x这是我设计的电路图:& i. K2 Z7 \0 F/ e
110344ej2z2oo2rflo7oe7.png.thumb.jpg
5 b* M+ u% g+ {0 i, \3 [

, b$ j& D- s# [  RPCB Layout:
8 Y5 }/ Q/ w' X3 W# I! e 110847jjbjvt34vwt3v5bb.png.thumb.jpg
* h! u' B; C. \8 B+ o
2 a5 ]. z/ ~( c% C
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
7 q. o( }# Q* k1 Z6 j6 m; O0 P/ r. y& h
2 U1 @! b% H+ w
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 6 V, f1 N& d5 Z5 g. ?4 `4 R

9 d9 n- P, j( x5 s软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
8 l4 d+ B4 Q6 P: [
4 x# }% ]* ~2 j: R  h) |0 {总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:, |/ d" C* M1 i% g, g2 I* ^
113818pmrfsb6z0byt6t06.png.thumb.jpg 3 k* y$ t6 @- k7 s
% u! l' X+ g7 ]
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)5 r0 f; E/ N+ F6 G0 n

+ _$ g! j, f8 [8 ^! c+ o. ]USB的中断ISR,bare metal哦
8 K- ]+ |5 i. P( ?
! j% V4 N5 W9 T
void USB_IRQHandler(void)2 g" I6 s  i( A  r3 W& h
{8 p( L; k- a" Q. B" H+ }3 C1 z
    if(USB->ISTR & USB_ISTR_CTR)
4 o( E8 e2 b. Q! r    {
) U9 g4 I# }" Y: P7 D. w( q' s        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
0 R, @- m. a2 W$ ?        {, ^  V2 T; X" N/ M- h
            switch(ep0_state)/ A) e$ c3 P/ x+ D6 X/ |
            {: z5 k7 \% Y' v. U2 f' \
                case 0: ep0_state |= 0x80;
$ V+ R, }( Q) b& k2 r                        break;
  p/ {9 R$ v% `, w, @5 _9 d# T                case 1: if(USB->EP0R & USB_EP_CTR_TX)1 v+ [, v9 p2 `
                        {
$ E2 E- K' i; g1 F7 v! j                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;4 c/ d( O* R& ?: {3 t0 Y
                            ep0_state=3;7 @0 ]' g% R% A  Y+ {
                            return;# A* ~; K$ t0 Y' Y$ A" P
                        }. `( l7 Q8 a6 ~" W; ^
                        else& |3 u& X6 E& [# p- k, }6 {: F
                            ep0_state=0;$ T, y2 j- R& {+ o" t1 d
                        break;
6 n% L6 @! ~% i9 {% D                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
7 R4 q8 K+ j8 C# j0 O. ^, h# `3 ?                            ep0_state |= 0x80;
- s6 q( j/ T# K* G1 s( B% h9 C                        else# r$ Y- n4 I( y3 y
                            ep0_state=0;
( g/ I. ~; C! E  g8 |1 W# n                        break;  p9 w* S9 v" ]5 s& G4 x
                case 3: ep0_state=0;9 q. @* o" ]! d2 S$ i- p$ y
                        break;8 |8 x" @$ c5 G8 P; W) l, J
                case 4: ep0_state=0;/ Q1 c! B4 y  t
                        break;. f+ ]" q$ [& R5 Y, m
                default:ep0_state=0;
3 [8 L, k+ }1 e5 T* g                        break;
7 b1 j4 p; O( \/ r- z# j            }
0 X: @! \& I4 E. ]0 c! [            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag  C9 X# \- N. i
            return;
; G- F+ U1 z4 I! c0 k5 A0 g        }
7 T/ [0 ?" G. Q! Y6 X        else    // EP_ID can be 1( @. D, Y7 w  J) V
        {
; w. F( ?! d0 q' ^0 p9 F7 v' I2 b            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag, G$ ~) e0 B  {. j3 b  N% ]
            ep1_wait=0;
" t. R4 w, V; ~: j            return;
, @# l1 [$ \& n3 Z        }
, {; f3 r# [& x5 N/ Q, V    }
" i- j9 E' ]0 m# g  t# g6 L    if(USB->ISTR & USB_ISTR_PMAOVR)& W& Z2 f8 |8 d, y' ]: K: b( ^$ }
    {/ \* Q0 S. u) h; q, j) O
        USB->ISTR = ~USB_ISTR_PMAOVR;
4 U- K# i, ~0 `& |    }
! j6 O8 L  v: _. w; d  _2 Y( D" n    if(USB->ISTR & USB_ISTR_ERR)) u( o; X# r% @# p8 E: H
    {
0 o4 E9 W, k! {! W        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear* `/ r7 `; m$ m5 l
    }
) q' O$ {& C& m! K; `: s    if(USB->ISTR & USB_ISTR_WKUP)
9 G" d( y9 p- ~. W    {7 T5 F3 `3 C1 i2 x. p
        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
, j: N- O# u, @& n" g, q        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
7 h1 w' L, l! M2 |    }) M% Q# f! O' \  _5 Q9 Z# x0 h
    if(USB->ISTR & USB_ISTR_SUSP)$ S4 @* l" Z, ]* x: S5 |, l# a0 X
    {
" |1 q. o8 t, `7 h; Q5 z        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
  y# G. t( V9 \$ Y' E        USB->CNTR |= USB_CNTR_LPMODE;   // low power
' j  f4 p; A% j5 f4 ]        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
* l5 z( E; }* K! v$ |' y    }
( G" D' j6 u7 p# \    if(USB->ISTR & USB_ISTR_RESET)
' Y1 I" d5 X1 ?; n4 l9 Q    {$ k8 \# d0 o. A+ [1 C3 d, {5 |
        USB->BTABLE = 0;    // buffer table at bottom of PMA
9 X) C4 i6 c/ k+ A3 q        USB_PMA[0]=128; //ADDR0_TX
! p+ [+ h2 ^. A$ W1 }- C5 E        USB_PMA[1]=0;   //COUNT0_TX
$ e0 s2 D. _+ E. D. f! r4 e9 R" }        USB_PMA[2]=256; //ADDR0_RX
  E& D) a: g  V, ~1 F. N* O  g        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes' p$ S: e0 b+ o8 ]
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;5 x( V! K: \! r  S* C0 h
        ep0_state=0;
" A) m* m6 z# Z4 B; r        USB_PMA[4]=384; //ADDR1_TX8 q, f' w- T5 ?" s; o
        USB_PMA[5]=0;   //COUNT1_TX2 u" h$ w* Q: j  _
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type/ `% c/ g+ ?0 T2 E! A7 h3 \
        ep1_wait=0;$ A0 j9 e# l  ~* m3 e2 {
        USB->DADDR = USB_DADDR_EF;      // enable function
( [: D* V. G* K' f- w( _& s        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear  F& ^. e/ M" u. j1 |: X  ?
    }% L3 q( @5 k. w% [' X
    if(USB->ISTR & USB_ISTR_SOF)0 h1 d& e5 V$ [5 J+ _7 R
    {# n4 b% L- N0 K2 J( e
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear$ }5 g. r0 {- o3 e/ q0 i
    }
5 `  Q7 Z; z: M+ n( |2 j}
4 n% N' X) a% w9 L9 m2 Z6 J+ b" {) P- ~9 N. A: N
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。4 G& @! v* V. }5 O; P1 N
. d; G8 F% m$ m/ ]: }
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
  G* ~  m" O; v. n' V8 C: q: t
, `: ^# w2 e# T
    while(1). P5 n% }3 e: I( [# r
    {
% b& [4 b! P/ F0 w! P; C        static char row=0;
, g% \6 L! j4 I3 f! g- [% C        __WFI();
" h8 x  ~% q3 X1 l6 e1 m0 Z2 g        if(ep0_state & 0x80)    // request data processing
& l% W9 y# o" p8 a4 i) j( p        {. b2 B! ~- ]& O
            if(ep0_state==0x80) // SETUP phase
/ Z9 q0 v% G, f0 Y            {
& O: O  {, ], a% v/ T+ Y! i$ F                if(!setup_packet_service())
+ d; I4 f+ k) Y                {
6 A& ?9 ]$ F" n9 l, O7 k                    ep0_state=0;! `1 K8 c% ~; I+ }- K
                    // not supported
3 @1 H2 ]% o8 B+ f& z                }7 A$ L8 w% }' }( u6 [: o' D
                // ep0_state should be set to 1 or 2, if processed0 x* v) i9 @+ {9 B
            }0 c1 [8 Y' d3 J4 G2 i) O
            else    // OUT phase* U& q% s4 G1 z5 o, \5 L
            {
) J" u" n! k$ Y$ w2 Q6 L1 ~& o                // process data
0 ~. n3 v0 W5 Z) W) o9 z                show_LED(*(uint8_t *)(USB_PMA+128));. V% t7 ?! K: S* M3 x7 B
                ep0_state=4;
; R" t0 m* L' ^& d& i8 U  p                USB_PMA[1]=0;       // Zero length DATA0/ p* b  c6 o9 `# Z  l* j
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
) q8 R$ _" H% u+ e/ w! o. v            }
0 S0 W" z8 H3 a1 A( f% j# d* C        }- S8 R- o. _0 z: N9 ^& D# L
        else6 b, u& ]' R  _& s5 R8 @; y
        {4 D, }3 ~# Z2 U. a2 k* h
            if(usb_address && ep0_state==0)6 E3 F/ ~% F7 H0 N! C( @' F1 M4 @# H6 j2 }
            {# d( Q9 r6 ~0 ~6 X0 }1 n( k
                USB->DADDR = USB_DADDR_EF|usb_address;
1 z- s$ X2 S+ i& q3 K4 u                usb_address=0;5 a+ p) y& o& B9 G
            }
" M4 ?! |1 }% W, j% Z- n6 O        }
8 |& ]5 D0 d# Z4 W        if(row!=scan_row)   // new scan line
( ?' Y& o! \/ r$ }. _7 R7 [        {/ x' }; t( ]1 `6 A1 a
            if(key_state[row]!=prev_key_state[row])& k7 y9 R7 h( P/ [
            {* s6 v$ H/ U( s
                uint8_t test=0x80;' z% }; L6 M2 G. p- ]) S
                uint8_t diff=key_state[row]^prev_key_state[row];$ e) Q! ^7 X$ d3 L4 ]7 f2 ~
                for(i=0;i<8;i++)+ \8 S5 a- f0 ]* V7 p* @. q+ T. @  q
                {
! E5 Y1 T, m( p4 Z7 X/ k                    if(diff & test)% U5 ~+ B& q9 s
                        update_key_matrix(row,i,key_state[row]&test);! {; o) b) L9 }1 `* Z/ ~% Y9 W) E/ m
                    test>>=1;/ e! b" [5 X5 t% Y$ V
                }
; C' [+ P! z. k* ~' y            }
/ x" T6 q5 ], K/ \- r6 x/ Y            row=scan_row;
/ R: _8 V- E9 x7 r        }- Q' u8 j- [9 @  t3 n7 M* L
    }
( z! D& Q9 ?1 R" g. W$ l
- E+ B# z/ o: \7 m0 `; g% j+ ?; `- \- \9 W7 d
EP0的控制传输,把用到的请求处理一下
1 t3 R" R  a/ W" [  achar setup_packet_service(void)
% g0 \1 p0 Z  r, h$ C/ @& Y{
" r& ?/ q+ m. K* Y    if(ep0_std_req->bmRequestType & 0x20)   // class-specific/ K, [; `  Q: \9 B( n( }
    {; e. R" X" i7 }, G, E! T
        switch(ep0_std_req->bRequest)
, i" p, k# f8 f; B' k! A# t  I        {
, W7 e8 o& E5 w5 ^* b8 _& Y            case REQ_GET_REPORT: break;" E9 ^" J& I3 Q$ L$ Y+ L
            case REQ_GET_IDLE:
. n& N2 x' T+ A3 ?) s                USB_PMA[64]=0xfa;   // return 1 byte1 b$ B9 {7 D) [% i3 Y5 n5 b1 W' r
                USB_PMA[1]=1;
# r5 z4 N4 w# Y" [                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
" q/ m/ d6 N& F" F; `4 M+ u                ep0_state=1;, ?8 Q) Z! {6 H+ d* {
                return 1;: d1 m( [8 f  E- o
                break;
- Y0 Z0 U3 X% s& S5 L& D! d            case REQ_SET_REPORT:! {0 h& i9 B0 I" }1 ]0 y1 r
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
! {6 f9 j  c1 o$ }3 d# [' H                ep0_state=2;
" H. K/ }' e6 A& Y/ O5 O; s& L9 e# x                return 1;
* u  y  P: z+ h2 ]( N# ]                break;
, w# @0 w" N2 {' B$ J            case REQ_SET_IDLE:
  v5 {, n; q2 }0 Y/ {# n% M+ O                USB_PMA[1]=0;   // Zero DATA
! D2 t; S# ]: Z' J0 i! G                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
7 G; q  S5 [9 T1 W) T" N8 B8 h                ep0_state=4;& G/ K( b& t1 t. g
                return 1;
  p4 d+ v- k( y: C2 ?% G8 N                break;5 A  w% x: R% N& f) ~- d
        }  M3 `/ A2 ]; q& n0 |0 Q
        return 0;& a- \- x7 D# L+ Y6 N6 d  {  n# _
    }/ A4 _9 v& F. V( c! L0 t( x7 r
    else    // standard. K& ~% Y. ?' Y6 N1 F0 i
    {* J/ _6 F! o% O/ N
        switch(ep0_std_req->bRequest)/ O% a) K: n6 g0 ^: _6 ^
        {
, g( ]$ M, R* P( }* e: _' {            case REQ_GET_DESCRIPTOR:
$ @" }1 X( h$ M& d+ E* I- p                return descriptor_service();: Y- U6 g( |' @; E& Q3 Z
                break;
0 k% u0 |$ r+ I" b1 F            case REQ_SET_ADDRESS:
5 k/ r# j& l+ N* B6 u9 A0 k& P                if(ep0_std_req->bmRequestType!=0x00)( U* |; r5 m/ O2 h. l
                    return 0;/ `# N  F, F; C+ y- }% i
                usb_address=ep0_std_req->wValue;. v- i0 R' _" f: Y
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
- M" G5 l+ ]( L4 w& s8 N: ?                USB_PMA[1]=0;       // Zero length DATA0
7 P1 }+ j+ T; y3 {3 K                ep0_state=4;    // No Data phase/ O4 @# o/ \7 _  C
                return 1;+ |, j/ B. N+ M
            case REQ_SET_CONFIGURATION:
* |) R8 m9 m# O: g5 O                if(ep0_std_req->bmRequestType!=0x00)
9 ]+ d7 p: Y! k  a6 Q                    return 0;9 N# Q  A0 O, U. ^. ?$ t5 J1 |7 n
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
6 M# j$ k( n) X                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
: Q: B: @! x  u1 S( g                USB_PMA[1]=0;   // Zero DATA3 i6 O0 ~' o! U  g
                ep0_state=4;    // No DATA phase
' w- Z9 L& O% F! E: s                return 1;" `; Y$ z& d' R+ n9 g
            default: return 0;8 ]3 h2 G- ~; j3 G6 ]5 `# C
        }
8 B1 Y. u* G. o0 K, @+ l    }8 `) d# e( _% @* d, {. G5 k4 D) Z
}4 u6 `. s! O  p! |% F1 c
# d8 n  T6 ?0 e! M0 R% j# p

: R3 q( l6 ~% ^% [4 h4 ^
3 U% b$ |8 f2 ^
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的2 ]7 [: y7 o/ d5 i, G" C4 o' Z: g& O
char descriptor_service(void)
  s( J6 A- G: O4 l{
/ Z2 k1 y- {5 U4 W- T9 {( h; |    switch((ep0_std_req->wValue)>>8)
6 D  u2 A1 u% f; |" ^    {
) p1 k2 h+ r3 a+ h        case DESC_TYPE_DEVICE:
* M3 E7 U+ B# F! _8 D            return ep0_preparedata(&DevDesc, sizeof(DevDesc));' Y# V2 |- C; F7 u; N9 m  H
            break;
0 m: X9 M% p  s        case DESC_TYPE_CONFIG:+ K' P" c! s( Z+ E  W' S5 n
            if(sizeof(ConfigDescData)>ep0_std_req->wLength)- H/ `' i/ V( a
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);- y, Y$ d( v* T2 ?
            else- i7 L) @" g: x: x" E
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));+ W/ d) X) C1 u+ [3 F8 M( _( x+ ^
            break;
. u: N$ G2 `, G0 z        case DESC_TYPE_STRING:% q: Y# i$ Z/ y  a+ a
            switch(ep0_std_req->wValue &0xff)
; `7 @9 L" X; _            {
( [+ d/ u+ ]7 N# \) k                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
% i% S' n7 ^+ A3 ?6 L7 N) r                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));# P6 \: M2 M: }" d8 W6 v! M7 z  |/ w
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
5 w, @7 Y5 V! ]! a% }& P                default: return 0;
# i0 C- D; l2 `. y0 [9 u, a: S$ v5 m            }- y, S7 v* @( X, I- c
            break;6 R6 g' j8 T( R- G
        case REPORT_DESC_TYPE:
1 q- K- M: }" e; f            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
" n' h( P- Q$ Z( h/ \& E        default:
- t9 T/ A3 ^( S3 ]- v            return 0;) W5 c+ j1 c$ R2 E& D" M
    }
9 C- X+ N4 [+ Z* P. `4 S}2 t" e5 L8 i& s9 d' e
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.2 W- ~* |, w# p+ `% T' o
void TIM6_DAC_IRQHandler(void)
4 l  o& L5 \* Z. I  n{
: U+ r" m) ]# X# o- D    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);0 g9 _$ ~( L3 F; r$ k4 w; q0 T
) Y2 q7 ]* e  C; q3 b0 W( t# ?

# f8 [( D# u1 ~    TIM6->SR &= ~TIM_SR_UIF;' o( t6 A- t7 J  ~( w" Z
    prev_key_state[scan_row]=key_state[scan_row];) f1 k( m! X  z6 f8 C0 g. I3 n1 F
    key_state[scan_row]= *PA_IDR;   // update key states. X/ Q4 _) ^% g. v0 {
    switch(scan_row)6 v' y+ S- b& o' G/ c# Q" X9 @
    {
/ a5 r# E2 Q, N1 i! {% `4 R        case 13: // next row PB140 W! R  d; i9 z# ?) g+ U1 a1 e# ~
                GPIOC->MODER = GPIOC_DEFAULT;% O" Y5 n! w2 H  E
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
  z4 y) Y1 P! q* b0 q* c' {                break;' y" f1 f) \, o' A7 s  M8 t! _8 w1 N! Y
        case  0: // next row PB152 U) y7 L% q' W; P" P
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;
; F1 \4 z, R# Z- \  K                break;7 q4 `( N: Y6 Y$ g- w" h
        case  1: // next row PB3. _# R. G0 z. ~5 C" Z
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;- v$ O9 [7 i9 V" q
                break;% B  o- h2 o! Q; [, x' Z
        case  2: // next row PB4
) B3 F& D2 J$ m$ _4 Y# ~                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;5 i) E3 v3 Q+ N* {- F5 v6 y/ C, J8 Y
                break;
1 ]& C, j3 h$ F6 {. n" w        case  3: // next row PB5
1 a: T+ _5 u1 a9 ^1 b                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;
. w1 N8 A$ J3 D/ R- ~                break;! ~8 A: @0 l  H, w7 R0 ~
        case  4: // next row PB6, o) ^. d  Z- C; i7 ]
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
; `- R' L! I! t: t                break;
, ]7 h& q5 R1 w9 |6 X- ^1 x2 g4 v        case  5: // next row PB7" ]4 ?) h# F  [
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
" F! P! k0 A. u/ O                break;
9 F! C( ~! H8 @( Q2 S; A0 H        case  6: // next row PB8& h1 s" ]0 h& Z" G. D1 I
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;* H7 {; R8 `; P; }' V
                break;
6 P( \2 L" W; l+ k* @        case  7: // next row PB9+ y- g" c9 o, }+ R! {, |
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;
4 J- d* E9 Y+ e                break;
1 j2 c6 G8 J; |( a! e( O        case  8: // next row PA8- U3 Q" w5 h1 u* r& P
                GPIOB->MODER = GPIOB_DEFAULT;0 }- `. `+ L9 n# L# b
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;
: Y) `. w- _, `* t0 b                break;
# i& I& g3 ^# Z0 ?        case  9: // next row PA90 d8 h; b; D: i/ N% h6 g
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
* g: J! @- u$ T                break;8 x1 }$ b* q7 C* d! ^1 \4 U
        case 10: // next row PA103 r+ n  |# S2 ~1 @5 d+ j; v4 I
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;( l+ X" h. L/ [# r; I
                break;( y. b- J# \. ?/ M( C
        case 11: // next row PA15( L, ], w8 g7 ]9 v' {2 R3 F% F
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;/ ^/ m# X( \2 w6 L7 D
                break;' Z5 d2 C# v8 Y' ~% b2 `+ M3 b
        case 12: // next row PC13
& L$ H+ i, E& l  L: ?, s                GPIOA->MODER = GPIOA_DEFAULT;
; P% f$ W% C* _( R4 e, \                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
. }5 Y1 ^! @& J8 ~% z                break;! P, S5 y- a6 Z8 @0 B
    }8 {: {" @1 C8 ]7 z9 u6 p6 ~
    if(scan_row<13)
2 O5 }' I( p  ^+ R! R) y5 y- a7 Q        scan_row++;- `# r  O+ }$ k( {7 t- s' M3 r
    else, w8 @, X8 e+ V! y! y/ x! a! k
        scan_row=0;' Z  W$ r3 t  W! y- f
}
! r2 |, N* A0 @1 Q# {- {3 m$ w2 ^. F$ M  t7 _% Z
7 h0 c. I" A$ m/ I5 c! ?  O! q
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 . w- }. Y7 q, L( T

+ K' }1 \( R6 B" C扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。$ f* {" T8 ~# w( u& c. c2 R0 h
( \0 ]4 z# M9 `$ n, u( p7 |8 [
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:

8 r* Z& {( M# r5 M9 A" U) C4 W7 N4 A2 P! T3 t$ l2 \7 ]' x% r5 n  L' y( G

' R/ D8 d* z+ E: K" B; xconst char hid_keymap_qwerty[14][8]={
- V' S+ ^1 A0 b) J' @& @    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
& z* ^! c7 ^2 E    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
0 q' P* V$ K6 {; `    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},/ s6 Q$ `1 V2 _7 W1 [1 C2 D' ]* H  W
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},6 O$ l8 Y) X$ N9 @9 O1 ]- Y5 O
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
4 `: d0 y( K  R0 o9 _    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
1 N0 T# e3 ]- q9 j: ^# G! c1 C- ]    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},; j  y& X' K  l: \* {
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
& P6 p, n( |0 H' X0 O    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
* |1 M! P6 X5 [: M/ {7 z    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
8 F# i2 H( p* O) e$ y    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
6 d6 O; z4 a  a% y/ A- h9 s  g% g5 \    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
* t4 ^. V( a4 c    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
+ G4 L4 G, k$ ~0 T/ U    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}. M& I" N$ N# `$ b6 C
};
! }* R' w* o- r' ?& S1 _
: w! N; R; M3 b' S" @# ?: p
% F# b% p; \% Q  z3 g; u
const char hid_keymap_dvorak[14][8]={$ l1 ]$ s3 d$ y, i& L
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},0 l9 J1 R# [  n
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
+ ^" W% w) Z, Y2 @    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
. v& f" }( }% Z* w& I3 P    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},. x8 M& o' E: q$ \
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4}," V& l. e/ s  ?' M7 \# ]1 x2 ]
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
4 z* F; }3 w$ G. J    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
* M  W( L; [  h) k9 J# S    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},* H& ?0 ?+ n2 V3 E
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},: x5 z  ~( e' y$ e3 I7 D# @# ?
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},6 R' K/ D* q! I
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},1 ~  N! j' m. J
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
6 C9 P# h4 z7 B* U3 w2 h5 u! \    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},* |  N% \: ]+ F' {0 _
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}- p1 b3 {. E; ]( K% n2 R0 Q
};
5 b: U/ [1 O+ u$ N( B
# f& q3 a7 a4 V3 J& A
% h0 a! K! z! H7 a$ ?( s上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。0 M1 H! Q% J3 X" D) z* `! K# i$ K7 c
9 _. L! e/ g8 B- m
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
. f1 P% [- V, Q5 @' c) U

7 q  o4 Z) k# }/ B+ Lvoid update_key_matrix(char row, char col, char onoff)5 _. R# @- r; O' @
{
: o" H4 r* i* F4 {5 X    static uint16_t hid_report[4]={0,0,0,0};
! T' p+ Y8 ]& I2 Z) n$ t    static char (*hid_keymap)[8]=hid_keymap_dvorak;5 [7 Q  e. l' u7 x2 }3 n5 R

9 o5 m) v) N' u. u9 r7 V

) x8 q; }0 i+ ^    unsigned char key=hid_keymap[row][col];
* h$ i% V# \' e( P% h1 y6 t6 Y    unsigned char *report =(unsigned char *)hid_report;' l) X. p! P( w) j5 G$ d+ E- a
    char i;
2 l& ~; ^; |( c; q  x7 Q1 |6 f2 |  H5 q0 {* {# _

1 m$ H( ?% R$ K5 \- `3 F    if(key==HK_MODE)) k- L* X8 Y* v  @  ~  K% K: }
    {+ E; |& R3 I7 _0 E0 Z( C* s
        if(!onoff)
5 b2 C5 D/ F+ R# w! e1 n: x        {$ R3 p4 X* F- L1 k8 x8 S: M, n
            if(hid_keymap==hid_keymap_dvorak)- N# N8 ?) U9 D* I- c6 e# f* n
            {9 O7 l8 a$ R' I; W" h7 X! j  [
                hid_keymap=hid_keymap_qwerty;
) i7 n& Y/ o, d9 n, i, h6 B  L! f/ q                GPIOB->BSRR = (1<<2);" u$ i2 H% F9 s: _- g* l$ P0 ?
            }8 _4 T+ o* j' |# ]# i
            else
3 o8 g! ~* \+ F! P            {% i3 M& ?5 I% r8 n7 v
                hid_keymap=hid_keymap_dvorak;7 x. m! K4 o7 w6 p" @. G1 o
                GPIOB->BRR = (1<<2);
. A4 Z5 s& R1 M2 J3 i            }
9 Q; n9 u6 l4 n        }7 f% @. t% U6 R
        return;
9 L# K- J) p- F  r0 h    }3 O3 i8 b, U4 R  p/ J7 x
5 z" y" p! `+ h4 [: o

; t5 W  @/ |/ R3 X: b/ ?5 J    if(key>=0x80)   // Alt, Ctrl, Shift6 V; v. f) V6 \" W! e
    {
9 v" O5 l3 I* v. p. z! v+ E        uint8_t bitset = 1<<(key&7);. m% X- L* b* B# o- }
        if(onoff)   // non-zero is key up. m; ?- T3 C# `( z
            report[0] &= (~bitset);2 H, T6 }% Q8 [, ]. x
        else& s$ _" a- S2 \( T, U7 u
            report[0] |= bitset;
4 X6 k) J- O" T7 D$ z    }- `* _- [+ D/ g1 v$ O
    else
) N: A1 n" t, o8 `) q+ ^0 c    {
) ~7 X. [! t0 z        if(onoff)   // non-zero is key up
. s- Q4 C9 \) v' v2 |( d        {0 H7 J1 F) b* |2 Q
            for(i=2;i<8;i++)2 w9 o: D& f$ }+ {$ A
            {
8 Y# ?* C4 u* ?. S4 X9 Y                if(report==key)
; k) \: ^& X9 ^( Y7 j! Y                {
; T5 q. u4 S4 A7 T! M/ t                    report=0;
9 ^+ b: U7 O, O6 b! A                    break;3 a2 X1 N2 L; \% M* y- L
                }$ g  K( \( G7 O+ c3 B* I8 b; x
            }
) q, v( C( o/ {" G+ _. F        }0 c3 D$ Y( A! q; j; y' @8 z
        else& H) M2 _6 l. c0 l& y" k% \5 S  S/ w$ H
        {
3 Q. q6 R2 {% {# v& y            for(i=2;i<8;i++)
  A/ g+ Z; k+ S& s            {
& \$ `1 \) ~, Y0 K; o                if(report==key)
. A) `+ i9 o2 J6 g9 j                    break;/ H) K* @$ O* j. M6 t: c
                if(report==0)
/ H/ X9 O) V/ V& n( a& a                {6 b# J/ m9 B) B' W- c- k
                    report=key;$ N( l7 V2 y  c: ^4 F. w6 _* V
                    break;
1 |) O7 @! P- ?                }
! a+ x* ?, F- S            }
, F. G' n( l1 }* _3 M1 j! k        }
- s! m* h& G! P. D7 d/ l    }
+ F; u! h+ c9 L9 y    for(i=0;i<4;i++)
2 ?9 ?( h- c2 M7 y0 C- t        USB_PMA[192+i]=hid_report;
' b8 Z, U6 K6 p; T; ^4 ]    USB_PMA[5]=8;   //COUNT1_TX5 Q( W0 G5 X5 _* I
    if(ep1_wait==0)4 p0 Y% G- G: ~
    {
  G* A1 X4 l- `: }. e        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
7 k4 W: T+ a( z3 @/ W% Y        ep1_wait=1;
2 f( N) Q1 ]+ M0 Z    }' Y3 ~6 j! d9 a
}$ L0 @2 L: b2 h! t3 `: L
8 j6 U8 {0 M+ i# N
1 W1 D/ _4 p% x- X/ k
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。% [2 z& O  _2 @1 m/ W# Y3 f
keyboard.zip (8.7 KB, 下载次数: 6124) 0 v7 X2 q2 `1 `
+ q1 n1 n: w# X; C0 |( P
: |% y& W0 l! ?- n+ I
6 ^+ l+ Q/ A3 l/ {4 J
! o( i. \0 g2 T+ R/ I
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
& S5 x2 F" O0 G& S不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:481 k& t+ B$ w4 K0 u: w
刚开始我以为要把打字机改造成电脑键盘
/ |2 Y( ^) e. u; g不过楼主也很厉害!
. S; N" _4 Y, F/ u* M% }, C3 X" K+ g
哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
* F" A7 O4 n. R) l2 L+ R. Q  G
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-5-9 20:02 , Processed in 0.184064 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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