请选择 进入手机版 | 继续访问电脑版

MiniDSO产品技术交流  迷你示波器-袖珍示波器-示波器探头-

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

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

[复制链接]

24

主题

63

帖子

286

积分

vip

Rank: 9Rank: 9Rank: 9

积分
286
发表于 2016-9-21 20:34:19 | 显示全部楼层 |阅读模式
本帖最后由 miaozhuang 于 2016-9-21 20:35 编辑
$ _. M6 c# f' d) F. m. N$ m# x. h3 ?' S6 g0 i# T7 g5 @- Q8 A! D
http://bbs.eeworld.com.cn/forum. ... digest%26digest%3D1
- h/ F  d% `. b: y! x  F4 E这个DIY项目的想法已经有很久了,如今终于达到了的设计的初衷。要体现“任性”的特点,先介绍背景吧。4 H% W6 m, Q3 V4 }
4 q5 x; a* s  O: e1 r% }
在看这个帖子的诸位一定都在用计算机键盘吧。键盘上的数字键是1到9从左至右排列,或者是右小键盘区那样三个一排有序排列,反正规律很明显。但是字母键却不是A,B,C...到Z这么按字母序有规律地排下来的。我刚接触电脑(其实还是学习机)的时候,没在意这个问题,觉得是要盲打嘛,反正对两手的手指头来说,按字母序排列并没有什么好处。于是用多了这些排列也就记住了,从来不管它为什么要这样。其实,PC的键盘键位排布上是延用了打字机的键盘,这是设备演变过程中很自然的一个延续。打字机的历史就要早很多了,我没有亲见过打字机长什么样,而且,咱们汉字是铅字打上去的,和英文打字机方式完全不同。
6 H. m* r+ V/ {/ i, r% v
9 e& _% H5 \0 ]& ?) z3 J9 I
235140i3a36qivqzuvmt5q.jpg.thumb.jpg / m7 P# S1 @1 q4 `. W0 i5 E
上面这个照片(来自wikipedia),是"Sholes and Glidden Type-Writer.",第一个获得成功的商用打字机(1873)。请注意它的键盘字母键排列。& D# v; s+ {9 O* Y) K
001734klbyoluenuwz4h4b.png.thumb.jpg ! m- z& @, e; _, f+ p
为什么得到这样一个字母排列?在当时的确经过了多次的优化改进,因为打字机是机械的动作,要尽量避免连续的击键引起冲突。结果是因为商业上的成功,QWERTY这个布局也跟着被越来越多的制造商吸收采用。在非英语语言的键盘上,个别键位可能不同,属大同小异了。最早的IBM PC键盘:3 D0 L$ f# B0 \7 H
003625r2agx2f5v922cf2f.png.thumb.jpg
: E, c- O$ y' L, g; W  r其实在电传动打字机问世之后,打字键盘的键位布局就可以自由了。但是QWERTY的流行没有被改变——习惯的力量是强大的。虽然是大众所接受,QWERTY也有被人诟病的地方,比如说左右手分配不平衡,在英语里面单独用左手能打出来的单词远比单独用右手的多。那么,除了QWERTY还能用啥?在ANSI标准里面还有另外一个键盘布局,叫做DVORAK., M% N5 W0 o5 M
Dvorak(德沃夏克)布局,是以其发明人之一: August Dvorak 的姓命名的。在20世纪30年代,Dvorak 和 Dealey 在他们多年的研究工作基础上发明了Dvorak布局,目标是减少打字出错几率、提高速度和减少手的疲劳。最初发明的布局是这个样子:

% X, s5 k) I6 O; o# @ 005836yvs0wvovwsssgd3o.png.thumb.jpg " y5 c  r9 f7 P
Dvorak布局的最明显特征是让使用频率最高的键安排在中间的一排(Home row),这样手指不用移动就触得到。当然还有左右手均衡的设计等等。尽管不是所有人都同意Dvorak布局能够比QWERTY布局提高键盘输入的效率,最快打字速度的记录的确是在Dvorak键盘上创造的。( V# Y2 Q1 ^6 [
/ e& i7 Q  K3 |! e5 B; {
我是经常要写代码的人,对键盘要求比较高,一定要顺手。从1998年拥有电脑开始,第一块键盘用了5年,实在是塑料结构磨损严重了才换了。第二块键盘用了大概也有5年,第三块是淘宝买到的和第二块同样的。除了手感,我对键盘还有个挑剔是要大回车键(老键盘惯出来的)。到了用上笔记本电脑,键盘问题只能忍忍了。我最后买的一块Benq的”轻指飞扬"绝版键盘因为是USB,作为笔记本键盘替补一直保留到现在。' |7 S& d8 U- ~4 @4 J& O# B5 h
* \+ j  P$ S/ _+ q) I* v
到2012年下半年,我在淘宝发现了有“机械键盘”这东东,认识了Cherry MX轴。然后到2013年农历年后,我花一百多一点买了一块老旧的国产青轴机械键盘,虽然很陈旧状态也差了,敲了一会儿我就发现:这就是我要的手感啊,一比起来用了多年的薄膜键盘简直太委屈手指了。我后来花了更多的钱买了新的轴(就是机械键盘的开关)来更换修复,使之成为上班工作用。
  {, s0 N! j0 z2 H% w! m
9 H+ U( A5 P/ ^$ R机械键盘用着爽,后来我发现手指别扭的地方了,跟QWERTY键盘布局有关系。了解了Dvorak布局之后,我下定决心,换用Dvorak. 这个过程很漫长,大约是一年以后才抛开了QWERTY根深蒂固的影响。到如今两年多,我也没有肯定我的输入速度是否达到自己曾经QWERTY时候最快的水平,不过可以肯定的是换了Dvorak,手指头是舒服了。借个图说明两种布局的差别:

6 ?, z! G* A1 D, n, ` 123206vx24rfxfp2dfrpxg.jpg.thumb.jpg
/ g% P% n  P7 [( i' \# V% ?! q$ G----------------------------------------------------  分割线 ----------------------------------------------------------' _& \" S4 y; h, [. {

$ ?  q; a: q$ d9 b先是改造的对象,主角: 这已经是拆解出机械键盘中的PCB板+钢板,并且拆掉了全部的键轴之后的样子。这块键盘买来时的成色相当差,很脏,惟有键帽还不错,但原本的轴已进灰,状态差。
& ^3 U6 @8 |3 t
020011osionbunl4ui44vi.jpg.thumb.jpg " X7 I! L4 Z9 ?+ F
轴全部拆下来之后才能将钢板和PCB分离,不然是被卡住的。原来键盘里面的灰比照片上还多得多。注意到这块DIP40的芯片,就是键盘的主控。7 M) @# ~6 r9 l; @
020017j8ycmnv7788bqv52.jpg.thumb.jpg
2 L0 V- r3 J) W' L( M% g  `1 W特写,80C49. N) h! ?) e* S( N/ {6 u/ y
021040oujzuvtut6iujtvz.jpg.thumb.jpg 1 |/ l( @) E: y, ^
LED部分,使用了一片D触发器锁存指示灯状态.
: e- T. E6 O* ~# c  B8 J9 Q( S 022209ixzjfjc9crmrrqjm.jpg.thumb.jpg
  A' |5 \9 r) d8 I4 v* T$ g6 ]暴力破坏,将80C49拆掉+ c' Q- g, p4 Z. N; G" t' B( q7 t
021113e48qq98vyohvhzzh.jpg.thumb.jpg ; l8 |$ x- R  D
拆掉原来的键盘主控,我用什么顶替呢?没有引脚全兼容的单片机了,而且我要制作USB键盘,所以……STM32F072,做块一样大小的PCB. 因为主要是使用原有的键盘扫描矩阵,有些引脚是不需要连的。
+ C2 G! \: R. i" L3 X8 S 021125nc9az6dj33rlds2r.jpg.thumb.jpg & m" ^& W$ ~) t7 ]5 R" c+ a- r
焊好元件后的板子,准备替换80C493 ^7 k' V  t- m6 @+ l* P
021146zmmpmh4pav4o7g6a.jpg.thumb.jpg
0 o6 o, u$ N% a  I: U- K& Y) g用剪下的电阻腿作连接吧,对好位把引脚都焊上。STM32F0的SWD接口务必要留出来下载程序的。! A/ H" p' x: p/ J8 U* R* v/ X
021104shifhnrqbr3o5nlo.jpg.thumb.jpg 9 `; k+ A% h/ M0 T
这是在软件开发当中调试的场景。USB线需要飞线,因为原来的键盘PCB上就没有USB.
, y8 R: H, M8 ]& O4 F 022003ym1p9u4ug40280uu.jpg.thumb.jpg
6 ?" l% Y" u- U9 T3 ]开始安装钢板,主键区焊上全新的Cherry MX茶轴(2.5 RMB一颗)。F区暂且空着,因为使用频率不高,换新轴就显得浪费了,等下再把部分旧轴清洗一下装回去。7 [( t9 i7 L& M2 k/ L) x
023313kt141q9qajtol7ma.jpg.thumb.jpg $ P1 Y, y; Y! A) J& N
我设计的MCU PCB要在键盘PCB和钢板之间。除了SWD的引脚,把UART飞线出来供调试的不时之需。+ E5 o8 ^% r7 p" O& [& y
023322nt7l5xb3ltttkltt.jpg.thumb.jpg " T  Q8 B* ^$ N- `4 @' A8 `
主键区键帽就位
- m$ j% m! w" ]* |) g: J 023331hin88e8wkrwzwikx.jpg.thumb.jpg
4 j0 w% ]. D) y, d  x5 P& x7 |4 o9 q' o编辑键区也安装好,确认这里替换后不会有冲突。调试用的线和针脚以后是要拆掉的。
9 r& h$ ~/ {" Z/ \ 023336wjzlgopugg1jyy79.jpg.thumb.jpg * V) R2 O8 u* h( R
最后的组装,USB线,以及切换键盘布局的附加按钮。部分键轴还没有装,低优先级的。
- f) j4 l+ D$ B$ e* c% I. H 023341sffu4j3g2323h6fl.jpg.thumb.jpg : ]6 ?% J) J" H
8 `7 A5 P# H6 D8 w
----------------------------------------------------- 分割线 --------------------------------------------------, k$ t) z3 {/ s$ p" Y2 j4 d2 Y
+ ^& w8 V4 o- I0 A; f+ D+ W

4 [9 l6 J0 m8 ?) r2 B( t5 y4 n9 ~4 J' h7 f* S7 _

: n' B- D1 ^. }" X6 J7 w/ |( E9 T7 C- e" u' H/ M; e6 j

5 Z3 ], h! P) G* M3 P  c
. \6 [3 n8 s9 P% S( g3 ]$ i+ q
9 t$ u& h  C4 |2 M, v/ I/ \' p: x5 ?$ c8 Z0 m) v0 n- d

  z3 I% Y3 _% z8 X# P
) y, [5 ]0 A" T6 m; r  `" d: g
回复

使用道具 举报

24

主题

63

帖子

286

积分

vip

Rank: 9Rank: 9Rank: 9

积分
286
 楼主| 发表于 2016-9-21 20:40:25 | 显示全部楼层
DIY过程直播完了,下面说硬件的设计。" ~3 Z/ `0 D6 U' c$ T3 t
80C49是块MCU,貌似也就在PS/2键盘上面用。搜到其datasheet对引脚的定义:! ^3 @8 w. [/ g; `
104025nzibm2rmiomhyirm.png.thumb.jpg
. o* i  m3 q! ~2 Z
  F" L% [. I/ S5 }! R
其实最关心的还是键盘矩阵怎么接的,这个我就靠人肉了,在的PCB背面寻着每条扫描行或列线找,记录在草稿纸上。最终整理出来的结果是这样的:(最上边和最右边铅笔写的数字是引脚编号)
! E5 a* X7 I: e. P: K8 ` 105004zkrez5houvkkznko.jpg.thumb.jpg 1 ~! U/ a* B7 P0 j# Z
" k5 B- n4 u- w$ O- K; ]
扫描矩阵是8x14的,最多可以支持112个按键,实际上只有101个键,空出了一些。对照上面那个引脚定义,可以把用到的I/O口确定了。除了电源引脚,剩下还有几个引脚使用到:PS/2的CLK和DATA占用2个,状态指示LED的电路占用1个,AT/XT开关使用了一个。我用STM32F072C8,有48个引脚刚好是够的,富余的I/O就飞线引出了。0 L" k; l2 H1 a8 J' T' o" f
& H5 i! Y1 N( Y
这是我设计的电路图:' ^; ]/ K' @2 |7 w- L& s5 j% n2 g' g
110344ej2z2oo2rflo7oe7.png.thumb.jpg 9 A: ]( e* D& ?
6 N. x% O, r5 {, K/ M8 E
PCB Layout:
" Q. g$ t6 F3 w3 q) ` 110847jjbjvt34vwt3v5bb.png.thumb.jpg & Q# H6 m* }- z
+ k2 U3 V0 A7 o" f5 S: L8 }9 ^
不从80C49引脚上走的信号包括: SWD接口,USB D+/D-,USART TX/RX,额外两个可用I/O. : e% T3 o2 l( n' j2 {
! `/ e4 ]- U; K/ X

( L- [+ X! A5 c! G, E% f2 g* K5 B5 S
回复 支持 反对

使用道具 举报

24

主题

63

帖子

286

积分

vip

Rank: 9Rank: 9Rank: 9

积分
286
 楼主| 发表于 2016-9-21 20:47:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 20:55 编辑 " L3 ~; K5 k3 ?6 _) V+ {. g1 y& Y
; E- N" P4 U: h0 s! i
软件上的工作比硬件多得多。因为想改造成USB键盘,不得不把USB HID的实现稍微看懂一下。PS/2模式硬件上也是保留的,暂时我还没去写软件。* r: }8 W& T# s& L

1 h; t3 S% _, v/ i/ y$ |总结一下,USB HID键盘需要使用两种HID报告:一是从设备到主机的,按键状态的报告,8字节;二是主机到设备的,指示灯状态的报告,1字节。第一个报告我使用EP1(Endpoint 1, 端点1)来发送,中断传输;第二个报告就使用默认的EP0,控制传输。USB的描述符,可以从现有的USB键盘上修改而来。下面是用USBTreeView这个工具查看到的的我的这个键盘的描述符:
' b5 m+ u# F7 G) b9 j6 q6 r 113818pmrfsb6z0byt6t06.png.thumb.jpg + ^( E3 q5 m0 h( E2 v) [1 I7 T7 h
" h* U9 [& o6 l/ i# n# O6 b
其中USB报告描述符我没完全看懂,copy了现成的(这个也没必要自己重新写嘛)
: f2 ~2 X  a8 B4 s3 ?- l7 a, Y. u9 |2 n1 a6 v8 v# \7 d/ H" M, l0 r. L
USB的中断ISR,bare metal哦
: h! a% E; V3 W6 F9 O
2 q$ A: @5 _. T$ E0 k/ O& Y
void USB_IRQHandler(void)" Q3 S0 ?* {8 e/ k1 d
{! H1 o4 a  D$ u5 r* `
    if(USB->ISTR & USB_ISTR_CTR)  W# m* D$ d) m0 H( [' I0 H
    {
" M4 ?  X6 V. a; r3 p        if((USB->ISTR & 0x0f)==0)   // EP_ID==0  p) I3 m3 f; e4 h* ^6 t* P
        {
0 E7 Y, r; U9 _! h$ U. P& ]2 ?            switch(ep0_state), b8 {9 Y) O$ Q6 U+ }0 l
            {
1 \: b/ J/ L2 `1 r! m9 o, y5 k                case 0: ep0_state |= 0x80;( o; T# b- H2 X3 W" s
                        break;
' ]1 Q& J/ G# M% c4 K+ w                case 1: if(USB->EP0R & USB_EP_CTR_TX)
, l, `8 }0 n6 E  y, \) D                        {
" V. t/ G; l# I5 w1 [* y& P                            USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;+ K2 e2 i7 d! J- ]$ n
                            ep0_state=3;( Z- G9 K- u& I; d. p% {
                            return;
% j$ o( `" v" g2 Z                        }- _/ b+ c- x: \# ~& U3 I
                        else
6 E5 E* T% r+ N) \) X                            ep0_state=0;, ^: z  X  L1 s$ `+ n
                        break;$ T% \% p# p* N$ I- J! ]- x- q
                case 2: if((USB->EP0R & USB_EP_SETUP)==0)   // OUT packet
' U2 i1 S0 U7 C- I! G( E                            ep0_state |= 0x80;
. A" i: {' M7 f9 }                        else
3 S0 @" {2 C! v- x- j' M2 M: m                            ep0_state=0;
  P7 R6 n3 v/ p& j3 v; z                        break;# }7 [+ o# u6 y0 g
                case 3: ep0_state=0;
4 m* m1 C% G: W. l0 }                        break;1 j* [0 e( R5 U. n
                case 4: ep0_state=0;
& V; h& k5 I' h' f4 D- ~( p- M                        break;9 V) O1 J& g) d7 d2 m0 M3 X9 [
                default:ep0_state=0;8 v1 t5 l4 j- B3 f1 Q1 E* Z+ @
                        break;& |6 p/ |7 @! n0 B9 o; U
            }8 O0 d; ~! ^" n+ y0 x$ I7 s
            USB->EP0R = USB_EP_TYPE_CONTROL; // just clear flag
0 t# \/ ^5 F, x            return;
  H$ ]5 O$ ^( w" }. m0 _6 y; |        }) c; B/ q+ H3 E" y/ }
        else    // EP_ID can be 1
1 I  e7 B' Z" `; t- T3 m" }, p        {$ ]& E8 N1 u/ V9 S+ Z1 z# @
            USB->EP1R = USB_EP_TYPE_INTERRUPT|1; // just clear flag
( w: D! j( Z. r  \# j3 s            ep1_wait=0;: x+ F! J3 a8 j& r6 M
            return;
; N+ ]3 J4 \2 ^+ s; |        }: \) h" q5 w  B: h
    }
% X" T. O; \1 y1 V    if(USB->ISTR & USB_ISTR_PMAOVR): @) s7 D: v' K+ T! Z  d1 x/ w
    {
9 D3 F' T/ E% |3 J9 |7 }$ H        USB->ISTR = ~USB_ISTR_PMAOVR;
# w' s7 s1 m/ o    }
: g6 u) o  ]; I/ t    if(USB->ISTR & USB_ISTR_ERR)
5 B: a5 \1 Q% t9 Y9 j; p    {7 V# Z! I' s+ a( D
        USB->ISTR = ~USB_ISTR_ERR;  // write 0 to clear
+ j' @8 h% _6 ~  B* u4 S0 `$ i    }3 @$ h9 L$ t1 b
    if(USB->ISTR & USB_ISTR_WKUP)
: u5 |5 |- q# O6 @4 m    {
6 I8 ^8 ]$ k3 d+ B$ K& \        USB->CNTR &= ~USB_CNTR_FSUSP;   // no suspend1 ~# {7 a& ^. n# s. Z2 a& u5 Y
        USB->ISTR = ~USB_ISTR_WKUP; // write 0 to clear5 I3 D% G7 i5 n/ i- l9 B' W
    }
) w: M/ P" b2 g    if(USB->ISTR & USB_ISTR_SUSP)5 B2 h8 [: x, b8 {" g
    {2 w% x5 ?/ h( Y/ ~
        USB->CNTR |= USB_CNTR_FSUSP;    // force suspend' y  s- \; J' ]: q
        USB->CNTR |= USB_CNTR_LPMODE;   // low power: F8 q7 M- O3 r9 F
        USB->ISTR = ~USB_ISTR_SUSP; // write 0 to clear
, l6 v3 l# s! n& ?    }
, t+ d9 ?& ]$ K: F/ ]    if(USB->ISTR & USB_ISTR_RESET)
/ c) ^% W$ T4 [    {
8 S& m7 a6 {! G3 S, S- V6 V        USB->BTABLE = 0;    // buffer table at bottom of PMA$ w8 d( Z! D" g# I  g0 I
        USB_PMA[0]=128; //ADDR0_TX' L0 n2 ]2 M+ H) X
        USB_PMA[1]=0;   //COUNT0_TX
: d5 {! U( }- j4 v# t        USB_PMA[2]=256; //ADDR0_RX
/ l- f7 S2 J7 n9 J9 }        USB_PMA[3]=0x8000|(3<<10);  //RX buffer 128 bytes
4 q- E+ Z- B: o& k$ U        USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX1;
4 o" \% {* a: Q% D* @) E- y        ep0_state=0;: K5 k# e% C" W7 B7 {& R
        USB_PMA[4]=384; //ADDR1_TX
  n4 _3 {$ ]& k: f5 b        USB_PMA[5]=0;   //COUNT1_TX
" V8 \4 Y) x( k$ R        USB->EP1R = USB_EP_TYPE_INTERRUPT|1;    // EP1 type( m/ [/ i+ h: w6 A
        ep1_wait=0;
/ ^0 L( J) I' t7 t( r        USB->DADDR = USB_DADDR_EF;      // enable function
" u/ {2 X, z4 D1 a        USB->ISTR = ~USB_ISTR_RESET;    // write 0 to clear
1 ~8 D' _0 M, [% G    }1 b3 B6 x8 C, d! d2 \: a
    if(USB->ISTR & USB_ISTR_SOF): y. h0 z1 ^; F0 ~
    {9 G/ z+ I% z  J! o, ~
        USB->ISTR = ~USB_ISTR_SOF;  // write 0 to clear1 E" {2 u7 g: e- e/ N5 ]  e
    }/ v- v( c$ w- L* I6 k. D
}
' x- q0 H4 L& @( A
* G; g7 q8 T6 n
因为只有两个EP需要管,数据量也很小,STM32F0的PMA(Packet Memory Area)固定分配好就不动了,读写数据都直接在PMA上读写。在USB Reset中断的时候,把PMA和EP都重新初始化。& C3 o9 J8 M% L, e4 Y! s9 B
! c- n- u- f% s# A4 R
主程序中用一个无限循环,每次中断过后处理一下USB请求,以及来自键盘扫描的检测。
5 a7 z2 V$ ]2 I3 p& x
5 g! K3 X# _6 Y8 a6 E3 x! l6 V
    while(1)3 Q9 E) [4 k4 H  ~! _- e+ q! k+ Y
    {
/ @- _7 t! \3 O7 d- u$ ~        static char row=0;6 ?8 t! ^+ d8 F5 N9 Q
        __WFI();( {8 K/ @4 w8 `  a$ h9 z" i( {$ E
        if(ep0_state & 0x80)    // request data processing' _7 b( r9 \! P) y1 I
        {
8 q# W8 J- p# v; P            if(ep0_state==0x80) // SETUP phase0 ]5 K5 |: H" g: {* S. n
            {; F/ u1 C1 T# G+ k9 g
                if(!setup_packet_service())' d6 Q+ L. F2 w* s
                {
# B! z+ e1 U% J/ e                    ep0_state=0;1 \  E, A+ x* p8 {' C
                    // not supported0 L+ i2 A7 G/ g! g$ ?
                }
$ {& _. T* `5 ~* Q                // ep0_state should be set to 1 or 2, if processed) F8 b5 S' W! P) j! J( `- o8 R
            }4 w& U* J5 L* O  n! d  \/ u
            else    // OUT phase, |( \8 r. z  A
            {
/ |# l4 Z  e+ }, }: c                // process data
+ x/ N0 ~( S4 R# M                show_LED(*(uint8_t *)(USB_PMA+128));
" W; d; Q/ a4 b( a9 W* S& L5 a% E0 l                ep0_state=4;! _; C& _& C/ F* V, {' \+ e5 D7 g1 \( G
                USB_PMA[1]=0;       // Zero length DATA0
- R4 i, U) ?/ c2 }0 D7 e* {7 j                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;+ h% U& ^8 X5 n3 x( r7 ]
            }
; n' U3 U0 F& d3 B) c9 w8 q        }
! ?* v5 t9 e  q' B0 e) |        else8 |8 v. K3 D' R
        {
; J" f8 P1 j0 f! @* o: d            if(usb_address && ep0_state==0)
% V  F0 ^4 g5 l7 H9 T7 k  A* U            {5 |2 O- ?2 W! B
                USB->DADDR = USB_DADDR_EF|usb_address;
. ]# b+ H% N- U  S" J8 a                usb_address=0;3 d' Y# r( }, a1 b8 }) W8 p6 W
            }
+ q- P3 C2 X( v5 M        }1 V) Y+ j& E5 g7 T; n
        if(row!=scan_row)   // new scan line: }6 \* G0 k# F# u( Q) p/ t) M3 m9 Z
        {4 o' h$ E9 n$ y( u* _0 U' r
            if(key_state[row]!=prev_key_state[row])
, D4 r, N; x! b' m7 D8 l1 d3 |            {
& j. Z" y$ `+ ?. a9 x! r                uint8_t test=0x80;
, |9 h. h$ n" w                uint8_t diff=key_state[row]^prev_key_state[row];
$ D  ~' d  j9 j: {3 t                for(i=0;i<8;i++)* A3 z4 l  }6 z  @" ~  [- r
                {
. ~* M7 J. Y) ~$ }4 N' F) O; L                    if(diff & test)0 |+ i2 ^. O' T) l2 ~
                        update_key_matrix(row,i,key_state[row]&test);3 n, A% f: E3 E( d; m
                    test>>=1;  N& O. I) f2 |+ ~6 {: S
                }' Y6 ]$ L- n& M7 e
            }( c" v0 R* M* y3 B: o6 U& H" v
            row=scan_row;
- u( Y7 R: R; S% o6 S        }
- l- w! C0 E/ U% o. B    }
: t$ [2 {# d8 @
1 T5 O: q+ W& D- Y2 g% y
+ X3 R; t3 u! N3 eEP0的控制传输,把用到的请求处理一下. y, E, D$ }1 z/ F
char setup_packet_service(void)# N$ P! L5 n6 T; W% K. Y2 h' Y
{, F8 G) s4 i9 V$ p
    if(ep0_std_req->bmRequestType & 0x20)   // class-specific2 r7 I7 S. m9 ~7 ?
    {
9 e: |, f5 `# f! ?( q        switch(ep0_std_req->bRequest)
. a* g9 U: M6 i5 @. G: t        {
* [* H& S, n2 D: n7 j5 J$ L: _            case REQ_GET_REPORT: break;* W  L% z/ v1 w) O4 a) b2 d) M
            case REQ_GET_IDLE:( y/ Z1 w8 Q* ^% ?& H! i
                USB_PMA[64]=0xfa;   // return 1 byte) e3 V. ?% l5 f6 B' l
                USB_PMA[1]=1;
1 w1 [" ]( \+ t* [                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
( F8 M6 e6 g2 r1 `3 T% a2 M                ep0_state=1;; z' @% ~% Z; O$ M1 I% i, \
                return 1;7 j) L& E7 m# z* T% X5 g
                break;
0 }5 z( A; J# J$ ^% r. i. t& f            case REQ_SET_REPORT:& s4 [' f) k  A0 n3 K; k; a
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_RX0;" c6 j$ o5 Y7 f% `# Y% q$ P; T9 s9 \
                ep0_state=2;
# Q. b9 I5 r0 n: }                return 1;- e. V! n/ r3 j) K& j
                break;1 s7 v* }- Y5 A6 I) }3 Y
            case REQ_SET_IDLE:
$ m) M7 w  o4 n2 y                USB_PMA[1]=0;   // Zero DATA7 y& H8 }6 t& d! Y
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
$ f6 Y5 x( @2 r8 i                ep0_state=4;1 \# s- G3 G$ S  `7 v& ~( m
                return 1;
! ?0 ~! u7 X2 \$ h+ G" n9 E                break;
0 [6 Q; w! b6 m        }
' L& w8 r$ O! ^/ D/ {9 ]        return 0;
, {, Y% i' I$ F( i; {+ Y/ i: |    }
4 t8 g) w1 Z7 I* V( b( C    else    // standard
- T& c: C. G7 Z  r& e) f0 C    {6 L) X7 X9 b: @5 N
        switch(ep0_std_req->bRequest)6 H: n5 o" D, S' e, p$ Y0 l
        {
- _" B  ]+ V; n. e: g            case REQ_GET_DESCRIPTOR:
. o1 P+ M$ t  `1 C/ o- l                return descriptor_service();
& A/ t0 W& p; T0 a: L# s9 @                break;7 c1 R% {( b5 ^) u/ Q% G# q
            case REQ_SET_ADDRESS:
5 o! {9 G* N5 h2 s                if(ep0_std_req->bmRequestType!=0x00). `# N5 `  M+ l
                    return 0;4 g2 U$ n6 `/ o; P
                usb_address=ep0_std_req->wValue;
; S+ E/ I  m2 G7 D' h# [% t7 M( }                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;- K! i8 W& B% H! w" o" l8 J2 J5 R
                USB_PMA[1]=0;       // Zero length DATA0- S) L% ~" a9 m* V
                ep0_state=4;    // No Data phase8 h: h5 E: l6 K
                return 1;/ i0 h) ?* \9 f# _, B
            case REQ_SET_CONFIGURATION:
0 m  N7 F2 U6 Z) t) i                if(ep0_std_req->bmRequestType!=0x00)9 D+ F' W; K8 F# p" o1 v
                    return 0;
! Z) T  Y9 @2 z; h+ f. |                USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX1|1;! S% N* w: [/ l; `1 q1 U
                USB->EP0R = USB_EP_TYPE_CONTROL|USB_EP_STAT_TX0;
# Q5 C1 v7 b; M3 a, U                USB_PMA[1]=0;   // Zero DATA& c8 [* ?9 x- c
                ep0_state=4;    // No DATA phase
) `/ }( s, s  g  L                return 1;0 b3 S& x8 T' F! A- K6 r
            default: return 0;
3 ~$ m* L' |& y% E        }" k' G3 I: W* h
    }% B7 l. J! l. ]' s
}
( h1 N, V  v) `" ?8 |# Z  e8 x
$ o+ `8 E2 e/ X# c4 K3 \
/ r# R9 q$ i) B$ |, P8 Z' J) B* X- t% }/ J  \8 p0 O, g2 j2 ]
回复 支持 反对

使用道具 举报

24

主题

63

帖子

286

积分

vip

Rank: 9Rank: 9Rank: 9

积分
286
 楼主| 发表于 2016-9-21 20:59:07 | 显示全部楼层
各种描述符,是USB开发首先要处理的
: `1 a8 Y0 k& C, ~4 Echar descriptor_service(void)
2 ]1 h/ u8 u0 A4 q* g+ H3 f{
/ R4 q" x' Q+ x/ l    switch((ep0_std_req->wValue)>>8)$ x* v' [* H8 |6 ^/ Y6 x  X1 g6 R
    {
1 Z  A  K! P& r* M1 |) z0 F. [7 O( h        case DESC_TYPE_DEVICE:" p2 Z3 s$ X2 ]4 D& Z
            return ep0_preparedata(&DevDesc, sizeof(DevDesc));
" c0 N: s/ E4 r) [. m6 ^: S            break;" T  r8 X' F4 S6 ^# c
        case DESC_TYPE_CONFIG:1 Z8 N" s2 W4 r( C
            if(sizeof(ConfigDescData)>ep0_std_req->wLength), w+ E* y- Q) @4 _) ~3 a* I
                return ep0_preparedata(&ConfigDescData, ep0_std_req->wLength);
) l; D7 J" i5 Z$ @# `: _            else# f7 M) T' x  Q9 m
                return ep0_preparedata(&ConfigDescData, sizeof(ConfigDescData));& r: r0 E+ q" m& ~" `4 [& I
            break;
' }. ^5 V& P6 q* X+ G1 R        case DESC_TYPE_STRING:% w2 p5 @# a( K* v
            switch(ep0_std_req->wValue &0xff)/ f# ^, g, ^! U# H, Y3 q
            {
8 x9 Q$ ?. a6 Y: o. [2 q                case 0 : return ep0_preparedata(&LangIDDesc, sizeof(LangIDDesc));
. v  w' h. G- B. L2 h$ k7 ]                case 1 : return ep0_preparedata(&UnicodeVendorDesc, sizeof(UnicodeVendorDesc));+ U0 F$ Y  d2 s& d' Y4 k
                case 2 : return ep0_preparedata(&UnicodeProdDesc, sizeof(UnicodeProdDesc));) j; B; H( {& q, L. ^
                default: return 0;
: f0 h1 T+ u, T7 t. {            }
; u) V. O- H$ N7 v' X' {            break;
; M: L) O3 |- R7 t* {2 Q        case REPORT_DESC_TYPE:
) ^- N' |$ n% W4 [- F+ ?3 W$ y            return ep0_preparedata(&HidReportDesc, sizeof(HidReportDesc));
! ~3 G2 d8 M+ A# l        default:. J0 `# D4 |4 a! E$ [! @
            return 0;
% {; V" G0 V7 [! v    }
2 H, f9 `* d* |1 X: D' Z* A0 F3 Y}
% }  D5 @9 v3 {; w- V3 c下面说下我对键盘的处理。因为有14+8条线,其中8条一组我称为列,用PA0~7读取;另外14条我称为行,在一个时刻只有1条为低电平(输出),其它13条为高阻。在列扫描线上加上上拉,因此没有键按下的时候,PA0~7都是高电平的。若某键被按下,当在所在的行被扫描时,对应的列就会变成低。我用了Timer6中断,每0.5ms切换一次扫描线,这样扫描一遍矩阵键盘用7ms.
: x& a* \. F. G+ @6 [3 h+ `# B3 n, @void TIM6_DAC_IRQHandler(void)  e" d' s9 T* e. M; @' p+ \
{
1 D* u. o5 ~' C% Y" N5 Q    __IO uint8_t *PA_IDR = (uint8_t *)&(GPIOA->IDR);. ^$ Y# ~8 }4 M& B9 F

: G% N. s  E/ R

) A' n4 ~8 r$ B4 ~" c4 t4 Y    TIM6->SR &= ~TIM_SR_UIF;
5 |& V$ _0 ^5 j$ R    prev_key_state[scan_row]=key_state[scan_row];9 k) z) d" r9 M/ a" x4 T
    key_state[scan_row]= *PA_IDR;   // update key states2 z: M; L# q3 F5 ~
    switch(scan_row)
6 O2 y* o. y+ K7 u. s$ X    {
% @! d5 e* P" O7 E5 A# l, I        case 13: // next row PB14
3 ^) i$ @% C! t* m4 B) }6 j                GPIOC->MODER = GPIOC_DEFAULT;# L! q# U/ E6 T- y- I+ @
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER14_0;" v; Q* S) [3 ^
                break;
6 e. L9 [; g3 S1 ~+ ~9 A* `        case  0: // next row PB15: a: [& ~% g' P- y' x: n0 d  h
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER15_0;% u3 D5 A2 z# |" r; R! F, r
                break;
0 j3 q/ X' [8 _' Y        case  1: // next row PB3" @+ f* ~  W9 g  N9 Z
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER3_0;
) z  b  U3 \9 B                break;
8 E8 V1 W+ h0 R7 ]1 G3 m        case  2: // next row PB4) V; p# O7 j- ~/ J( l5 R
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER4_0;) r2 h0 @/ o+ `$ _9 }/ n
                break;
2 g% {  o+ d1 A8 F        case  3: // next row PB5+ F. K; h$ Q4 o7 y$ o* m
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER5_0;
* p+ v* @  [% p9 g" r                break;  L; D! ?3 s5 Y
        case  4: // next row PB60 d' `! o' w. Y; U9 X
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER6_0;
* H% c1 I3 f+ r8 W. {9 A                break;5 y+ p2 `8 B# i2 d$ E
        case  5: // next row PB7& R2 W8 ^) H; G  s) L
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER7_0;3 c; K! `( F/ r
                break;) `- p% B; c1 W5 J
        case  6: // next row PB89 m  M& J, f; S; J3 r3 E& p
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER8_0;+ {' ?8 t$ L8 L# P& s
                break;% f) B! \' N& M5 B; O% d
        case  7: // next row PB92 N, M$ R- e- Q/ F; j
                GPIOB->MODER = GPIOB_DEFAULT|GPIO_MODER_MODER9_0;6 s7 A- h4 x) P# f7 L. h
                break;6 U+ B7 k1 K3 q2 J( B: I8 |- M. p9 S
        case  8: // next row PA8+ K6 ~8 \, \6 o$ ^$ f8 f
                GPIOB->MODER = GPIOB_DEFAULT;
$ V# ?+ _, A, x* Y( G# I                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER8_0;
/ I- @- c  B4 \( I8 o9 _" S                break;+ ~) L0 A' q% w% F3 q
        case  9: // next row PA9
! C" S1 B4 H* L' G                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER9_0;) v/ C* K1 @- T9 o. `, f
                break;  `- N' n  H1 u% e) Q4 Z% D
        case 10: // next row PA10
# S6 B5 B* J5 I: e9 @& W* g                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER10_0;
7 Y6 F" I# k4 w. L" [                break;
; k( N& b% l4 F5 r- b0 c        case 11: // next row PA15' {" ]: d+ g; ]  V9 e9 \
                GPIOA->MODER = GPIOA_DEFAULT|GPIO_MODER_MODER15_0;  D1 A7 \# H  v, k
                break;
  O8 \7 K+ |4 f: b        case 12: // next row PC13! v8 G  T% u6 M, X/ K- @$ G2 f
                GPIOA->MODER = GPIOA_DEFAULT;" m6 x, Y/ U; n% H5 E* v; |5 s
                GPIOC->MODER = GPIOC_DEFAULT|GPIO_MODER_MODER13_0;
1 k5 ?/ b8 }+ `, P4 ^                break;. M- t; m: E  r1 K; w
    }( k: R7 t9 S4 {
    if(scan_row<13)
) X7 Z  a" g  u  c$ l9 D8 S        scan_row++;0 v/ l5 }6 j5 R8 _3 k. F7 D
    else2 O- O2 `& m, [8 R9 n
        scan_row=0;( c# p- \: C  ~( G; c4 _! |2 n) u
}
1 |# \' ~" B" U7 F' Y
  B1 R5 ^; r7 Q. t9 N
/ m0 D+ _1 W" ~* Q3 {, W
回复 支持 反对

使用道具 举报

24

主题

63

帖子

286

积分

vip

Rank: 9Rank: 9Rank: 9

积分
286
 楼主| 发表于 2016-9-21 21:01:28 | 显示全部楼层
本帖最后由 miaozhuang 于 2016-9-21 22:02 编辑
, S5 n- Y, q' |& s7 ^+ W$ M
) O9 r  j  n2 K: F扫描的状态被Timer 6 ISR记录在 key_state[] 数组当中,上一次的状态保存为 prev_key_state[]. 这样在主程序中只要比较这两个数组,就知道是否键盘的状态有变化了。有变化(按下或者抬起)的时候,再调用 update_key_matrix() 函数去生成USB HID报告。6 m! }4 |( o0 X- v# n3 E0 S
9 x4 [$ v' G  \" ?1 p7 D
要从键盘的扫描行、列坐标得到按键的编码(此处不是ASCII,也不是PS/2的键盘扫描码,而是USB HID定义的键盘Usage ID),需要用到查找表。我需要切换Dvorak和QWERTY两个布局,因此需要准备两个查找表:

- i) Y$ q5 [# s; W6 m7 @
/ k1 W( w3 H4 M, }2 _! w$ ~# ^7 h: i# `" p& |8 l
const char hid_keymap_qwerty[14][8]={
, z2 T5 ~) B; T. g/ z5 f2 `9 v$ L7 a) S* l    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_R, HK_7, HK_F9, HK_Esc},( J4 r3 g4 v! H3 j4 z0 i
    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},
8 b5 U% W6 R7 J' G, H    {HK_Period, HK_V, HK_BSlash, HK_J, HK_P, HK_Q, HK_4, HK_F6},
1 H  j1 n# V6 N: G8 C" W    {HK_Comma, HK_C, HK_Quote, HK_H, HK_O, HK_Equal, HK_3, HK_F5},
$ D1 A2 W0 T% W- l* g+ e    {HK_M, HK_X, HK_Colon, HK_G, HK_I, HK_Minus, HK_2, HK_F4},$ o9 A" m+ D4 \- \4 R- m
    {HK_LShift, HK_NONE, HK_Caps, HK_F, HK_U, HK_0, HK_1, HK_F3},
6 @! G# l" o0 P0 q    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_D, HK_Y, HK_9, HK_BQuote, HK_F2},
% u  I- Y5 w: h' Z    {HK_NONE, HK_RAlt, HK_LCtrl, HK_S, HK_T, HK_8, HK_Enter, HK_F1},# ]/ j! G" S; A0 D5 m
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},$ `4 M! s5 u3 K  U7 o* F+ e
    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},
% P2 t( C5 c6 c8 c6 R& c6 O    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},5 o# h' T+ j6 o, T
    {HK_Space, HK_N, HK_Z, HK_L, HK_RBr, HK_E, HK_6, HK_F8},: d- E% A& M  Z; }6 L+ l' z
    {HK_Slash, HK_B, HK_Delete, HK_K, HK_LBr, HK_W, HK_5, HK_F7},) \- G: `0 U. @7 I
    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}0 k3 i7 N0 Q. c8 E
};
  |9 g6 c; \% f  i5 x. d8 `' N5 c
0 ~2 M& B; D* E4 {
const char hid_keymap_dvorak[14][8]={
8 I; _- j2 Z) Z3 V9 k) B5 D' o1 c    {HK_RShift, HK_NONE, HK_NONE, HK_A, HK_P, HK_7, HK_F9, HK_Esc},
/ |* X5 Z$ [" i1 ?8 s% {    {HK_F12, HK_BS, HK_Up, HKR_Slash, HKR_Aster, HK_NONE, HKR_Enter, HK_NONE},4 g7 a, K! b. |6 r& c
    {HK_V, HK_K, HK_BSlash, HK_H, HK_L, HK_Quote, HK_4, HK_F6},. a  b4 g9 C# p
    {HK_W, HK_J, HK_Minus, HK_D, HK_R, HK_RBr, HK_3, HK_F5},
  b6 U4 b  y7 t  \% x* L5 J% J    {HK_M, HK_Q, HK_S, HK_I, HK_C, HK_LBr, HK_2, HK_F4},
: x0 H2 o0 w5 n    {HK_LShift, HK_NONE, HK_Caps, HK_U, HK_G, HK_0, HK_1, HK_F3},
5 g) J% K4 h* H; t- S  G7 R    {HK_Scroll, HK_LAlt, HK_RCtrl, HK_E, HK_F, HK_9, HK_BQuote, HK_F2},
# i! g/ n, A4 V+ r* h5 Q" @' P( ^    {HK_NONE, HK_RAlt, HK_LCtrl, HK_O, HK_Y, HK_8, HK_Enter, HK_F1},) q2 `2 r. H. W) Y
    {HK_PgUp, HK_Home, HK_Insert, HKR_5, HKR_6, HKR_4, HK_F10, HKR_Dot},
& G5 k6 r! L1 M! z    {HK_Right, HK_Down, HK_Left, HKR_Num, HKR_Plus, HK_NONE, HK_NONE, HK_NONE},9 y6 A+ \' Z& J9 P; U$ f; E; E
    {HK_PgDn, HK_End, HK_F11, HKR_9, HKR_Minus, HKR_8, HKR_7, HK_NONE},
4 M" b) U6 s6 A& j    {HK_Space, HK_B, HK_Colon, HK_N, HK_Equal, HK_Period, HK_6, HK_F8},
: N- ]# q) L9 H1 J: L* w: w5 P    {HK_Z, HK_X, HK_Delete, HK_T, HK_Slash, HK_Comma, HK_5, HK_F7},
# n6 d9 r  }. w* U    {HK_Pause, HK_Tab, HK_PrtScr, HKR_2, HKR_3, HKR_1, HKR_0, HK_MODE}
  w4 {+ ^9 R  j; ]  j" y};
4 m9 ]* P9 ?" I  B2 W! s! ?0 n1 w2 B: V3 ^8 s8 l0 W
& L" i$ Z! a# A$ F
上面的宏定义另写在 translate.h 头文件中。HK_NONE是没有实际按键的位置,HK_MODE是我另外加的一个键,用来切换两个键盘布局。这个“一键切换”的键加装,可以用原来键盘上的AT/XT开关,也可以用我预留的User I/O,做到后来发现用键盘矩阵的空闲位置更方便。6 a( j5 x$ I8 P  I9 Q! x
% e1 R( K+ Q# m% e8 Q, p
HID报告的8个字节,第一个是8个特殊键的状态(Shift, Alt, Ctrl, GUI),第二个保留,后6个每个非0值是一个按下的键的Usage ID. 产生HID报告的子程序:

) @6 z# h8 t9 R8 P: z7 ~1 l
6 R5 `7 U6 t+ _6 d# A; ^& c4 \2 W$ k4 lvoid update_key_matrix(char row, char col, char onoff)% h9 g% k) p. B% N" S
{0 E: D" ?* m& P& a  E
    static uint16_t hid_report[4]={0,0,0,0};
9 P, P0 g9 Q" e; ]! Q1 P' u    static char (*hid_keymap)[8]=hid_keymap_dvorak;
) U6 b, w% h/ R6 h/ N# b2 g3 i: C' W

2 P/ P$ V4 e' K5 d% t3 M, p    unsigned char key=hid_keymap[row][col];
, V# ]5 ?1 E3 B& v4 f    unsigned char *report =(unsigned char *)hid_report;
8 v4 P7 X% B$ E' o: ?    char i;+ E' M# k% r8 l0 s

+ h5 d+ C" V' B7 A6 y
1 U- l+ y3 l* n4 g7 l
    if(key==HK_MODE)
: W9 g  c' s0 G9 r( V- ]    {
0 I3 a0 o: Z4 r+ V3 p        if(!onoff)/ y6 i, ?& ~# n) }/ i" Q
        {
  {- m: a+ T: D# \) \6 F8 O            if(hid_keymap==hid_keymap_dvorak); \& ^/ n$ ]7 y9 {0 \0 w1 z9 @6 N
            {
3 o% t5 D) v  P9 ^0 z8 j( a2 g                hid_keymap=hid_keymap_qwerty;! V2 h; b$ I, r
                GPIOB->BSRR = (1<<2);. ]8 E* k% K. n" K7 l/ d" C
            }
1 K% P" A& T4 E3 m. c; v9 f: [            else9 \. z2 \& ^  Y5 ?% q/ n" |
            {
& w6 e6 d9 n) E' k9 }                hid_keymap=hid_keymap_dvorak;
9 N9 |% ~) ^+ l8 {7 o  x: E9 G                GPIOB->BRR = (1<<2);
7 d3 |$ @4 w# U2 V- p            }
" i6 q3 ^! H/ w4 o/ H        }
  j; T8 e: N! \, P        return;
4 ?- @; T8 e  {; j    }
' Q: G/ \5 [1 S/ e. L; L( W, V! K  r+ T

, {/ F- j/ N3 |7 I    if(key>=0x80)   // Alt, Ctrl, Shift" f! ~) q9 ~! d+ J
    {
0 S6 A1 {7 ?8 R8 M( }+ x        uint8_t bitset = 1<<(key&7);1 ]; r3 O3 D  ^9 _3 z+ ?
        if(onoff)   // non-zero is key up* v3 M8 {" v; h% Z1 Q8 D
            report[0] &= (~bitset);
. a- d+ T& k! m9 z! d2 @2 v        else
+ u% a1 W* j" ?$ p* Z            report[0] |= bitset;/ [9 n4 d6 z- k( Q0 O1 e" {# |4 P# ^+ C
    }
, `3 Z; S) x( ]' H4 S    else" z# k. U  t/ O( Z  z
    {
' z( D, t' o8 I' J        if(onoff)   // non-zero is key up8 J  m- B/ U/ q3 X1 ^' c% V+ P5 i
        {8 T6 s# k6 z8 a, J% m
            for(i=2;i<8;i++)
+ D$ o) B0 s$ Q0 o: f/ }9 _7 L            {
) V6 W3 o5 E" R8 i# ^  T2 _                if(report==key), i% {/ j& |' w
                {( j" s8 l6 U5 O  y
                    report=0;# @# V  W) Z& z3 _0 ]* h
                    break;
* {9 O5 P7 @; M- p0 O                }
4 a5 G5 F! r8 L2 W: [: }6 N3 S            }
9 @" s5 u/ T5 \0 V        }  S+ |% S( p; Q8 ^1 F9 D5 g
        else
) M/ t* P) b. g7 c% y) u        {- W, H' b# L: D
            for(i=2;i<8;i++)
( z0 m( }9 [# y# t& Y  j            {
4 Z; ]/ q# A6 h) K7 I) N: z& O; l                if(report==key)
9 I6 [4 Z" N" |6 R2 L) g( y                    break;( i! Z7 l6 N! P- e9 J
                if(report==0)
) R1 u6 ?9 y* [                {
# ~* _7 f  G! Q& V2 K, m                    report=key;
' Q% U9 C# g# d9 W- ~7 l                    break;
: B/ m  o3 g, q) }, P0 z8 p                }
1 y3 F& T! Q# M7 G. L& I& V            }
9 U! g2 o$ D/ C+ {1 ?0 S        }3 w$ v/ V  U' O$ G) E* l
    }
' i( S- k3 }1 w3 P4 u3 K2 d4 b    for(i=0;i<4;i++)
: h0 e: n( c& j4 k& w" p        USB_PMA[192+i]=hid_report;
8 j4 H# c6 L7 f4 v5 e    USB_PMA[5]=8;   //COUNT1_TX
0 \' s/ B, s2 U1 u  l    if(ep1_wait==0)
$ i6 W. x* E# s" h" G    {
# _2 C: W; j1 k& B/ J$ b        USB->EP1R = USB_EP_TYPE_INTERRUPT|USB_EP_STAT_TX0|1;/ j+ F# S, E& u. w# h1 F3 o
        ep1_wait=1;' }/ D$ E: Y; f" [8 l" {* {
    }
/ b9 I( n, H& b}
2 a( \$ R4 [1 {! O( g. [+ ], K4 o% n3 f2 _5 Y, e: A

$ w  J- V; J' M完整的程序在附件中。我这个程序没有使用任何USB的库函数,完全是从底层操作,这样对USB的工作细节可以了解得比较清楚,当然,也费了很多工夫啦。5 V' g4 J4 G7 t! v: K* g
keyboard.zip (8.7 KB, 下载次数: 3478)
回复 支持 反对

使用道具 举报

24

主题

63

帖子

286

积分

vip

Rank: 9Rank: 9Rank: 9

积分
286
 楼主| 发表于 2016-9-21 21:08:09 | 显示全部楼层
在电子论坛上看到的,这对于改造键盘的小伙伴来说,提供了很大的帮助,步骤很详细,拆解换主控,设计pcb板,编写固件,看的是很过瘾。
. C, k$ t/ M5 }& o4 \
回复 支持 反对

使用道具 举报

8

主题

150

帖子

212

积分

中级会员

Rank: 3Rank: 3

积分
212

社区居民

发表于 2016-9-22 13:48:09 | 显示全部楼层
刚开始我以为要把打字机改造成电脑键盘
+ B0 d2 q6 `3 x- X/ q不过楼主也很厉害!
回复 支持 反对

使用道具 举报

24

主题

63

帖子

286

积分

vip

Rank: 9Rank: 9Rank: 9

积分
286
 楼主| 发表于 2016-9-22 19:17:17 | 显示全部楼层
网络孤客 发表于 2016-9-22 13:48
2 j# T9 h! x" D. }7 [0 r刚开始我以为要把打字机改造成电脑键盘  C: k8 a0 M5 s+ P# s( R
不过楼主也很厉害!
3 B  x6 t5 z* e1 t7 L9 w3 [
哈哈  是有点像奥
回复 支持 反对

使用道具 举报

0

主题

3

帖子

4

积分

新手上路

Rank: 1

积分
4
发表于 2017-7-10 21:06:48 | 显示全部楼层
很厉害
3 V. ]4 A0 X3 Z! r
回复

使用道具 举报

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

本版积分规则

Archiver|手机版|小黑屋|MiniDSO产品技术交流 迷你示波器-袖珍示波器-示波器探头- ( 粤ICP备07030012号 )

GMT+8, 2019-1-17 01:11 , Processed in 0.111402 second(s), 27 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

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