zoukankan      html  css  js  c++  java
  • 关于游戏手柄按键的设计

    一、背景

    近期开发了一个空鼠遥控器的外设产品,採用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板的触点上,因为导电橡胶的导电性把触点的正负极连通,同一时候导电橡胶的柔软度使得其更easy与触点结合紧密,手指交叉状的触点也加大了正负极连通的easy度,这样就实现了一次按键动作。

    如图:


    2、硬件改进

    在电路上,非常多游戏手柄採用的是正交矩阵的电路设计方式,这样可以节省GPIO的用量,在GPIO不够用的MCU芯片上,是一种好的选择。但假设用这样的方式,软件上就不能採用中断式的检測了,而要採用扫描式的检測,须要定时去扫描横轴和众轴的GPIO,当检測到某个横轴和众轴的GPIO为低电平时,就能确定其交叉的那个点所相应的按键被按下了。在我们的MCU上GPIO数量是够的,所以为了软件实现的简单起见,还是维持原有的每一个按键相应一个GPIO,每一个GPIO接地的方式。

    矩阵式电路如图:

    3、软件改进

    软件开发完毕后,測试时发现一个问题,就是当按键按下时,正常情况MCU仅仅会上报一个按下的中断事件,但有时MCU会上报3个中断事件,各自是按下、弹起、按下。这样的情况可能是GPIO电路信号有毛刺或者MCU内部中断缓存处理有问题造成的。要解决问题,须要加一个防抖定时器,在获取到一个按键中断事件之后起一个定时器,在这个定时器超时前,假设再有中断事件上报,就直接丢弃,当定时器超时后,再收到中断事件,就能够继续正常处理,同一时候又一次启动这个定时器,从而形成防抖处理机制。

    四、总结

    项目过程中,从结构、硬件到软件都走了一些弯路,一方面也是之前没有类似产品经验造成的,在经过研究分析和參考了他人的设计之后,这些问题也陆续得到解决。在这里把这个过程记录下来,兴许有人碰到类似问题时,能够做一个參考。

    (完)

  • 相关阅读:
    为什么new的普通数组用delete 和 delete[]都能正确释放
    虚幻4属性系统(反射)
    CFileDialog类的默认路径
    把单一元素的数组放在一个struct的尾端
    在UE4中使用SVN作为source control工具
    单精度浮点数和有效位数为什么是7位
    Valid Number--LeetCode
    归并排序
    堆排序
    直接选择排序
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/4094892.html
Copyright © 2011-2022 走看看