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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
4 e, m# e! ]% U6 @/ M# g  `5 R) t# _2 y5 J6 Z& e- k2 _" Y* O
http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D15 W, S' K* S- g+ J8 S  f/ z0 w
这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。
% V& s% z" z2 N7 {- w6 N0 b
* \+ J1 F* S) c8 n! A3 [在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。

  b0 Z" x, a4 K! A+ O1 W. w( h
: Y; f3 R3 D8 d9 T
235140i3a36qivqzuvmt5q.jpg.thumb.jpg # [9 k  M9 a3 U  L/ U2 |! g
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。
3 `$ X* W5 a/ v# M# y 001734klbyoluenuwz4h4b.png.thumb.jpg
1 \; ^/ q% R; p为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:- t% y0 h. s) R# J9 H) }: J! f$ x
003625r2agx2f5v922cf2f.png.thumb.jpg
0 U- ]# T2 A* W1 p7 U其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.: Q0 J% R& B" k
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:

+ f, J* C) n; a' P8 U% h8 o# [ 005836yvs0wvovwsssgd3o.png.thumb.jpg ) _/ l# h7 x8 N6 [) S3 p  y
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
4 t4 Z. S4 t3 c: b! _8 m
8 z; d, U* z, p* ^我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。* i; _6 k0 w* n9 {' \0 S" P1 f1 B

9 u# Z8 E  J- D4 S: t( t) A到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。* j+ ~# k- I6 s6 T

9 |* p: H: v( q4 W/ L# g2 f机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:

8 ~! L" W0 |" w2 b6 u 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg 9 U/ b* c7 [7 T% x
----------------------------------------------------  分割线 ----------------------------------------------------------
6 I# |2 F8 O8 J: r# u# x' u; n2 r: h- Q+ W2 f  V1 k
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
$ n9 P, ?5 w7 g2 ?/ ^) g4 F0 Q3 x% M
020011osionbunl4ui44vi.jpg.thumb.jpg
9 @# o# w; E; Q0 k3 {1 x轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。" i0 N9 P  v) l2 @& }( `, n
020017j8ycmnv7788bqv52.jpg.thumb.jpg
' q* N$ O$ U6 @特写,80C49
6 K' f+ L$ ^! c+ `/ k6 R 021040oujzuvtut6iujtvz.jpg.thumb.jpg
$ z( L& ~! p. K1 X5 kLED部分,使用了一片D触发器锁存指示灯状态.
' ~( A* O' i# ]" g$ X 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
$ v3 ~( m( e6 r1 P+ V. ~- B2 O( l6 x暴力破坏,将80C49拆掉
. e7 m, ~$ Q/ E) B' n 021113e48qq98vyohvhzzh.jpg.thumb.jpg ; i! t5 g+ V+ Z4 o8 A+ u4 ?
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。, ?/ e; j7 }; i( ]
021125nc9az6dj33rlds2r.jpg.thumb.jpg 9 z* c* ^& }: s% u: N6 a5 v" ~" @
焊好元件后的板子,准备替换80C492 r. O( w: K0 A( y7 `: \
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
5 s' d- t0 K4 g0 L6 l& F* Y用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。
5 V+ {( c$ f# T$ n0 c, [* N1 A, t 021104shifhnrqbr3o5nlo.jpg.thumb.jpg
& Q# J. F& v9 {+ L这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.: p2 c- ?6 k+ o% i6 U! z3 U
022003ym1p9u4ug40280uu.jpg.thumb.jpg   h$ y) i0 G1 l, s
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。4 ~1 u& b2 k0 ~
023313kt141q9qajtol7ma.jpg.thumb.jpg
) {( g9 {; y) t- {$ B我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。  g  r7 g5 Q. ~) j1 I3 ^
023322nt7l5xb3ltttkltt.jpg.thumb.jpg 8 K) ]9 q9 p( Z- q, C8 w
主键区键帽就位2 ~2 {8 g+ l" X6 O6 x; w" h
023331hin88e8wkrwzwikx.jpg.thumb.jpg 0 m% C6 {6 R& `- A" k& ]
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。) n; S3 x( }# a2 ^2 ~* n6 J
023336wjzlgopugg1jyy79.jpg.thumb.jpg
  {4 V. I9 b+ I- J5 R最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。
% G# o% G: Y* ]' Z7 ?; [2 Q 023341sffu4j3g2323h6fl.jpg.thumb.jpg ) B& ]# k! p5 R9 B" u# Z* }; P

1 l2 n/ F! {1 C----------------------------------------------------- 分割线 --------------------------------------------------: ~; y' r  L: @* P3 z$ U; B5 W

3 ]6 [- m7 N8 p' B: K

# f, V! L6 {7 n3 c8 O5 q2 r
# ~* Y/ y- e+ k: B
- ?& J/ a) S( i
. g1 B0 s1 @7 y8 t4 S- O0 P  B; [

/ ^- |9 j0 K, W' w) h9 ]6 k2 ~- Y9 ^5 C* S! E9 h; q

  W7 ?1 Q! i# p) e8 u: ?& j/ t/ d7 m* S0 J2 |

# k. K5 f9 T. h9 k; o: m/ _
: r& V  H% }1 F
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。( u/ t; n# ~) C
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:1 n- I9 M# s0 s& ^$ j
104025nzibm2rmiomhyirm.png.thumb.jpg   y2 q4 @; h* a. b
4 f/ K7 v% ?. v/ Z& d0 _/ A
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)" Y+ S: w0 o5 N( c' Z
105004zkrez5houvkkznko.jpg.thumb.jpg + u* W" C0 i' @  H. Q$ h1 D

  @- h3 h& S: }: ]- ~" q4 ]5 i扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。3 k8 K3 }+ M9 {0 ^! u! n

6 D, }! N* ?4 Y这是我设计的电路图:
, P* |7 k/ K/ j$ G 110344ej2z2oo2rflo7oe7.png.thumb.jpg
. H; `8 i" x; w  @- ?

8 q, y1 B% ^/ }PCB Layout:
9 U+ z1 L: E0 a) | 110847jjbjvt34vwt3v5bb.png.thumb.jpg   n/ [7 \+ G, D1 S/ R0 f: V
$ k7 D/ i' P/ a
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
+ U) x6 I' h0 k6 P7 u2 ^7 k$ x! F" d% C- H* ]* h
: v& `( @0 e: ?* k* [. D& y
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑
6 T2 [4 Y$ d( j1 V2 w" j9 n
; ]8 b7 K) Z* F软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。/ w* d& H9 n4 c+ c7 |5 ^
3 l5 K4 y  m) q
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:% \. }& B5 q" z; T( o) V
113818pmrfsb6z0byt6t06.png.thumb.jpg ; i3 j- n$ h+ M0 f
8 ]5 z  k6 I4 w& _8 h9 X
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
% o7 j7 s1 Q  Z( w1 M& D
) @. @! v. Q! G3 }$ I* f, V' n$ y% \USB的中断ISR,bare metal哦; t. x. a* Z8 @2 N; b; W
' E  e' G) r/ P8 m& w) G3 Z
void USB_IRQHandler(void)0 p* M7 v2 M/ J& n; P
{
2 }3 |5 T6 h; y) \8 F    if(USB->ISTR & USB_ISTR_CTR)
! R  ]! G- o& K8 H4 J: |    {
, {- A9 @  h! p! n# H3 I* X, \; e* _        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
  D  y4 L- V4 h# }) t1 t1 |        {
# W) S1 U' k& v+ B' f/ \            switch(ep0_state)
" p) \2 b! ~! w5 A( [( ^            {
7 O5 Z3 u- S) R                case 0: ep0_state |= 0x80;6 A0 P  o! e& q) Z
                        break;
1 Q3 ~4 u$ m( J7 }                case 1: if(USB->EP0R & USB_EP_CTR_TX)
/ x: c" ~7 n6 S4 N                        {) `" _; t. T$ Q: p! S+ x9 D3 g
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;3 A" D# n" y$ E# i6 O0 n! ?( ^$ _
                            ep0_state=3;
8 U4 ?6 G8 ~4 Z% K( N                            return;6 H9 G9 B: h6 A/ u; f3 N7 q
                        }
; D# a4 m4 y2 f0 \8 B; H                        else
2 y+ I2 y, b% v  q/ [  Q0 z                            ep0_state=0;+ Q; w: r% q# B
                        break;
' U6 _' W: }, I. h# L; j7 @6 U                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
; i( {! {' s4 a% r( {/ {                            ep0_state |= 0x80;
9 p8 A( ]; [9 Q9 e( C0 p# K$ Y6 j3 j                        else3 K4 A7 }8 h$ h3 x' P" `0 r
                            ep0_state=0;
8 u( \. a3 G8 Z( b. O; [: V( V  q                        break;6 e* ^* N" M7 g
                case 3: ep0_state=0;- M% ]1 u9 Z) u8 J7 q7 S# ?" V! s( m
                        break;6 p9 z+ g+ I! C7 S* @# c
                case 4: ep0_state=0;2 {5 X+ t3 r( j
                        break;
! Q! ~5 R; o5 ^1 W  P                default:ep0_state=0;# d  ^+ Z# U7 s2 p5 G
                        break;
3 k2 O6 E% o; z$ M            }
6 k  [+ t8 b# n9 I            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
1 G0 Q7 z( l. w0 K. A1 m            return;5 K4 x- y* F- Y8 a. c) O: r
        }
" w0 c  c/ e, Y        else    // EP_ID can be 13 s& s, N8 J3 g- O8 w' ~% i
        {
( V9 Z/ m, i1 Q8 N: ]* _! \8 i7 w- X' k9 x            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag7 D7 `- F! @$ [) C/ V
            ep1_wait=0;+ p' I% Y) R# w
            return;* U; }4 Y2 x% ?$ p' }
        }$ T; M0 e- [6 L# [: w
    }- j, U3 a4 j" W
    if(USB->ISTR & USB_ISTR_PMAOVR)' O3 ~3 G- Y0 }/ S; Y$ I. @0 |
    {
: ]- g" {5 H' {# @" e! b. A        USB->ISTR = ~USB_ISTR_PMAOVR;) l* A9 u- W- A7 x+ K0 K4 G$ W
    }$ q' T2 X0 a0 ?6 T6 [
    if(USB->ISTR & USB_ISTR_ERR)9 V( H" R: T3 V' t: Y+ \2 S
    {$ K6 v0 {' w2 x& C. C! F* L
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear( i* g; s3 r2 k8 U# M! b! h2 [$ ~. f
    }' G. A9 z& c( F1 I4 i! p/ n2 N7 k
    if(USB->ISTR & USB_ISTR_WKUP)7 ?( ~8 z- J$ J3 _- B) O
    {; g" Z, y. C& q) r* w4 z& ]
        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
4 |8 W7 p* p- B2 W7 T+ D6 i% ^' Z        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
9 S# R+ |" ?& o8 _" E    }% y$ q: Q, f- ]% V7 q
    if(USB->ISTR & USB_ISTR_SUSP)
6 M1 _, t  |  X    {8 y  i8 y) [7 o( B4 ~
        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend( O1 m' M1 F' E4 \
        USB->CNTR |= USB_CNTR_LPMODE;   // low power% Y1 r4 v1 F- N) _2 b  ]- K, p1 W1 f
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear8 w# M. Y) R/ t$ z
    }  J! |. e  H. J$ U- J. S
    if(USB->ISTR & USB_ISTR_RESET)$ \) Z/ y$ ]& H$ W
    {; a# N7 s5 x* y; w6 e  l
        USB->BTABLE = 0;    // buffer table at bottom of PMA
+ \5 ]7 {$ y% `+ I$ g        USB_PMA[0]=128; //ADDR0_TX6 D2 ^$ {4 R2 j2 D& E
        USB_PMA[1]=0;   //COUNT0_TX$ x. p$ b. m- Q
        USB_PMA[2]=256; //ADDR0_RX, Z! X- S5 B8 [0 V, G
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes0 M7 ^* J8 B& f# m& T9 L
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;$ a; f# W) o7 P, n
        ep0_state=0;% v/ y4 B# A) u5 r% i5 e* d) b7 R7 A: z
        USB_PMA[4]=384; //ADDR1_TX& J- M3 M; }( m2 k9 K
        USB_PMA[5]=0;   //COUNT1_TX
( p$ f) d) \; G0 r        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type9 H) a& |; w+ P$ ?+ l
        ep1_wait=0;
& {, A0 t. i$ [$ M2 l& o4 }; X7 i        USB->DADDR = USB_DADDR_EF;      // enable function
; |! i3 _* O0 p3 [/ ~2 N# m2 z        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear8 C8 z- |3 E$ k/ f, ^( L" W# j
    }/ L; D# O$ i$ I0 J2 Z
    if(USB->ISTR & USB_ISTR_SOF)" P; j1 Y# V: X! A
    {4 g% h4 i, d9 f+ {- W# r9 x
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear! _" r: e' i. t9 G! \4 M. V- |
    }4 Q' |$ L* Z9 }7 T. G" U$ z8 T
}
7 [& {9 O. b% y7 L3 i! U7 ~
9 D' _& }! a" C8 b1 y/ d$ \% E
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
& Y. @/ q3 w+ v" C1 |5 o8 E8 z! u, u; n; X- c1 E! Z( Z, f
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。1 C" B/ ]7 S& z3 M/ J1 {

) l/ [" z1 r  T9 f# e& `
    while(1)8 u6 A7 l5 _0 `
    {0 @5 _- L) S: p9 y) A9 Y& o- A
        static char row=0;
3 Z3 y6 v$ g; |        __WFI();1 a, X( }# ^6 r; ^4 M
        if(ep0_state & 0x80)    // request data processing; c+ d) u& O  m  v2 q
        {% V  I# f* N' o# g5 b0 k
            if(ep0_state==0x80) // SETUP phase9 m0 c1 k6 q5 K. _
            {
6 N! F; Q0 j4 K7 N) x                if(!setup_packet_service())
9 ]. h6 m  @; V8 n  N' m                {' t! {; \4 |9 w/ \& W7 F
                    ep0_state=0;9 Q. C. [: G* x! Z0 Y: I
                    // not supported
. ?) l2 g7 p  C2 G                }
! _+ I7 T+ k& c6 ~) r- D! a                // ep0_state should be set to 1 or 2, if processed+ c  X0 D" {. y! V9 O$ d
            }3 N! E1 R# \4 Z: b' _; a- x! T
            else    // OUT phase% h# }1 [3 O* b
            {" ~; F$ p5 T; @$ G& u7 [# s
                // process data- J) o2 Y% |3 u' ^  c7 p
                show_LED(*(uint8_t *)(USB_PMA+128));
2 w: h, A: ?. I                ep0_state=4;/ d/ `# q4 Y0 ]9 t! D) c
                USB_PMA[1]=0;       // Zero length DATA0
% p8 F$ {: q+ U/ f+ ]1 F" V                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
! r7 K6 }$ G, p& x  z8 @3 t            }
3 e. M# U+ J) P6 ~* a  k) m" M        }8 e, f; k2 f6 R% N! e' J
        else
$ k6 h  I+ k; R  l3 V        {
. @% V! h7 Q4 @4 H            if(usb_address && ep0_state==0)
4 g0 f8 h! J/ q# @. N2 _  X3 F            {
; ^, _! W3 k* N" H2 q9 Z                USB->DADDR = USB_DADDR_EF|usb_address;5 l! o3 n; d+ O) d$ I0 C6 y4 j2 s
                usb_address=0;" |) K% u. h+ V' Y- d& _
            }2 s' B" \% N% Y( C" \6 ~
        }' p! M: [( j' Y, U9 j
        if(row!=scan_row)   // new scan line" o* x# n! R: s" Z6 }8 ]: @
        {5 J# ?; r( C/ h8 B0 m, l8 B* f: Q
            if(key_state[row]!=prev_key_state[row])6 @( F) a4 I& H3 j
            {
* U( o. ~* N- x                uint8_t test=0x80;
$ b8 p) I* @5 `4 K9 y' t0 p                uint8_t diff=key_state[row]^prev_key_state[row];/ e; E1 y1 x5 `: G0 i: I& s0 J
                for(i=0;i<8;i++)
6 Y& V  d0 d# G                {6 J! I# S( b! l+ ~
                    if(diff & test)
4 \; P8 V, v( l+ n+ {5 U                        update_key_matrix(row,i,key_state[row]&test);/ w1 R) H; v: e4 F! S' E, V
                    test>>=1;% S% a# c; _/ j" v' L
                }6 q9 g1 q4 L# ?3 b
            }" I/ J5 U( U' t: B4 |9 ?
            row=scan_row;3 X9 N9 w0 n; }1 A3 P
        }
8 U/ i; s2 e8 k9 A# Z4 F    }
& Q% P2 E1 G! J4 `" F7 C
! k4 k+ I# y! w) _' s1 O2 j1 h
EP0的控制传输,把用到的请求处理一下
; Z8 u( }5 j7 ichar setup_packet_service(void)3 I7 P! T; u' o/ o% h6 j3 q, u4 A
{( A4 j# @7 T3 w7 _1 w1 ~6 ]2 e( c
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
, Q& l; [3 Y0 `    {
# A& H1 G2 h, w- E' V  @        switch(ep0_std_req->bRequest)
' Y3 V7 e8 r3 B. D5 o$ L: T        {8 u" |- K! S2 ?6 T4 N% }3 e. R4 ^
            case REQ_GET_REPORT: break;- q7 }: O# d  j/ F9 h
            case REQ_GET_IDLE:3 L. o3 e7 W! f
                USB_PMA[64]=0xfa;   // return 1 byte2 ?* F+ }/ [+ c- }
                USB_PMA[1]=1;; f% V+ d8 {/ y
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;% v+ U) A) I) Y! a% W) }
                ep0_state=1;) r/ l1 I6 {" |; e7 S6 u9 q
                return 1;
% s5 W- R( D" d' X0 z- X                break;
) n( Y1 @. c3 f9 g  |' N! `" c( Z            case REQ_SET_REPORT:
, j. J6 V6 t  ?! n( |                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
' S% J  [! p* B$ x# o                ep0_state=2;
$ x' j# n' H6 d4 c9 L                return 1;
* H" y& m0 S. i                break;" _. @) n3 C) d* m! T1 v
            case REQ_SET_IDLE:. b/ V. K: Y' d" O7 G0 F6 p" _, _
                USB_PMA[1]=0;   // Zero DATA8 D$ }# s* L& d- L% W, H
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
% c9 E) c1 e4 r% a) ~- G% O' p                ep0_state=4;
$ u! y% c; \( O0 w# f* n. N' R5 `( r& s                return 1;# `& e# `% P2 x( V5 E3 M% e
                break;3 Y- L, I& W9 u" \$ Z! z; }# V$ d8 `
        }# U: M/ l: e1 v2 H# J. ?; I
        return 0;  ?+ S, d9 J3 a+ ^% J
    }
% T- Q. f+ I! c/ @: R, K4 e! {    else    // standard) O* O# x5 U. X$ V+ Q9 b" f9 I
    {
' @$ @/ o3 n) f9 O        switch(ep0_std_req->bRequest)
- ~% M1 N& L0 g' U+ R3 K  x        {- H! i2 z$ B. f5 v, y. j& X5 n
            case REQ_GET_DESCRIPTOR:
* I3 y/ J- \) K0 U                return descriptor_service();' Q# O5 j8 O" Z$ W: `5 \
                break;. j# ?. g1 r4 @2 ~+ u
            case REQ_SET_ADDRESS:
8 D$ o* \  ^( j5 x. h                if(ep0_std_req->bmRequestType!=0x00)
$ W/ ^& a  L, }5 ^+ x; ^                    return 0;: r2 i! N1 b" |. ?6 e
                usb_address=ep0_std_req->wValue;
9 x8 E+ i  ?( ?. k! H& U: A( v                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;7 i; i: `( F- {2 x
                USB_PMA[1]=0;       // Zero length DATA0, I3 O4 M6 u" j! a, d+ _) B. Y+ i
                ep0_state=4;    // No Data phase1 T3 `% z* k+ a
                return 1;
" ?6 A) I. p' c4 c" A$ c            case REQ_SET_CONFIGURATION:
0 A/ l; @+ V  y' r# q4 {) t                if(ep0_std_req->bmRequestType!=0x00)
+ e  f. u+ a% p2 y                    return 0;/ t0 m& H6 L! Q( u. E7 Q: c# _2 y. g
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
8 a+ D4 U, d, p: Z3 F. Z                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;: f- ?- D, C% l) l$ t( e
                USB_PMA[1]=0;   // Zero DATA0 ?/ S$ x1 K6 B0 v) E2 ^- D* R* z8 z
                ep0_state=4;    // No DATA phase+ u* H) |# u) r; n
                return 1;
7 V' R; ?' g0 V8 k% z/ h% a            default: return 0;/ N7 m1 F5 y$ ~- T5 V
        }( w$ {3 C: j) ]. Y
    }- i5 ^7 P  \  p7 Y3 g  L. _$ v( A
}7 @( j3 u# d2 x0 ]
; o  d5 ~$ H' h( ^1 D

3 B! }: t3 {- W2 a
9 E; Z& u: i- y3 \$ {
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的* c' ?1 K) S9 o8 Y$ ?) h
char descriptor_service(void), @# y4 K9 [$ B/ p- D6 j
{. m/ ~2 j3 h9 a+ _3 `
    switch((ep0_std_req->wValue)>>8)
- p4 n/ @) z; N! v2 J    {
* W$ u: B1 D% m! H6 s% O        case DESC_TYPE_DEVICE:9 G% P- y( Q3 B4 c8 n0 B) L
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));* f7 ]/ p. L1 l3 K1 \! [- [3 Z" h
            break;. y, G) @6 R0 i+ ]1 u  z
        case DESC_TYPE_CONFIG:
- s! S. `8 y8 }' d* ]. [            if(sizeof(ConfigDescData)>ep0_std_req->wLength)  r% k( O# x- c! M
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);/ X: `& c& {; @; j" M2 y. |
            else7 e# E( J# `2 h4 m' t
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));& y3 N. O* A4 }5 R- \
            break;
7 V# {- Z6 m" r& Q        case DESC_TYPE_STRING:3 u" d2 D. b9 l4 V3 N, p/ U9 e
            switch(ep0_std_req->wValue &0xff)
4 j/ u) O0 @: ?' I, K8 n, H$ l            {
: K$ i; ~  f$ e' v# h. W2 C                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
2 a3 V1 ?& L8 K% Y) N! K4 p9 J                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
! P) J- l/ S: ~# }                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
8 D7 n' l# Y! S( R, F0 u  a                default: return 0;! A4 Z7 L, ]7 J# {5 Y: d2 ]
            }
# Q& ]8 N! ]& N            break;
. T+ K& J3 T9 M' F6 E! Z8 q        case REPORT_DESC_TYPE:& n4 w/ R- ^$ E% A
            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));% }+ ^1 i9 w; k! i2 u
        default:
$ B% C7 U$ m: Z" d, f            return 0;
) y" m* ~2 N, |0 f) |% \    }
$ X* U# Z# H6 O$ a+ Q% v}7 o7 m9 A5 a8 n: b4 U/ u5 `! E
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
: q% s7 m3 W  ^4 g0 U; Lvoid TIM6_DAC_IRQHandler(void)8 |6 R5 M/ v7 `- ]6 B! Y
{
" k8 n: S* M1 ]& {, H3 Z( y    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);2 a* Q# G2 I8 r! J+ w1 @
( \8 b" c, b5 M0 Y5 x- s( t

; w, q- I3 M9 k" m. ^' x    TIM6->SR &= ~TIM_SR_UIF;
6 |1 k* |5 {& j, q3 n, ~9 C% W    prev_key_state[scan_row]=key_state[scan_row];
$ r+ i8 [4 Y5 u. c    key_state[scan_row]= *PA_IDR;   // update key states
4 z! l1 Q  Z- T; d" c  e    switch(scan_row)- h, ?0 ~- W0 W% s# I9 K6 D
    {2 Q. ]1 [: r9 r' y& z
        case 13: // next row PB14
$ L. x. H$ B) y1 O                GPIOC->MODER = GPIOC_DEFAULT;4 {7 _8 x+ Y( A' q& k# T  o# K. o+ T
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
! M1 b' P, v$ q: }5 S8 e                break;1 H: G! }! s9 I) ^& Q
        case  0: // next row PB15
9 S8 S+ j- ?) U/ ]3 D- A                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;7 h/ m% X3 A3 u
                break;
; N* A, u! X/ s        case  1: // next row PB3
( g8 A  u. y6 y6 W( ?+ m$ l                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
! |% B+ S7 i9 ~- x$ F. f2 P, q                break;
/ A7 r. Z6 L  m! d$ Q        case  2: // next row PB4
: @8 s: K% v! k7 n                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
0 n# n) O8 d0 H% P1 d! N                break;
* ]! ?8 ~( p1 a/ q; B* w6 a        case  3: // next row PB5
/ Z9 {! {+ h3 o# ?  Z* n                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;4 B: J* ^! {3 A7 W- G$ v. O
                break;: s  v/ f: f6 j# e: J. G3 A
        case  4: // next row PB6
$ W8 R! P5 f0 M5 J9 h+ }* P5 A' E                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;" b9 Y- a0 {* H$ q0 {9 ?
                break;; V. W9 k& J/ [% S  [
        case  5: // next row PB7
- R* E' r! U" r3 a                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
8 j- n. j5 m  b% O  T4 X                break;
1 p& B% \! n) Z4 G4 L% c        case  6: // next row PB8/ K5 A$ m" }* b, R4 R* [
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;2 Z4 J  I% y8 H* v) {# C; u0 h
                break;
0 O1 _6 [* H1 Z: L        case  7: // next row PB9
" ^! W1 N. h- B* o9 c5 A                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;
5 Y; i0 b( T% Z; J; r; T                break;
' f' w0 R0 e1 N0 |! G        case  8: // next row PA8
" G1 i$ X0 V2 s5 V* R                GPIOB->MODER = GPIOB_DEFAULT;6 M1 }: t3 O. ~" B6 l+ k) ?
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;/ l  x+ ^- w: T: D# v, Z! G1 s# y6 X
                break;
5 @) r' {: B, I. ]! }2 `        case  9: // next row PA9
8 r; H! s- v- ^, b                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
0 h0 T0 N" w$ ~5 e" U* U* v" b& t                break;
1 U' p, l4 Q8 U2 A. f3 U        case 10: // next row PA10
5 o! C/ U" X+ ^4 Y+ k, k( M                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;8 L8 {1 |# {& W$ p4 m
                break;5 X; c* W7 J) y& i; E# H/ j0 s
        case 11: // next row PA15% V$ j- D+ w; i4 D7 W5 |
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;9 M/ p/ q% g: I9 h3 W
                break;
% [9 H5 [/ t( s! h        case 12: // next row PC13
8 _- n. B; H% j1 x1 Z* c  B6 M                GPIOA->MODER = GPIOA_DEFAULT;. U/ v  y3 O( T$ r9 D' `
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;$ ~0 r9 T; b1 M/ G
                break;
4 [$ ?7 |  a( K# A    }
1 v% |' q2 n, {) v$ {- ]7 y- r    if(scan_row<13)
1 @9 ^/ L/ J0 l" x( ]        scan_row++;% o8 A$ m# j3 n. r
    else
, ^  |+ \7 A, R! ^; B/ U: j1 j$ H        scan_row=0;+ Q( P+ T2 R% P
}+ f% f! d  K0 G& o: m" A
5 j4 [. k" N  U+ w6 [, i
* _9 C4 o( ?6 f! [+ {2 L$ `+ A9 Q
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
( F6 f" k- F7 |6 ]+ Q# }7 l1 n" K( l
扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。) t( H! ^, s; n; U7 B" D8 ~/ O

2 W! F4 g- K- R, c要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
( {% d4 w: r4 e0 g/ T, M1 I1 n3 o
- O- J4 X- {' p" r) g
  K# c3 ^8 g# B/ Y) {* y) W
const char hid_keymap_qwerty[14][8]={
" B0 B: G+ \# Z; T2 X! ~    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
, A. s# o  ^1 v1 v" |/ w* @( W- h    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},9 a' j1 ?' Z3 t/ ~' E9 t1 v6 G
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},4 F5 q$ u! O0 k' N( C
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},8 {  D- \; ?9 n" k4 Y* X
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
2 s6 i$ ?" g' A; w: a    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
' t' p$ ]7 p, G) Z    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},$ g$ b- `5 X* M# g4 }/ F$ P+ R# M
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
+ p6 T2 C, r3 s+ P  A    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
8 w8 ]) z" K  E6 q$ d. Z. K* ^    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
$ Z, s  `# S) C% I6 P" P    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
* r3 h, W, b+ }7 r0 `& t    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},' ^5 [6 U1 F, J2 n6 \
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},6 q4 F4 b/ R1 A' v6 d) ^' z
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}6 g5 z4 |5 `3 [
};
" x3 }0 t3 e: j2 q' R
  m0 J3 I; |& a/ Y7 q
/ S8 Y3 X) [7 ?; V# j
const char hid_keymap_dvorak[14][8]={* a; Z- X2 ^" {4 d) v
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
, x7 R) a0 w1 D5 {! o- q, u    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},& a: B6 M6 [8 J8 G
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
& B5 f" o4 c7 E1 c# k0 a    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
- A$ t' h7 @. s0 G7 Y5 F    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},6 N: I' C4 h7 i3 M. Y
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
1 L% a! |, V, G$ s    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
& t7 \! Y0 j: Q6 D    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},/ D& |( d& g( V  |$ b
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},  k& I- i9 \3 E! Y8 u2 P, r! \
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
% s6 A7 o9 |7 z) w    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},- t! D) t2 }6 l5 Z7 J
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},# D8 g  N( x/ N- l
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},' Y8 ~& ~' T0 O; J$ n3 j3 m
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}( D2 k4 }9 V+ ^4 P: p
};0 S% g( }- d. R8 t% @) ]6 h* D

5 l% n- ^4 u; b1 M' h, d1 I: E  Z2 S' K7 J5 F5 ~, }9 ]' L
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
7 W  V3 ?8 Z% P
5 S: C8 U0 V" X5 q* k2 sHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

. z$ [1 ]5 X" i0 A3 D1 K4 k$ W: a. P* i% V! x2 v6 d
void update_key_matrix(char row, char col, char onoff)
4 S6 |! _& i3 K. {{
. d! Q) i" V' @' \* I7 i    static uint16_t hid_report[4]={0,0,0,0};' o4 x1 ~" j- t8 r5 {+ T
    static char (*hid_keymap)[8]=hid_keymap_dvorak;' z6 E! g2 m' i: x4 {! Q8 W  [- V

1 w% G: O& h+ Q: U) e0 [6 L, V7 l7 ]
# a, |3 L2 l' |$ H9 Z2 Y
    unsigned char key=hid_keymap[row][col];
9 B7 P9 v! X& k0 w6 J7 @# b3 T    unsigned char *report =(unsigned char *)hid_report;8 I" v' j! U. u2 e/ k! D
    char i;9 q2 N' t5 J5 y/ \9 ^
+ J) |  U$ V" o& l2 \
$ S3 H! a0 [8 ]4 ?  Y$ j1 e
    if(key==HK_MODE)! }7 I' T5 u! F$ I2 k
    {" h+ r3 s" X$ C: i- V# _
        if(!onoff)6 J+ D5 p- o0 G0 V4 M6 J- @5 Z) Y% ]* w
        {! n- u; _3 W1 j' k
            if(hid_keymap==hid_keymap_dvorak)
) Z% y" ]0 |0 y; _. I5 j# y            {* y# `3 t$ h& F
                hid_keymap=hid_keymap_qwerty;7 |  H  ^3 j7 i( }# }+ w2 m2 E
                GPIOB->BSRR = (1<<2);
9 A4 y. P! ?& s6 L( Y3 D3 U            }$ o/ B% s  @' N9 T( f7 O% Y
            else! {$ H  h6 K+ D. d! i5 I6 @6 n$ }
            {
* R; ^1 q4 e' h& o                hid_keymap=hid_keymap_dvorak;( Q- Q; T5 N! T3 i
                GPIOB->BRR = (1<<2);
  |8 _5 [/ r" Y0 L            }( {' f5 S: y9 A, P, d
        }
0 U8 s" t) `1 s        return;% F! k6 Q! d5 u
    }
7 |$ j# l( d0 R  f1 @6 u9 Y
! G8 L& @  J" S% k$ l" O0 }

& J$ t4 f2 \. x% `7 C& C    if(key>=0x80)   // Alt, Ctrl, Shift
" l4 u. y% d# Q& \" i    {& W' k+ c" ^0 a% k5 C( H2 C* n
        uint8_t bitset = 1<<(key&7);
2 E( J5 \4 R2 W0 ]3 H5 a: O        if(onoff)   // non-zero is key up1 u, y8 p5 a; `- m0 y2 v
            report[0] &= (~bitset);/ v2 d; _) Z/ f' U% M. V% Y
        else' o# Q. A9 g" I: x3 @
            report[0] |= bitset;; v/ ^$ f( ]: P6 F# [
    }2 Y, y1 r9 i/ X
    else6 v6 t6 h1 a5 D: _; D
    {7 o  l: F- ]  \$ u. O
        if(onoff)   // non-zero is key up
* z' [4 N3 ~5 i5 I! @% S        {
; L; m: h- u+ @2 w6 j9 Q            for(i=2;i<8;i++)& U4 b" i- c, y0 V( M+ q
            {* o! j, w3 n, M, z: v
                if(report==key)
4 O/ i4 M; n- @                {) Z% t8 U$ Q) @. ^& G$ f
                    report=0;# H- O& i$ U+ u/ Z) v
                    break;8 U( T7 F; m1 Y* R; z7 `# H# v. ^
                }4 B: u* h) b& X& s4 ]0 |$ g( Y% M
            }
% A0 a+ `5 C* M0 u  f, d7 Y        }
; N4 P: `" _+ o. M- t) M6 ~. r        else$ ]5 T0 f) v/ m7 {0 Q
        {
, ?* o4 Z) B# S6 R& ?$ z            for(i=2;i<8;i++)
; c( a0 v" t2 P* i& G7 s            {
% K6 c& x4 F& g2 A2 s: P% g                if(report==key)
+ j( p/ g. w& V                    break;( i2 T: G. @+ q2 U
                if(report==0)
: ]* Y' q% t" J7 J- V                {; d9 x! ]9 v7 |
                    report=key;
) w. g  S& y, ]& S6 u                    break;2 \9 h, ]; C1 T  z
                }
/ x! Y( D/ \% }4 a  J: ]            }) X0 S, p# L9 `4 n4 }
        }1 s( ^4 s8 D6 M9 n$ R6 X. `& _
    }
3 `. N& t5 N* o! W    for(i=0;i<4;i++)
, ]2 ~4 ]6 X2 g* C" }$ q- w7 f  i" F8 B- u        USB_PMA[192+i]=hid_report;! L# i0 F4 A; H2 Q2 S# S6 F
    USB_PMA[5]=8;   //COUNT1_TX( {" \' g- W6 R  y% L* R
    if(ep1_wait==0)
' o( s$ [& C( @9 d4 {  O$ ^- m9 A, L    {
  [! e; h  P6 e2 w        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;: |& `. A: G! {$ A
        ep1_wait=1;* ^0 l4 D0 @, H# O" S, B/ X9 M0 ~; z
    }7 z# H# d7 I! u: N3 D
}
/ U9 J& w" z( `
- z. B* _" t+ n3 `7 E; j8 `' y& T& L; i' H7 B
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
5 z9 w2 ]! m$ I" b) @# U- [' I keyboard.zip (8.7 KB, 下载次数: 6694) ( G9 I+ Y4 f6 v9 Y8 ^! V( i
* ^8 X" U! B- V
: l; R+ X3 |( h$ ^  g6 }) i: n7 _6 F# h
1 ]8 D" f# n% s& K

- P' Z0 F1 j9 _+ U8 F  m! b
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘2 X! r; a+ b9 c+ I1 I
不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48( N! ?  z) ?! I
刚开始我以为要把打字机改造成电脑键盘; ~% ^4 \5 p* o- E$ L
不过楼主也很厉害!
. x- D& c8 b0 c
哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害, n- ~1 v9 G' }
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-10-6 07:23 , Processed in 1.344329 second(s), 27 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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