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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑 " h( `; H3 f) F9 T( W' @' g, g

5 T2 O/ k; a4 r7 t; n% ^3 T/ rhttp://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
1 N3 `8 r8 z2 _, [0 M2 B这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。0 }( `! o5 a* ~! t5 `- U

' w, u1 m9 m, ^& ?在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。
0 P: g' N4 n2 x! Z0 X  v
1 ?' P/ b% S) o/ ]
235140i3a36qivqzuvmt5q.jpg.thumb.jpg
$ A& [: k3 e. O
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。% t8 A1 ]8 m# Z  X5 ^9 B: ?; R
001734klbyoluenuwz4h4b.png.thumb.jpg 9 f( ~3 i% k% y2 s) |8 J
为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:* `, G0 r6 S8 n4 I& v
003625r2agx2f5v922cf2f.png.thumb.jpg
0 N1 J/ _6 p* w. b, @" \其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.! {% K4 U" P4 r5 g
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:

2 F* J9 H+ T: i+ T 005836yvs0wvovwsssgd3o.png.thumb.jpg ' \* t. L: a" y% g# S  d
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
+ R1 B- s& O: b( Y  A  ~' J: x) N0 v7 ]
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。, [- D" z' O5 V7 W- J, x) D

7 Q" j1 p9 C  s$ j9 _# Q* s到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
# [2 a0 Q1 H9 w% b2 T7 m8 c, q) ~; \
+ M& B' @2 @0 t机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
" J9 ~( m, p0 ?0 h6 O2 v5 k# X% P
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg + P3 m4 ^* R0 z5 U2 c: W
----------------------------------------------------  分割线 ----------------------------------------------------------
- P! O( K0 d: c' y+ G+ D4 ?& J. m3 a& s1 ]# c
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
  O, O9 d$ I- L& R/ i, Y
020011osionbunl4ui44vi.jpg.thumb.jpg * e) r9 |, u$ V
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。4 H4 J8 ]4 u4 A, N( r! R' H: P; B
020017j8ycmnv7788bqv52.jpg.thumb.jpg
6 ~, s# R- i7 D. B特写,80C49
# O# ?  k$ u+ w% i( P, D 021040oujzuvtut6iujtvz.jpg.thumb.jpg
7 k( P4 ^- R0 f! ^4 ?7 q6 rLED部分,使用了一片D触发器锁存指示灯状态.' i# o# l9 @! c( X1 o! r
022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
7 O4 ?+ ~6 {# c* ]9 ^* z$ O2 O4 P暴力破坏,将80C49拆掉- s; U- s0 T+ f+ V3 b* ?6 q
021113e48qq98vyohvhzzh.jpg.thumb.jpg 0 @3 v& v: f& l% `( w7 t. Q# o1 Q
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。9 K) u; v# P* {0 W9 W# C, b
021125nc9az6dj33rlds2r.jpg.thumb.jpg   l. h5 j6 l6 M! I# I
焊好元件后的板子,准备替换80C499 _0 V) K3 X5 P! S1 `; I
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg 6 o* g8 `- D2 }0 p# x& K2 s- o6 r7 k
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。5 o( u, D! y, X; Z# E
021104shifhnrqbr3o5nlo.jpg.thumb.jpg
0 g4 |  B; z8 D0 E# f6 m这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.3 l; j2 {$ y8 b' j) x* l* P3 k
022003ym1p9u4ug40280uu.jpg.thumb.jpg 2 S$ q: Y: N7 L4 [
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。
3 D7 g  N4 L" W1 b) B 023313kt141q9qajtol7ma.jpg.thumb.jpg 0 F. h  l3 c/ M! Y1 ^% w+ w8 T9 ^
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
' D: ]& V0 q  t3 [. ?6 Y 023322nt7l5xb3ltttkltt.jpg.thumb.jpg
5 }# r) Z' H& F$ X( T
主键区键帽就位
! Y! f9 F! d1 F  L& E 023331hin88e8wkrwzwikx.jpg.thumb.jpg
6 ^1 F1 @) V7 y, T" d编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。$ t* b! j/ }# c4 b0 w3 M- M
023336wjzlgopugg1jyy79.jpg.thumb.jpg
2 i, ]$ D/ u- V, Y最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。) Q+ ]  T* i" e/ a/ d" t
023341sffu4j3g2323h6fl.jpg.thumb.jpg 2 d2 m; {- E, u8 _( L7 x

, F0 B$ ~, X  ^" Y  n6 ?* M----------------------------------------------------- 分割线 --------------------------------------------------5 D4 {3 e7 _- w- u- @

) ^; u: }. K; I! x5 U) X  R

* Z9 g2 @7 H6 l5 ]8 D* n" g. D
3 S& |# ]- p7 q0 U4 s
) Z1 B/ J; E5 h9 {) ?
" I, X: i( F2 I. X4 k
: A; v2 {4 x; \! e8 J: |7 w6 \

! s* Z' v8 L+ {+ I: k) D! q
, ]9 ]2 d* V" R+ a5 `% ~4 d' T# K# |) k- B
; K% _$ F& c( c8 T, F4 O

, ^' Y+ z7 H8 T, |
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
+ j! X" C9 F8 B8 K7 a80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:" \1 w9 J, E- C- S% F3 y% b* v
104025nzibm2rmiomhyirm.png.thumb.jpg ; [' O# z0 j+ I, u
2 u- i* C* x* O; J; R" y; ]3 C
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
: k6 O2 s" k' f' R8 P 105004zkrez5houvkkznko.jpg.thumb.jpg
7 E. [( B' f# R! X

0 h( N4 y( ]" T+ u; P# d扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
% ~& S& i: i# d% w1 ^+ l) |  B1 v2 ?  ^. c, @$ E% E. g
这是我设计的电路图:
! h' H2 i8 k0 Z. p7 v. w! c; p 110344ej2z2oo2rflo7oe7.png.thumb.jpg 7 m  V1 h" b* _3 C0 u' m# y

9 }9 w; k) o8 D8 v9 b( r( M* J1 R: ~PCB Layout:
/ {/ V. n! q/ X+ }) s( O 110847jjbjvt34vwt3v5bb.png.thumb.jpg
2 h3 o% T7 X5 N" D

& @- G$ u3 X# r) Y8 c不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
2 G; i7 L! i% ?* w# l
/ H8 l& n, \% K( Z- @+ \

  q' t' N* G" V$ \& K7 L
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑
. }  J. u0 V9 L. d0 T4 |, q, C
8 _0 o, U) ]+ p; C! n* |# ?3 Y9 f5 _软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。+ j" M! q/ G2 S. }7 R9 E

& l7 p. G& }% O" S总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:/ c- M( b$ C1 d$ ?, }1 P$ T" N
113818pmrfsb6z0byt6t06.png.thumb.jpg 3 d3 N" ^! _) b/ B* W0 z
4 b5 A) H6 e: n
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
' a, e1 w5 W" h& T  s1 }% s5 j$ B" M1 }! n) E3 m# H* e
USB的中断ISR,bare metal哦
! Z4 L) k9 K, T2 w2 h, [' v; [  z. V

+ e% Q( d! x* e# |2 I+ ^, q/ fvoid USB_IRQHandler(void)* \$ M% [0 r$ C' Q, E: I/ [4 i5 s
{
7 V) a- y5 A0 s5 W% P- b! x    if(USB->ISTR & USB_ISTR_CTR)5 h( e% U0 Z. c1 [6 M8 B. j
    {8 W$ ]  w7 }* c# J3 N" W
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
% R2 j% K. a/ @        {
1 i- i! [- |: w7 L            switch(ep0_state)
( e4 I8 h3 g& t# Q            {6 G7 N1 {- x/ F9 s& @0 K/ ^
                case 0: ep0_state |= 0x80;
# J. @' p. z* M" D* w! ^                        break;, I9 @& z  u1 V% I- r! Z) |3 }: j
                case 1: if(USB->EP0R & USB_EP_CTR_TX)
0 _" N3 _- k7 a( J                        {
/ x. v5 H) X; ]8 ?0 w! n/ h                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
* S0 I4 W% V* N3 C1 x                            ep0_state=3;% I( S8 S6 k$ t( c6 S  ]9 _) y
                            return;6 t. t5 [  w$ y2 G
                        }
" S& M) E- c, H9 f                        else) F! J5 f/ D3 [5 n
                            ep0_state=0;! Z4 n6 |+ f: G- `& @0 M. D
                        break;
4 ]4 @, w4 O. a/ X9 Z9 f                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
, K8 o+ l5 b2 u2 D1 y" X7 n  a6 `                            ep0_state |= 0x80;& w, I5 y( ?( j! U. @, D8 e: z+ p
                        else
2 D/ `! L1 y# A8 K; V- R' m9 {                            ep0_state=0;
0 }% U! U* s1 t                        break;
4 Z$ o; W1 \& Q; w                case 3: ep0_state=0;
; z/ P% A- [( g' i5 i6 F                        break;  i" B" G+ D! J' Z4 N) N* X# @
                case 4: ep0_state=0;
8 G6 _  W" _6 Q, W, d* t3 D                        break;
. Q) Y4 u8 [7 F; c. F8 r1 s                default:ep0_state=0;
3 i; \! M6 F4 f                        break;
5 |$ P6 N/ L# {- g: l/ [; h- P0 U1 r            }/ L* q1 G% ?2 ^
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag; S9 F. M# D1 B" F- U# x
            return;
9 y' j2 i) \: r+ t6 {7 G2 {0 O5 Y        }  A# R& h) F& I, A8 U' N& w
        else    // EP_ID can be 11 r! T0 j  ~# e* S  M
        {
6 M: l7 z6 Y1 d/ j$ \            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag7 {! t$ ?. o% H+ B$ H( i
            ep1_wait=0;
# Q" y  h2 h: Y            return;
- H+ X/ k; t( t6 ^# r+ M        }% `) t9 k1 R& s/ p. w  z4 f
    }
; ]3 y# V4 V, D    if(USB->ISTR & USB_ISTR_PMAOVR)+ J  k. w9 X, z# A
    {
8 k" a. J' u% e        USB->ISTR = ~USB_ISTR_PMAOVR;  o7 e9 {# U$ J$ s
    }
. N& z8 n! J' f+ N2 {' P( Y( a+ \    if(USB->ISTR & USB_ISTR_ERR)% W3 i1 t1 i5 A; v3 ~7 R
    {4 O3 I( T% X1 O) u- y9 Y
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear/ G5 ~" \) q! P, S- a+ c
    }0 N* p5 ]( }- V& x/ ]3 E' z
    if(USB->ISTR & USB_ISTR_WKUP)7 I9 f5 ?, C9 d6 L4 u8 S( t0 Z2 g
    {
, _" {9 Z  X8 u5 j" z0 p        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
1 [* p# x- u' {5 k        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear( ^; }: \0 W, w$ A7 P7 b
    }
- p: m. M3 f: I: C# k! O    if(USB->ISTR & USB_ISTR_SUSP)
' [: Q) o% ~5 `' c    {5 J+ O  W2 ?; J7 L2 s
        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
" Q+ h7 t& S& v9 W/ v        USB->CNTR |= USB_CNTR_LPMODE;   // low power
* E7 l; b4 P  A0 u) @% _; o# e% e% K        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear- a* o" S# h- e6 w/ J% d5 X% @; T
    }$ D* _( Z: [. M: [
    if(USB->ISTR & USB_ISTR_RESET)$ @! p1 I1 V* g
    {
& ^% ]- a$ Q1 y; `2 `  a! _        USB->BTABLE = 0;    // buffer table at bottom of PMA6 N4 Z/ `3 N: g5 u
        USB_PMA[0]=128; //ADDR0_TX9 {: p, h- T8 \2 i$ [
        USB_PMA[1]=0;   //COUNT0_TX2 p- G( I0 i  @0 `' p
        USB_PMA[2]=256; //ADDR0_RX4 i. D& ]# G, b/ d( ~: W3 h  [
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes, A# [6 H, f, \% E' b  V5 d
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;1 {' Z  w- n' H' t! k
        ep0_state=0;
7 n/ j, }7 n2 B3 t. K+ _2 J        USB_PMA[4]=384; //ADDR1_TX# _7 Z8 H1 |6 f% t! q7 ^
        USB_PMA[5]=0;   //COUNT1_TX
0 N- v3 Y" c) `+ \7 V        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type% V, @# Y! ^' P* [
        ep1_wait=0;* y/ ~( ]# X# X# Y8 U$ B! {
        USB->DADDR = USB_DADDR_EF;      // enable function! f+ E1 B8 a3 i5 a/ H6 o
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
% C/ y; Z% P2 }    }/ L6 x6 _1 t# C% I
    if(USB->ISTR & USB_ISTR_SOF)
3 ~) U8 B6 \" w0 S9 W2 T' {    {& S& T7 y/ I( O; i' y
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear$ P* v& I- B. A# L: }/ V
    }
2 q- F0 z3 W0 y/ ]}
( k0 r' h; b1 F& R2 W0 U4 D- u$ _! e: i4 H1 h2 E1 Q3 e
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
) k0 Z% s) u+ v9 M% v- ^2 W/ G; ^; w% Y" H* z4 ^6 H
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。! P0 ]. {# E1 t4 ]0 E$ ~

0 i2 z& U0 b# L0 O9 _9 T4 R% H' `- F
    while(1)- S( ~# e& Y: H2 f1 c1 ?9 y8 \
    {% t4 t& p% e4 D: e- k# a1 K
        static char row=0;
. c; m  E" u/ J  G6 u, g        __WFI();
; R4 J4 F% H9 e$ Q4 {5 S2 d        if(ep0_state & 0x80)    // request data processing
# Q5 d' [$ _5 F% g1 @        {
3 A& [% f/ u: r( C8 ^            if(ep0_state==0x80) // SETUP phase
4 [# n1 P$ @6 u1 i, r            {/ m8 U0 N6 H* O- A+ b7 g, H$ _
                if(!setup_packet_service())
* `+ i- D) |1 f% l/ W% f" H! R5 u                {; y; S5 [7 U1 I/ \, x
                    ep0_state=0;- K7 z% V. C- T+ L. v2 M
                    // not supported
3 _. e5 x9 w0 r  `( l                }
. @: A! q3 g5 n( n( u                // ep0_state should be set to 1 or 2, if processed
1 V. O' H  L8 i. ?; r            }/ V7 S# k" F5 U% n' Q8 `
            else    // OUT phase
( L: t5 G5 z$ o5 o            {
! Y+ ^. V9 h& g                // process data7 ]' ~- j1 Z/ y
                show_LED(*(uint8_t *)(USB_PMA+128));4 l% x. D: ^) S( g8 _( `
                ep0_state=4;& _1 t* x1 F6 _
                USB_PMA[1]=0;       // Zero length DATA0
1 y6 d, A9 }+ i3 G0 h                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
: b% f7 ^8 \. z3 d$ D            }
0 n# ?3 `* [( r& M        }
9 r; A) \, F8 x3 h; i        else
. i3 L; |; l9 H  D- }        {  O9 I# v% Q& v* S1 B; U, q
            if(usb_address && ep0_state==0)$ c7 `- i9 q: ~) [7 G! b
            {* P! \  _5 N% n" i" v
                USB->DADDR = USB_DADDR_EF|usb_address;
/ d3 x. l: j) N2 S3 x3 U                usb_address=0;0 f% s! J5 D: w+ E
            }( x& x: A$ F! k" M9 t" f! ^
        }
- h' X( t/ Y/ x        if(row!=scan_row)   // new scan line/ n; o' O! V. N* I0 h) {9 p( A
        {
  S3 b2 d9 ]8 L& q* s            if(key_state[row]!=prev_key_state[row])
2 d- _0 Z0 [# o7 x( E            {
, w" v+ e) ~% E. F, @. ]! m- ?                uint8_t test=0x80;
: u! @* k* w3 l/ I5 [) t: G                uint8_t diff=key_state[row]^prev_key_state[row];  e% L/ {/ H  [- g. J1 ]) p: V
                for(i=0;i<8;i++)
! Q. G# Q; b. A/ P                {
7 E  d  H! `/ z# o                    if(diff & test)/ q5 n6 Q$ j4 v7 W
                        update_key_matrix(row,i,key_state[row]&test);
7 z$ |* q3 `0 |4 C5 O- Y                    test>>=1;" u+ F8 _( G+ K" R9 f$ l
                }
+ f4 t4 X- ]3 S) b- k            }
0 u5 d' w( o! i- `            row=scan_row;
. T  x. t6 |# W" [* L; g1 s  P        }# J1 h% \( p+ G, j+ B# r
    }
9 j$ ~9 z" ^/ R3 {$ x; g
6 o& \3 b: w5 i
) y, n; H: x3 i3 `& X& _" d( o* NEP0的控制传输,把用到的请求处理一下
' [5 s4 @# @3 vchar setup_packet_service(void)
8 b( Q5 f. p; E{6 w9 Y% g0 J: g
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
; e! e1 V1 ^5 {/ \$ D    {
9 I" {/ C2 j6 W7 M        switch(ep0_std_req->bRequest)
5 z& s3 h4 ]2 p: `7 D& A! A        {
1 I  K/ [, D: v5 u/ c3 I  X/ Y% Y( Y# E            case REQ_GET_REPORT: break;
) [, |2 ~% C0 d5 c) ]) k            case REQ_GET_IDLE:
+ E/ C2 v. L& ~2 [8 M7 W8 R                USB_PMA[64]=0xfa;   // return 1 byte
. m! n2 W. H2 m7 q                USB_PMA[1]=1;  V+ H: `8 ?# z  c1 M4 e
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;( e* J. J& R1 x; Y  ^" `1 L8 c( w
                ep0_state=1;- W& }2 b3 Z) P+ [: p6 I' v; X
                return 1;
& S9 }7 o) Y$ O% S+ Z                break;
5 ^+ B, A- D% H, M# h) K            case REQ_SET_REPORT:$ R8 e8 K$ E5 A1 O* U8 H7 `
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
4 L# N7 Z5 O6 g3 o1 H" q                ep0_state=2;. c$ @* J+ e* V- g8 B4 L# @
                return 1;
; P1 l( A' f7 Y* g5 q# O) Q% W! p                break;, c8 D* `7 J, b2 J! d4 |
            case REQ_SET_IDLE:
9 |" C9 a' Y+ [8 }9 Y                USB_PMA[1]=0;   // Zero DATA
# P2 k6 u3 Z4 k2 M* b                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;+ E' v7 }9 e% s$ j( [
                ep0_state=4;
5 q) d0 U3 G! y                return 1;7 g" O. u+ a+ C2 ?5 R4 o5 i
                break;7 L# L1 o" i$ P- L, K7 {
        }2 T& K. i" Q+ U) t  _
        return 0;
0 F3 v  m$ T! }) G    }3 ]% u9 t6 f, y. O1 H
    else    // standard5 o$ w& e/ g4 O! \
    {
1 \  S, S: \, L4 g/ Y3 {- L) E- A4 P. M        switch(ep0_std_req->bRequest)
$ C9 B3 B, V8 Q. i# s        {& ^" y8 |1 m  y, T: m* \- t/ W
            case REQ_GET_DESCRIPTOR:, }& h8 u# Q! ]0 ~; j2 w, ?, m
                return descriptor_service();6 b) \. ?# ~# S) C6 q
                break;, k" O" K; s# u1 d; g* I
            case REQ_SET_ADDRESS:
# F  g+ p7 i* G6 ]. ~! ]                if(ep0_std_req->bmRequestType!=0x00)
8 o2 J5 H0 V3 S$ J3 h- i                    return 0;4 F) }; \& ?% w8 e+ R6 V  x
                usb_address=ep0_std_req->wValue;9 x* ?2 c  d0 n! S' a
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;' h9 `& Q0 n% O; `6 Y. [: A
                USB_PMA[1]=0;       // Zero length DATA0
: Q! Z' m. E: t; o1 `0 l$ ~7 {& n                ep0_state=4;    // No Data phase
5 f3 s1 ~  D; p9 k9 Q# }. G( Z- A                return 1;
) ?. W/ o/ f8 K8 T% M. ?            case REQ_SET_CONFIGURATION:2 D7 R5 D9 ]7 F* L! X
                if(ep0_std_req->bmRequestType!=0x00)
1 h& H3 d, M' @; Y" d                    return 0;
6 O! t& r4 C/ ]* x9 S                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
$ M- U, k! R" W9 @) p- r+ [                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
8 V- v, m8 M3 {7 _9 n! e                USB_PMA[1]=0;   // Zero DATA
' L) x( V7 D3 x2 m2 l2 s0 |& e                ep0_state=4;    // No DATA phase4 D) o; Q: B, _0 U: U
                return 1;
5 g/ L- W3 a( k: ~0 H            default: return 0;0 u. l: g9 v4 @( X' E
        }1 }; ^6 U& t7 G2 Y8 @) q/ ?
    }' C. W4 O9 b) v) ?6 q5 V1 T) i$ u: M
}4 [% N: }  ~  W# f( ?- B& c
2 @& u% D6 Z/ G9 o. D  {6 N
5 y. P! E. P) _, ~& w  q& Y

) p3 `$ u" z- ?3 L& ?8 `& W. w
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
! K+ C! m" u; a. pchar descriptor_service(void)4 {) @2 ^% C& p+ t) r/ A
{
3 _4 S' i& t* K) `$ Y* f& e2 t    switch((ep0_std_req->wValue)>>8)
; m+ l; Q4 p2 \$ `' ^2 d3 i    {' j4 R  K; ^% T# U7 e
        case DESC_TYPE_DEVICE:  @5 G2 b/ f! _' w; b! c4 I) {
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));" R- M" ]9 f" \# W: A& A
            break;
7 W6 S5 _+ S# D! d/ Z        case DESC_TYPE_CONFIG:4 v1 _2 l) h2 Q9 r: \/ ^
            if(sizeof(ConfigDescData)>ep0_std_req->wLength)
9 k# K3 E) a& c; W' ?4 I, h5 V. G0 X                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);% l- G# X6 J; f2 w
            else
; P7 H' B" R% u& O. j4 T8 e                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
9 x$ [1 Y' O5 k' A            break;" }+ D9 W& D: E1 v9 \1 e# T% y
        case DESC_TYPE_STRING:
: [; I! ~, ]( n* x            switch(ep0_std_req->wValue &0xff)
/ T7 D3 p3 j8 p) w5 ?" B            {
8 R! V) x: ~& Q- k& ]                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));0 ^7 n# f. I: u) V2 x
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));; }  J! n/ w: ]
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
6 H& H, p  W( r5 D( w7 N  m# B2 |9 Y                default: return 0;, r; Q9 [+ |1 X& R
            }. {6 J. k2 C0 N, M/ U. G9 g
            break;
: N$ V- ]& o% }        case REPORT_DESC_TYPE:
0 K$ V2 w/ @* @% S- w2 Y( A            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
/ E4 @. _. P! i) w1 k( j        default:
- k4 T9 R+ e+ B% g3 Y5 Z            return 0;
8 c0 |$ X3 V1 V8 d2 q+ K    }3 A) s! ~9 q+ a4 j: a
}2 m. L& H9 l; s* K: I' B
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
! M4 p- r, O6 Evoid TIM6_DAC_IRQHandler(void)
; p" t4 Y6 s$ E* x{
* H6 l9 _5 ?+ j4 _    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
$ ?9 [5 \0 P) s4 a0 V! o; T/ Z! ~" m$ F0 [7 m/ O3 f! @

, v: C( \2 Z, ]& ~. {9 @9 r& L    TIM6->SR &= ~TIM_SR_UIF;
( v% f! H; }, S+ q, j* [7 Y    prev_key_state[scan_row]=key_state[scan_row];+ P) C6 S, e" {5 t- J" l7 A
    key_state[scan_row]= *PA_IDR;   // update key states
' o# |# h  j: \1 a6 y    switch(scan_row)
! ^' T& S1 @8 |" P' z* v' Q    {
! H1 o) V/ c8 X- v: D0 F        case 13: // next row PB142 H9 r/ J: w, T$ i# @
                GPIOC->MODER = GPIOC_DEFAULT;) X% s$ U* T. L% a
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
5 ]6 I3 k) w" E0 a3 ]0 @4 e                break;
# g5 ]3 ^' ?8 [2 c3 Z) c7 G/ G        case  0: // next row PB151 p& J0 z4 ^; k7 u
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;; [' p; Z3 Z/ j: C3 U+ E% c
                break;% i! ^. j* }0 o. ~5 ^) y
        case  1: // next row PB3
8 n; d, K$ |7 Q! G  }" s                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
& a% U' Q6 _& _- B3 g0 \                break;0 S% ~) {; z! ~( c) _1 F
        case  2: // next row PB4
* t; k5 B! @  i- G* R9 n! O                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;" M7 A, d; X8 P1 t
                break;
+ E' X, j' r) w% ~        case  3: // next row PB59 y5 |! ]9 Z' u/ h1 y3 k
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;3 G0 P+ ?2 v4 H' s, F
                break;/ C) N9 V/ S" e; ~) Z5 S
        case  4: // next row PB6
+ S) T- W: E% ]/ u7 V  @2 g, C                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;' W9 p/ u, a$ d& T# s% b3 o
                break;9 L* r5 P  N) s& J' y
        case  5: // next row PB7
5 S$ A/ j% Q( Z. B                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;. v$ y' i, K0 s0 P) m' z: K
                break;$ o$ x) f, n9 x8 G; B
        case  6: // next row PB8
# ^  ~7 Y& ~! |) S                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
! _9 U4 |7 ~. g$ R! B* T                break;
5 O3 j8 s3 b  a8 E        case  7: // next row PB98 U2 ]& z! [' B9 {
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;* S0 M0 h& a& j* v
                break;
# X" [& U4 _3 g2 t. Z7 n, c        case  8: // next row PA88 R. `) M6 G4 {  E; I6 c4 J$ W
                GPIOB->MODER = GPIOB_DEFAULT;
5 k$ v. E/ M" c- G  C. |- x( t' U3 w                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;
2 a- [& N9 z3 d/ G# s                break;  B1 e! ^% b  [, K( k9 m
        case  9: // next row PA9
9 B# j3 m7 H, l; \. Z7 s' `% I+ A                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
, y* {, Q: T8 w' j                break;  d, c+ Z- l1 U" x) r8 V- _; w- x
        case 10: // next row PA10
+ p  D: V! n/ Y                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;6 [: v! m! _7 h$ n5 _5 {% {  }
                break;" \& n$ X2 ?5 h
        case 11: // next row PA15, T6 a- E9 S- V$ A  t
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
' M, a& D% S7 O) c2 ?& k( s9 t) N                break;
' e5 l1 W/ m% u- I/ w" r7 M/ M. d        case 12: // next row PC13" P! C/ b4 {5 ~$ K9 T
                GPIOA->MODER = GPIOA_DEFAULT;
. O% y6 U8 ]8 R% {- _                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;. Y( H( v5 C# Q  Y% w8 P& ]- ]
                break;
) X" R% K2 [  E    }
. @$ E: L" d7 ?8 w1 z7 f' ?# ^    if(scan_row<13)8 U+ i8 _# v2 v8 t  y/ p% N; W( w
        scan_row++;
( r- W! n' W: c3 i  p7 T6 f! [. y9 J    else
9 @9 e" t' D  H. K" x        scan_row=0;
+ j! v9 n3 r& E* }3 O0 d' m}1 {3 l5 S# W0 f  o* c& @, A+ V9 y

+ e+ q5 O3 M2 u
* s, q4 P- h, w3 r: E
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
+ {) W; w. g+ E! t, x" M$ `3 q1 c
, X8 g$ a& C" b4 S$ {扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。; _6 E4 E5 ^* }# d

) C' l& Z; ]6 L' h2 Z% `+ B! q% c" h要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:

  ^' h+ }* y: w9 S4 ~6 C
8 w2 v0 C% x: e3 ~7 r6 e5 f1 S8 g" X
const char hid_keymap_qwerty[14][8]={2 ^1 r7 }/ _8 Q+ s; b( u
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},4 _. {% @; b  Y
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},6 t: y+ }; M1 A1 y) b8 @
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
4 S* F  p# |7 w% ?6 r7 `    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
# X. q2 V4 w, N! ]% z    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},
$ Y# B4 n- P- ]& J6 w$ u    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},6 }8 J3 l% e- _9 Q1 j' e' z
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},& V; B2 {2 y4 h% N  q
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
# H8 ]- e4 ?& s    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
; @& w+ x* W8 W( _0 I$ j3 A    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},* x: P) c% t/ j1 m0 p
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},$ l0 N, P, T5 @3 y$ U* L
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},3 B, h7 h8 W) f: C
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},; l0 B: k& @2 r$ W: D# |& o
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
2 D/ D- e5 ?9 q( l* ~7 C. h5 m" i};
" \* P$ Z8 I! u0 ]8 }3 `0 l- K$ Y
; R3 u( G: ]3 i, G8 k$ v
- C) z2 {1 y0 \; p# A
const char hid_keymap_dvorak[14][8]={* X+ R1 p% u6 t& q& z$ e
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
1 O; `9 E# R' k6 q: i    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
: F4 X4 @* ?- W) |" M. c- m+ V* Q    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},2 ^& T) q( L4 S( K) ?: x
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
* Y7 n" Q. E7 G% h    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},1 v  v, A% c! M  Y' v; e" }
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
; x" v8 p( N- G7 r  `# f# z5 r    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},9 B( w: v6 W. {/ Z+ E
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},; q( v0 d( N4 n& S- ]3 D3 c( D+ b
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},/ T4 K" @* o: R* |5 ~2 A
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
8 G- x8 o0 m& l# ~. w    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},1 Z- |( _- q  w9 a9 ^& ?
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},) h# u' X  t" g5 y
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},8 j$ h& q3 A8 Q1 H8 G8 n+ G0 S
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}) X' _0 ~. q- e6 `" a
};; s5 h4 O! T% _" i& u- n: |
4 \3 k' L' v& r( S( s. h9 ]

/ q" g$ J; K9 X( M; H$ Q" B- o上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
5 e) z6 e8 d; `/ s- C% _9 @) h! s; A* n3 K1 o: |$ X
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

- O4 k% Y: M: g( Y) ^
5 |. u8 K3 v5 P4 ~0 C9 U( Tvoid update_key_matrix(char row, char col, char onoff)
; q" n1 y' ^5 C  a{
+ N5 X. C- W3 l" R, M! H) b    static uint16_t hid_report[4]={0,0,0,0};
& d$ F/ k/ \1 `8 _    static char (*hid_keymap)[8]=hid_keymap_dvorak;3 H' c& N( \$ F+ {, J

. }6 ~" B2 d& ^3 H

4 L3 H2 D- N% v0 g$ f9 d# {2 u6 o    unsigned char key=hid_keymap[row][col];# Z. G- E! e. P; s
    unsigned char *report =(unsigned char *)hid_report;1 d* [: f% p, Y% a$ g
    char i;
4 ^( |$ T! x6 r6 P  e4 f, Z% B4 H& r0 t8 k' J% G7 w

  M$ Y5 h( v- v. H0 ~' h+ w- H& T    if(key==HK_MODE), ]4 X' d4 g7 I" ]9 e# g2 d$ b! ~
    {
4 H8 D# T3 W' y. F0 z4 }        if(!onoff)* v- m% b0 d9 C. P3 ^
        {
) F+ F3 [  ^" F* v5 Q" ~* S            if(hid_keymap==hid_keymap_dvorak)/ i1 @9 E# m' B( B6 H
            {
5 p7 b5 `0 [5 c+ q, `0 X                hid_keymap=hid_keymap_qwerty;* h0 ~6 A5 M& K6 f
                GPIOB->BSRR = (1<<2);
3 b% J& S* m- d: S& k' K/ p1 \            }
( K- A( c6 O- `8 o0 [1 [            else# r' J' O9 P9 `, a! a4 y
            {- d9 H0 W+ K7 R8 f  q
                hid_keymap=hid_keymap_dvorak;8 g9 y! D: U! z, W, u' T% X9 R! d- _+ Q
                GPIOB->BRR = (1<<2);
+ Y- i$ n1 j) o            }% H) `2 E9 R( i0 k$ J% u
        }! A  j( j- o, ]/ M- h: i" O  q
        return;; }1 V# P+ m7 `0 I. Y
    }
( u5 }8 d4 h" ^/ {, R0 _$ ]
6 W0 U' v$ L  ]; h* I1 g8 C4 _

3 c3 ?; F! G9 M1 I( ~: y, r4 m    if(key>=0x80)   // Alt, Ctrl, Shift
# W1 d/ z, j1 f/ s6 n    {
! m. D! E1 }# `        uint8_t bitset = 1<<(key&7);
: [' ~8 s  o! @6 M* X        if(onoff)   // non-zero is key up
9 d& ^: x+ A/ F4 a" Y            report[0] &= (~bitset);4 f5 v2 m* f/ B. Y2 r
        else
* |& s$ u9 a  i9 Y. ]4 n            report[0] |= bitset;6 @8 H! G/ k5 |4 D8 W' K8 g) b; h
    }9 s/ a; X$ ]( j# `, Y  v
    else* l; ]' f& Z, J7 C  s
    {
$ K+ v/ z) s6 r+ ]        if(onoff)   // non-zero is key up6 S) b5 x/ f$ ]% a: i
        {6 @* {( o. ]  H
            for(i=2;i<8;i++)0 k# [0 o# @# d" n4 E5 F8 y  @
            {
* h5 A& ?6 h6 ^1 w9 D* X                if(report==key)- H1 j& p/ Z, c# J/ l: y
                {
! R/ N: R: M% P3 x                    report=0;; v1 B( P: q9 v* l5 c3 G0 b1 i
                    break;
  a0 |8 n+ E, \: C' h9 S  v2 o5 Z$ b                }
6 w: x7 N* V( g" Q$ P            }
5 l. B, a% j: r' G        }
* {! h+ n% o  h8 n# V/ a% u, E        else+ P7 T  Q+ T  J$ w9 _. }5 d
        {) y6 @( B: T7 ^0 b6 d' R
            for(i=2;i<8;i++), L, S( @. i( A. P: I3 I
            {
+ _2 S4 _* `7 a8 o( P" m; L: y) }                if(report==key)
; w3 c4 x5 e9 D3 T                    break;
5 Z0 J6 R. c! ]                if(report==0)+ b/ P7 q" ~6 |3 A# j
                {
  x/ G" T* \2 Y5 s5 w1 p4 G                    report=key;
: h! d; v8 K8 r! A                    break;. `+ ^7 [' N, k% d9 e! q
                }
+ i% B7 d2 ^! n% F; I            }5 S0 H0 ?+ ~2 c1 p7 Q
        }1 V. p) H4 ?6 }3 L, I/ l
    }
  g. m, Y% a2 c! S    for(i=0;i<4;i++)
! u" }5 e( f; j/ N7 D        USB_PMA[192+i]=hid_report;. b8 f8 q/ Z* Q+ M; v
    USB_PMA[5]=8;   //COUNT1_TX
: Y5 w0 _6 g, A, H9 ~0 O  F$ N    if(ep1_wait==0)
* O7 w1 j! `1 D: G$ g    {' M! g  H: ^$ b; `
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
, S/ ^9 u# ]( T  h' U        ep1_wait=1;3 O% k( w7 g! W6 q
    }
, X$ z0 E6 j- r' Y) A0 L  E. ~}% Q9 x/ m1 c: v( i3 g& B+ @

! P  f7 G- M, i3 x! r4 t3 j  [4 d4 [8 ~1 w8 W$ K% S% s
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
% }' x& C  R4 ?' |6 Y9 p keyboard.zip (8.7 KB, 下载次数: 6302)
2 [: g& U3 s0 c) x$ X

6 ]4 y1 H$ J* X/ ]& N& B1 e/ E, U2 I& K6 k2 r  ?% s+ h0 C

  f1 I6 q0 j: v+ g4 O, ~- i: u
; N1 k1 \. C( h. u
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
$ @# ^+ E% }& X4 Z: c% E" A' o不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48: b# o! u: Z9 P3 `/ N
刚开始我以为要把打字机改造成电脑键盘$ Z, G) W# Q2 s! h5 }6 ^
不过楼主也很厉害!
2 c1 T2 k% H0 |- ?8 d; ?
哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害' E9 j' m; `9 c) ?2 o
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-9-17 01:07 , Processed in 0.166749 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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