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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
6 V5 v# f& }  P" m
- {% Q: K) c+ |% w% J; F/ O6 yhttp://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
5 R' E" b3 o5 G( H这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。1 @$ I  G8 f* q. }- ^) q5 B

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

( B5 ^# ~" \& K' E 235140i3a36qivqzuvmt5q.jpg.thumb.jpg 1 q3 I  e  I& U1 m+ V1 |
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。
3 T0 i" |' W! F% i  U7 o5 j2 v1 x 001734klbyoluenuwz4h4b.png.thumb.jpg
0 U  a- o, y6 F1 Q" K为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:, f+ T) @/ Q8 D/ k
003625r2agx2f5v922cf2f.png.thumb.jpg " [$ A+ X' J+ A, Q5 f( }2 f. ^
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.
+ [' i: h; ]$ v2 e. T$ EDvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
$ `6 S/ Z/ u  F5 b+ d. A2 r
005836yvs0wvovwsssgd3o.png.thumb.jpg
+ S' j9 Q; z# Z1 M$ xDvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
" `- P- n/ [" d3 m9 P( M) B) [) l' l3 P$ |# O, E/ c7 i
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
; x0 M, P8 W. l+ k6 u+ p( R
7 K( f# E1 G6 v, {4 D  x到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
; X. o4 y5 u/ N* J* u
1 ?7 e( i* m& o) x! y机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
0 A! J# G5 E5 {* V
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
% \# H8 ~7 g- s6 i. n----------------------------------------------------  分割线 ----------------------------------------------------------" p( `8 x5 ]% }6 f9 M  n
  k% m) t7 Q3 Y; l$ j% B
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

3 Y- Z) B1 ^, O) j* @6 g, Y 020011osionbunl4ui44vi.jpg.thumb.jpg
# i' Y+ P9 W# U. J! E! a轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。4 o" I' F5 b. n4 J: ?
020017j8ycmnv7788bqv52.jpg.thumb.jpg
" j. g1 Z( E, V4 E, m* ^. J特写,80C49% r+ ]1 b' e; s& B  Z0 \8 C
021040oujzuvtut6iujtvz.jpg.thumb.jpg / ]- R8 o; g& R0 ]; n* r7 G2 A; m
LED部分,使用了一片D触发器锁存指示灯状态.4 H, D, e( F$ n& r! W" M1 H
022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
" l$ f" h- X. C) D8 u暴力破坏,将80C49拆掉
( S$ D6 K' n" C( Y& B' q% T 021113e48qq98vyohvhzzh.jpg.thumb.jpg 7 E  A7 ]" w3 F2 c& A3 Q5 ?
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
- a: n- R5 S- o2 P0 a' h# [7 O. W 021125nc9az6dj33rlds2r.jpg.thumb.jpg
7 G; o  i% `0 r) _  W
焊好元件后的板子,准备替换80C49* Y' J, O% @: b
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg ! l: Z) V9 T7 @4 ^9 v/ l
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。- D/ l2 H' O- \* I' g, j
021104shifhnrqbr3o5nlo.jpg.thumb.jpg . ]* ?$ p$ o( H% y
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.
$ |. G/ T4 ?* v4 R$ [" q; U: v/ W 022003ym1p9u4ug40280uu.jpg.thumb.jpg - k9 n; F. H! x- Y" A7 f: o
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。9 @5 g; t- W' W! c- x! C8 V
023313kt141q9qajtol7ma.jpg.thumb.jpg ; w, v3 G( {- V; F
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。. E; E: [: Y( r- n$ q+ H
023322nt7l5xb3ltttkltt.jpg.thumb.jpg
  N; A/ S$ b' q0 y* S6 k
主键区键帽就位  I! N/ q7 s8 ^! F
023331hin88e8wkrwzwikx.jpg.thumb.jpg / f+ }: V; w+ ~/ _) C
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
. Q4 @( y: O6 h. O8 P; f" B 023336wjzlgopugg1jyy79.jpg.thumb.jpg
+ X( v! T/ O7 F  [% D# r1 b最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。
% t: q9 C/ d* ]% s% d 023341sffu4j3g2323h6fl.jpg.thumb.jpg 3 Y7 w; t9 u& J8 k+ k( M) F

- A: j4 Z& P- R0 p----------------------------------------------------- 分割线 --------------------------------------------------" c/ y: G6 e& L$ h; q
1 y& W3 t) j% Q0 x7 {

/ w+ f; A9 z/ D7 s/ m: q) I; u; y' }" X, z6 ]2 D
: b0 l+ ?$ m# ~) k: m+ d

5 S4 s- ]8 w3 e" j% F3 c  @& S

2 A% }) \  g6 [7 C  T% q! M! [3 w% Z% T/ g/ ^9 H1 h; t  q

( c0 O6 j5 ^% p1 F" R. X. t
' m( b) r0 y! M; `* V; Z  ~" M7 y1 r: @- Q3 q+ \1 }9 e

( F- G' d, o# E
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
' n5 W! }' F1 i80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:
- m8 k2 X7 ]  A" S 104025nzibm2rmiomhyirm.png.thumb.jpg
+ X" y0 r! ^* D! x! A4 X

6 k/ A9 I, o2 D, @* @9 R6 P5 U, Q. v其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
5 D8 ~/ b9 Z: j2 t) @ 105004zkrez5houvkkznko.jpg.thumb.jpg
( r( e$ d% q- ^: @' {+ o; Y

* S" Z0 w/ l$ G# ^: P! g扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。% ^, L0 Q* D; Y9 ?
' q( ^- a7 T4 C; _( g2 ^2 Q% n0 c
这是我设计的电路图:
# X7 S6 n7 ~7 R0 n3 D) U5 P 110344ej2z2oo2rflo7oe7.png.thumb.jpg - U4 L. v  Q9 B2 U! c0 G) O

# p; z: o5 K- q3 D/ lPCB Layout:
0 g- {( {& v. [7 _8 B 110847jjbjvt34vwt3v5bb.png.thumb.jpg 1 [; _5 G, z; p0 ?, b! K  M
0 o# T$ t( d+ n
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
+ d& b% e4 S" f1 u  N  v/ a
* G- r9 E! r# ~; ]5 K
+ W: H- B6 K% P( X
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 5 W8 w- `- ?" ]* `; Y

! N0 p' Z, B' t/ C软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。, ^% O! I1 M1 h- z- |+ h( T. i

$ x7 n1 b+ `/ v8 I8 S总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:5 ~9 d0 o% h3 @% E) J( e. w0 D
113818pmrfsb6z0byt6t06.png.thumb.jpg 2 J/ G' r$ k, P8 C

/ S+ g" \2 N- @6 O9 V其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)6 X& t6 f: k( u3 P7 r% v0 ^

  v  I2 g+ c$ wUSB的中断ISR,bare metal哦; t4 j  ?7 L' |# c9 |, B

9 ?7 B3 y. g, ^2 Rvoid USB_IRQHandler(void)
9 c) Z- k6 A- J# x% u) V) K# w2 L: l{) ^$ u2 {3 \9 U& o8 P, }
    if(USB->ISTR & USB_ISTR_CTR)
" Z9 {- Z( v- l! ?    {; {5 Y5 X! W# S7 |6 y' u: r
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0; ?0 T! }. `# L
        {! q9 A% }8 e2 U5 D% i8 H  B
            switch(ep0_state)
* R: ^- B0 l0 ?& u            {
' L8 B+ R0 x8 j7 h/ A                case 0: ep0_state |= 0x80;
) y2 \9 H& h0 j% I* G5 x9 Y                        break;
" D( T% z7 _- i- X7 a                case 1: if(USB->EP0R & USB_EP_CTR_TX)
5 }$ g. F$ `0 ?! g; H4 I6 i  @                        {
1 O( B& g4 l/ t' U0 \                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
8 V% g0 ]/ ~0 u( `* R) t, H, ^                            ep0_state=3;' G- T( B! u- A. S! l
                            return;
! i. e7 ?: Y5 O                        }
- ^( D; O1 z9 m                        else
; l( U" @: P* D* j0 o/ J3 t( p                            ep0_state=0;* x; K- ?; Y) K
                        break;
7 [3 f, E  S6 C1 L2 E                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet$ o1 W% a9 h$ V1 U$ i) C  g
                            ep0_state |= 0x80;
, M' ~, l  O+ p, A5 P. r6 Q( O# f4 S                        else
/ ?$ h* w% W# r( \% b                            ep0_state=0;, V; n5 q4 j1 L+ T. y
                        break;
8 O' o4 a- @# ]" e6 ?3 p                case 3: ep0_state=0;
8 w8 y- I5 ?* r6 M1 z% N                        break;) r* y& y0 Q! _% D" c6 ^, K
                case 4: ep0_state=0;! i, v7 E/ Q8 k
                        break;6 j5 L& P! Y6 j$ r) u
                default:ep0_state=0;# [: i7 M0 o8 X! x# a
                        break;
+ @* {5 y1 _. X, B! {% F            }' |+ ~, s! o" E) O3 B; @
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag: Z9 d- O) s6 \3 Q; ~2 r
            return;
* }' a2 Q6 E- V. V        }
9 q+ d5 ]% ?% g2 h        else    // EP_ID can be 1. U1 R* Q7 f( T
        {- N) ~8 o+ N. p: i
            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
) ]! r; ~6 C6 i. b) `* r( V% B$ W- {            ep1_wait=0;
! D- o# Z7 P; B2 d+ X0 {$ F            return;
& }' C% w' R# {, \3 t% S        }
" S6 Y+ Q& r: R; p+ O6 W+ [3 R    }' D' F9 t. u, Q! Z$ e* E
    if(USB->ISTR & USB_ISTR_PMAOVR): [4 g& M; v  W9 l- ?) Z) t$ [
    {
2 m) s2 Y$ r. \, c4 P! l1 s        USB->ISTR = ~USB_ISTR_PMAOVR;; b# s0 F& l. l5 k
    }
4 r+ q1 l4 O6 U& ^2 }6 R  x    if(USB->ISTR & USB_ISTR_ERR)& b3 Z9 M8 J$ X3 r) m: h2 J! ~( |& v
    {) w, d) H7 Y# v& b# o6 M
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
' {1 T/ E; {$ D" z7 s; M    }
2 L( ^1 R/ b: k    if(USB->ISTR & USB_ISTR_WKUP)
" A* ]" e; b6 l' v8 A7 X    {
+ Y! v! M9 E, ~% C/ x        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend7 i! a0 \( g8 D4 u# [
        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
0 u6 c8 `& `/ p$ _3 @% E    }; I' z( L6 a: e
    if(USB->ISTR & USB_ISTR_SUSP)
' m2 ]) u6 D6 `) t7 v, d4 I" n+ k    {
. ?% b6 ^1 O$ W% {$ w+ y- y2 t        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend9 d1 [. r. r4 _7 }" S7 j
        USB->CNTR |= USB_CNTR_LPMODE;   // low power9 R/ D* W) S$ r7 @- }0 [
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
2 E/ C5 k+ Z- F- P, l    }3 s) ^8 d- q  ]6 ?; z" D: `$ I
    if(USB->ISTR & USB_ISTR_RESET), x: R; C& Q# s" Q- D
    {
% G9 ~3 z7 }$ K8 }        USB->BTABLE = 0;    // buffer table at bottom of PMA
, B. L( ^% m% d) l6 ?        USB_PMA[0]=128; //ADDR0_TX1 C- `. |( \7 K) z6 [9 m( E
        USB_PMA[1]=0;   //COUNT0_TX1 P7 C/ e9 V3 Y1 q1 Z/ q, [
        USB_PMA[2]=256; //ADDR0_RX3 J! b" G: Q" a
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
& _( Z1 |! U! I7 \2 N: r9 K: x        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
% V. p# F# P4 \6 b( Z' u        ep0_state=0;
5 u  T( ~5 Z7 w2 O9 G+ }9 p        USB_PMA[4]=384; //ADDR1_TX
/ j* v. H: f0 V        USB_PMA[5]=0;   //COUNT1_TX1 H4 X5 @( i$ N" j' l& X
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
+ ]9 Y' `/ w" C9 N) \$ [8 z        ep1_wait=0;* G, X9 T  ^0 g' w/ J; M: c2 d% w
        USB->DADDR = USB_DADDR_EF;      // enable function
+ E) [2 D* }8 A4 |2 r* P+ O" f        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear" d% |+ |+ v, z$ }
    }. I/ j+ }9 r! G7 H, I8 l) S* U
    if(USB->ISTR & USB_ISTR_SOF)
0 E) e! V6 J$ Q; I1 |$ g    {
7 S! b+ ~% W4 o& }" m        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear
# H% j0 k: c, b7 k    }
; J* P( n8 m! F}( _+ S- b; ?* l( L8 |

% o6 P3 ?" N# ~8 ]1 ~3 X6 @* H
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
3 t/ t  s" Q" M3 j( i8 e$ Z$ V. U; {  N
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。& x2 ?5 w2 q8 Q
' e/ s% \# J+ @3 A; p# ~& C0 A
    while(1)# b+ B# h# K7 q9 V& z2 |1 Z  P
    {
8 g7 U! }* p5 R        static char row=0;2 j/ o' p9 M3 N& _- E
        __WFI();
- s# A  m& V- v; z4 r! q* p4 _+ r        if(ep0_state & 0x80)    // request data processing
1 |* ?* m' E, m5 ?        {
: U! g. ^. Y* a+ R            if(ep0_state==0x80) // SETUP phase3 j4 U% u8 ?6 i$ l8 j" M; O
            {
' F& A& a: E: i' j                if(!setup_packet_service())
$ W* u  `1 F2 B7 q. H0 S                {
$ Y5 O* A1 j: I8 i                    ep0_state=0;+ j4 p9 M( s8 }: l( J
                    // not supported+ I' D/ U+ E! o. G2 n
                }
+ w# e7 @5 h' U6 L5 S) ]' i                // ep0_state should be set to 1 or 2, if processed: K3 O9 ]6 f) F/ \7 A" I( t+ E
            }
, M5 }. p+ b0 Y& }' Q6 b" V. h8 v            else    // OUT phase8 I0 G$ e3 i5 n9 k1 `* Z
            {
) A" ?* G+ N$ P/ D                // process data* X  @7 _7 h$ h# T
                show_LED(*(uint8_t *)(USB_PMA+128));$ s: `4 o* N& Y8 ]0 T5 |9 u6 E. D
                ep0_state=4;% y1 r: D+ ]/ f
                USB_PMA[1]=0;       // Zero length DATA0
. ]( d+ A9 ]* D, c                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
5 g# V$ H" G+ F7 A% K  b            }
9 e% w6 V9 U7 d) j0 V        }3 t, g- x0 [! |. p+ i1 C
        else
3 Y- g1 B9 {% i+ C        {
, B0 Z, B% N" z0 P2 I7 ~' \            if(usb_address && ep0_state==0)
  Y  I6 J& i# L# E; x, C            {; Z( W' Y( M4 Q" P- V( [
                USB->DADDR = USB_DADDR_EF|usb_address;( H6 H( B/ I' j8 ?3 Z/ W
                usb_address=0;, T, H% q2 \3 o6 N: j( _5 U
            }
& a3 L" S1 g8 d" p( ^: n# h        }
7 P0 w- t" X7 c2 ^+ Z) i        if(row!=scan_row)   // new scan line6 {5 w" R% w. N# L9 `. H
        {
+ x2 x# Z* D3 T            if(key_state[row]!=prev_key_state[row])
& Q0 F: J7 i# d            {
5 b7 l; H, p' I3 H) T/ P. p                uint8_t test=0x80;
1 I+ L3 s3 n) l# l5 [: E                uint8_t diff=key_state[row]^prev_key_state[row];
8 Q: _4 y6 o8 L                for(i=0;i<8;i++)  ?9 h- y# q* \! u+ C  t: n, w
                {( i, c. f" y- u' e  p
                    if(diff & test)" ^% x* p/ _! x
                        update_key_matrix(row,i,key_state[row]&test);
& }! `5 q% U4 |, }9 \: I5 u/ c5 c! r                    test>>=1;8 r- [4 e! h; [3 d
                }! B4 d  \. i! b! ]( K7 b, t5 e
            }0 A. P+ T, H  C# J! i$ p, i
            row=scan_row;
4 H2 |; M0 i# g        }. H: K5 W- Y/ }, P5 o4 H. Z+ [
    }" v. y1 H) g- @( p; x, C9 o% {

8 q1 H% d* G) T  H: `" x  A1 e/ h5 R6 K. |; S& V8 z
EP0的控制传输,把用到的请求处理一下9 y+ N5 w6 E2 P' S; E, w( X1 _
char setup_packet_service(void)& [6 z* E3 d5 Y, ^: I
{
: [2 b$ }  p! N$ j5 E2 p- c2 X    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
( l, [( Z0 p: `2 H$ e    {
! P4 o! c7 m5 ]) h2 E" F3 A        switch(ep0_std_req->bRequest)
9 q8 P$ W7 `: A        {9 M! V" q; o' w
            case REQ_GET_REPORT: break;" ?0 V6 r* v# s7 E2 T6 Y+ r
            case REQ_GET_IDLE:6 u! W& C6 K5 D8 t: @  ]
                USB_PMA[64]=0xfa;   // return 1 byte4 T! f. V4 i2 a7 n
                USB_PMA[1]=1;
, S# R; D* c/ j1 e& s: ^                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;4 d7 n7 S7 S4 u4 q9 \
                ep0_state=1;
& C5 h: N# M3 @& a9 q: O7 @4 M                return 1;; ~5 Q7 y, R2 Z/ q3 [0 e! Z
                break;$ Q* O# X4 D7 b( L
            case REQ_SET_REPORT:% s2 \- [/ u1 _" b/ C9 y: \2 o
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
$ ^% U, E5 l( W& n- G5 Y                ep0_state=2;# R) Z6 k. f$ W2 Q  z7 |
                return 1;
1 Q" E9 H$ J' t4 [6 m/ _                break;: |- X/ L9 |, R( k3 @7 ?
            case REQ_SET_IDLE:) ?. A6 M2 g. ?) n3 I
                USB_PMA[1]=0;   // Zero DATA
% o& y; M% |+ W/ g) L$ |                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
2 S! R/ W5 k$ Q, [8 O- g/ V9 p                ep0_state=4;
  V" W& r2 D% N: _, X                return 1;
( r1 W% K6 G1 |2 ~( r3 k                break;' F( G: u: H( c! j, Q( e4 }! A* y/ }
        }; U% X3 S4 D; \! V: R1 K
        return 0;4 V) E7 q0 g2 p" n
    }& U+ k  P3 U- ?/ _5 H
    else    // standard
$ o3 f1 m9 [4 ?( A    {; _) Y1 G+ }3 D4 r) B! s4 x& }
        switch(ep0_std_req->bRequest)8 ^1 W4 |7 L* u7 c  u" b
        {: Z' M. C1 u+ |# k/ ^) t1 @5 z& [
            case REQ_GET_DESCRIPTOR:( G4 [% Y* B! `7 h( j$ U
                return descriptor_service();6 u! c& ~) ]4 T) `" w
                break;2 z0 @4 y3 g" K: ?8 y
            case REQ_SET_ADDRESS:
7 g4 l8 W# d9 m7 h                if(ep0_std_req->bmRequestType!=0x00)
) s; W$ i) b" y$ c                    return 0;
; t& p3 @' e6 I! p  d0 L' Z! h! @                usb_address=ep0_std_req->wValue;1 ?8 u7 F, x' T+ u8 V; {
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;! e5 B" O; ?  F2 k
                USB_PMA[1]=0;       // Zero length DATA0: v$ o" ^. D# E& v4 Q' i
                ep0_state=4;    // No Data phase
* S" J! `, N& c. g: i5 u  J. d                return 1;
  Y3 [# M4 ~) e( ]1 U6 R9 _% ]            case REQ_SET_CONFIGURATION:
# n9 D) M  @# j( C  o  n( \4 P                if(ep0_std_req->bmRequestType!=0x00)0 l; L! q/ h* ]! E* A
                    return 0;, K) X2 h! ^5 g
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;  `  X2 }: `2 R+ e
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
* ^( G5 w5 {2 Y  I4 h                USB_PMA[1]=0;   // Zero DATA
$ u) c; H% s" V                ep0_state=4;    // No DATA phase1 W6 K, X: L; j& |. w
                return 1;# V4 s8 o+ G/ l! S
            default: return 0;7 j8 h" |8 |2 h! p
        }4 ]4 F% C2 x3 _- Y. D) B, E) I
    }; {- H$ ~- O& v; e5 I* x
}' @! P% v0 r2 {8 I5 g
: D) z' X1 K& V1 r

3 O$ N0 N. L6 w0 ?) J6 z
1 e  H8 G6 S; S. _3 Z& _; I4 V
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
$ s0 H* L: d% y& Q5 L7 i( ychar descriptor_service(void)
+ i( Q# G, d. ^' V# I$ L% Y) z{0 e/ t, s2 V* R' ~% B9 N
    switch((ep0_std_req->wValue)>>8)4 R% B+ W" n% O7 U0 p
    {
1 W! E: C5 z" C6 d        case DESC_TYPE_DEVICE:
. y& l" z1 ~" n/ g  V            return ep0_preparedata(&DevDesc, sizeof(DevDesc));2 U9 s1 U* @! r: K1 f! m
            break;1 t) Q( f  [& ~* G6 ^, m7 G; l
        case DESC_TYPE_CONFIG:3 L7 ^7 r1 R8 H6 O
            if(sizeof(ConfigDescData)>ep0_std_req->wLength)
  U6 C* k( u. L3 O) I+ w. d2 [                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
) p5 J0 t* z8 K% P! d. Y            else
' O$ I2 O3 x4 c                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));7 j, L% S+ U/ A
            break;
# j  l" ?1 J7 \5 f+ h        case DESC_TYPE_STRING:
3 G6 j) H  N, X9 b- T" f( W* T            switch(ep0_std_req->wValue &0xff)+ i! E0 C5 u4 Q' H, [7 I
            {- G2 X0 U* n0 N9 A. `7 X
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
: n0 s( v/ X1 U$ q7 ~                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));5 p/ U" m8 u0 R( z3 c
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));' |; Z# V, r1 f& S. X0 ^- `
                default: return 0;5 c: s) E5 X7 P: `) {. g
            }
2 J( Y* O" _0 q0 r+ p% P8 f8 k4 Z            break;
3 z4 C6 Y8 ~0 i! t: e. C1 @' E        case REPORT_DESC_TYPE:! t# `2 D% U: T+ l8 i  f
            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));8 f& m3 M( I( o
        default:3 F+ X' x# n' q0 N- i
            return 0;
" p' R: p) [" w1 y8 R5 ]  I    }7 [$ H/ i  }, C
}
3 {  ^1 W1 [/ ?$ a* P7 s8 c下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.  o" L! x* ?) [4 k3 p+ i
void TIM6_DAC_IRQHandler(void), {/ g. _8 T$ ^6 H
{! ?) l% M2 p% P5 D2 g# L" c& ^
    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);
2 R8 D# Y9 G5 h% I5 u" y
, R! ^7 v- u8 b. W2 _# @/ \

, u# Q* `9 ~" g3 ], D" O  l8 }    TIM6->SR &= ~TIM_SR_UIF;  [& }! C* u) j/ d$ B0 @
    prev_key_state[scan_row]=key_state[scan_row];3 p* B8 T  v! J1 q4 h. Q
    key_state[scan_row]= *PA_IDR;   // update key states
+ O- f0 `, S9 P, G/ z% Q. ~    switch(scan_row)
: o/ f& t2 E0 F# O    {
  v# m' S* y: ~" a        case 13: // next row PB147 i' E) u: O9 R4 `7 V) S
                GPIOC->MODER = GPIOC_DEFAULT;
! r+ ~& V( k9 ]) m% f                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;) `/ C6 e& o( J0 N0 K
                break;
( u' B/ D" j1 B8 {) F        case  0: // next row PB155 B1 s$ s/ M( z5 u8 N7 Q
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;/ O4 Q/ n3 W, l6 E
                break;
, x2 z& J& V, K* S; B        case  1: // next row PB3
2 x; T9 O2 l0 g/ ?* O                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;0 n- M5 g" J3 H5 @& J+ |0 i, c0 N
                break;% d' C0 w* P/ N/ e7 Y/ W! Z
        case  2: // next row PB4& I  O! J3 G! _
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;, H  c. {$ R# z$ ]5 _
                break;) j# v# v7 D- @& e9 m) J
        case  3: // next row PB5% H1 g5 p  v+ ^' U- _: c: {
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;
$ D( G% o5 R; b; ^3 I0 g4 j( U8 h                break;
4 O5 j, E7 V3 n, o8 d        case  4: // next row PB6# n4 |9 {( _( Q& ?; ?
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
$ X) A" C- \$ Z                break;
$ O- G' s4 p- s5 \) X7 p8 V        case  5: // next row PB7
; h' |6 ~9 ~2 l$ p- r+ O                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
. e$ b! o5 f0 s                break;
4 q2 u: t0 p2 A( z        case  6: // next row PB8
! o- `' A) j: t3 h                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
! v! f! Z/ Q* ]0 ]# {                break;% O2 U7 u: F5 [1 e& ^, ~
        case  7: // next row PB9
6 d' ^6 G. x, {/ S5 v. f& o4 X( G                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;
; O$ e& o1 U1 ?! h                break;( a7 c) L# X9 H) u1 ?
        case  8: // next row PA8% a' [- C, u7 `2 W4 P! D: R
                GPIOB->MODER = GPIOB_DEFAULT;
9 Y* ]* M( b6 J# r                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;, m8 h5 C0 F# H4 g. K- h( t
                break;/ C& S7 C8 B3 P& X" B- i2 ~
        case  9: // next row PA9
0 j! U9 v8 ^/ p) u2 ^                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
: z. f) S3 t4 f, U/ C8 T2 c                break;
- ^  `4 x' [9 c! O7 N, h; }. [) W6 f' V        case 10: // next row PA10$ u4 e" B8 y4 @* o
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
: u# a* P+ Y" v, E! b                break;% h$ z7 B, n# x  L: g2 A
        case 11: // next row PA15/ q" e9 x( L/ |
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
) _. u3 X, z$ _. V9 V, K1 c# Y1 t                break;
* m) ?) H& f& M- O' @; k* I/ ]8 L        case 12: // next row PC135 ~/ x  [1 p1 [8 W
                GPIOA->MODER = GPIOA_DEFAULT;
& s/ u9 K2 A6 X                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;$ _6 {5 ]1 |% Y. j0 _" c" c8 f
                break;: h; J6 V& u. A9 N6 E2 y+ O) ^
    }
& t8 q) N, j5 ~! _; N    if(scan_row<13)2 ?% q. z- k8 m5 q. {/ e$ Q
        scan_row++;; j' y" K) w2 E. |8 F
    else
2 q0 _; U+ H, \2 W$ m7 v* y: `, ?5 I        scan_row=0;: |* h  Y" J. t% j4 z0 y1 E
}
: Q1 N7 a/ O8 L: X
/ X" J: h5 J: v( ~
# v7 ?. g# {9 D) {# p
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 ' E; H. C# [, v5 k. n! {; A9 M

/ {2 Y( @5 U8 I  Q扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
5 w( w: l! ^# \
' d% S9 I$ n3 G6 r$ }1 c要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
5 {+ z1 U. P6 k5 h% `: F  t1 d, _

; H0 T. d4 A1 b$ S0 v7 r' W; f; x+ }' Y8 P6 G* L. ^
const char hid_keymap_qwerty[14][8]={
( k- V" [1 \  V: J% e1 g    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},1 ?4 U6 i) Q% I0 o1 e$ g) f5 z/ k
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
8 y8 P. R* _+ I9 g) x: A    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
7 f9 a, G$ p+ f2 E+ s" }6 t    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},# Z! N# v/ D0 ^# ~
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},) h) U6 H" ~7 e$ d% D
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
7 f* ?/ d8 g. j# C: g. }% p" T    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
* S& j" p) L  B, p+ o" T    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},8 p7 y' ~; _9 u& p
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},! G$ H! N' t  [- K1 v" D
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
; E& k& ?0 E  I6 c    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
& k2 v$ r7 {, _2 @0 B- b( z% q$ q1 f0 d    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
' v+ [: _8 }/ @" z; E2 W9 k6 w  ?    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
* A' m: ~* @5 g6 ?/ P: D& a    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
& A' d" J- \: ~% j/ W, r};
% N, F5 |% d1 f* L, T+ Z0 A- D" n' Y2 b- l" l- h# W

. n' i8 m) k  Q9 o/ O5 {const char hid_keymap_dvorak[14][8]={7 j' r5 T' ~4 ^6 k+ f6 g. e0 `
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
; E# j/ d, A, a/ P: ^- v2 K& U    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},/ A% Y8 D. R; {. c6 A: O. D
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
, k# A% E# ?- Q/ r9 e( m1 n    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},8 a) v2 n! d6 ]+ D& w
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},+ l7 S  Q& R, g6 ^$ a% m
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
4 M6 h# U* q7 y- O5 q1 H    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
! n9 X, {8 [4 O4 q+ k& p    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},( u1 T* }; |2 H  P+ r* ~/ J+ ]
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},% }# p8 M$ d" ?* z
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
0 X, a8 ]+ l+ \# E+ _  d9 Y    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},* J; B' |, S, r/ g  _" A8 V
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
! M3 \' d# X0 w    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},) T* P, @/ G/ w7 R- e) R
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
4 E! G4 J( v+ R+ j* W: y};
0 G: X* ~3 K5 C8 i% b; e
& O: ^; `  z4 v8 {7 i$ E
" ?9 N3 j, Q5 Y: f+ n& z% ?9 ]上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
* J0 o% p" [% Y$ b: p8 u
+ `2 R+ W! i! X" GHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

# M- B0 Y& H. s; x. [7 x: @  K3 J! _$ ^- ?4 f
void update_key_matrix(char row, char col, char onoff)2 _. A  C9 r7 R% g7 {) h
{5 J4 W( V+ F7 H' r: y
    static uint16_t hid_report[4]={0,0,0,0};
( H& u1 k( X6 k% g  n    static char (*hid_keymap)[8]=hid_keymap_dvorak;
3 V* n1 C( Y5 r3 W- y7 D' A; O  |
% ^) v1 y/ d; d; N( f7 R! q+ h
    unsigned char key=hid_keymap[row][col];( o1 i* J+ S6 |( ]9 W7 Z) z0 v
    unsigned char *report =(unsigned char *)hid_report;1 P1 j1 A1 K* z) Q
    char i;. l: \4 D- r- a2 ]3 g8 S
6 t; G9 B4 ~' v+ Q* }2 f' n0 {
. A, A# Q# j& P! Q( G
    if(key==HK_MODE)4 r7 j; k4 E' F4 {, S, a9 V
    {
- ?6 b  l9 Y9 V- e4 N- b        if(!onoff)5 o$ g  S& @* R+ x
        {
( @" J, h; R: [4 w5 f            if(hid_keymap==hid_keymap_dvorak)
$ I2 o+ N6 i8 R9 u            {
' y" @- ]2 b6 Q1 @                hid_keymap=hid_keymap_qwerty;) c: P1 ^' \" t8 U- t
                GPIOB->BSRR = (1<<2);
! A% U5 F8 g# V! s9 }* {1 y            }6 J1 T# H9 S" N3 X
            else
: O$ Y+ @" I+ d# w+ J" R0 I/ d8 p            {2 e$ D6 B6 x! q, V# k$ P
                hid_keymap=hid_keymap_dvorak;, L% Y: t2 J0 Q9 i* h! b8 w
                GPIOB->BRR = (1<<2);
1 ?: Q3 J, V' T" w5 y# E1 l            }
* `0 }  S1 u3 m2 o2 y; i        }8 ?" m2 s: P! q+ j9 V
        return;+ L/ N$ s; V+ r
    }
8 \0 h- _# [. K: Z% k8 y1 P
5 w$ p8 b. ^5 D: Q- d, \
7 B3 R) k2 t. ?* Z: z- U& B# a
    if(key>=0x80)   // Alt, Ctrl, Shift2 J2 m; |( @5 ]: D
    {
, h% W9 l9 h# A' ?# u( ]7 c. R        uint8_t bitset = 1<<(key&7);
) B- c: {3 `* Y7 ]7 J7 d& H# c+ e        if(onoff)   // non-zero is key up
  w: Y" O8 C" }% _            report[0] &= (~bitset);
. z7 }# y" {" J; M        else- r$ i$ t) K7 E# `- W0 u
            report[0] |= bitset;
; X* R3 z: M5 I& |$ \) a* [    }
  t% }1 I7 W; p    else
3 x% w- T8 F* a* A- X9 q" s    {
2 f- _3 c8 Q% |7 F# P) `& ]7 H        if(onoff)   // non-zero is key up
/ _: c8 r: Z0 ^/ |9 L7 K# k0 |        {
" g$ h. M/ m7 s' `# y            for(i=2;i<8;i++)( Z. [; o; d" U, A$ [
            {0 o7 k9 q3 C8 f! _8 N. \" @1 n
                if(report==key)- B9 L/ V5 Y# H2 w2 K
                {" T0 N4 [' A' I7 g9 b7 I/ n
                    report=0;
4 a, S! u4 R/ R6 D, ^                    break;0 M2 b1 d, j+ f0 Y
                }
( z* ~1 }2 E* P$ N1 }8 x& s            }
/ f# Q7 a3 r# \* X: H, J9 i$ `        }9 l  u% a) D) N# T' u9 I
        else
5 j* Y7 M' j2 p4 Q7 T        {
  c0 g, X1 O3 |% @% D+ j+ B0 j- [            for(i=2;i<8;i++)8 F9 V+ T& u" \) h* R- h
            {6 h* |- ~' T( G; [% @
                if(report==key)
2 x) j0 Q$ r1 j9 ?                    break;$ b( ~4 w# U, R$ i$ ]" m, k
                if(report==0)9 ]/ N1 z1 n- f. N
                {
% ?' R7 N7 C- U7 U' B( u2 v                    report=key;/ _4 D* w3 W2 Y; o7 b4 C
                    break;, e7 P- K9 b! h" ?! P, l; j' e( f4 A
                }
& p8 a" R/ i( f+ b            }
5 R! }! }2 c! ?/ R, Z! d3 V3 _        }( v% F8 H0 N( r! ?3 ~
    }
' ]3 F+ |4 J$ k. Q% X# G# U    for(i=0;i<4;i++)9 Z" x/ u* f6 _7 j
        USB_PMA[192+i]=hid_report;
# H0 V8 @( H5 Y7 \, e% p    USB_PMA[5]=8;   //COUNT1_TX
; s" U- f. X7 }: c    if(ep1_wait==0)- \/ h; c6 z% x5 g8 U  W- N9 r
    {
6 W  X. w1 J$ E0 W  ?( o  D4 b4 H        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
, F) r2 x- ]  U4 U+ K( |- P        ep1_wait=1;
# h: t( j& f6 z5 T" U2 g' L    }( I  `# r! X7 w5 @) z" m/ N  C
}) W# j3 {& t( g/ q0 K$ A) }* n
' |4 B1 D0 Y& @( G
# f" W# o) S8 ]% [8 Z, n! Q* N
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。
4 i: ]! ?, K  G keyboard.zip (8.7 KB, 下载次数: 6653)
* [$ @0 V) J. V1 g( U( z

- K+ L8 l9 R5 C9 |' m
) S+ O" [1 I+ T1 A: @
; P* W+ V9 ]( k8 b& b
. z/ d+ I" i! I# P# C; F& K! v
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘, n$ c- z$ @8 P7 N
不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48% b) c% V- n2 V" r
刚开始我以为要把打字机改造成电脑键盘
$ a. l$ ~$ W: _6 u不过楼主也很厉害!
& r! @$ t# E0 b/ @! U
哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害  G7 n! E8 _" G7 X! E
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-8-20 02:44 , Processed in 0.172566 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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