一、背景
最近开发了一个空鼠遥控器的外设产品,采用Nordic51822 MCU芯片,基于BLE4.0标准,与OTT盒子连接,同时具有遥控器、空鼠、游戏手柄的功能。其中在按键的设计这块我们走了一些弯路,现总结一下经验教训,以供开发类似产品时参考。
二、初始设计
1、结构设计
最初按键在结构设计上采用类似路由器按键的方式,比较简单,在PCB按键位置放置一个波仔片,然后上面加一个橡胶的按键,按下橡胶时,其内部的触点压下波仔片,波仔片把电路连通,从而实现一次按键动作,属于机械式按键方式。
2、硬件设计
在按键的硬件设计上,我们采用MCU的GPIO来检测,通过把MCU的GPIO引脚连接到按键上,MCU内部把这个GPIO上拉,然后再把按键接地,形成回路。当按键没有按下时,回路断开为高电平,当按键按下后,回路接通为低电平(低有效)。软件通过检测这个GPIO从高到低的电平变化,即可检测出按键是否被按下。
3、软件设计
由于产品要支持游戏手柄,所以存在多个按键同时按下的情况,并且每个按键都需要各自独立检测按下弹起状态。软件实现上,把每个按键相连的GPIO设置成输入模式,并检测从高到低的电平变化,当按键按下时,电平被拉低,这个电平变化被检测到之后,触发一个中断。接下来的处理有两个特别的设计考量:
1)软件接收到按键中断后,把这个中断事件放入按键对应的中断事件队列,然后起一个50ms的定时器,定时器超时后就从各个按键对应的中断事件队列各取一个事件,然后通过BLE上报OTT盒子。这样设计的原因是因为有需要多个按键同时按下的情况,而实际上人即便同时按下多个按键,肯定也是有先后的,所以需要有一个延时上报来允许在一定时间内先后按下的键被认为是同时按下的。延时上报定时器每50ms超时一次就处理上报一次,只要还有一个按键的事件队列里还有事件未被处理,这个定时器就不停止。
2)同时每个按键的事件队列长度只设计为3个位置,按下或者弹起各作为一个事件入队,中断检测到事件后从队尾入队,定时器超时后从队头取事件,当某个按键的队列满了后,假如又来了第4个事件,就把队列第3个位置的事件删掉,同时抛弃掉第4个事件。因为每个按键的按下和弹起事件是交替出现的,所以这样做就相当于删除了一对按下和弹起事件。这样设计的原因是用户有可能十分快速的按键,但我们起的延时上报定时器是50ms上报一次按键事件,每个键一次取一个事件上报,如果当队列有积压时而又没有删除事件的设计,就可能出现用户密集按键停止后,按键还陆续向OTT盒子上报,这样的用户体验是不行的。
三、改进设计
1、结构改进
产品第一版出来后,因为采用的波仔片机械式按键,所以每次按下都有一个塔塔声,经过试用大家普遍反映按键很硬,手感不好,需要改进。最初我们以为问题的原因在于采用了机械式按键设计,如果要手感好可能就要采用电容式按键设计。电容式按键的原理是当手按下按键时,按键里的触点往下压,造成跟PCB板上的触点距离改变,从而带来两者间的容值发生改变,检测并计算这个容值的改变,就可以转换成检测到的按键事件,但经过了解这种方式,会引入软件算法的复杂度,我们在此之前没有经验。
我们经过研究其他厂商的手柄发现,大多数游戏手柄并没有采用电容式按键,而是在PCB板上采用手指交叉状的触点,然后上面覆盖一层软胶,软胶内部有一块导电橡胶,软胶上是硬塑料的按键。这样当硬塑料按键被按下时,压下软胶,软胶内的导电橡胶向下压在PCB板的触点上,由于导电橡胶的导电性把触点的正负极连通,同时导电橡胶的柔软度使得其更容易与触点结合紧密,手指交叉状的触点也加大了正负极连通的容易度,这样就实现了一次按键动作。
2、硬件改进
在电路上,很多游戏手柄采用的是正交矩阵的电路设计方式,这样能够节省GPIO的用量,在GPIO不够用的MCU芯片上,是一种好的选择。但如果用这种方式,软件上就不能采用中断式的检测了,而要采用扫描式的检测,需要定时去扫描横轴和众轴的GPIO,当检测到某个横轴和众轴的GPIO为低电平时,就能确定其交叉的那个点所对应的按键被按下了。在我们的MCU上GPIO数量是够的,所以为了软件实现的简单起见,还是维持原有的每个按键对应一个GPIO,每个GPIO接地的方式。
3、软件改进
软件开发完成后,测试时发现一个问题,就是当按键按下时,正常情况MCU只会上报一个按下的中断事件,但有时MCU会上报3个中断事件,分别是按下、弹起、按下。这种情况可能是GPIO电路信号有毛刺或者MCU内部中断缓存处理有问题造成的。要解决这个问题,需要加一个防抖定时器,在获取到一个按键中断事件之后起一个定时器,在这个定时器超时前,如果再有中断事件上报,就直接丢弃,当定时器超时后,再收到中断事件,就可以继续正常处理,同时重新启动这个定时器,从而形成防抖处理机制。
四、总结
项目过程中,从结构、硬件到软件都走了一些弯路,一方面也是之前没有类似产品经验造成的,在经过研究分析和参考了他人的设计之后,这些问题也陆续得到解决。在这里把这个过程记录下来,后续有人碰到类似问题时,可以做一个参考。
原文:http://blog.csdn.net/weiganyi/article/details/24959447