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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
- x, s) Z. E, l% V, i" u- d- m+ Z  R0 c' Q4 j& j
http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
. s0 G* [! |3 D; \4 m% |6 i这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。
1 d8 Y: g9 J: }- t+ M
- O+ W7 Y- e, N5 P: ]在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。
* Q7 Y' }4 D9 \+ B$ X( P$ R

1 U; M; A) L, S 235140i3a36qivqzuvmt5q.jpg.thumb.jpg 6 y& N0 r9 q0 n0 {
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。) m& m9 V$ K! a( x9 T+ o% U% [
001734klbyoluenuwz4h4b.png.thumb.jpg # I/ W* V5 X  ~2 B6 u
为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:4 @6 S7 C$ m) ]4 x) A5 H2 S
003625r2agx2f5v922cf2f.png.thumb.jpg 4 t" o6 @) A8 q" h
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.8 F# [! X# m/ r: I  R7 t  D
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:

/ n' q: A8 l; k 005836yvs0wvovwsssgd3o.png.thumb.jpg
+ @+ F+ b" [! u% T! t# Y; n( tDvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。% T1 }$ p9 X  a
% |+ G( K0 E! Q, G; N/ N$ j6 L
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
1 H/ E7 r6 I& W- w9 r* C- U: D
1 ]7 u& ^$ v/ S+ D! y2 W4 ^到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。+ f( C: c, c, x9 Y; Y

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

& ]0 g8 s& x* N/ w) c; j# W* d4 s 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
: e. ?) \4 l! G. N----------------------------------------------------  分割线 ----------------------------------------------------------2 D6 K  w8 a$ ]9 @" Y  a& M

2 r8 m; v3 M9 I) W* ?6 b先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

& m1 D; V9 C  N: ?& c' {( H 020011osionbunl4ui44vi.jpg.thumb.jpg
( D1 j; d/ Q! `4 R. y轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
( m# a4 m, ^7 _ 020017j8ycmnv7788bqv52.jpg.thumb.jpg . s5 p) e6 N% X
特写,80C49/ P7 Y: t9 e  i, ^1 E
021040oujzuvtut6iujtvz.jpg.thumb.jpg ' I+ {9 J1 J) M* J5 O* G* f
LED部分,使用了一片D触发器锁存指示灯状态.
8 W. l8 J6 A( u8 D9 z 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg 4 `7 a! ~+ V7 Q, x) n1 }
暴力破坏,将80C49拆掉
3 v% j$ }( t& J' L8 X* t 021113e48qq98vyohvhzzh.jpg.thumb.jpg
# {& ~; t# l* r) P0 P& ~拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。9 N: l- y7 L: u* @9 O# M. Y- C
021125nc9az6dj33rlds2r.jpg.thumb.jpg 0 v% e# k5 }# P; z4 R  Z
焊好元件后的板子,准备替换80C49- N) b, ^5 `& i5 k7 Q2 U4 P" \) Q
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
0 n2 e- f( m, S/ h  F( l4 Q用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。, t) Q. z6 ]/ F# G0 m% w- v( ~+ V
021104shifhnrqbr3o5nlo.jpg.thumb.jpg
# c6 P8 F' L" v# t9 y: N( a这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.
: O- ]! p; V7 ~# v) Z 022003ym1p9u4ug40280uu.jpg.thumb.jpg
, q# [: |$ N# n' l开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。; G8 o  i7 `0 z
023313kt141q9qajtol7ma.jpg.thumb.jpg . @& y0 a, Z$ g" @1 ~! C4 L# T
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
6 c& ]5 m; X1 t 023322nt7l5xb3ltttkltt.jpg.thumb.jpg 6 B4 m! |9 L- R/ ~1 Y. A) c' m
主键区键帽就位
7 N$ {4 R% b7 A/ I2 s 023331hin88e8wkrwzwikx.jpg.thumb.jpg
4 U, A  N1 ?( ^$ }/ Y8 @! d" Y编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。7 y! G6 F- {, Y* O2 n
023336wjzlgopugg1jyy79.jpg.thumb.jpg 1 F9 @( A5 ?. I( N2 Y9 Y* t
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。
# X2 d2 z( j! ~2 \ 023341sffu4j3g2323h6fl.jpg.thumb.jpg
) v* P% W' @2 C  J3 F- s* k( g1 C

/ ]5 s  u# a, R3 ^& n* u* r8 w4 r----------------------------------------------------- 分割线 --------------------------------------------------9 E; a+ A- u- X6 ~  I& Y

! @! G. [# p3 X+ E% E' F, F5 }

- U6 {3 y( R6 H( n( N/ e
. \, Z5 p/ X- d) }% @3 ]) `$ }  w3 T& y7 [6 ^( ]

: ^7 p8 x! H. X3 P" p- v% w
0 Y3 u* a+ ~; }! p
& x' E7 g' K9 ^4 {' g* p* z  p
% }. N' i* h% q% s8 p! p6 F
8 a+ a$ i  Y3 X  v( ?
% e& |4 t9 G4 F$ N8 {. h; z& G  d

/ f: ~3 _/ l* t
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
  _6 c" N, j* n) m80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:) q; D- ?9 a9 S$ ^" P1 L$ Z
104025nzibm2rmiomhyirm.png.thumb.jpg
% T/ I1 V- y2 v8 K/ \2 h( T+ g9 ~

2 `7 ^9 K& |' W: {其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
- o) B7 m$ J- \ 105004zkrez5houvkkznko.jpg.thumb.jpg
9 w" e! T6 j6 N- Q
$ h8 ]$ u4 e2 {- V% q2 X; n0 [/ t
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。% e0 \& u. [4 i5 a

2 v1 ]% P$ o+ @6 o5 D/ G这是我设计的电路图:) n+ c, n# v4 r* d& [( O6 s
110344ej2z2oo2rflo7oe7.png.thumb.jpg
! b( v3 L  c# t0 o& [) e1 B
$ |- Z1 Z8 G$ |5 v
PCB Layout:
/ W) V( C/ c; a' N7 W7 {+ A! J- S 110847jjbjvt34vwt3v5bb.png.thumb.jpg , f  Q: [! b2 y+ y3 y4 `0 P0 h
- }3 Z8 H- }  Z  H2 k
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
/ `; f4 A! X) ?' d( y  e9 z
/ X  \/ v$ B  z5 y3 L0 |
8 e/ e9 L* C, G. ?; K$ N: C6 Y
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑
! u5 o, T( O8 ~- B* v! i8 W( v, N
& j) N6 `. q& I' y  q  A  v, Z. P软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。2 B6 H. z& d. k
1 Z6 O  B0 `6 u5 c: [- E) H
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:
5 g" I+ [" S4 r0 t4 q: b* n! @1 j5 p* e 113818pmrfsb6z0byt6t06.png.thumb.jpg 0 I: q+ G$ V$ u: ]
1 J. j- W! ]2 l6 x6 k
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
; k3 i6 z5 D1 U. t+ I" \+ X- z: O
USB的中断ISR,bare metal哦1 X: ^* E# H+ R7 \- \
+ Q* o7 c( r2 ^* q
void USB_IRQHandler(void)
7 L! U4 W$ V. U5 |, P# o{
5 E/ p, ], M  K4 I! e    if(USB->ISTR & USB_ISTR_CTR)
( ?5 l8 o5 k' S! A3 `  x    {6 A6 ]! f  n- f
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
" D' V, ^. j1 `7 @6 T        {
! @6 {% l- b6 m- S3 Z            switch(ep0_state), Q8 c" x  s* l
            {* ], u, W; H! ^* Y$ a! M7 Z  U4 L) C
                case 0: ep0_state |= 0x80;
6 ]; W% |0 [7 J' X* k                        break;
, I( k* S7 U  q2 M, [) \                case 1: if(USB->EP0R & USB_EP_CTR_TX)9 N1 U+ k- U' x$ O5 T1 {$ \0 C
                        {
. E4 N2 x3 D# n1 p                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
; n" c3 |' t6 t8 D% ^3 z                            ep0_state=3;
9 b7 P. \2 E' s0 R                            return;
0 `) D/ K* t0 Y1 G4 ^                        }1 N" h0 o5 Q! i  I) u. R# \% c
                        else
6 J3 O( v4 q0 D% f2 e8 p  p6 G+ A                            ep0_state=0;
* {, n# G. w" N3 w# L                        break;
& E* M* \% _; V; N* B# w' ]                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet+ K# A+ Z. K2 N1 b* c% P) C
                            ep0_state |= 0x80;5 t; P8 Q! Q: O; I( ?
                        else
9 m+ J2 S# s: F6 |* Y, q                            ep0_state=0;1 K; E3 A$ v) n7 K3 g  C1 X0 b6 p
                        break;
) t' {9 j& A  R9 c  X4 [+ d                case 3: ep0_state=0;0 W; }8 J1 D" W/ U: e9 E
                        break;
( ^3 w* p# Q. B                case 4: ep0_state=0;
- l* G4 {7 ^: l1 q; j* R2 j1 h  q                        break;
3 B4 S2 m, B! ^( Q. a9 A                default:ep0_state=0;
# V2 c; I" s# Q) H5 l- l( ~                        break;8 Q1 s1 Y  N/ }1 M  W% y& p
            }
" D& j, y) ]6 u/ m4 h            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag8 z. B& ^) T' c+ S( p! \6 k
            return;
1 m6 L4 U* V% H& I$ \. m, E6 w        }6 M, r& m! k3 a1 p
        else    // EP_ID can be 1
! m5 X7 M/ y* O$ c, {, n6 V9 m( N! S        {
8 {( _& V. s' o+ S* x            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag0 Q& {+ _! U1 @
            ep1_wait=0;% v% X1 X! E8 ^
            return;, i+ d& B! ^' l4 C# E- w
        }
2 R" _+ Y( C# t( z    }, r0 `, [! j5 o5 L% w) Y, D
    if(USB->ISTR & USB_ISTR_PMAOVR)  ?2 N4 r+ k0 I* p; X
    {( K3 g" b! W) K6 e2 Y8 B) x
        USB->ISTR = ~USB_ISTR_PMAOVR;2 _+ O# \, l* ?
    }, z0 S" G7 H: O  b4 }5 [% N( R
    if(USB->ISTR & USB_ISTR_ERR)
6 @9 p. H0 ~7 b% ]. y0 E    {
; J; P& v- u7 @        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
- u' Z: `" z* k: O    }. G  W2 H0 Z* t) s& g4 m
    if(USB->ISTR & USB_ISTR_WKUP)
" F3 X5 s* M) S$ g    {9 `- ]  J" \& w6 @% ]
        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
6 c# X" I# O; ~. K        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear9 m# P1 V) c. j9 V) N* U' T
    }1 i1 ?$ n$ X1 ]5 [  L/ f6 n
    if(USB->ISTR & USB_ISTR_SUSP)' L0 U; D; C9 ^5 h" T
    {
! C- x& }# `' W7 {, \5 m        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend
$ ?5 V; {! Y, h9 {- Y        USB->CNTR |= USB_CNTR_LPMODE;   // low power
' l* L+ ^7 O: ~) E        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear- c1 P7 f% i) a$ c/ T) [
    }
4 L4 \, X0 H& ~. |& |8 G    if(USB->ISTR & USB_ISTR_RESET)% X- S$ A5 ~* Z6 v* x& l" P
    {
2 x+ ~8 e, Q5 f) h8 L$ h0 ]        USB->BTABLE = 0;    // buffer table at bottom of PMA3 V, b7 `! z, {9 v9 Z
        USB_PMA[0]=128; //ADDR0_TX
% w4 s0 a0 _1 Y* r' n        USB_PMA[1]=0;   //COUNT0_TX
0 z) g) ^( I$ z( m) I        USB_PMA[2]=256; //ADDR0_RX# S% E  ^# Z# z
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
3 T% y+ I, Y. B% Z6 t! @( L        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;0 P9 @$ ]6 l% s* l
        ep0_state=0;2 @) `% b) p# G
        USB_PMA[4]=384; //ADDR1_TX
/ R' l6 r3 Y0 r! ]  z/ n9 S        USB_PMA[5]=0;   //COUNT1_TX
- X, E( Z8 C# V        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type, i( b& p: n4 m5 z+ b
        ep1_wait=0;5 n4 H0 D0 g& T+ L, \7 J
        USB->DADDR = USB_DADDR_EF;      // enable function7 w2 J* E2 ^+ m8 Z  L, X! w
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
+ Z* H6 w; Q/ I4 G% ?- Y7 j2 B    }
" c8 G+ N: N5 F/ E  L0 x# G    if(USB->ISTR & USB_ISTR_SOF)
; y+ A1 `$ J5 ?' l/ x    {2 V  F2 u8 G' M: M- q
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear
- n  h% t# a! a+ E& @( y/ D    }
$ M/ \' ~6 |+ u& H; _# l}
' r- P2 `0 l! ^8 p+ D: S% y; W: j1 ^# a% L
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
( e, K5 Z2 w( }+ o& P; F1 H  N/ Y. V
2 e6 G: D$ u" d+ B! D主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。/ f! y9 w4 j' M) z+ D

7 o1 C; t" ~5 G" }, t
    while(1)
  k7 z" p3 P' K( k& ?    {- q6 o4 u0 `9 c* P2 K; R( [
        static char row=0;& V6 `4 ]* ^3 J7 i3 R
        __WFI();
: Z0 ?5 S1 T8 P% g; ^        if(ep0_state & 0x80)    // request data processing
' A# ~/ `8 L+ Z5 m! D: ~# `        {
, N! Q' \9 @6 @7 J- `' s  H            if(ep0_state==0x80) // SETUP phase
* Q/ H- |  Z. d9 f" M2 q3 R            {
& h2 v3 r  `( h+ X                if(!setup_packet_service())
8 Z& j' V. P. b5 f3 n( b                {0 z! Z% ?. P2 g* g
                    ep0_state=0;1 E7 y: j( c1 ^$ D/ p( }
                    // not supported
8 A& l+ |/ i9 v2 S3 X3 o+ c9 }                }
" U; ?% Y! C) @$ P0 d  e$ D3 @                // ep0_state should be set to 1 or 2, if processed
7 g% ?( \# C5 D0 R7 o0 r+ }            }* k9 p8 T% C7 l  c+ a* v' p& i/ S
            else    // OUT phase7 B& z8 L. S: ~9 P4 O9 k" S
            {
; k; c0 j, w# \3 [                // process data
& \1 N# n8 x6 t2 F7 L. F$ ~                show_LED(*(uint8_t *)(USB_PMA+128));
4 B) _. `% V2 r2 h5 |% @4 w                ep0_state=4;# }- L  y8 \% Y: B9 ]3 t$ H
                USB_PMA[1]=0;       // Zero length DATA08 S5 ?* }0 p; `4 F# _- R: V+ b' E
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;6 S: R" |$ r4 W) g  A
            }4 z, ~* Q; J) J# Z9 v4 {
        }7 c2 n9 u! @6 Y7 `
        else+ J  F3 f! z% W# b2 i
        {, C+ Q, \' u( Q  ]7 R, p( \/ q  m
            if(usb_address && ep0_state==0)
. ^5 G, q+ W+ U$ p$ o            {
/ Z* r7 t% x' c$ F* ~3 j! y  q                USB->DADDR = USB_DADDR_EF|usb_address;
8 L# `% Y( k  X. v                usb_address=0;
- H' Q! A" T& y' E* X8 ?            }7 X, C! K0 a: s. h' R- C5 Z
        }# U0 ?6 }4 Z1 A2 w5 j5 h$ S
        if(row!=scan_row)   // new scan line
7 i1 b5 s" N* D6 h1 P        {
% B" k! S2 B2 V4 s* g; Q  P% v            if(key_state[row]!=prev_key_state[row])  o2 n1 L( ?  I% x
            {
: `- d9 {  Q* C' F                uint8_t test=0x80;
: l1 E4 r- }7 J$ y. U: ], c                uint8_t diff=key_state[row]^prev_key_state[row];  l6 ]* w. A" O; V! m' l9 K
                for(i=0;i<8;i++)* d( W  H% t. R7 W7 y
                {
3 c) f; ~" h% j' ^6 h                    if(diff & test)
3 U1 B  u" n' l7 G: t0 x7 y% P/ ~                        update_key_matrix(row,i,key_state[row]&test);# E! f! |3 Q7 h* a! {
                    test>>=1;, l5 u" M' E( I8 Y; q
                }* d" g, o0 _1 R6 L5 F% t3 J
            }
$ m1 g; I3 Q3 _5 M1 Z1 T            row=scan_row;
" V. ^% O. _. x, L3 L        }
: V& r* F# w! E; j( g    }
6 K4 b- I! g5 C! ~$ `- C  A& T* j' `- S; X. Z

, M# F* P; X( T' iEP0的控制传输,把用到的请求处理一下
. K) `' k1 ~$ [! L2 k* fchar setup_packet_service(void)
; ], @; ~# R  m{5 O6 P4 n9 ~7 E  n, M
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific9 F. G/ m+ r! r- I$ p8 ?8 z( n5 I
    {
+ `, I& i. y: Q$ h6 l        switch(ep0_std_req->bRequest). I$ `& x) m; Q2 i$ c8 ^8 r
        {
6 d" r8 j5 Y& j            case REQ_GET_REPORT: break;
; k4 E, O! y* K5 u. e            case REQ_GET_IDLE:
- y% q( b3 E+ e                USB_PMA[64]=0xfa;   // return 1 byte
  m! g3 x$ v- U  a- y                USB_PMA[1]=1;3 o* `' V, ]! a
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
" r) P( r" i* ^, f                ep0_state=1;* M, q, {3 l4 S, S
                return 1;6 K* Y- j) B9 x; q$ S9 W8 R6 N
                break;& X- U/ O- o, ]
            case REQ_SET_REPORT:' U  i$ ^' [) W  L  @
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;, N+ }0 `4 F9 l( h& A+ e
                ep0_state=2;
4 ]1 ]9 L- f/ q- P                return 1;+ D: }( ?, o% ^: x& H
                break;; d0 N6 U  Y; W- w8 c! ^
            case REQ_SET_IDLE:! n% G5 M3 m" a; e' k; V" N
                USB_PMA[1]=0;   // Zero DATA8 F) f4 l" X+ i* B; t) f
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
+ N$ e6 }8 {8 i* q" g                ep0_state=4;: G2 S8 G: a7 y) u' r! x
                return 1;% c' v8 z  b/ Z7 X
                break;
6 J5 @$ t3 B0 r' W# A        }
/ H1 i0 t1 E' L+ g% X9 E- j+ t0 v        return 0;# M, P4 P& a( b4 x$ V8 H
    }
9 o3 U4 O& V3 F1 J2 ?3 V- E    else    // standard
( V7 l6 H. M2 y: D; g# ]    {$ z0 W. h! k  x  ?  S& m% C; y
        switch(ep0_std_req->bRequest)
7 y/ g9 F2 G" {3 x1 k3 @        {
4 k' N" Y$ p5 B# u5 c            case REQ_GET_DESCRIPTOR:+ @. V. C, W) l, b) ]
                return descriptor_service();7 M0 {+ I0 B5 T6 \$ m  Z
                break;1 D: \% u4 w, p5 j- M
            case REQ_SET_ADDRESS:
) o  h( F9 m8 ^: V  |3 x                if(ep0_std_req->bmRequestType!=0x00)
5 v: M# R) m4 r# x                    return 0;
% {. q/ }& h& T% l9 P                usb_address=ep0_std_req->wValue;! t1 C' c5 _  N8 ^
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;2 f9 a5 z0 l. |, f9 |8 g8 l/ Y
                USB_PMA[1]=0;       // Zero length DATA0& D- W3 E2 w) E  l; H# M& R; ~' J
                ep0_state=4;    // No Data phase" _- }& ~0 Q$ e/ r) H  h( |1 Y0 o
                return 1;( z* w, R) Z. N) o. I/ b* Y
            case REQ_SET_CONFIGURATION:/ H3 ?+ F- Z, l. D
                if(ep0_std_req->bmRequestType!=0x00): P" a" d" d) B2 f1 a1 ?* [2 l
                    return 0;& Z- H5 q9 R, }8 c6 J$ i5 z- x
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;- p! y0 M/ x7 ]; @0 e9 V
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
8 S: ^+ D# [4 a6 d$ o- x& ~/ p                USB_PMA[1]=0;   // Zero DATA
  n" ]' O' A. D! U, X8 x" h                ep0_state=4;    // No DATA phase
1 B2 n- _" U& Y2 p" p/ P                return 1;+ g. h' U4 H* [) y6 r% \8 O$ s
            default: return 0;7 d9 i  Y/ h; ?
        }
/ l; J; n/ X! j4 I% n3 x6 ~- E    }
! e, R+ ^! n3 d5 E( Z}; h: t" e) r+ y
' ^! F  P) S6 z# e

1 L" w' B9 g, t
4 F: D& O8 o( }0 @4 v8 ]0 V
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
1 I: }6 R: i  Y5 ?1 i3 gchar descriptor_service(void). l- ]6 s4 e, `' x
{& s/ a- b  r9 t" j  N/ H
    switch((ep0_std_req->wValue)>>8)* A9 @! z# x5 z7 _3 T
    {
- O/ b' y% o: S+ p! C        case DESC_TYPE_DEVICE:$ G2 }! R% c, E* C+ j
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));
7 J/ E: f# N5 c9 [+ a            break;  e$ M( {) }( M% H1 H+ b
        case DESC_TYPE_CONFIG:
9 ?- Q% [# [5 u  r: n            if(sizeof(ConfigDescData)>ep0_std_req->wLength)+ {0 ^! Z6 V. S
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
5 S8 ]( _) C) K; j. g) v3 o            else
- ^/ E# l% M! t' X& X+ B                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));7 D" R8 A* a3 J' Z6 G$ c1 w
            break;5 X4 c! ]0 @7 d5 p
        case DESC_TYPE_STRING:7 B. N6 u1 n( M8 m: J
            switch(ep0_std_req->wValue &0xff): Q$ {* W& ?+ R
            {) e2 G7 q9 ^$ N& B+ E' d
                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));+ K) L+ V  U8 a
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
6 c9 Y- l6 R8 z# n: i                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));, m0 r2 r7 a( p. O/ U0 c
                default: return 0;: ?! A4 ^: M( F- d& S
            }
$ m0 @, t: Y0 n            break;3 j; _5 }$ r, P
        case REPORT_DESC_TYPE:
0 o' y% ]0 ]* ^; z7 z! E7 L, [8 b            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));" J: A4 w" c: Z* Z! Y# d& B
        default:
/ E/ o" l, f+ F! @5 q, |5 @9 o            return 0;6 l9 c9 [) M- h2 Z
    }6 |9 ]3 S6 R8 O! s
}* \1 u0 c6 ]& T6 _
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.3 ]+ z% L# [1 X" V9 }0 O
void TIM6_DAC_IRQHandler(void)% X' _3 ]) i" T! A! s
{! @/ s' t& E: j1 ~5 O
    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);. L- f' Y+ Z8 X0 S3 `

, e- w' D$ u5 M# q! S
- v3 A7 X; w: s  a# d0 e3 z
    TIM6->SR &= ~TIM_SR_UIF;% s/ w& k& ]4 ]( s/ u
    prev_key_state[scan_row]=key_state[scan_row];
: Q3 r: I5 r% O3 }( r5 x0 N    key_state[scan_row]= *PA_IDR;   // update key states
7 O5 Q, R$ H9 l" a' i6 F    switch(scan_row)
$ p7 \, ^7 ?1 R1 a2 G0 h8 f6 G    {+ f7 g' y) A  P7 J9 |
        case 13: // next row PB14
* s+ S& Z. o9 q/ D; o. W/ I2 ~                GPIOC->MODER = GPIOC_DEFAULT;# c& Q3 {- u6 ^% b. f8 k, O9 y
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
0 X8 X+ r7 B" H! V8 Z7 W  G                break;- e0 h, E! H+ ]- k9 X: j+ m, T
        case  0: // next row PB15
$ K: D$ a/ Y* B                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;
: t9 @/ U& S6 g( m                break;5 ]1 O9 o2 [+ C
        case  1: // next row PB3
: @# v$ ~/ r$ \) H" S' b                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;, |, P4 f' H+ q* D2 [: o% {
                break;3 @- F* I5 s! d- N& @" s, N9 ]7 O
        case  2: // next row PB4
0 a3 J% L4 {2 p" i, s                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
% w* I; K# M) u; ^' F5 R* o                break;
3 ]' G  X2 ]+ W$ [, _; _9 W        case  3: // next row PB5! F, _/ _! {, t: O
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;- \, y2 \# D' U! X  L& w
                break;0 L( w2 ~9 A. C3 ^# ]2 k
        case  4: // next row PB6  _; L! G' d/ n$ C1 E
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
# e; Z- l! t8 S" F* i                break;6 e% q9 t3 `+ T4 z6 j
        case  5: // next row PB7
* h5 A$ {" R! W; x                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;, ~0 _9 |- k; H8 `4 S; h
                break;& @1 k# ]" ?; o" J; y" |/ c1 E% k: b
        case  6: // next row PB8( d! ~! O) h1 z7 m! A9 X
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;
- L& w' A- m2 Q% W                break;
/ ]8 h" L5 G+ g9 q3 E        case  7: // next row PB9
0 V2 Z9 d# Y' @9 |                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;$ Z5 }8 A1 W; W" I$ l6 Q
                break;6 y2 I& ]4 ?8 @& H+ s
        case  8: // next row PA8" K% F2 r# `) [# W: h
                GPIOB->MODER = GPIOB_DEFAULT;
0 V- |) z9 {) Z* d) {9 E                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;5 Z& `. f6 H0 \0 k
                break;
9 l3 h6 ]7 M8 \7 `" A  L& q        case  9: // next row PA9) G$ h# X; L* o& j
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
0 D0 p: s  ~' r6 t! c+ G3 Q' [                break;. N, w: }, g9 I$ j% M7 M9 F
        case 10: // next row PA10
* i# M' w. d# ]2 S  f+ `0 J                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;2 M& \9 |; l( t. M
                break;: l0 E, u: T1 s/ z5 H# f9 A
        case 11: // next row PA15  B6 v' M" ^& z0 C: [! h
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;" `; X5 Q) t" h$ }3 a) Q2 x9 a0 }
                break;& T" c8 b9 Q% J
        case 12: // next row PC131 @9 ]# h% V7 u, D5 T
                GPIOA->MODER = GPIOA_DEFAULT;  S& h( C9 E  S1 I4 q
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;' G% j- S  P7 j: q& R: T
                break;4 [" x9 e) U2 m8 O$ ], ]' }
    }
+ P" @! z; y# u& r. M0 ^8 e1 Q9 s/ F    if(scan_row<13)
, C2 g+ E; Y' C! s7 r        scan_row++;
" Y& ^, s) e8 C" E    else  n4 q/ S4 w) w# B1 J6 [
        scan_row=0;; i5 B7 i! d7 R  U: @, H
}
7 f; J% H3 c% K; q4 v9 T9 b0 t$ e" [

" I3 Y) L: W- x. U
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 & d$ C8 e/ w4 ^6 o

: l$ ?6 O; p  l" j' x+ y% q扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
2 r& T* b; k" R" O% c0 U* U# a$ H% e& H, O8 a8 f3 l4 f
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
6 W/ i6 l$ ]3 U3 i; M

- B" _# }. ^6 B1 O, n7 I0 u: ~# F+ E6 B% j
const char hid_keymap_qwerty[14][8]={& S! I9 \$ r' r7 s
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},' W# o& v6 [, k' P+ p
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
4 P+ n$ p& s5 ]/ G4 d    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
% e  v6 J5 t: I" u' N" F# z    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
8 h& B4 S* b! C" u: M    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},. ^. L1 n* n( L/ C/ L
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},- i; b) O8 x$ b! ^8 S8 _* ^
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
, \; I1 {! R( t$ ^2 A    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},& i& N+ j' N3 n3 J
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
5 B( O- i; S: s    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
% y1 z: v0 A" ?* }# j1 n. C) }7 P    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},/ t% b" U! ?8 |" I
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8}," k+ I9 ]+ [- S# |) s1 [: P8 G3 E
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
) c5 x* d$ V; R) K    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}$ n) r; Y; m% @8 J
};
  ^2 T; [6 q7 s
( M/ ?( E4 h) h
' S+ T( h! X4 F  X0 N, T
const char hid_keymap_dvorak[14][8]={  v  v* c* i& v0 P9 ~/ k4 S1 f
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},; q& A% l) l4 A  B
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},0 w$ D# ?, [0 z, O+ a. Z
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},- \. q0 O! K  ^( M$ y* w( z
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
& J- l$ A0 t5 N0 @7 ^' X0 z    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},5 p% s) C' H: J8 X9 p" p6 s
    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
" @0 n6 N7 @8 y    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},2 R$ ~. f9 P/ M$ V" l5 {
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},2 {& H' [( g% K1 o/ c' F
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
* e5 S& j" y) j) c7 N    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
% D4 |% y/ ]  f; H1 w    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},- `& z' s6 ^, K8 N0 C
    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},2 b3 ?7 n2 x0 \# @5 b% w: e
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},, i! O, b9 D1 j( X1 T7 X4 J2 F1 q5 L+ p
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
. ?5 F' W( T  u$ b6 E};
2 c6 s9 U6 T; C; I8 A
% |; i. O) L( B- {# L) }. Q/ w" \6 J4 ?# H/ U
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
3 \! K% Z+ O# w7 I0 w2 V" W" v6 s! v; ]
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:
9 p# A* O* O9 P' Q- Y& \" V1 B4 K7 M

- E3 q( a) t+ I' x: [void update_key_matrix(char row, char col, char onoff)4 Q# k  R2 A8 E4 w+ h! B( @
{6 M# ~0 d3 m6 x
    static uint16_t hid_report[4]={0,0,0,0};
3 r) @+ V2 J, i! h" d    static char (*hid_keymap)[8]=hid_keymap_dvorak;
3 \" x/ x6 V: {* S/ x! B5 j5 W% N; c5 m& R' s  E
: Z$ a! q3 C; L( s: G! y
    unsigned char key=hid_keymap[row][col];* Z1 e8 Z+ A2 }! {
    unsigned char *report =(unsigned char *)hid_report;
8 Y3 L+ M4 a& e: k: N4 {0 I    char i;
3 n- w2 e, G# V: h! L' D
8 k# W3 N' b' F. Z; @2 R( A3 d

( t  f. i  z4 U# ~2 _2 N% W5 ~# ~  k    if(key==HK_MODE)
* T6 v( e+ Z5 J    {5 Z5 i# n" |. p0 W. Y# ?  s
        if(!onoff)/ e$ m! v( `1 O) j# r
        {
" @1 r! ]4 Q5 Q7 m' [            if(hid_keymap==hid_keymap_dvorak)
2 c9 f' f! \$ B. y            {
" h! S; o/ ^' X+ p! r& c2 l                hid_keymap=hid_keymap_qwerty;8 n# m, U, H1 O0 K# h" Y, V4 S
                GPIOB->BSRR = (1<<2);
7 v: B- l% b* a            }
7 e4 L  j( w6 I  f/ w            else
) @" A$ i* A. J$ t$ s  R% K            {/ e4 l2 i- l2 M& G
                hid_keymap=hid_keymap_dvorak;4 K8 X$ k# V) G# x8 R
                GPIOB->BRR = (1<<2);
; @; K2 o( L0 D. h3 M            }
6 ~# K1 n/ P) Z6 g( G        }, L6 ^* l5 u2 V- `) `& Z
        return;
9 B1 t6 a5 q) L' I% j4 k& B    }
/ H/ K: m. c  F) F7 k  O5 K
# O/ A! n6 }* s% E

% F; A4 o$ h- [& I5 a- L4 P5 o0 g    if(key>=0x80)   // Alt, Ctrl, Shift2 M. C( Q/ E0 m. S
    {. \7 ]+ [1 r4 x# P$ v
        uint8_t bitset = 1<<(key&7);
0 G" x: M7 @. H; Q( |- o7 L; {) ~        if(onoff)   // non-zero is key up
7 ?# G% w- _) \/ ^8 ^            report[0] &= (~bitset);. j# S6 y  j4 N! X- v7 u  R( q/ `
        else
7 `% e+ ~1 z  [* q  r/ @6 @            report[0] |= bitset;9 r- _4 z  a1 r9 v! p
    }
  R0 z" m% G9 ^# S/ B) L* ?    else' V! N* J/ H6 |! Y3 b' |3 r9 i
    {' O, l% m$ M. E" D
        if(onoff)   // non-zero is key up
5 s5 O( W: u# c" {, }        {
2 _2 Y+ \  q: e2 O- u0 m: M            for(i=2;i<8;i++); f' Z6 d' ^; H! u0 p; W) u
            {& s% O" J5 U& S; y/ r1 G
                if(report==key)
/ T" P1 l& P, X/ Z. s5 G/ T6 {                {
- T: C6 O' X) y) q% h6 N$ ?                    report=0;
+ r5 l0 Y) r' J; K: \                    break;1 i& `$ U4 n; i8 Y* V
                }1 }3 w+ a: B- G9 T5 m8 a
            }
2 D& T- p4 X( v1 P        }" b  ]: `4 g+ y: C
        else
6 `, ~2 M3 [7 t        {) u. \& e. o$ F: N- M* F$ I% b1 m
            for(i=2;i<8;i++)
1 P* N& U0 V: p" [8 H            {
- K) @% e- ]7 f* k2 ~                if(report==key)$ l; G& R! Z3 b) ^: z7 A
                    break;" \% E( U: [" {) X/ t  |0 W* p' v
                if(report==0): S8 Q# P* k# M/ w6 o
                {
8 J. g- N$ L# [                    report=key;
! f- L- a3 k( t' e: {# ]                    break;
+ X5 _; t) _( U% c) k                }
' B1 d$ R  r+ u/ p" r) N! i            }) D1 Q  T6 q2 j- H& k) d
        }  G; B: @2 k4 f8 B( E
    }
. k8 t8 T( u" ^3 M7 c    for(i=0;i<4;i++)
8 P; e1 i# P$ W  V        USB_PMA[192+i]=hid_report;+ _7 ]# E6 Z( f4 K1 i. w
    USB_PMA[5]=8;   //COUNT1_TX
9 I5 L! [& L0 n& U5 d    if(ep1_wait==0). G8 f) \5 Z& Q( ?
    {7 j& G/ U+ K6 h# K7 }1 ?
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;7 j1 o3 n) Q7 I' B) Q% j1 @  M
        ep1_wait=1;
  o3 w# n5 L$ w7 h2 k: K$ D8 M; E    }/ @; ]/ L3 L, i4 w- P  v
}
1 _( @5 ~( N, Z* ]2 e* W2 Y: b
8 v: I6 L% @0 I1 ?5 \$ o+ j' v# Q$ I( A5 G, P; F% x
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。( i  b6 ]5 L- F9 j8 X& ^
keyboard.zip (8.7 KB, 下载次数: 6582)
) d' T  M; x, P, f! {
+ I  A" y* D; Q

& l; B2 }' ?2 x: z
: h/ z) T( ^% \/ u8 U( r! z9 G5 `- @
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。0 ]' @: N7 u7 y! @3 g& O; m2 X5 ?
回复

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘3 `* \8 d) w, O
不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:489 F- b) Q- ]4 W/ b; b9 |# m  ^
刚开始我以为要把打字机改造成电脑键盘" Q- S% r3 |$ Z) q
不过楼主也很厉害!

) T, u3 u5 U8 Z/ W: L1 _哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害* U2 H5 E& J+ H. e7 [- u
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-6-28 03:01 , Processed in 0.189522 second(s), 27 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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