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

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

[复制链接]

24

主题

39

回帖

288

积分

vip

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

7 e5 m4 Q- j) S5 N. F 235140i3a36qivqzuvmt5q.jpg.thumb.jpg , m% y4 S+ f7 K% ~7 j% b
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。
8 E' p+ T) i4 c+ k4 E5 t5 ] 001734klbyoluenuwz4h4b.png.thumb.jpg - V$ O- b: V0 v8 Z+ ?8 D
为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:7 ^5 K- F* X( l( {- ?3 Y; x& ~
003625r2agx2f5v922cf2f.png.thumb.jpg
8 ~* k2 K% y+ z$ X6 ^8 Y其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK., h5 |8 n4 P5 n( Q3 N* ~. l. D
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:

7 w( a! C: n. Z5 o  u( ?& W 005836yvs0wvovwsssgd3o.png.thumb.jpg
4 }3 J  j5 \; l4 y$ V* eDvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。7 h5 H2 C5 ]( ~) N; E5 c  H$ `
, x6 a7 F: l- s# X$ V% p' g
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。- Z( |0 X0 s7 t+ w  t- ]# Z) x
( n$ q6 B1 U$ `
到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。+ p, s; m: j( H( n7 A
4 R! j6 ?" T1 b, X! I  S
机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:
% q8 ?$ R7 ]* v9 f$ o
123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
+ F2 O, G) E: C& ?& J----------------------------------------------------  分割线 ----------------------------------------------------------
9 c7 ^+ l8 e2 q1 p" p# w- ?
) a/ _+ L+ G" b$ U7 E先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。

2 d/ y' g+ R! O$ I0 @ 020011osionbunl4ui44vi.jpg.thumb.jpg
% |! j" u9 `1 B  k1 c- t/ C+ `. m轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。
: w! A# ]$ E: ^5 U 020017j8ycmnv7788bqv52.jpg.thumb.jpg + V9 p/ i1 C( I5 n: S- ^$ i3 W
特写,80C49
/ t, \0 T# k4 A" B 021040oujzuvtut6iujtvz.jpg.thumb.jpg
. X- e4 j* K' G/ z8 oLED部分,使用了一片D触发器锁存指示灯状态.
, c. T+ w6 N, y2 Y) t 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
- o% T' c, e2 ^2 @* |) n3 b% q暴力破坏,将80C49拆掉
* W  j: R0 \" T/ |6 y+ N9 a 021113e48qq98vyohvhzzh.jpg.thumb.jpg
( z! m# v. L$ r& M拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
# ?  @, A! r' u: ], D3 K! O1 w% U 021125nc9az6dj33rlds2r.jpg.thumb.jpg
5 B/ |- X$ U/ x- [. t7 O
焊好元件后的板子,准备替换80C49$ H3 D+ s5 a% L, t
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
$ f- I  Y' I# D2 u+ Z2 d用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。. i9 ^9 z  B6 O6 \8 X) ?3 ~& o
021104shifhnrqbr3o5nlo.jpg.thumb.jpg
& O% g8 l# h) }5 G% o9 G这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.& G' m0 r1 Z2 @4 J* ?
022003ym1p9u4ug40280uu.jpg.thumb.jpg
) A# X" |4 e, S" ?4 ]; Z( U开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。
; H# W$ i1 b) l8 I" l7 s' G 023313kt141q9qajtol7ma.jpg.thumb.jpg
- S& S* U+ V: f- c+ ~2 ^# t我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。- A6 u! ^% i3 U0 M7 i1 g
023322nt7l5xb3ltttkltt.jpg.thumb.jpg
' ]4 i3 |2 l+ \+ t: n: o9 |9 `$ K
主键区键帽就位% {4 ^" G5 z* {2 ]1 V  @
023331hin88e8wkrwzwikx.jpg.thumb.jpg
  f5 s; e0 a$ r" o编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
( u8 p* T; X- |4 O+ @! z! \5 a3 V 023336wjzlgopugg1jyy79.jpg.thumb.jpg # U& `! f+ d! {: N
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。
( G9 \" H1 T) x4 L6 C 023341sffu4j3g2323h6fl.jpg.thumb.jpg
; T5 X+ z% k8 F/ l2 S2 c; p

! y8 l- }. S  R----------------------------------------------------- 分割线 --------------------------------------------------9 K% Q+ v' s) Z, H. ]  c3 [

1 S: g. o9 D2 K" [2 q

6 B% r8 P9 b" ~+ k
  n% O+ z$ o) R2 @& {
2 t6 e( j+ D4 s  W4 j- ]* d& ]
% z' Y* ?7 O( h* d) P

  ]$ i, Z, {  h. ]& D9 ?& t0 Q  d: J, q- k
6 r7 u# ~9 T! V& O0 k6 j3 l

  m) u' D, C+ V9 y7 J- ]2 @4 P# p0 O! [. f( N% _1 U) j! M7 y% n
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。5 N& C) `/ _+ \
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:( P  D( @: R6 c
104025nzibm2rmiomhyirm.png.thumb.jpg
& A) h; y! y6 _! E
) y# V: P" H3 L- \, \9 H' k
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
" q7 m( S: H/ H5 z: ~) l* U$ j 105004zkrez5houvkkznko.jpg.thumb.jpg 2 Y$ D% Q$ D, s( h% Y: b' X& L0 O
" w! ]# n, k4 o% e  R
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。
1 R) b$ F- J; t' e3 E; t' Z  V6 Q. _
这是我设计的电路图:: h8 A. \" N) W+ D3 j* f7 @
110344ej2z2oo2rflo7oe7.png.thumb.jpg + H$ k  ~7 U' C4 M! g* i8 l- ?

2 E& g( f0 D" e! t, V0 ?; Z# ~PCB Layout:5 k. D2 k! g# G7 P6 P
110847jjbjvt34vwt3v5bb.png.thumb.jpg
, u4 D& m& ^/ u- u! L

' {; d' o1 a  f5 x8 d8 L不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O.
3 f- y5 J6 h: u+ w) U9 s! |: f) W. I1 k1 b; P5 p: [

3 @  L, T0 }  g
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑
( g7 j0 }9 t# v* K1 w- h
) a6 t( q3 B* o+ n/ D, Y8 [软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。: H  a  g9 G, i9 ^5 j3 p# T
% K- J  `4 B- B( x! G
总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:! F2 H% u3 N' v7 K& B) F1 }' a
113818pmrfsb6z0byt6t06.png.thumb.jpg ! L7 _( D' J% o7 I, u" Z
3 r8 w& H9 A- E6 C+ Q5 ~
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)- ?: m. b  {2 L- i5 m5 q
7 y0 T' L/ U5 m! U
USB的中断ISR,bare metal哦4 m' b9 t# k9 ^
3 c- \+ ^+ f$ ~# `9 @
void USB_IRQHandler(void)8 c; r) T, r( s$ I5 I+ }) Y3 Q4 V
{' `8 j9 @0 Y( {0 n; Z% P
    if(USB->ISTR & USB_ISTR_CTR)5 D) z0 N: g8 e, \( z; M
    {
5 o4 Q5 c: H$ h: O3 N        if((USB->ISTR & 0x0f)==0)   // EP_ID==0, y. y  x6 K, T1 H5 L
        {
% V* a1 N% u& A+ o5 h. K            switch(ep0_state)$ h7 p# J& @' x
            {: k! Y1 C  `: w! p; Y* t8 v
                case 0: ep0_state |= 0x80;. d( H1 Q  A( C
                        break;
* O3 ^+ Y0 u( I5 T7 w                case 1: if(USB->EP0R & USB_EP_CTR_TX)" q( s$ |7 q- P1 ?: L! y
                        {5 u+ a+ T+ R6 P) K
                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;+ ]0 d9 q" F6 F$ S" [2 ]
                            ep0_state=3;  l) T; g! L8 k
                            return;
0 e6 t7 {; I8 s2 p; }( y( J5 d                        }. ?  x) h; `% }
                        else7 v6 w: @% D4 \: U6 w  V6 C
                            ep0_state=0;
; z* R+ z5 ?1 ^6 {3 O2 \                        break;
" T6 U' L9 `2 {- J                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
6 |# M1 t( Z1 j+ Q0 S* [, _" @                            ep0_state |= 0x80;. c3 Y. E: @' o3 w. V0 R
                        else
; z5 }  `( C3 B6 f2 C' O8 g& ~                            ep0_state=0;' k3 ~9 w( H2 u$ T; W
                        break;
4 d1 c. t" e8 c                case 3: ep0_state=0;
: i/ W; h3 m8 Z6 {                        break;
% r" V0 n9 N$ S$ _1 _6 s                case 4: ep0_state=0;
5 ^/ f: R1 y$ Y" ]                        break;
: X7 a3 I* I: _! n: W1 a- M                default:ep0_state=0;
& x6 j# Z: U' d                        break;
; |2 S: t  X$ I& C3 u            }
% B; P0 Z$ ~' i            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag" u2 x9 I' [. I8 e3 [
            return;
5 G8 _; e' n: P1 Y. T/ B        }4 p0 E; [! D5 G: I% O" L
        else    // EP_ID can be 1
; m" N- i- X1 Y        {
9 L: Z* C3 G5 U& F# B            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag! n" i1 q! k4 j: h2 L& w; z
            ep1_wait=0;
/ o- K% O) e0 d' F4 V, E! |            return;
4 g1 {* T/ Z  g' {) k6 L        }
& Q( o" a+ W5 u- c& T# J' ?; ]    }
& t+ f  L. \5 Q4 Q* O! g5 p    if(USB->ISTR & USB_ISTR_PMAOVR)9 x  M0 I# v5 c% D! S3 n! V
    {
, G5 E' N" w- C5 Y: X' `, `        USB->ISTR = ~USB_ISTR_PMAOVR;/ h+ E+ T& |6 }& m
    }% y5 z8 v3 R& M; P) L
    if(USB->ISTR & USB_ISTR_ERR)
( I( ^' _3 B7 m1 A; n+ i" _  y    {
/ O& }8 y. H# C. _' C# Z. ?. \) @        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear7 a1 {# H5 Y% k- d! P, f! y
    }
+ }  d& F& ]  B; H" y    if(USB->ISTR & USB_ISTR_WKUP)
9 ?) k. w/ D9 ?9 ^' _8 a# x    {  Y6 u' W7 U& v- B
        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend7 s! z2 z  @8 ]+ z8 T9 R# T
        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear, u* I3 S6 T5 @  b
    }
. v$ H: D0 m3 D0 H6 t    if(USB->ISTR & USB_ISTR_SUSP)2 |3 m. O/ a3 w* M
    {( i1 q9 y% W1 D# z  z1 x
        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend9 V) V& Y* ~! F, n# b/ |( p2 q
        USB->CNTR |= USB_CNTR_LPMODE;   // low power
6 l% Y3 G* v/ U8 ?: f5 M3 W! V        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear$ B1 P; i4 v  X
    }0 c. |; s; P9 b( w% p6 N3 ~
    if(USB->ISTR & USB_ISTR_RESET)
+ Y9 B! ?( q5 n0 ?$ `    {
, d8 y) s+ t9 T& a6 `; h- {# K        USB->BTABLE = 0;    // buffer table at bottom of PMA
$ b$ m, g- J/ j- ~5 |+ U3 H        USB_PMA[0]=128; //ADDR0_TX/ W, b6 b1 ?* j0 Y
        USB_PMA[1]=0;   //COUNT0_TX! }, r2 ~$ k: ~, ^+ t! A; N. l
        USB_PMA[2]=256; //ADDR0_RX8 F( o5 ~+ e4 s0 r  `% P( `7 i
        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes# Z, i7 X$ E+ ?+ k
        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;5 I5 N: g4 d& ~' z% i) U. c3 S% u
        ep0_state=0;+ u# w6 c, ]2 s- u: w3 h! i
        USB_PMA[4]=384; //ADDR1_TX
. f1 b% P8 s! D        USB_PMA[5]=0;   //COUNT1_TX5 o4 l9 q, ]$ T6 ^) H. ~
        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type8 B! I: k7 G3 u2 @% R
        ep1_wait=0;
, e9 r5 p/ I, O' j3 M        USB->DADDR = USB_DADDR_EF;      // enable function9 j" ~; h8 n0 j
        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
/ S( I" i/ e) ?! g! G+ Q    }
/ w: T" r. B0 s, h/ p9 Y* r    if(USB->ISTR & USB_ISTR_SOF)$ g8 b, @+ X' b
    {7 x8 g- t& i2 N6 `% Z! c* _$ e
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear
8 W4 [2 H, D. [9 \" f& V    }
: {/ x2 l5 Y. V, e; ^+ M& m}
8 u! [1 s) u( {2 o4 }" K( R# W5 n: ]2 K
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。
$ k$ O+ ]3 ]! O; ]  Y( B3 v) p2 q6 e/ c7 z  Q
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
1 r" {% ~5 O9 ?% r2 y. Y' R7 e7 e* x1 M6 y! N* b4 T. y
    while(1)
7 W! l$ T0 J% s3 t    {
  |% ^1 j9 v& Q        static char row=0;/ y0 U! d) F" k
        __WFI();3 b  ~' [/ t3 `  A/ q% }
        if(ep0_state & 0x80)    // request data processing
- X) g. T- i" N. n        {
) e+ Q$ {9 ~/ p2 v9 I$ v/ p* |            if(ep0_state==0x80) // SETUP phase! }: H0 S) x3 B0 B" \# x
            {
- Z. {* w. R( t# W8 T5 q                if(!setup_packet_service())* N! q8 Z3 M* i: I; x' E( @
                {
- l+ V4 E! w& S5 k% ^& g& H2 K                    ep0_state=0;! d+ [0 ]! \5 V2 w& G, d/ N
                    // not supported
& k4 ]  }& ], @! G3 u0 r                }
5 M/ i; T$ g# a5 J( ~                // ep0_state should be set to 1 or 2, if processed
8 ?& G/ ]9 Y  E: O3 }            }
( i  G" ~4 M2 a% e7 T1 {            else    // OUT phase6 I) L8 M1 G4 A/ B5 Y' G
            {6 J* Y( H$ h8 k( {8 n
                // process data
" P( P  D$ J! K/ |                show_LED(*(uint8_t *)(USB_PMA+128));3 w* U2 R8 e' [! a' s
                ep0_state=4;4 J9 Y9 `4 _0 s& x, ~$ X3 Q
                USB_PMA[1]=0;       // Zero length DATA0
* w$ u" U6 u" P. B2 y                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
5 L9 d1 J, B2 H$ k* \. A            }4 X( ?, t2 z1 _7 r! x
        }! M1 `" d3 q6 ~  r' l
        else! m- Z+ H4 x$ u6 O
        {0 M% o* v$ [3 c" g' H6 W0 j
            if(usb_address && ep0_state==0)
( I4 O, h- ?& L& e5 c            {( p$ d! v  O2 n$ \" `0 g1 [
                USB->DADDR = USB_DADDR_EF|usb_address;  v6 M6 g  d" H' c" U2 p( U
                usb_address=0;0 P  H# q! `( K: [* X
            }5 f0 q' m  o5 y8 }
        }. T' w8 h4 W1 J7 P. g# R
        if(row!=scan_row)   // new scan line
& L5 |- e! M! ]3 I  x        {! \9 t3 ?0 o. X
            if(key_state[row]!=prev_key_state[row])9 T6 S( W# Y: Y9 u' ?; f* i
            {$ q; V4 ~4 V6 I9 W, @
                uint8_t test=0x80;
& ^8 F6 [: N3 q: n                uint8_t diff=key_state[row]^prev_key_state[row];$ t! q& [3 _' g$ a. `6 d; R+ D
                for(i=0;i<8;i++)
; O6 [1 f$ F7 b" s* k& k. V                {0 Y2 B1 x1 i3 ~8 r
                    if(diff & test)
! q5 O/ C& B6 o& I& E$ g, q0 x6 e" [                        update_key_matrix(row,i,key_state[row]&test);
% ~* x" R4 \9 u9 i9 T& [0 J7 T                    test>>=1;! ~9 w; ?, H0 E
                }" f( Q7 n/ C9 }  e$ {: C% P
            }2 Y- k" U. R+ Q( c) O
            row=scan_row;
: m( q( C, `/ s$ u- W  l/ t        }
% B/ o0 D4 _& p' f% n. h    }
$ x$ P- Q7 d1 ]" O2 l" k  P1 q  r* `! \$ J1 |( X

* B% N- k8 f5 l$ ?+ }EP0的控制传输,把用到的请求处理一下
7 y" y6 ]6 G& n/ t8 S/ b9 Wchar setup_packet_service(void)
2 x# t% w  q- X4 G) C. G3 K! R{
  g7 m) }3 M& V* V! U5 W    if(ep0_std_req->bmRequestType & 0x20)   // class-specific
5 m. O9 w$ |6 J# Z* \. ]* a    {
7 ^, ?! p# p6 P        switch(ep0_std_req->bRequest)' I; h1 o' I% H9 q
        {0 F& n0 g- Z* U0 k+ s/ S8 m
            case REQ_GET_REPORT: break;# O8 D! ]; u( X8 V
            case REQ_GET_IDLE:4 Z0 _# I! _  D, t( k6 K- z4 X
                USB_PMA[64]=0xfa;   // return 1 byte
2 u! \1 k. R) Y, j) e: @                USB_PMA[1]=1;
! ^" D2 {/ f6 o& y                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
* e2 P) g; x8 F) I0 g+ ~$ P6 t                ep0_state=1;, F2 S' Q, T; h( G! M1 s
                return 1;
1 a3 S( L2 w$ T, u. Q* {                break;
: _9 @, X. Y1 b5 c: o  c            case REQ_SET_REPORT:! U6 n, E- Z; q" e( F
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;- O; V  W1 W! J2 i0 f3 d7 u
                ep0_state=2;: `8 m9 ^4 u  r. y5 e/ Z
                return 1;
1 U3 S& {& h- x- w7 B3 c* }                break;
1 c8 o; w# j+ [4 R9 W            case REQ_SET_IDLE:) }; v0 }' ?, V6 d( ]) Z- q# i- O
                USB_PMA[1]=0;   // Zero DATA
# M; m5 W  Z! Z, q( r" _/ k                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
9 G6 P$ c  Q1 P4 L5 M6 }* s                ep0_state=4;- \: \4 l, d, j9 {- Y% v7 \
                return 1;& @- I  ]! x, r9 A- b
                break;9 O' _: E" z' n4 v8 j) S
        }
# U( G9 X- \* j' t- i        return 0;
+ W) f( [& L: V0 `+ A, Y) L- v+ E    }
& `, w$ Y3 t, [/ Y( q2 h- L    else    // standard7 y( o; \* \& m+ t; R
    {
  T2 ?$ h# H6 Y5 V        switch(ep0_std_req->bRequest)0 E1 {! b2 ~4 ^' f( R5 N
        {$ A' T# x$ s& j0 T8 Y
            case REQ_GET_DESCRIPTOR:
! |8 @9 t" [6 f3 R! I8 ?) G                return descriptor_service();
+ T& Z( ^; B4 B% G! K' I                break;
) w  {7 G- b. d: c3 Z            case REQ_SET_ADDRESS:
) D( \- M2 y" b  e                if(ep0_std_req->bmRequestType!=0x00)
6 Z! D% `" I: r+ }- q) }# P                    return 0;5 q3 \1 S6 A' e
                usb_address=ep0_std_req->wValue;  \. b! x$ u, h; o$ x3 s4 N9 j7 w  L
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
. T4 a5 W( r* i+ i( ?                USB_PMA[1]=0;       // Zero length DATA0' J% l7 h8 f6 p" L* N4 I
                ep0_state=4;    // No Data phase
; c+ Y( s( E( G- [0 Y! K5 ^( D                return 1;- I) p( \( P9 K  ?, F/ C" O3 B
            case REQ_SET_CONFIGURATION:0 v7 W5 ~' U2 i9 D  d! e; x
                if(ep0_std_req->bmRequestType!=0x00); o5 c6 r- a. O7 A! F9 ~& O& r
                    return 0;
8 v7 D+ B' m) a                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;* G2 E4 S1 B' I6 w7 y6 \- Y0 D' n
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
. L4 W8 O/ x! E( c) X* W                USB_PMA[1]=0;   // Zero DATA6 P' O8 b0 E, O. M8 a+ j
                ep0_state=4;    // No DATA phase" t; t/ E& i) y, o" g/ I/ g  S
                return 1;
9 |/ Q3 t3 R# J  ]+ o/ ~            default: return 0;1 D: J- P( L  y8 Y& ?4 i1 I
        }) n; q% Z0 C1 X
    }* O  z+ E, ?- H- d$ u$ a- ?' _
}) w5 ]: V6 ~8 _% P7 n4 E* E( W3 L6 W
+ N1 l! x; W" i1 ^
& W6 P) n2 T- m( ~, C' g

9 p. b2 i" C. _9 t0 l) g
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的) q' ]7 U$ `$ `  d4 n% |' x; I
char descriptor_service(void)
3 X) j" a3 F3 B  t  ?{( A" [/ @" z9 _0 J+ {6 X
    switch((ep0_std_req->wValue)>>8)# f) e4 {* a/ r
    {
0 Q% F/ }& R: X* c        case DESC_TYPE_DEVICE:
8 f' j8 b& I" a: Y0 G  k" a            return ep0_preparedata(&DevDesc, sizeof(DevDesc));
8 A* l! J. J3 ]8 V# H% t            break;
9 P! ~: i$ t* \& M% H2 s. X0 |' m  W        case DESC_TYPE_CONFIG:
$ m5 Q; i0 I/ P' M" ?! G; z  p            if(sizeof(ConfigDescData)>ep0_std_req->wLength)
- @7 Q: z3 I' H7 j                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);9 s+ y2 u9 x% y9 c0 Q( X
            else
6 N( j: e5 @, ]) C                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));
0 D) z" \/ J# O5 a& Y8 }$ {# f            break;
* x* v  ^9 O1 p        case DESC_TYPE_STRING:. m$ j. A0 b, w5 I% F
            switch(ep0_std_req->wValue &0xff)1 x4 h+ W8 J4 f+ J; a
            {
( p( ^' ^8 G: t, Y# K                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));3 {2 ~% u: [3 }- B- r
                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));
; t4 b) j0 ?: t$ F0 E: n                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));. N# }5 L8 A4 r# U
                default: return 0;) \  |7 |  k/ f' Y  ?" t9 N4 |
            }
$ G* U9 c# w. G8 h# E" [! F            break;& I$ V) p! |9 P- a! ~" k
        case REPORT_DESC_TYPE:9 C4 l6 h" k/ \' j5 b' U1 |. Y
            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
: G: g* H9 e3 J  U7 c; |        default:" x% w0 y, K1 }1 ]! u* n% _4 r( j
            return 0;
: w3 J/ n6 f2 x$ K1 D+ k' d    }2 M+ u- z% d8 S* z8 S" B% W
}. h& t. {  s! ^' A0 N0 R6 \- v; I9 J
下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
0 J% f1 w* `) Q0 g; M5 u2 P2 evoid TIM6_DAC_IRQHandler(void)
0 e* m* _! n6 i( t. A3 M{6 W& R3 e. x6 I3 E0 h
    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);% z/ `- H7 C7 r4 ]

* p: `7 A* H! V5 G& T) T0 V
) ?5 d' ]/ z  S* N
    TIM6->SR &= ~TIM_SR_UIF;
, Y% U& s2 U; X) W; p    prev_key_state[scan_row]=key_state[scan_row];
8 g2 L" t2 N7 L0 @6 R/ o    key_state[scan_row]= *PA_IDR;   // update key states
4 t1 R1 m) {6 }+ q- R0 U, ^& M    switch(scan_row), t1 \9 h: T4 @, o- N  n
    {( u* H$ g: M( ]+ A3 e7 I" O
        case 13: // next row PB14
; t9 t" o1 \2 ^& ?- i, a                GPIOC->MODER = GPIOC_DEFAULT;
1 U4 _6 E$ i* h7 F4 Q( X                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;9 j: @) @3 }$ h! i9 i, }, T
                break;
7 i5 D& Y0 e( [8 `- G* x# [. ]        case  0: // next row PB15
+ S3 M$ ^' f/ V0 g1 Z% v                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;; s# Y+ H( k( F7 N! H0 b2 z; p& b- C/ e
                break;6 w" }7 h5 R7 V$ E  }2 `7 ]
        case  1: // next row PB3
9 m3 G. ~+ _- U' k8 K! X                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;8 W9 H$ V5 n8 U
                break;6 }4 ?; @3 `5 i! {
        case  2: // next row PB42 k) P5 ~  g/ r, O0 {1 W; ?
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;
) c/ t6 I0 J7 l5 _$ x* P                break;
' {7 O0 q6 \: w; H7 A        case  3: // next row PB5
, a8 f% \8 B0 I) P( Q( I$ b+ I                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;
. N+ i6 V7 A% m                break;
; h3 W, j0 A# w+ x        case  4: // next row PB6
- C2 r4 N$ ^, z# c0 ~                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;( _: ~6 v' k8 ^& a: [/ b
                break;
* a& A4 n$ c+ t: a7 a5 f        case  5: // next row PB7
/ H# I  f& i( P( T. q                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;0 r3 z- V2 V1 r% l* o) x
                break;1 n: U! b$ ~1 m3 m
        case  6: // next row PB8" _8 n! p7 B& r' ~# J2 Z
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;# k8 k7 X/ M) g' r
                break;
6 e4 y& E% M6 ]1 Y5 r# U        case  7: // next row PB9
* s1 E* i5 s+ h7 d                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;
, g  h1 V$ ?+ ]; S7 o8 {                break;  H) \$ v6 n3 M% n1 E2 H8 k
        case  8: // next row PA8
+ d% C+ g5 n+ {                GPIOB->MODER = GPIOB_DEFAULT;
3 R5 v/ r% K, v0 o                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;; f# f) J# I5 B+ u
                break;. ^* W8 L! P+ h7 S
        case  9: // next row PA9
4 r0 T! I/ B% j1 P* N# A                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;
3 h+ Q% Q: }: m: R6 @' B                break;
; @+ k# t1 F. ]" x: {8 N; i3 z        case 10: // next row PA10
, v0 l9 ]- k. `* J0 X. K2 y; W                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
/ v7 H/ ~: |  q9 a1 z: x. h                break;. z  _; K' \" ]
        case 11: // next row PA15
4 N! K; b# H% O7 q- G                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;
, C+ L5 l% n) _# @8 ^                break;
8 s8 `4 n0 P& S. |, ]% s- p        case 12: // next row PC137 R+ G8 G8 L5 w' y
                GPIOA->MODER = GPIOA_DEFAULT;
* E6 f- e) i- i5 F6 W                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;* G; ~1 R0 M4 i, B( t% k  M- q7 R
                break;
! h9 i7 P; d5 h, p$ \2 ?) S4 ~) R    }  L* w! s4 @+ A0 b! @# o$ k
    if(scan_row<13)8 O4 [# I4 {3 k* G' e+ V9 q' A
        scan_row++;
, e9 |% O4 p. p3 d8 `9 t    else9 z  a; T1 r" f* c8 z, q
        scan_row=0;
0 P1 t3 \# u$ o0 r# |}
1 O3 I( O9 i: s. E" D8 j0 @. @8 E8 U1 r" W; e! N
  `! Q2 ?& K+ \$ K: U0 C
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑 ( ^& W  w  h9 z7 @# ?3 `/ _7 v/ x

# {) H$ S5 a, @扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。
+ _5 r2 ~, t; c0 e" h8 M5 m* O/ Z: R& k# R" {4 r* s$ }$ {
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:
8 P1 h% |& H+ A% ?2 c
- @* y$ \$ X8 O! X# v! k# q+ t
+ h$ ~2 y9 Q0 I7 [
const char hid_keymap_qwerty[14][8]={+ [" n; b% C# Z( m
    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},
" W- m" {% v& v* x6 z5 `! B# G    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
9 F" V; w+ k, s) A. ]    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
& a. Q; Z( K5 L- _' a    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},6 B% a8 M( K1 K& u8 T  ]$ `
    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},% |% W) X: `" `, D
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
4 ~$ y1 P1 }) `    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
' ~3 @8 _6 H  J! B3 o  P8 V    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},
' {1 o0 n" f$ T+ g* e    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},6 ]+ V  V* l+ T6 H- j+ ~8 U( E% B
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
3 ^1 c5 r' k6 z    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},, K1 n. R: ~& o7 W3 U
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},  P( _* _* s; u5 x9 {; h2 g  h
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},- o) C5 o. E  _: r) \
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
& {; e0 w- j4 Y: o4 {; r};  U, w/ A* F; p: T) W

+ |2 k# d- _+ r

% R7 N+ L" f3 b7 x, Rconst char hid_keymap_dvorak[14][8]={
; `+ M2 @9 r; ]3 c3 `; Q    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
- {/ q5 Y& }8 G    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
# N( e7 N- {' C! ?- L$ J, D: Q  L. P    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},2 v- r% Y9 b3 V+ I3 `" q6 ]
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
# Z& d3 l) F5 A! b% ~    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
. U; w+ @0 u2 r0 `) [    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
6 m5 e: I# i( G  G4 X1 n" v+ e, n    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
, V- @; W3 z3 l5 [) X    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},% C5 @# M# d" k* |, c0 Q
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
# ^0 u& s8 ~% c- y# H, X! J    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
3 \& Y5 y1 V  r! ^7 j9 R7 P    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
: C  N3 ~) A0 l' t( O; s. {    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
4 c+ O/ w% n0 C; P    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},! w# f! U: O( z! @" I0 p
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}9 w+ `  s6 u3 o# M4 v# h
};
6 ^0 m  D7 G1 D8 Y' q
) @  F# q+ {" g) M$ G1 l  y( N' l
1 U  E. j, ]9 P3 C  E. U上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。
$ r4 |0 M1 ]+ @2 y2 B; F* g5 n+ u! ?
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

5 ~8 `. |2 y% a0 X" f# X3 P# l* N: O8 M$ g2 ^* X& @
void update_key_matrix(char row, char col, char onoff)
4 W2 e' O5 K' f: H% H9 T{
8 ?& X5 D) \' u    static uint16_t hid_report[4]={0,0,0,0};
% x' j4 r: \- J2 P7 k4 c) Y    static char (*hid_keymap)[8]=hid_keymap_dvorak;
1 O3 d7 R+ _1 Z/ D( D9 W+ m
7 `# T- ~3 S) L9 F# R& |+ z* a
! F, x2 A8 M" b( r. |' t& Q
    unsigned char key=hid_keymap[row][col];
! T) E- K0 G5 b    unsigned char *report =(unsigned char *)hid_report;
, j, ]$ M; j! T    char i;
$ ?/ @* x5 Q5 ]! N. h7 g- X; R5 k' K" Q$ b3 f% W' }
9 @" L) i6 j# _- Z4 t
    if(key==HK_MODE)
7 {: L' ]* W5 M. n" ^. K3 h    {
3 f, d, @$ u  z7 m        if(!onoff)- `2 a8 m9 [! i, h1 b% q; j7 c3 b7 K# ^
        {% ?) `* ^  Q6 T4 q* Z
            if(hid_keymap==hid_keymap_dvorak)
! p$ ^; v; F$ d& e3 f1 K& a1 N            {# J5 l, w" W  q: C3 ]- x' v- e
                hid_keymap=hid_keymap_qwerty;% c: H; ^6 i$ i0 F4 B5 I
                GPIOB->BSRR = (1<<2);4 C! I9 z6 ?* c. _5 s6 M
            }
% V" r* |8 F* ^/ n/ ], g            else4 Y1 t) J3 s- ?2 _! ^
            {
1 Q' d7 f9 v  k( g: H                hid_keymap=hid_keymap_dvorak;' `+ U* {" u- R6 V
                GPIOB->BRR = (1<<2);
( {% h1 s1 {9 ?8 ]* X# C            }
4 j- ~( o; \/ |, V$ l' B% X        }, ~& R' J0 D6 Z) S
        return;
; C6 L0 C9 L( l" w+ j) p# }# F    }
4 I( I  f2 T( Q! b, Z9 s. Z1 Y
  T# S. b1 n- b5 ?; i
# F$ U5 ?6 u2 G& G3 \: B
    if(key>=0x80)   // Alt, Ctrl, Shift
& x1 Q2 R, f0 I( o* h1 H" O" ]    {
$ l3 z& h' n6 T7 O        uint8_t bitset = 1<<(key&7);2 h2 C. M) V# W, K! y6 l7 v
        if(onoff)   // non-zero is key up' w% [' i) z/ p. y5 e9 v" e) D1 q
            report[0] &= (~bitset);
) T( q& u* m* j2 }        else2 M6 A7 U6 c0 O# C
            report[0] |= bitset;
/ t& \. ?$ a8 ~' t6 H/ e2 K    }
% Y  G& H& Z- e6 k$ o. o2 Q    else
) s$ z3 L7 I  ~, O, m2 }    {
" U9 u) n6 j1 ~' q        if(onoff)   // non-zero is key up
+ U0 S( n$ G4 g. I6 t6 d        {, \+ ^2 t# [; F% {7 c+ |% C
            for(i=2;i<8;i++)5 m  S- L) h/ K. @5 s! ]
            {+ C% J  T- P9 h" F
                if(report==key)
  A/ M; o: t  w; O' r1 K9 G3 d9 Z                {5 U; L# Y6 q2 k6 v& I# G5 H
                    report=0;9 T; f7 q$ E0 N1 D
                    break;: O3 l/ ^3 r$ s8 A% W' h" n* Q, c
                }
0 ]& W! w6 M0 n! E2 \! ^2 e            }9 ?0 |' e: Z( @& S/ w7 E6 Y0 E
        }9 d) a: Q6 N. s4 X
        else* ]# s; R2 x. N) _1 M7 \
        {
  v  u0 `6 |) a4 k4 V7 L            for(i=2;i<8;i++)
) _/ m) e% r- b7 t            {
& J+ Z9 n' v2 B# H# |4 C                if(report==key)
, u- E$ ^& P% q- }+ Q2 J8 L                    break;2 @1 j! S* ]8 K9 C6 G( r
                if(report==0)
' M) _4 h% @5 G! R/ H* ]+ D                {' T& v/ L. E8 y1 ~; ]* h
                    report=key;
: x3 z- o5 l; i+ M& o                    break;
; h/ C# W2 F; }$ g                }' ~" V/ w4 r8 s# u; P
            }$ z" t+ F1 X) q/ V0 ^" C$ m
        }; }3 }: |+ q- W) M, `7 N* `
    }
0 P" S/ p$ j: G- |6 }& O% j' V/ O' m    for(i=0;i<4;i++)
! `2 q/ {5 c2 \  M# o- x        USB_PMA[192+i]=hid_report;# T0 ^$ J& C+ D/ ^# p- x: b% M
    USB_PMA[5]=8;   //COUNT1_TX8 J2 m/ L0 g- P9 p3 e- Y/ T- T
    if(ep1_wait==0)
7 Z  v, b' }4 J' y; A& X/ H2 b. |+ c    {
/ X( y; K8 [9 r        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;
7 t# U5 [6 a0 p! x7 Q4 j        ep1_wait=1;
& H0 f& ]4 ~# ]    }2 m7 i! _* }5 L* y
}! B$ x6 t: z* C4 v" l: s
2 T' B( G6 S8 N8 D0 `
, `) ~6 ]- I& J- V; y8 t
完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。9 s0 J" g$ w* t6 \# Y) r
keyboard.zip (8.7 KB, 下载次数: 6375)
! }- C0 g" @, n4 w4 F) i# [

8 n* x3 p2 ~5 M1 d; m3 C# R
. e9 E# U: C. ^' d$ ]) I0 c% d+ |6 O' M3 U

: R; K) D' T( c! n
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

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

使用道具 举报

8

主题

143

回帖

217

积分

中级会员

积分
217

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
: r6 |  I+ ]6 l3 x$ `: w/ w: D& y不过楼主也很厉害!
回复

使用道具 举报

24

主题

39

回帖

288

积分

vip

积分
288
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
2 s+ |1 g8 {; x( u! |4 F5 @" P! N刚开始我以为要把打字机改造成电脑键盘  Y& w  ?) F, L
不过楼主也很厉害!
& p) E) c- s1 g: t! D) _- T
哈哈  是有点像奥
回复

使用道具 举报

0

主题

3

回帖

4

积分

新手上路

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害9 U8 \- r% [0 P0 O! i
回复

使用道具 举报

0

主题

2

回帖

2

积分

新手上路

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-12-19 05:00 , Processed in 0.173387 second(s), 28 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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