本书的原著为:《Design Patterns for Embedded Systems in C ——An Embedded Software Engineering Toolkit 》,讲解的是嵌入式系统设计模式,是一本不可多得的好书。
本系列描述我对书中内容的理解。本文章描述访问硬件的设计模式之五:去抖动模式。
去抖动模式
(Debouncing Pattern) 是一种简单且有用的设计模式,用于消除因金属表面间歇性接触而产生的多个错误事件。
摘要
按键、机械开关和继电器是数字系统的输入设备,它们存在一个共同的问题——当金属弹片接触时,金属会发生变形或“反弹”,在开关打开或关闭期间产生间歇性连接。由于与嵌入式系统的响应速度(微秒级或更快)相比,这种情况发生得非常慢(毫秒级),因此这会导致向控制系统发送多个电子信号。该模式通过等待初始信号后的一段时间,然后检查状态,将多个信号减少为单个信号,从而解决了这个问题。
问题
许多嵌入式系统的输入设备使用金属与金属的接触来表示感兴趣的事件,例如按键按下、机械开关动作。当金属移动并接触时,会发生物理变形,导致间歇性的弹跳接触,直到振动减弱。
模式结构
模式结构图如下所示。
去抖动的核心思想是在检测到信号变化后,不是立即响应,而是等待一段时间(这段时间足够让弹跳现象消失),然后再采样信号状态。这样可以确保获取到的是稳定的、非弹跳引起的信号状态。
模式详情
应用客户端
应用客户端
(ApplicationClient) 是去抖事件的最终接收者。它的操作函数 deviceEventReceive()
仅在事件真实发生时激活。
弹跳设备
弹跳设备
(Bouncing) 表示设备硬件本身,比如按键、开关等。sendEvent()
操作只是激活嵌入式处理器中断向量表中的一个中断,而 getState
操作是通过读取内存位置或 IO 端口来实现的。DeviceState
通常是一个双值属性,即 ON
或 OFF
。
去抖动器
去抖动器
(Debouncer) 是一个软件元素 (这与 弹跳设备
是一个硬件设备不同) 。它处理输入事件、进行去抖动并保存当前设备的实际更改状态。
其 eventReceive()
函数由 弹跳设备
的 sendEvent()
服务激活。然后,它设置延迟计时器(如果需要,还会禁用来自设备的中断),然后检查设备状态。如果在去抖动时间之后,检测到当前状态改变,则表示事件 (比如按键按下事件) 真实发生,因此它将适当的消息发送到 应用客户端
。按钮的旧状态存储在其变量 oldState
中;每当检测到设备状态更改时,此变量都会更新。
去抖定时器
这个计时器通过其 delay()
服务提供了一个非阻塞等待。这通常是通过操作系统调用来完成的,但也可以使用特殊的计时器硬件来完成。
效果
在实际应用中,许多硬件设备并不具备内置的去抖动功能,或者其去抖动效果并不理想。因此,软件去抖动成为了一个常见的解决方案。通过软件去抖动,可以确保应用程序只处理那些由设备状态真实变化引起的事件,从而提高了系统的稳定性和可靠性。此外,软件去抖动还可以提供更多的灵活性和控制选项,例如调整去抖动时间、处理并发事件等。因此,在设计和实现嵌入式系统或类似的应用时,软件去抖动是一个非常重要的考虑因素。
实现策略
在嵌入式系统或低级系统编程中,中断是处理器响应外部事件(如硬件设备状态变化)的一种方式。当中断发生时,处理器会暂停当前执行的程序,转而执行与中断相关联的处理程序)。为了将中断与特定的处理程序关联起来,系统通常使用中断向量表(Interrupt Vector Table),该表存储了每个中断类型对应的处理程序地址。因此,当 弹跳设备
需要使用中断与 去抖动器
通信时,必须将去抖动器的处理程序地址注册到中断向量表中。此外,由于中断处理程序通常不能有参数,因此在设计时需要特别注意这一点。如果确实需要访问硬件对象的状态或数据,一种常见的方法是将处理程序设计为静态成员函数,并通过其他方式(如全局变量、单例模式等)来访问所需的数据。
如果不使用中断向量表,则可以根据需要将此模式与 观察者模式 混合使用,以将事件信号分发给多个观察者。
去抖动计时器可以使用特殊的计时器硬件,例如硬件定时器。如果使用RTOS(实时操作系统)计时器,则必须注意时间单位的分辨率。例如,Windows 的标准计时器分辨率在 10 到 25 毫秒之间。你只能获得作为基本计时器分辨率倍数的延迟时间,因此如果你想要45毫秒的延迟,你将不得不使用最接近且大于或等于你所需时间的计时器分辨率。对于去抖动应用,这通常是可以的,但你的需求可能会因你的特定硬件和应用程序而异。
如果你不介意在等待去抖动期间完全占用你的嵌入式处理器,那么这是一个简单的问题,只需使用阻塞延时即可。
实例
见原书。