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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

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

& w& G1 F  o: O
& y3 |: v5 a# t
235140i3a36qivqzuvmt5q.jpg.thumb.jpg
) j0 ^1 X/ A9 P+ x/ `' t/ u
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。
+ A9 C1 a$ d2 S! {) k2 D! M( s 001734klbyoluenuwz4h4b.png.thumb.jpg
. v5 B3 T* l* c- i为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:& X  y0 d- g0 K% g
003625r2agx2f5v922cf2f.png.thumb.jpg 8 [9 A( R* Y" _9 \1 @/ T9 ?6 p
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.
# P) w! T* ^$ [% N7 PDvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:

* j1 D8 h  K) o+ ]- q 005836yvs0wvovwsssgd3o.png.thumb.jpg $ H2 P" G+ b) s- M* a- ]( @
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。
& r, R8 X; `7 P) W6 X* F% m8 B1 f0 }3 H& Y
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。
" l, X) ~9 f* W$ g9 {  ?
4 L* T1 k" C, y5 ]# F  j: k. C到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
/ e2 ]" G2 x4 z0 B( R
+ T% U" c# F& P, y0 r  r& c  t机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
! G& Y9 t4 ?3 O* l& K0 P; N
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
# N( P* j& b, p+ c; b5 J----------------------------------------------------  分割线 ----------------------------------------------------------0 e& @$ r# C2 }3 Z: V1 U/ n

9 n. D$ P; p6 a( u先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
* v9 S  a1 _8 }8 F9 {: g$ _% A
020011osionbunl4ui44vi.jpg.thumb.jpg
+ r" |# P9 v  r9 e9 }" j轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
% @+ K6 O- M  S/ p 020017j8ycmnv7788bqv52.jpg.thumb.jpg
6 t6 A( G9 p) }8 M! {特写,80C49+ e; Q- c* G! L( }5 S9 N8 v& P
021040oujzuvtut6iujtvz.jpg.thumb.jpg
) p/ i$ s0 m* \LED部分,使用了一片D触发器锁存指示灯状态.. X/ X2 C0 p0 Q* b
022209ixzjfjc9crmrrqjm.jpg.thumb.jpg   P5 V% j% V0 A: g- j* R1 g- D
暴力破坏,将80C49拆掉
0 Q" q( |) F2 z$ _: o 021113e48qq98vyohvhzzh.jpg.thumb.jpg 3 k( }5 r; a5 D: y9 m
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。+ R8 i/ {7 v) ]
021125nc9az6dj33rlds2r.jpg.thumb.jpg & ]" N' s7 V$ Y
焊好元件后的板子,准备替换80C49; Q, x5 m! C) }" J) ~/ U
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
# z: P! B$ p8 ~1 H& Z. y用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。. D" j" \! |1 ~' @: Z* q( R* ?3 B
021104shifhnrqbr3o5nlo.jpg.thumb.jpg   T( a# [6 Z3 ^/ J7 c
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.2 a( d& o  f( |  k' X" Y
022003ym1p9u4ug40280uu.jpg.thumb.jpg ) F) y! \. {0 `! {" y
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。* O0 {2 l8 V& i) T2 T& @% Z/ s
023313kt141q9qajtol7ma.jpg.thumb.jpg
9 e: b) e5 q9 i* s% C我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。
* U3 U$ m) B1 @+ S 023322nt7l5xb3ltttkltt.jpg.thumb.jpg ! H6 ~6 M2 i; r
主键区键帽就位! s  m6 w7 h. X0 o
023331hin88e8wkrwzwikx.jpg.thumb.jpg ' h. [( x! [7 J: m3 s6 W, p
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
$ M6 U, o' k, ^6 q3 b 023336wjzlgopugg1jyy79.jpg.thumb.jpg 1 j; C' G3 D; P& F* y
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。2 `' V/ p4 q* Z. |% ^  I
023341sffu4j3g2323h6fl.jpg.thumb.jpg 1 B$ s" O+ x' A& t5 v9 ?' B
4 s" O4 y# k6 P" g5 F
----------------------------------------------------- 分割线 --------------------------------------------------8 c7 a1 A, S- Z

9 D' a- e  q) ~: e3 K

* S1 Z$ n' J. [3 P  ^8 a1 B
/ g* T1 J! O! g$ \
5 [- p3 a( A, [; C  y2 P8 ]/ [" `1 a% K8 [3 t. d' s, Y
' F9 G" Q* j8 f$ h4 f# Z

1 n# `2 p1 \% G9 L, g* E' m
' Z( _% j% n- P# b* ?4 D! L! `. K6 ]. U8 ]& Q) i3 f4 N2 v3 F4 H3 r8 E
' \8 h3 @- l. i% ^6 [5 \8 E2 Y4 i

8 p  r3 @6 p) O7 Q, F+ r% I
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
/ f3 M3 }9 l+ a1 B80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:1 Y$ k& L( N% e
104025nzibm2rmiomhyirm.png.thumb.jpg 9 e9 |: h3 X: v/ O8 ~
" q) M: Q. r! m# L
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)1 w. c, z! A- t# g
105004zkrez5houvkkznko.jpg.thumb.jpg
. x" N  @# h9 w* F0 W% W5 N

& p% |0 f+ ^- H3 }3 [1 R6 Q扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
( R% T6 Q" k. D/ f( Q1 z" S: ~" P
4 p5 O% B7 d; u: m! ]这是我设计的电路图:
) T* {, L' a0 V7 Z 110344ej2z2oo2rflo7oe7.png.thumb.jpg 7 ?& D8 \) ~$ z  s

. W+ h3 d6 M' D+ b2 F9 m9 FPCB Layout:: o- H. d* W& b; P1 R; ?
110847jjbjvt34vwt3v5bb.png.thumb.jpg 6 d4 x8 B; H) _
8 n7 E# }  A% S
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
( H* [1 \9 S! x( X8 S
) f! C( E3 F, o# B6 |

2 ^2 H2 Q9 g! q- D
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 0 V3 K+ x, A2 @- E

3 U9 i% W. y& F5 p( P) N软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
& G. t; t3 h# R0 y
1 ^, M# l& C* \" |+ R6 C总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:6 E3 ?( |! x# z" p0 a- j2 i; M+ w
113818pmrfsb6z0byt6t06.png.thumb.jpg
$ N" A  ^1 O8 K# g0 o8 a
; L3 y( m" t0 M9 o) R2 v; n
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
8 W5 g6 ?9 \1 P$ s* s% N& Z, b0 B9 \- D( W. _' F  \" T
USB的中断ISR,bare metal哦
  I& ], G6 H$ k7 G

- `, g. O* D8 ~( Z- z) E6 ]void USB_IRQHandler(void)' Z" m. m# G4 g2 q3 y; k3 V
{
3 r; a4 H' `! c    if(USB->ISTR & USB_ISTR_CTR)
& b" W: N' f# z' }# x! c5 }    {
: V7 E9 C6 F, ~2 S, b7 c. j        if((USB->ISTR & 0x0f)==0)   // EP_ID==0
3 j! D+ ^5 j8 O9 M% p7 k        {6 q' z) p$ D6 d4 i% K/ A% a
            switch(ep0_state)2 E6 K* o9 Y, {( D% A
            {4 y% f2 d1 v! k, u7 }
                case 0: ep0_state |= 0x80;
" k0 y, @& S) h4 z                        break;
/ h6 h. H6 w0 a6 {( O4 W5 E3 z                case 1: if(USB->EP0R & USB_EP_CTR_TX); h4 q1 P! k/ m( n/ O5 y
                        {$ I* E4 r( r. p3 j' A$ O# u" x& m, o
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
' A4 E3 Y5 w) z% l- L. W) T' ]: ?                            ep0_state=3;
: Y0 N' X! Y6 T' X- Z4 l                            return;% i2 I+ h, m; L7 M9 D
                        }2 m8 H+ ~6 ^+ [
                        else8 q- }" Z0 v/ k4 T. ^
                            ep0_state=0;
7 j, e$ I, X! ~/ L3 b) }3 p  C- w                        break;5 W/ o; I3 Y- R9 S
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
+ ]1 P$ g& M& c  o' `5 |1 n: p                            ep0_state |= 0x80;3 E- ^4 U* ^) ?1 r+ u$ J
                        else. j4 u6 q6 \: w! y: B
                            ep0_state=0;
# @) d: F  ]& Z0 N7 V# u& U                        break;( {6 E& b' X6 D/ L/ L
                case 3: ep0_state=0;' @" o4 c* w# g3 E
                        break;5 X2 J# S- {. W4 V; G
                case 4: ep0_state=0;
3 ]+ x4 ~; `$ F$ h5 a' q& K3 Y                        break;8 u# b" o- Y7 T$ z$ g  Y
                default:ep0_state=0;: _% y/ q1 F% {1 j# q4 ^, @
                        break;
! ?% u) G4 g+ x            }: b3 b& _3 J/ U0 T) g: q- a
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag' [/ E, }  R" H1 V. @- U
            return;4 y* [4 R5 e/ |% t' \7 ?& i
        }
0 f, g6 z" W, o/ L! ]$ }        else    // EP_ID can be 10 w2 T! [% t  g5 ^2 i
        {
8 Y8 o" _6 u6 A4 A            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
* N; d: y$ h/ l/ c            ep1_wait=0;( I  ^) K$ K1 a" ^
            return;: p& C# M+ u( Y
        }
& r: j# z2 v0 H! }    }7 p2 F  U5 R" k1 k9 r) ~; C
    if(USB->ISTR & USB_ISTR_PMAOVR)
* d2 f3 c& c; c5 s  A' S# \    {' ], R, H5 `& C! m& o* b! v; ?
        USB->ISTR = ~USB_ISTR_PMAOVR;
6 z  `5 }1 e5 V/ ?( o' E5 Q) C- {    }
1 n* |. v7 D8 h$ `, H& V- o    if(USB->ISTR & USB_ISTR_ERR)* b9 I8 v; R: S7 I$ W
    {& X9 G( s' Y. ~, f5 ]7 A) h+ t
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear; t4 E5 X  h7 K7 c  Q4 H2 @9 ?3 N7 n
    }- ~6 T1 k/ q9 w! o. t* `) v
    if(USB->ISTR & USB_ISTR_WKUP)" h  V; f6 a0 i9 v/ p! S
    {5 K2 S$ M" ?) @/ l& @
        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
; W1 {/ }# {2 M% p, w& X9 ^$ b        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear0 U2 }* S0 d- o0 f. w: [# P
    }
+ a- I! y+ V1 i8 b9 i3 [; _- x    if(USB->ISTR & USB_ISTR_SUSP)
% h8 ~* m1 Q! F8 Z, o: e1 U& c    {
$ w- U, i$ x; s6 C        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend, Z* L* h% \5 R8 Z5 B1 B+ a
        USB->CNTR |= USB_CNTR_LPMODE;   // low power) e+ i4 ]9 f0 F7 G$ A$ x
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
! C8 e0 \2 ?6 C( a' \+ y    }( G" Y. i- x) G8 p6 L# C
    if(USB->ISTR & USB_ISTR_RESET)
. J! @- T' E' h) d8 f+ [    {0 ?- L6 a/ n: `( Y
        USB->BTABLE = 0;    // buffer table at bottom of PMA5 {  H8 `% r/ p1 i+ b
        USB_PMA[0]=128; //ADDR0_TX
) L( e% r% X# _7 T2 _2 u: h; y        USB_PMA[1]=0;   //COUNT0_TX, `8 R# h8 w& g9 w6 Q. j& v6 U
        USB_PMA[2]=256; //ADDR0_RX
* a( F# P0 Y+ b6 @+ i1 q! M: U        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
$ z. C9 G4 V. J6 t0 C/ Y& v6 Q0 N        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;# C8 C0 R: Z8 P2 k" N
        ep0_state=0;3 n* l# ?' O$ f+ J
        USB_PMA[4]=384; //ADDR1_TX. n. E- R! u7 f/ N8 U, Y" W
        USB_PMA[5]=0;   //COUNT1_TX
3 t. z* R! }0 J2 D! t, m        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type, E3 u1 E, m0 j' _1 X0 d
        ep1_wait=0;  O7 w7 v7 C& M$ H' v8 V
        USB->DADDR = USB_DADDR_EF;      // enable function# N/ X! n  ?2 O+ s2 L  P7 b6 L
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear! i! k9 S- l/ q
    }% m- r& D, W: n" ~. L' a
    if(USB->ISTR & USB_ISTR_SOF)
4 z  d( X/ K5 |    {
+ w% r6 ?( O: ]2 m+ R) n7 D4 j! F  Q        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear: @6 g) X+ z: g' g4 P1 _# t& O: ~% g
    }
) D3 w& A, U& A) g}2 h- q. E- f+ U7 {6 S# d* c# @
+ B  f1 N/ l$ D/ n) a) S) [
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。9 E- P' x5 N- Y  R  b7 ?

! z4 I; ^8 G+ V9 {! F5 P6 e主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
6 G9 C* {2 b9 f' H# T4 M! D/ [/ U! h* N
    while(1)
5 h+ J* \, @- e& j2 g- r: @    {# X" r, E$ Q3 g0 X2 _
        static char row=0;8 C8 C! D; ~+ b% }+ F
        __WFI();' X* ~% n' W4 E) O& j
        if(ep0_state & 0x80)    // request data processing3 b3 |1 `. v) h! q* W  A
        {
6 O2 ]1 e7 F( |1 k9 u: ~9 h            if(ep0_state==0x80) // SETUP phase
4 y: l  Z( n5 ]# z% H( t* `8 W            {
9 R( g. A" A' I8 r2 S2 J( ]- w                if(!setup_packet_service())
' ]: \" k1 ]: R9 z" [# Y                {1 t: v; u3 ^2 k  ?
                    ep0_state=0;" b- c& ]2 C6 @9 v5 Z/ O; u
                    // not supported
- p& {! W1 A+ j" F                }/ g! `! T: ]! M
                // ep0_state should be set to 1 or 2, if processed
0 P& ?, K8 R& y- `            }! F' \+ W* c! @. g1 |4 T# v
            else    // OUT phase$ c% @" i6 E- e  F: S
            {
! j, e3 i# G7 b6 g                // process data
5 N9 k# d3 f/ R( o3 J* `                show_LED(*(uint8_t *)(USB_PMA+128));! {% h8 l2 ?: J$ _" W  `0 n$ t4 W
                ep0_state=4;
. O' f" Q: H4 z5 I' V                USB_PMA[1]=0;       // Zero length DATA0
8 i/ V8 p7 R, n5 V  n( g$ x/ |1 w                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
+ e8 N3 h/ X* t( B1 x3 i( o1 [            }! f0 Z( l  y* I- ~' ]$ ~; ~0 V
        }
4 v, ?  P7 A4 V0 N. m3 m9 Y5 h' Q        else
$ [1 w5 `) W8 ]$ G& [+ O        {0 Q. e) h( Q2 V6 n6 T! e5 P
            if(usb_address && ep0_state==0)% F. S. N/ Z0 I9 e- t7 }2 z8 X
            {! I9 N! Q+ ]% {/ o/ ~& n, A5 g- G# X
                USB->DADDR = USB_DADDR_EF|usb_address;+ ^/ w3 N1 n' A; n3 A/ n7 ~; e7 D
                usb_address=0;4 ?* p. ]* }' d
            }* P. Q: [& T% D) m6 ~$ E
        }4 w, B2 }0 f7 V, g7 P
        if(row!=scan_row)   // new scan line  c: ^  O- }$ J2 {
        {
; E: I0 P: [$ l            if(key_state[row]!=prev_key_state[row])( e( N! U1 R' U4 P& Y
            {
- d' y9 f, G+ s5 d- `                uint8_t test=0x80;
! s1 q! j0 R4 b7 T! L                uint8_t diff=key_state[row]^prev_key_state[row];
; p$ P& U5 d( d0 E' F0 Q                for(i=0;i<8;i++)
# v* {8 N0 Z: H+ q                {
% A2 M' q. B/ _) h" R: Y, F) [7 z                    if(diff & test)$ X, H# `3 d# D6 k6 B3 z
                        update_key_matrix(row,i,key_state[row]&test);" N+ V8 {! z$ U8 s' ^
                    test>>=1;
% Y% s% B+ O& ]1 ~                }
; N( X+ t* l% h9 Q  s            }
1 ^4 b' b, M, i            row=scan_row;( ~# J4 |0 ?1 }" b) l( x
        }. F& I. q5 f, j! s
    }, V- k7 g+ u8 e$ u! J# o

& M8 j- U6 q9 c4 x3 }' t5 h, X. z: x2 ]6 R" w& L
EP0的控制传输,把用到的请求处理一下$ G% W& @- m* p& t$ t3 |% D6 o
char setup_packet_service(void)
$ |# O2 _' E& P0 w. V4 w- y% P{& g; v6 ~1 E6 u. D
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific" e; u. |& k5 @# _7 x0 t6 H& W
    {
9 _: G9 E) t: o' [2 _) V        switch(ep0_std_req->bRequest)
: R3 ?9 o6 y. J; n2 O" n' {. d        {
$ ]4 i( o1 h" R# A4 I6 C) }3 N            case REQ_GET_REPORT: break;& ?- a, w! i1 t" M
            case REQ_GET_IDLE:. i7 ?$ o9 B) L. x2 \- |+ f
                USB_PMA[64]=0xfa;   // return 1 byte3 ?) f8 t# ~2 }3 f
                USB_PMA[1]=1;
% v* v0 W2 o  X! b# t. W4 K                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
; M. q' \* j" B6 U                ep0_state=1;
! A. ~3 d6 t* N4 U. i                return 1;5 `7 [/ h0 Q# f% v
                break;( R" ?& |9 r- v" ?; T( Z
            case REQ_SET_REPORT:
7 s8 a% P/ S7 U, p0 x+ F                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
0 [" r+ m( n5 t6 d0 H$ E                ep0_state=2;! B* M8 [! b$ T& x/ p; |  [
                return 1;! ^" z& n, d& v9 N! W, M
                break;5 _# C- _$ G  ^0 j$ {* ^- g* Y& ?# X
            case REQ_SET_IDLE:+ I0 `0 Z" h, g# G  {; r. c2 |# ]
                USB_PMA[1]=0;   // Zero DATA: \* w( M' s% p( P2 J9 w6 Q
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;) Y4 g" \( [: u3 x! R# C
                ep0_state=4;7 V% P  q! r% \6 u
                return 1;1 ?% N. B" [- p4 m+ o3 A; Z
                break;. R6 C$ U/ q3 z0 o. J4 t
        }
9 p% Q+ `! y4 N( R: X; e8 v( g        return 0;
, B% `3 j2 {1 c# ?8 Q    }- r) ^) W3 I+ {/ q0 G3 M$ n
    else    // standard
8 O1 E3 y( f% I: c2 `- @( c8 L0 o    {$ G7 _! v* ]$ u" `2 Q5 E1 o/ Y- u
        switch(ep0_std_req->bRequest)
& k& _" J; f4 n7 P+ ]( }$ \* v+ A        {4 I8 ]" F7 O& w0 v3 H2 m2 ^
            case REQ_GET_DESCRIPTOR:# e8 q* K- p# h" q1 _7 t
                return descriptor_service();
2 e, Z' l2 j6 _                break;: s) t$ j3 q4 i4 ~! a. C! [0 t
            case REQ_SET_ADDRESS:
7 `3 f* e% z/ b! x5 l9 B                if(ep0_std_req->bmRequestType!=0x00)
9 l/ v# M& L0 \* C( Z                    return 0;
5 k2 P5 V$ C. \* t6 e                usb_address=ep0_std_req->wValue;
3 ]. g, d1 E% G3 c                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;+ y! {$ q  e. f- @. ]
                USB_PMA[1]=0;       // Zero length DATA0
" S5 H! g- L% G% l0 `' ?                ep0_state=4;    // No Data phase
/ b: [7 X% b4 J/ C/ P                return 1;
. P- O. ^8 [5 W* Q5 X8 `# |) `- i* x) d            case REQ_SET_CONFIGURATION:
3 Q7 a) r/ r1 ^5 M5 e                if(ep0_std_req->bmRequestType!=0x00), g, R( T' C& Q
                    return 0;  z( y0 |  K6 f  W
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
4 d% H5 a+ f9 ~# J; a8 b' {                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
( {5 W0 u4 F$ r( M$ m# q                USB_PMA[1]=0;   // Zero DATA3 Y( {0 }, M, }; J0 A
                ep0_state=4;    // No DATA phase
- [4 o7 \! d: _: ^& Q2 F# j$ v' P8 Y                return 1;/ ]7 o- O& U/ M6 r1 O
            default: return 0;
7 ]* s9 Y* {) w3 M4 ~: W        }" W. d, N5 b/ T
    }, E, Y& e; x$ \2 u+ I
}
) J9 X- I0 t, J/ a* l2 k0 e7 i% z! }$ g3 I0 Q" }" Q: Z
, g. p% q0 [2 N
1 S2 S; L7 Q0 }# `& {9 _
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
9 u& Z3 h% X6 `  @# tchar descriptor_service(void)0 L6 v2 R) J5 T* D, R
{
& ?6 S7 d; A) L' G9 o& g, H    switch((ep0_std_req->wValue)>>8)8 @: O9 i1 Z& I% z
    {% G2 }6 M' D1 b4 F! A
        case DESC_TYPE_DEVICE:" Q$ W5 I2 F1 Y4 P9 e, X
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));
9 n. f  q4 j- R3 g9 v, ]* |            break;
- b* @# b  |3 Q& r# n        case DESC_TYPE_CONFIG:; e/ ~3 ^: J$ n9 N( V1 ~6 j
            if(sizeof(ConfigDescData)>ep0_std_req->wLength); V( R  X# K6 J& @
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
9 R& y  V* i  A  \) k. F* Q+ N            else
4 K& ~) y+ |6 d3 I  V+ g1 \9 J" X                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
+ a9 @( |1 y  F$ a# P: u  d            break;! T' `2 g( G( {' S5 J+ i* O3 \
        case DESC_TYPE_STRING:
6 I, {- J  ?/ c) P  x            switch(ep0_std_req->wValue &0xff)9 {/ X/ Y1 I- L4 V' w
            {
1 P9 z& D0 W  I4 n0 H& J' e( n3 i8 \                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
1 d7 @3 `* d9 v  j' l4 }  w8 W                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
, f7 x: n- T# j/ `' c' N% y) I                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
7 J1 T3 X! z: Y- N                default: return 0;5 y7 a9 m" w# y/ {9 v% F+ ^* w2 u5 M
            }& s9 z5 O$ i3 q3 f
            break;
$ z% P7 ~( l  f/ {" v        case REPORT_DESC_TYPE:
0 N% y; d! G$ ^2 B' q            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
7 {1 H, @# |" l5 t3 E: A9 M9 y$ C        default:8 l) v/ n# f" v6 z) _' T
            return 0;
# Q4 z, W8 _. ^. P+ \: W9 f    }
- f& o# }$ ]# N}8 h, [5 }/ r: l1 b
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.* D) t% b- g) L
void TIM6_DAC_IRQHandler(void)1 ^7 |( r' a2 {- H( k$ R, Q
{
, J+ N0 U$ @4 s" s% X+ `7 y# Y    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);* N  ^8 T! t/ |; I6 `
/ G7 \4 i" Q6 X  \( |

: ?) ]  y3 ^% h. H  k5 l    TIM6->SR &= ~TIM_SR_UIF;
; \  [9 x- p. w, y. f+ k    prev_key_state[scan_row]=key_state[scan_row];
- N$ v4 l  Q$ s8 X    key_state[scan_row]= *PA_IDR;   // update key states
( V8 X( d' s6 n) P2 X3 o6 M4 v% }    switch(scan_row)
5 {* U& a3 i. {; k0 ^4 Z* D    {% b4 X7 U) l+ R1 P. D
        case 13: // next row PB14
' B4 J$ M6 Y' b                GPIOC->MODER = GPIOC_DEFAULT;7 w; O% ]/ A/ X/ t7 ^- y
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
2 P) u$ [6 k* r- X" V: P* k                break;
. \- ?4 u' {6 U0 C( ^& P        case  0: // next row PB15
; B/ g/ l2 O7 O" Y4 m' X                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;
6 u; e$ ]4 y$ w/ J                break;
" P. ~: Q, {* f1 v4 n5 W        case  1: // next row PB3
2 D6 h7 }7 E: m0 v$ D0 v                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
9 r( `# `+ m2 X5 V0 S1 ~1 G                break;
( Y1 ?1 C  v$ c9 Z0 b9 w        case  2: // next row PB4* ]# a) C4 O, t3 ?( s
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
; n; W  p: Q% s' f0 s& E                break;7 R3 |( W0 \1 n2 E4 J
        case  3: // next row PB5
9 d/ w1 S1 ^, [+ W# f* C4 K$ Z                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;
- z/ M- N7 X- v/ d0 ^                break;3 l& `$ U7 u6 z- t
        case  4: // next row PB6* X% {! s* T8 @% G4 q* s4 a
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
# y& I1 x- u2 Y4 g3 _                break;1 p* @& |8 T( S6 L% P
        case  5: // next row PB7) n9 H+ c2 v% j% _/ r( j3 Y
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
" H. I, I4 b! A/ n, K                break;
  h6 ?5 R! X# a! y) e        case  6: // next row PB8& q* c& Z; d( Q( [: L' g
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;! r) V4 B. z- f! S% W3 K1 C
                break;
% l" {- j5 B. r0 P/ h1 p        case  7: // next row PB9+ t5 @) ], I& m1 A& l
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;; S6 D4 x: B* x5 X+ }
                break;
' h8 J% f# X5 j& {9 r        case  8: // next row PA82 d* A( H+ [, j
                GPIOB->MODER = GPIOB_DEFAULT;
) \2 A  \, ~8 L$ v                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;
8 j& r( _/ l, _. N                break;- J5 E' v+ x: G4 K( b. q- Y' }, |
        case  9: // next row PA9
4 L% A  p4 g! m- A, ]  R2 E/ ?                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
$ }0 T! ], W+ ]                break;0 a- Q2 ~) C5 X) \: }5 b
        case 10: // next row PA10
, I4 I" @9 ]# ?                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;% m' i$ z( F+ l% R) Y1 N
                break;$ q) \- O, w& |! l, X8 ?
        case 11: // next row PA159 x+ Y1 r+ R+ `+ p# n5 P7 o  W
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
  }$ V2 p% a' n% r7 W' ?7 x                break;
/ D1 D4 K; P' e. T% Z        case 12: // next row PC13
. f/ o" A$ S8 Y6 z# W                GPIOA->MODER = GPIOA_DEFAULT;, p7 H8 P" |" w" V# x, n  v
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
/ j7 `. p, D8 A! X                break;
* L, v! l5 a2 m- O" P; j* L/ i    }& Z$ V- q8 M; L; y4 f8 x& H
    if(scan_row<13)
8 j, Q& `2 g+ O- ]1 A3 g5 X) Q        scan_row++;
3 B) ^% {- W2 b! ]! Z& d    else
, v1 J( R+ l) d        scan_row=0;
5 V: N% o8 x# m1 s( {1 S}
' |0 h2 S# a, W/ r6 w9 K
, B/ Q. _1 z& U1 }8 R5 I# h1 [- z, C' V5 b6 n4 [* r  X* h. m; i& v9 v7 g
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 - r: y3 j! H3 _( h9 @

! t7 A$ W3 W$ a* ?+ y扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。& ]. i# ^/ g0 w/ d7 l$ O1 ~! ?7 R

1 @& v, G6 W$ [( S' W/ K) v0 Q" J要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
. L+ x# ^. F1 ^* D2 }' x! @: T

0 m+ W# C5 b$ R) L8 I" X6 a8 D/ }1 y" p; {5 Q# o0 @9 j$ e  h
const char hid_keymap_qwerty[14][8]={+ ?4 ?7 [2 g9 d% X
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
, D9 Y* g# D; F. ^0 L) W6 e    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
" ^% r: m- G1 i& q    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6}," Z; S8 {1 i* ?5 |1 c9 C
    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
; y! h, M4 v/ y    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},% I7 ^8 I4 {$ u! L( i7 z
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},  n0 o2 F. E% U) W9 j3 @9 N" h
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
) P( M! K! ^7 @! X+ b6 o- ]! a( |    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
8 b$ _& p7 p& G$ L- q. |    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
' O# o7 A( }" Y9 O* @9 `  S    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},# k5 i- B1 Q6 u! K9 E* I: ^! c
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
/ E! c5 c6 b; v+ e    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},- H& H# J2 e" J" D' J$ s) D+ y
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
$ {& L- t5 U0 S+ a# W& R    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
3 G( p+ `1 p$ S$ h4 [3 U" C};
0 x: V& a% p2 n% f# t3 Q* l
  v( [5 c/ h/ c& X( C* D$ K1 N  Q- }

% a5 g5 J. `6 ]2 ]! ]' gconst char hid_keymap_dvorak[14][8]={
9 E. E6 u( ], `8 a8 \3 E' w- x    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},6 k+ W! u5 `* ]" f
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},3 ^  `& p; Z  K% y+ X
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},
0 t  y/ |3 g1 d. i3 ^    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},1 u% l  v: D$ _; ^/ d
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
) m2 A3 T% V( j) s% }) I    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},; t- u0 R' V1 W$ q5 o# p6 y
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},, r/ P+ e: G6 E3 D9 ^+ F
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
. i! d; z9 ^+ o' I( ?5 t& ?7 l    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},/ R" a- Q! U6 |4 c
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
7 z& C' Y' |8 a/ L: z* l    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
, k3 o  _' T6 l, P    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},+ X9 d+ A; |1 d9 m8 o5 @
    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
) b- |% D' X/ ~+ D    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}5 v+ K, s  q) C7 f8 M: G
};
$ X, c9 \% i* o) s1 u1 G- N' }$ l, J' i6 Z; ^! F* t
2 t9 Z) c/ |+ Y0 x% `; o. U
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。- Q0 [) K% l, a8 I$ i7 R# |: R

8 c3 M  b' b/ [" DHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

- h: i9 f* u( b3 a. V% }0 }$ F& P7 _2 N
void update_key_matrix(char row, char col, char onoff)% V% j( k2 F# I& {  k+ B- F# V5 P1 b# l
{
1 Z( t! L2 L9 k5 a0 C, v    static uint16_t hid_report[4]={0,0,0,0};
7 A4 y* ?, H6 \$ a    static char (*hid_keymap)[8]=hid_keymap_dvorak;; k4 g! Z- u5 _) E

& L; e7 `: ~7 g6 V. T5 L8 Y

0 m7 n4 n8 u' B7 r+ a& I+ r* E    unsigned char key=hid_keymap[row][col];
. {0 u2 Z( q  p7 H+ u    unsigned char *report =(unsigned char *)hid_report;# ?4 W7 o3 f) n% G" @
    char i;
: g9 D* g. f* g
0 x( Q8 t$ Q  B+ S* M. f" Y

) i$ v8 r  i. X* i" e. K    if(key==HK_MODE)) X8 ~5 i" X8 k6 M  i
    {
# j# v) J* p0 D! T3 Z" p" ~        if(!onoff)
+ q. P5 q2 L1 K' z- A! [0 t7 ^/ d        {
# N# M; J, [) A0 L, m4 P$ Y; x: u9 J, A            if(hid_keymap==hid_keymap_dvorak)) z. F! f# P; E% r# U2 U& G! n
            {
; t( W; z' U4 t/ ?/ @/ E                hid_keymap=hid_keymap_qwerty;
: V- u- H# y) [                GPIOB->BSRR = (1<<2);
; t$ t1 f7 V0 r! X            }: b! |7 e9 \- {& q+ I8 n7 p% ?5 \, o
            else
$ V3 [4 \4 X8 n            {
7 w. J+ o: e# k0 J! b! @                hid_keymap=hid_keymap_dvorak;; U  }: v6 o1 D8 \0 @* R9 |1 I
                GPIOB->BRR = (1<<2);
* W$ ?8 m# z% u5 j. B" C            }9 j, a: G+ w; e9 `
        }! N( x" j0 A& R, Z) l8 {4 x
        return;& I  p3 m  D# ]! ~. U( A$ _
    }
3 i9 L" x' l0 E, ]) C
, Z2 X$ K9 u3 @! o( Q/ S
3 b1 p6 m. d5 s
    if(key>=0x80)   // Alt, Ctrl, Shift& b% J. W; d+ T5 U! H7 |
    {9 b- {$ t8 Q/ M; c9 a' Y
        uint8_t bitset = 1<<(key&7);' l% v$ N1 q2 J4 \8 D
        if(onoff)   // non-zero is key up: o1 N- Z! ?2 u& h* ^0 y, Z
            report[0] &= (~bitset);
* V/ B7 e% F3 c$ }        else) [) }- ^/ n- }
            report[0] |= bitset;$ }6 x& w! y7 L" I! o$ y6 B8 ]
    }! O% L; x' f8 ^. }1 z# }* d7 U
    else' Y- u0 |6 J3 K* Z) G5 @
    {; c# l& j( X% R  C$ Y
        if(onoff)   // non-zero is key up
! |  u" R( i& ?1 l        {
+ z7 P8 L2 Z' ~7 `% k& e, x            for(i=2;i<8;i++)
7 y/ r# k. ?9 U            {: r, g; L! L$ w) t1 O# O3 w5 m
                if(report==key)
0 {& e2 Y/ B% a/ t( Z                {: U5 q' t2 P4 U
                    report=0;
9 y( W8 v& r; c& ]; z                    break;; n3 S, ^% G# I$ Y8 ]
                }$ k4 {" ?# V% R
            }& S5 @: B+ P# c' U
        }
- k/ v  Y" R* k9 T        else+ Z: b6 Y9 P& P7 {5 F
        {2 R! Q% V4 e* C$ Q' R- F
            for(i=2;i<8;i++)! Y" ]: d5 Z' ]
            {
# i! I( V. ?4 l/ n                if(report==key)
0 H& Y2 T+ i% ~$ i3 O3 `7 ^0 m* P                    break;
# b, J0 q) o& }. }5 b                if(report==0)1 C. W0 L. ]% [# e7 w8 w9 x
                {# `- s, d) m4 w* n8 W; K% n" i3 a
                    report=key;- b/ z& C1 @, d$ z% \3 |* B- q
                    break;1 x+ s0 ]0 ]0 b  T9 H$ Y5 M3 o
                }/ L" C4 U# f6 Z: \# N; \
            }+ o8 i( |/ X! \
        }3 f) y$ a3 l; K0 W' e
    }6 G8 i& _# w6 L3 V9 y
    for(i=0;i<4;i++)
& E5 M5 M3 p$ t4 y; l* ]        USB_PMA[192+i]=hid_report;4 s# T6 W% P; {3 D
    USB_PMA[5]=8;   //COUNT1_TX8 d3 F7 k# `% `
    if(ep1_wait==0)( e9 C, @) ^9 m0 v/ q* L' `+ ]
    {& W! l7 m- Y* H' I0 _  ?* I
        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;" g9 D8 P# B7 h" H
        ep1_wait=1;( t$ i! s  I- i
    }
1 j! |. t% Q! r' S& P}
3 F: B! C3 e# h* r, q( ?
- j3 R+ n  F/ e; A* G  c# U& t# M: G
7 _3 x( B2 i3 E# s完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。0 ]2 Z$ N. K: v& d
keyboard.zip (8.7 KB, 下载次数: 6418)
' G; ^+ ?# @: I9 y# R2 A0 A
, ?7 F, }0 I, {' _

! W; M6 ~( e) V9 n3 X/ Z. ?; i: p- k4 ~
8 |. k* q1 n6 B4 y/ q) u
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
, z6 c; `# L& t& I1 Z! T1 j不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
/ @" F' M6 x4 D刚开始我以为要把打字机改造成电脑键盘
+ P2 \4 F# y. _不过楼主也很厉害!

( T4 R: S  A: @2 |* c8 ]哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
; s9 _" g( }8 l. h( h# \  O# Y! ?
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-2-16 23:52 , Processed in 0.168894 second(s), 28 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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