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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

积分
288
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
% D' a7 d- t  d4 R1 e" ?5 E$ w" U! f  Z3 q- e* ^& ~
http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
! M5 F% _2 B4 I这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。/ Y# f6 R) A8 [8 M, n

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

5 s( D( u# y# V) Y

# @- _$ [5 `. L) I  g6 t9 A7 H# V6 z 235140i3a36qivqzuvmt5q.jpg.thumb.jpg 3 c, Z5 i" T  f
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。: A2 X% p+ k: F
001734klbyoluenuwz4h4b.png.thumb.jpg
0 w0 D, u6 s, j7 t( G# L) R. L! ^为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:
6 f" _5 h, A$ C 003625r2agx2f5v922cf2f.png.thumb.jpg ' M: k0 {/ s- t) w+ m5 b' z
其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK.
; C" Z2 L  _5 w$ B) n# H  TDvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:
7 a5 C5 S( B- I# ~, q+ ?  V
005836yvs0wvovwsssgd3o.png.thumb.jpg
+ a- e' i0 {$ `# r# ^, G1 W6 fDvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。6 \* f9 y% Y$ n7 Y$ f7 w7 W4 a: d4 ?6 I

. [% m, k/ ~" h( d& @9 L5 @我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。& x; M7 r. g% q% p

+ Y: v( x! P9 ~  C* _: l到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。6 ]5 l8 @. ]9 ^% ?: L

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

3 [* e  I+ u  z" x4 S6 t 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg 6 [7 n' B$ F+ W
----------------------------------------------------  分割线 ----------------------------------------------------------* K! r. u9 u1 A/ C  P# e6 d$ J
1 n2 [4 r" E" ~( z; j
先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
$ `: _  ]) Z8 {) Q- G, j
020011osionbunl4ui44vi.jpg.thumb.jpg 6 K) Y9 s# e; p, j( o
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
( G  ~- T) R; t& I2 E5 H2 O( T 020017j8ycmnv7788bqv52.jpg.thumb.jpg
: H0 d5 A0 D' d' r特写,80C49
* Y$ q- }# f% S+ c$ f& {$ a. [) }+ R 021040oujzuvtut6iujtvz.jpg.thumb.jpg ; `; e; ^4 d, F: @6 I
LED部分,使用了一片D触发器锁存指示灯状态.
  l( _- H5 I' {3 u! P  _* s 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
% M3 _( m9 O# Z) y, N5 _+ |暴力破坏,将80C49拆掉$ O& i9 z$ e, g, C; }* M2 X9 c
021113e48qq98vyohvhzzh.jpg.thumb.jpg
4 N4 O5 e4 ^' z; x, L4 K拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
1 `9 a$ Z/ d' T, I! K 021125nc9az6dj33rlds2r.jpg.thumb.jpg + i1 j. J3 l" e7 i1 G! I0 H! P) ?0 r
焊好元件后的板子,准备替换80C49" U! r% |+ q' Q* z: l% h
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg ) ?6 N( a% @: Y! B
用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。5 e* {+ G3 f$ A. {
021104shifhnrqbr3o5nlo.jpg.thumb.jpg
: T# O% ?/ T( }- \7 |  U. e这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.
0 i" ~' G$ L: d6 a% x( y' o 022003ym1p9u4ug40280uu.jpg.thumb.jpg 2 G) ?, t, M. f  H5 Z: D  W
开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。7 ]8 @/ {+ e( y$ P
023313kt141q9qajtol7ma.jpg.thumb.jpg
- }  [. ^% ?0 i; ^4 Z我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。& i& }" y' v/ [6 r8 U+ X* D
023322nt7l5xb3ltttkltt.jpg.thumb.jpg
) P: t$ Y! z8 o; E4 @1 E5 X
主键区键帽就位
- q5 f$ ?6 N" G( ~2 B' }1 A 023331hin88e8wkrwzwikx.jpg.thumb.jpg 3 [8 _6 C3 D7 v0 s  w4 K/ Y
编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。$ o8 [: _2 E4 X! q3 N& g
023336wjzlgopugg1jyy79.jpg.thumb.jpg
+ y. T+ E6 J. a; S最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。$ h, N  [' ^9 v% {, r' y7 X. X
023341sffu4j3g2323h6fl.jpg.thumb.jpg
, x: u& P+ ~& ]- k
, h/ n3 @, E) z2 ?4 W
----------------------------------------------------- 分割线 --------------------------------------------------
- N  e9 |; h/ @9 N% m2 l

2 L# y) i$ q7 g3 H6 Z* s. o
; [0 _8 `& q) G1 a- {$ F: o5 [

2 F, l: Y1 U, q6 M8 X! z9 f/ d: J1 v9 j! p7 O, f3 E
' y  d6 s/ c4 i
% u" @+ n0 V% ]) e* l8 h- t! H5 I
! ~6 ~: N) b+ m9 h

" \' {5 R. d  L, n+ t2 k5 x2 f/ i3 _6 `3 O0 a+ v3 X5 b8 R
* H7 T6 l0 e4 ~1 m! d# ?0 g6 u

' q, N3 G/ z  S$ ~- g3 D' a
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。
- r0 D0 k. @. X9 E7 u80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:* i& F+ @0 U/ m. y" {/ A
104025nzibm2rmiomhyirm.png.thumb.jpg
) \, r! _7 z6 m+ ]" `  X

* o& h2 J0 P9 ]& F  e" G其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
) M* I  K7 w9 z 105004zkrez5houvkkznko.jpg.thumb.jpg ( v- j) X9 V6 v, O/ ^
+ ]0 O7 w4 ?' r; h
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。& [/ J, G/ g7 w) W, t0 T2 t( s/ x
% O5 A% T- C( q! R
这是我设计的电路图:. P8 z4 J; m4 k3 ^& P% g5 L/ H3 x3 p
110344ej2z2oo2rflo7oe7.png.thumb.jpg - [3 [- r  Z! U; {3 u9 i7 o

6 ~; a2 k% E8 L* @: q0 CPCB Layout:2 `( F1 [- L) [& `
110847jjbjvt34vwt3v5bb.png.thumb.jpg 3 B: |3 z/ S3 M$ A
2 d: i! V7 {- q  s! w' G: H
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. . W! I# L# Z3 y# N0 {
  ^* i3 r6 e$ s# R. Z
: _( g' o7 T: o$ v8 ?3 ?# W) Q0 m
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 4 }% a( H. U, u" \

8 D, g# p# X# K1 l5 @软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。
* O) B" v! K5 T/ z* x% f
" ^% q$ y' L# k$ r3 d5 c% j; n总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:  ?% {/ r2 w3 }5 C# s9 T: w
113818pmrfsb6z0byt6t06.png.thumb.jpg
) H2 s$ ^/ w4 q# B4 m7 q' M  J! s
' w& C; n$ I0 H
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
6 w" |: N% J5 e) D# V, B% j. l' m0 V5 J5 m0 B9 j
USB的中断ISR,bare metal哦4 s7 \% \2 M! Y3 T
1 ~# v3 ^; V( }9 x/ e/ D
void USB_IRQHandler(void)6 x" V3 B/ `) S( R$ E9 x" l
{7 v2 X: E: w" b" g7 d0 Q8 [
    if(USB->ISTR & USB_ISTR_CTR)& p  I4 v4 o6 z# j
    {5 H% V* x6 y) W7 R
        if((USB->ISTR & 0x0f)==0)   // EP_ID==0! H; H/ p$ }3 C% \+ b2 D( K
        {
  D' y) K) O$ \7 ~' o* w. r0 N            switch(ep0_state)
5 O8 _5 r. F2 z& F            {
# q9 s  F! ?. ]- W7 m# {) V3 V                case 0: ep0_state |= 0x80;" O( G5 y$ J* {
                        break;" K+ h- E6 s- m$ h- o
                case 1: if(USB->EP0R & USB_EP_CTR_TX)
1 o* p3 O& [$ p& f- N                        {
9 p+ P0 ~' H$ A; |" X' k  e                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;
8 A6 `  T1 S2 K/ D$ c( q                            ep0_state=3;
! l& v% v! ]' h) i; K4 v                            return;+ h/ V2 `+ W& m5 c( s; v/ F
                        }
5 ~1 J9 P4 n, |                        else' y# M' o' u9 n9 Q- O
                            ep0_state=0;
4 F" i/ M3 n2 R- f  m* P                        break;
, s! ~+ g8 b8 r2 j' K5 P6 @- o' l                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
2 \, K$ T9 ^/ ]4 x+ G  s# H& ~% ?                            ep0_state |= 0x80;
4 G" s; [5 e- ^                        else
: B+ h0 {: {, b                            ep0_state=0;$ W( @) \8 a' Y& d  u
                        break;
; h; k( P! ~* E+ T                case 3: ep0_state=0;/ M+ D& q$ c$ L: l2 u! F$ j
                        break;+ D/ ]0 N- o3 a4 w, n# g! I2 y
                case 4: ep0_state=0;
0 [' z( B7 A. w5 o; [% {                        break;4 ~& b6 @. s/ i& ~" r7 K
                default:ep0_state=0;
" @  t; v1 a* z* J                        break;1 I6 c+ O1 |/ z& V" g0 m" \7 Z0 R2 J
            }
' f  y3 z5 b/ `8 W/ W2 G/ f7 {0 S5 C            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag$ g1 C; X; v+ a6 ]
            return;
% r* C7 _0 @% Y# s        }) Z0 y( {8 F- ?; d& M
        else    // EP_ID can be 1
# J6 N% J1 _7 w; U3 V  ~4 T        {' b  h. j2 A9 U! n* k+ J% h: {* w
            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
. r8 g- W% t$ R. D/ m            ep1_wait=0;; q- d* p4 w" w; Z
            return;
3 z. l' `) h1 a& n* E        }" W" p3 N1 P6 h* L9 Y1 F1 L
    }  j  O7 A8 D5 I
    if(USB->ISTR & USB_ISTR_PMAOVR)# ^8 A6 @/ h- ~
    {3 I" E* R) Q: A  R$ k: L
        USB->ISTR = ~USB_ISTR_PMAOVR;
! C8 }' i' m5 `6 p* s+ _( ?    }
# _) `8 m7 U# h5 M/ a    if(USB->ISTR & USB_ISTR_ERR)) H! i& _: @/ a/ c
    {
4 B$ F+ A1 F9 E& E- T9 V- R        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear1 s6 g+ Q8 E/ a9 `1 r$ Z
    }
6 r$ C3 m# ], s! ~8 Y1 T    if(USB->ISTR & USB_ISTR_WKUP)
1 ^2 j* \# L; ~  D" g2 M, u. v    {
% o/ Z( l2 p6 k8 V; t8 b        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend
. B) h5 G$ z. l        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear
8 v+ j# P8 n  j+ f    }" Z6 r& U7 G3 y2 N8 T. Y4 {
    if(USB->ISTR & USB_ISTR_SUSP)" u) I! O% x; @. a* H
    {
" e5 {. r7 ~5 j8 F' J6 F. Z5 G( _        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend( J3 l6 a; F( k' B" b4 n) ^) C5 s
        USB->CNTR |= USB_CNTR_LPMODE;   // low power
# E: p+ W9 e2 N! u6 s        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
3 M, ]7 @7 I2 E' ?( W: V2 u    }( `) |) ^+ B5 \& M/ o7 x
    if(USB->ISTR & USB_ISTR_RESET)
0 M0 V) T. k5 O& |  V4 `1 E    {
* C; ?9 t& o1 m. u2 r        USB->BTABLE = 0;    // buffer table at bottom of PMA% V/ X) E* Z& V4 d
        USB_PMA[0]=128; //ADDR0_TX
" ^. \$ R# ~$ y        USB_PMA[1]=0;   //COUNT0_TX) n% {# _4 W8 w) e) `
        USB_PMA[2]=256; //ADDR0_RX
! U5 R/ M# A0 t! [8 Z7 {        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes7 q% n1 {" n) X* P3 Z* o
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
# Z& i8 I6 D* a        ep0_state=0;
( J9 R0 v* P3 ?! h! W        USB_PMA[4]=384; //ADDR1_TX
; p8 d' H6 I, L2 d9 J" w3 h        USB_PMA[5]=0;   //COUNT1_TX
( p1 T. G$ @$ x3 E$ H3 a2 K        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type
6 T1 ?6 ?1 ~4 H& T! T        ep1_wait=0;
" ?/ z. L3 d, A' }- P2 D7 J( M        USB->DADDR = USB_DADDR_EF;      // enable function
3 ?: T4 M# N! Q        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear, L2 U  R( D+ X  z- a) ]. ]
    }; O! w4 [7 i2 A1 m
    if(USB->ISTR & USB_ISTR_SOF): ?+ L# c, V* P/ y! G
    {2 x/ H- |' ?, C6 B
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear
" q' H" @$ I$ B    }6 O5 E- R7 E2 d. \; \
}
' U8 t) \2 |3 y' m2 l
5 L" K4 f3 s0 `  J1 T( H2 {
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。1 {9 }0 P6 R0 w. m2 G/ |# P

4 [# I0 G8 e! C1 t+ G& Q" i+ t9 D( B主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。' R0 S) U& @* c% }1 t8 W; c
0 x; u4 }) @' ]0 r
    while(1)
. u/ @3 }9 c1 S7 f. _    {
2 }8 x- `3 a+ v: V% e        static char row=0;3 F" ?% v% P5 C' w4 H4 z2 K* S
        __WFI();7 L. F) i6 e" o% x" a) _; ^/ }3 h, `! p
        if(ep0_state & 0x80)    // request data processing
! u, d% F1 y  r  L# W' c0 F; Z        {
8 k# i: e. Z: x$ k; u+ o* s            if(ep0_state==0x80) // SETUP phase9 f0 n' {' G# G7 V7 X* _
            {
2 b0 m3 j  T* O                if(!setup_packet_service())
% i# z, u4 e* I: I5 l                {& |4 c4 g9 D2 ~# z
                    ep0_state=0;- L1 _. s" s- F( @
                    // not supported
% E% b2 _/ W( b- A3 v                }) C% l+ M$ r5 ~
                // ep0_state should be set to 1 or 2, if processed
  Q# N9 b. n) c- c' c- H! w            }! U- a8 x. @, h; y, q
            else    // OUT phase
, Z; m) Q: {* g! b+ l0 A            {0 F4 [2 L. [, R& A/ Z& L1 a" A
                // process data. p4 r, ]5 e3 W) [; H, j9 l5 ~1 ~
                show_LED(*(uint8_t *)(USB_PMA+128));
: x: d2 E& {' a7 s2 `                ep0_state=4;* z4 `" N3 j* _* y' U5 I, h
                USB_PMA[1]=0;       // Zero length DATA0
; ]( ]" u: l  Y                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;7 h6 d" S2 G" b) V- S3 v
            }2 Y& z! U8 W' C, m1 m* h
        }7 e0 Z. p, L5 J, e- f( K
        else3 n0 \  Q6 I( u; q
        {
+ e0 a3 ~8 P7 v. X7 x3 q1 y$ F            if(usb_address && ep0_state==0)" y( h5 k( @7 h' i/ e
            {
; k% e% C  L0 I2 r                USB->DADDR = USB_DADDR_EF|usb_address;
4 n. `$ [8 v  Z, z( W# P! w+ H# p                usb_address=0;
% p, w* [- N7 U( n! i  ?            }
! e# B8 w0 V) w; T+ R0 R% E        }
4 a* n) N, A6 B. n        if(row!=scan_row)   // new scan line( S: a/ Q$ I, x; ?7 N8 ?/ P
        {
4 {( T+ @( c7 f' G            if(key_state[row]!=prev_key_state[row])
# `7 b  Q. Q4 a. z9 [8 c2 {            {
! g! T8 `  ^* t2 j. j" D/ Y. [                uint8_t test=0x80;- ?7 W5 k$ T# Q0 ~, G) ?4 b
                uint8_t diff=key_state[row]^prev_key_state[row];; c  D. c. \) L8 _
                for(i=0;i<8;i++): c& _& z/ m4 }' N, t0 T
                {8 J# p* l) |3 F+ w$ m2 q
                    if(diff & test)
4 a. |# j0 M" h+ R2 c* I                        update_key_matrix(row,i,key_state[row]&test);
6 s1 J) W( y$ B' c" k! ~                    test>>=1;5 k. L; q1 s- Y( k) Q: }7 y
                }
7 i+ Q/ W- E4 M# m  K0 e            }  k6 Y  b9 P* h3 p7 i
            row=scan_row;: y8 o, p& D6 [
        }
- {; O/ g7 V5 n0 M    }0 u( g) u! D. d4 s$ V. P: R
, @9 d" D; a) }$ k/ I- j
+ w+ H" `7 g$ ^; R; j# s0 I3 C
EP0的控制传输,把用到的请求处理一下) z$ W& B: s4 n" l$ J. @
char setup_packet_service(void)
3 B- I8 ~/ e! t) u{
$ V3 c$ u: \) N8 N/ s    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
* S1 ^6 H+ y, E    {1 F; Q4 K" q4 D2 r% P* m
        switch(ep0_std_req->bRequest)
8 q7 |; M+ L  ?: E0 V        {
* O, \' o/ X( `( I/ a3 m4 _0 G+ q            case REQ_GET_REPORT: break;$ }* o" L- |" r
            case REQ_GET_IDLE:8 S8 u- J. L* z
                USB_PMA[64]=0xfa;   // return 1 byte
! A: B7 }' i2 n9 q. l                USB_PMA[1]=1;8 j8 ?+ B6 X- X
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;+ }  U+ c9 @& H) N0 N6 }
                ep0_state=1;1 G+ @% O' u5 C7 r$ x
                return 1;
6 C2 b3 h0 z  U, l% W0 ?                break;  q9 H1 T7 ~" R0 Y
            case REQ_SET_REPORT:
) H! i/ Q- {1 ^' O0 P* H6 t8 U                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;; {+ K1 {: Z( k# a
                ep0_state=2;5 i7 k8 S  d* h6 R6 K
                return 1;
6 f7 B3 Z' |7 z7 v+ N" K; v                break;  P7 r  X+ G% Q1 P
            case REQ_SET_IDLE:
$ G% k4 E' {) r( ~5 n: q                USB_PMA[1]=0;   // Zero DATA
. p% T8 F1 _- v                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;1 b/ J0 ?; _- ~! h
                ep0_state=4;
' w  ~2 P9 v* d                return 1;
) l6 V. L4 ^9 ]                break;
3 Z7 Q4 b0 O' R2 B8 k0 L        }
! u- \. A, L% o3 p        return 0;5 ~0 a, c" ^) X! W1 C5 a+ x
    }3 X* d( H9 @: e; y5 Q7 V5 ^
    else    // standard
. w, ]* y  [, ?. V' l1 e- z    {
2 |9 t# [# x( F! P        switch(ep0_std_req->bRequest), a) b' y* c0 e0 s% U/ D
        {/ a1 r; b% W5 r. D; d, A
            case REQ_GET_DESCRIPTOR:
7 M5 E- W; ~2 @5 g                return descriptor_service();+ G5 a# G2 V* O+ L, |
                break;
) E9 {, V: Z0 d- _/ ?            case REQ_SET_ADDRESS:6 w% Y1 e( _3 i# G7 v. @& l
                if(ep0_std_req->bmRequestType!=0x00)
* D/ @2 U- R. h6 J7 i4 |- T1 z$ T                    return 0;
8 E6 j9 p1 a- `+ O; p5 w                usb_address=ep0_std_req->wValue;
! l; w) b- ~/ e. m                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
1 g, F( {. ]# j. n( y6 ]                USB_PMA[1]=0;       // Zero length DATA0
, N" A) ?# C; r0 ]+ z                ep0_state=4;    // No Data phase+ M% V: \, Y3 w- T% `% |7 W
                return 1;# l% d  X5 |0 a" |  h) E
            case REQ_SET_CONFIGURATION:
5 P1 R: Y$ Q  n! B+ d                if(ep0_std_req->bmRequestType!=0x00)! L5 _/ H; s: T/ N) f5 D
                    return 0;7 y9 q( R( f  g. ?0 l9 |: r
                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;
) i) m8 y$ E3 x1 L  K$ j5 q$ Z                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;9 ]* ~) a  T- E0 z6 {- \! j: G8 n
                USB_PMA[1]=0;   // Zero DATA
7 U; x* a& J$ I                ep0_state=4;    // No DATA phase
& D) q! P& E) q3 H                return 1;
$ {6 n6 C8 g4 a: T8 H' |2 T" g% h- t            default: return 0;
& L7 h$ g( @) k, U4 e* H/ W: e        }1 \$ }3 d! s& i0 Y- [
    }
) A! k" t3 e5 ]% N}0 W, G3 O- Y9 S8 g( J, @5 c9 \

# m8 [0 T% b) @/ j$ \: _0 _# R% U3 y1 L( ^! Z

1 F3 i# Q0 y; x1 k; @' L
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
! @& b$ e. E2 I9 @char descriptor_service(void)
* G- V# A" j% f" x6 ~2 I{6 y5 r0 |- }/ `8 [  q4 q
    switch((ep0_std_req->wValue)>>8)
: M2 z) Y$ c/ d    {  _% P* `5 A( w. @; K
        case DESC_TYPE_DEVICE:
# x& N$ v0 x0 K& b            return ep0_preparedata(&DevDesc, sizeof(DevDesc));# A; q4 D2 u& p" f! D/ B
            break;. _' K2 c% g0 s+ S: L. o
        case DESC_TYPE_CONFIG:
: a/ E% M! x6 A" \2 P  k            if(sizeof(ConfigDescData)>ep0_std_req->wLength)
% o0 J+ O& a* a; H" E) r0 w                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);% R7 D& x6 i$ e" B/ M! }
            else  Z7 v  o1 V  _# `2 R8 u' W% y
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
) a5 \9 g! l  E; t% g2 H3 w            break;
7 `% i! _5 M' x. k( ^        case DESC_TYPE_STRING:
( l" M$ f) e7 }            switch(ep0_std_req->wValue &0xff)
( I3 `4 b, X0 I            {
+ t! u8 r( P% N1 u+ e% a                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));' P( A, ~4 m7 W- s
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
$ f+ ?( `  z4 |8 `; w/ w7 h( z! F6 l                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));
1 @7 j8 d; g6 H+ t+ E8 H2 j9 a                default: return 0;
; [1 ?: i/ y$ |8 S; @( ?3 T            }7 L0 \) ^; B2 y4 @) }. G; H; F, r" q
            break;
& m3 Q# q* P5 I9 ]5 h        case REPORT_DESC_TYPE:
, L3 f( _0 i+ m  `& p            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));4 O% R# e# E% ^. u# j% p) M
        default:
0 o* s& b6 h) c; u            return 0;5 i5 S" G- H4 V% l% q7 t- y# C" V# m
    }
) Z, @* j  b1 M6 d9 \}( t0 k5 S3 z1 \; y9 E
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
; T8 S- W- |0 I' _( evoid TIM6_DAC_IRQHandler(void)
  [: G+ K# j6 {3 z, _{
  W. Y% V) U" p    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);9 M6 M; L' s) m  ?, I
4 h. k- g5 W. X- r

6 T* P5 b$ j) F. z( R& F  `    TIM6->SR &= ~TIM_SR_UIF;
8 q* o! [6 j- e    prev_key_state[scan_row]=key_state[scan_row];, Q! M+ ~6 q) p" I
    key_state[scan_row]= *PA_IDR;   // update key states+ h2 X" y1 I8 [& Q- D1 r* P
    switch(scan_row)
& v% ?& a' ]. l8 L# }7 K6 ]: T    {
' q% _- k3 ^3 p' J# w; T5 F$ Q        case 13: // next row PB141 p5 M! a8 J9 a# Z& x# E8 ~$ q3 s3 H6 G
                GPIOC->MODER = GPIOC_DEFAULT;
3 N' E$ G" W9 E2 d& Q) t- }2 p                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;
+ n  A, z+ Z" f! c) S+ X3 `! C                break;- W5 |& O9 T4 C( Z1 G0 ~: P
        case  0: // next row PB15
, ^3 a% @) B( _: m                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;
* g1 b/ L, C6 q9 ~* |+ n& G                break;3 S" M' m2 ?6 c! r' F
        case  1: // next row PB3
: x' [7 z" i) P6 y3 s  o                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
7 r1 s9 f; p% A. f) \$ J, U; M                break;- C3 M/ C. z1 @; N9 r% o# E& N9 [
        case  2: // next row PB4- F  b- P; @+ U7 s# X
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
' U" X$ h5 [1 i/ w                break;9 @4 ~4 _7 v+ \% V& g6 r  X
        case  3: // next row PB5
" f$ r1 y# G( M0 J( e                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;6 }  m# j+ d. _% T. C
                break;
: B5 ~) G( c5 J6 s* F% I        case  4: // next row PB6/ t, r- ?3 k) F# D) F7 `
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;# C  c3 ~; l- D) \' _. j
                break;
" D; G% k2 V+ o& }, @        case  5: // next row PB7- M9 p. j( P7 M) K$ B, Y% |7 H) {, v
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;
  n0 |0 J& S% W5 N, h                break;; J- z1 c2 {) [& H) v4 m7 _" A- m# V
        case  6: // next row PB86 V( p9 b: s9 g; g0 K6 ?8 W! |
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;  z2 D0 ]2 ?+ J4 f7 r4 J+ g
                break;
: C/ u* P$ F0 A9 @        case  7: // next row PB9
0 h5 C1 [% D: o8 }' T                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;2 p4 h2 U$ s' E
                break;. @+ v3 w; n5 s. Y" q$ q+ j8 F7 U
        case  8: // next row PA8+ {! S8 \8 c1 P) n/ a  c5 t% c
                GPIOB->MODER = GPIOB_DEFAULT;7 i8 N! m" p. \# U# K
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;5 g% Y# y2 |) ~* s
                break;, {% y1 {+ y( b  y) u
        case  9: // next row PA9
& M6 q6 C4 Q  g8 _                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
% v6 z+ x( U" C% C% {                break;
8 E, T0 G: G+ G4 S  v4 _1 z6 x        case 10: // next row PA10
1 D/ `1 z+ `) o* W# e                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
# M1 x4 i% u# l& X) J                break;
7 F! W$ J* q/ d1 {1 i        case 11: // next row PA15
! `6 I( n4 F6 r                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
. r" `. D2 A4 l$ l1 z7 E2 b                break;$ A$ X( t$ F* _/ K7 D4 ~1 a
        case 12: // next row PC13
4 J" w# V  a+ r: ^' s                GPIOA->MODER = GPIOA_DEFAULT;
) q$ q" T3 l0 c6 q) s9 L# G                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;& m) ]# S/ o6 Z# ^8 {. O% u1 a
                break;, x2 S' t/ A; ^
    }/ d  B: }$ b5 j. t; n/ w) |
    if(scan_row<13)
* e# L  B, a6 X1 U8 u        scan_row++;* e7 I! j" ?- L1 K: l
    else
; S  [6 W/ q: Y2 D2 g        scan_row=0;: y+ W& g8 L8 c- f9 B; q! a2 h
}' @' \* w' g9 i) V9 ^- R

: v' \! L2 r* q
2 Q" K+ H1 |& Y( h
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 - w) M/ H  b, Y

( n4 p0 w$ D  s) j+ H/ i9 W7 L扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
8 }+ W% Q( |6 ^+ D& y" i
  D0 m6 p6 L  t: d% X1 B" I; L/ f要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:

" J; w; E' o0 }/ I. P8 F2 A4 ^1 G* [; L, z" G0 k: r# c9 u* h6 e

3 b+ @; z/ h1 V4 F) Y' `+ qconst char hid_keymap_qwerty[14][8]={
5 w5 V  L& ^) `  O1 ]3 A    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},% f' g1 d" h& t# {# f0 A) Y: n
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},( ~9 ^2 z$ {& c9 _3 {( x  c
    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
+ h. J- C) b  B) Z0 k  B; Q    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},; I, K7 M( {5 S5 H9 a
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},( C( q; [& |) L
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},; U) D7 l: j+ g. V7 @6 a' s
    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},/ n% g1 E( F- M) O4 s
    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},1 ^3 Z! G! m# _0 k& [
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},9 q  e( Z0 ?/ q( w& c
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
: p/ L' }* D; r4 @& t+ i3 \    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
* h4 ?  o8 K+ Q* O    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},
% f/ h  O; l: ~! k7 h1 L    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},
2 _2 S( g) {" z2 ?1 u/ y. r; W    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
9 O: s* `7 r6 f' F; \};( `! L5 T" [4 k; S8 }5 L

! |: `  m) f; I1 V

1 @8 g: B" a* j2 S6 A5 \& yconst char hid_keymap_dvorak[14][8]={
& b* _# U2 D1 d4 o8 D    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
" ?, P7 M: e! g    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
; d) |& O+ e5 n# Y; i$ u/ l    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},3 r9 n2 s3 d* N6 J& Z
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},! Y7 R' M7 i' w' D8 b5 g+ K/ Y
    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
' w+ e4 `1 Z/ T    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
$ @. V: D& q! }) {# B3 f+ p    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
, T" K  F! }. g1 w$ P  v" C/ Z2 k    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},
! d9 d) V5 U5 j) b. K, U    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},' v2 v2 `# O& r. E4 a% _
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
* o, q5 Q* R3 k1 M* ^; b0 n5 y# V, G    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
' C" Q6 ?" ^& c6 ?5 O8 W    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
; M& X- [- ]' O% _" ^    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
9 L$ s6 V* G# M. i; X8 \& p1 ]    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
- a7 _3 `# j5 ]5 p! x& R1 Y& M( f3 S0 b};2 J- W7 n( }3 O! K: D- L& E4 a5 d+ D
) @- O4 b. m7 E4 a# y

% O0 B% Z% v# I% H' r4 d+ l上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。* \, E3 z- }$ O2 [' y+ m& @( J

, F% E% P- V6 B& M$ z  CHID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

% Y) F+ h& _! R% [9 Q( _' {, f' [+ C* O3 Y  ?  B
void update_key_matrix(char row, char col, char onoff)
( b! y3 B/ y$ Z' f# l: U8 [{1 |, y. L6 k9 H& J- V7 }9 m
    static uint16_t hid_report[4]={0,0,0,0};7 \( X7 J8 `+ n- h" I
    static char (*hid_keymap)[8]=hid_keymap_dvorak;. |9 N  Z+ K4 H
) p+ @8 v2 z2 v) g9 x" H
; q7 {: B: h) T( o  C2 \
    unsigned char key=hid_keymap[row][col];, {/ r6 ^/ I- O
    unsigned char *report =(unsigned char *)hid_report;" C- a1 U5 {- I( }0 w% J
    char i;
& t( ]; C- y* X# j# q& h3 t7 ~( J
+ b' N+ W# o" X# @+ X7 M* X: q

4 n1 G: N- _+ v1 M    if(key==HK_MODE)
* I1 Y2 w" M/ O+ C8 e    {! k, q+ P% J7 \
        if(!onoff)0 X8 X1 L7 R& k( m, @
        {2 c2 ^! B; H2 v' d8 ?
            if(hid_keymap==hid_keymap_dvorak)2 Q1 y( _9 \+ j% I0 l; g8 {
            {+ M" q5 X9 X  f# C+ P8 Z. m! p
                hid_keymap=hid_keymap_qwerty;2 q9 H3 V9 x* I8 K1 }1 T
                GPIOB->BSRR = (1<<2);" F. Q3 ]: z5 |2 M
            }
2 Z! ]2 |; s$ |. b2 U+ c; m3 R: ^            else8 E5 k) Q/ J" N6 Y4 f
            {
, F" ~6 A- L' w# ~6 a$ i3 v2 Y                hid_keymap=hid_keymap_dvorak;
0 p7 `1 r, M4 G  N& c+ j; y' k3 N                GPIOB->BRR = (1<<2);+ i$ i, S& t4 P& b* k& l' B6 P
            }
  _: H( B( j- U! E' g        }
# l* d/ E. X; Y        return;
; {; ]$ O5 g4 r    }% D5 U9 k$ _# j: {& J6 D

: ]7 c7 p2 g8 r, [  @7 z( m
: T' s% i4 T  {. q
    if(key>=0x80)   // Alt, Ctrl, Shift" c" e+ N, p. Z
    {
- e: S/ o  Z1 v        uint8_t bitset = 1<<(key&7);
- _1 J5 I* K. e! q0 _- M/ \7 F        if(onoff)   // non-zero is key up
4 M* l9 b( g1 c' z" a            report[0] &= (~bitset);. ~" f% @) x. L6 B$ n0 N# R
        else
1 P. P' e; t* _3 T* U            report[0] |= bitset;
+ d' l: W& U' s    }2 Y$ ~: Q$ [' i4 ?
    else5 t. ?+ l. g+ H6 @
    {" G7 ~5 I. A4 h# Z! @: g
        if(onoff)   // non-zero is key up
0 ~1 U, |( w3 ^5 `8 g; v% D0 Y2 |        {
) j* e% f% D, J; u" E            for(i=2;i<8;i++)4 I  _7 N$ Q  o; k
            {$ P5 i4 y7 ~  c5 s( s6 T
                if(report==key)" T* u/ n( |8 `4 b# Q
                {9 A2 E* F* O0 j: W
                    report=0;0 e# T6 j# V. N( X8 v, _/ P# }
                    break;
) i0 O4 l# p3 |$ {8 Z                }; h; Y4 |& Y) \; v5 m4 F) l0 x
            }
4 E1 ~7 {+ r9 t        }7 a4 ]/ a+ c1 C2 J$ t
        else, {! s( r( {1 S" c. a* t
        {
4 k- _+ `/ G- _3 [) U            for(i=2;i<8;i++)
5 T2 K0 c, Y+ W            {' P! A& j- ]& P/ M' V2 t6 b( j$ J
                if(report==key)
$ _+ z8 z5 M- \7 B6 {                    break;
1 V. c" q; \2 m, ~- L& w' _                if(report==0)
( f4 t( p3 A- V. B  V/ L7 q( b  y                {8 @' k! M" ^) {% a
                    report=key;
$ h* R& q1 a4 l5 K: w                    break;
4 ^7 @( c1 D- R% y* K5 J                }
- e* Y* f) ^. @3 t* q: |1 f) @0 v! a            }
/ z+ w6 P3 x, t- {6 A6 w        }% d1 i  y+ c  x1 B/ z8 n
    }
& Q/ q0 y, A& p2 d$ b    for(i=0;i<4;i++)
( }- [+ ^& x! P: [. q5 f        USB_PMA[192+i]=hid_report;: X! Y  f; G) f) G) L( [
    USB_PMA[5]=8;   //COUNT1_TX5 `9 m- B6 ]) y4 \& F# ]5 K  @
    if(ep1_wait==0). w8 o7 s: c, ]5 H/ w' U; h; h
    {
* |# {* Z+ G/ s9 z        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
$ l2 r% a& w4 i9 E        ep1_wait=1;" A& w7 s& |5 Y. b. Z
    }: S4 F/ S$ e8 g
}5 @6 n. Y) B2 ?2 c; Y9 u

$ V* L, w) S& g) N( w. Q  p
& q8 `) V- J9 d: C完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。( x1 g4 f1 f& T1 Z4 v
keyboard.zip (8.7 KB, 下载次数: 6581)
' u4 `% f0 w1 d5 `9 B7 n( l

8 P( V- `! R  J! s
& f* U7 z+ Y* ^, q) B- u# [, W, a0 O" R! w9 p% D8 v
3 S- a! \& ?  n' u' ~
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

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

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48, U3 T! y1 ~& {4 N
刚开始我以为要把打字机改造成电脑键盘& `9 m* z, k+ l; G! R! K
不过楼主也很厉害!
* S0 `0 r3 G' _/ k. E  ?
哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害" T! l0 g9 K1 G
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2026-6-28 01:22 , Processed in 0.182486 second(s), 25 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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