延时消抖与定时器消抖

次元: 365bet备用地址 时间戳: 2025-11-28 15:56:06 观察者: admin 访问量: 3854 能量值: 92
延时消抖与定时器消抖

一、按键抖动

按键抖动是电子设备中常见的问题,尤其是在机械开关如按键中。抖动发生是因为当按键被按下或释放时,接触点之间的快速接触和断开导致信号线上产生快速的电平变化,这可能导致微控制器错误地识别多次按键动作。

按键抖动的时间一般为10~50ms

二、硬件消抖

硬件消抖主要是通过两种方式实现:RS触发器和电容滤波器。

RS触发器:

RS触发器利用其特性来吸收按键的抖动。当按键被按下时,触发器会立即翻转状态,而按键的抖动不会影响输出,因为触发器的状态改变是瞬间的,不受后续抖动的影响

电容滤波:

电容滤波器通过并联一个电容器在按键两端,利用电容的充放电特性来消除抖动。当按键产生抖动时,电容器会吸收这些快速变化的电平,从而减少抖动对电路的影响。这两种硬件消抖方法可以有效减少按键抖动对电路的影响,确保信号的稳定性。在没有MCU的情况下,硬件消抖电路是常用的消抖手段。而在嵌入式开发中,通常通过软件消抖来实现,因为这种方法成本较低。

硬件消抖会额外增加硬件设施,会有更多的成本,并且不能保证完全可靠,因此更多时候需要添加软件消抖,常见的软件消抖有延时消抖和定时器消抖。

三、延时消抖

延时消抖是一种常用的软件消抖方法,其基本原理是在检测到按键状态变化后,通过引入一定的延时来等待抖动消失,然后再进行一次状态检测以确认按键的真实状态。这种方法简单易实现,但也存在一些缺点,比如会占用CPU资源,影响程序的实时性,以及不够精准。以下是具体的实现步骤和代码示例:

1.延时消抖原理:

当检测到按键按下时,先等待一个固定的延时时间(通常为10ms~50ms),这段时间足以让大部分由接触不良引起的抖动消失。延时过后,再次检测按键状态,如果按键仍然处于按下状态,则认为是有效的按键操作。

2.代码实现:

#include "stm32f4xx.h"

static GPIO_InitTypeDef GPIO_InitStructure;

static EXTI_InitTypeDef EXTI_InitStructure;

static NVIC_InitTypeDef NVIC_InitStructure;

void delay_ms(uint32_t ms)

{

while(ms --)

{

SysTick->CTRL = 0; // 关闭系统定时器后才能配置寄存器

SysTick->LOAD = 21000; // 设置计数值,用于设置定时的时间

SysTick->VAL = 0; // 清空当前值还有计数标志位

SysTick->CTRL = 1; // 使能系统定时器工作,且时钟源为系统时钟的8分频(168MHz/8=21MHz)

while ((SysTick->CTRL & (1<<16))==0); // 等待系统定时器计数完毕

SysTick->CTRL = 0; // 关闭系统定时器

}

}

#define PEout(n) (*(volatile uint32_t *)(0x42000000+(GPIOE_BASE+0x14-0x40000000)*32+n*4))

#define PFout(n) (*(volatile uint32_t *)(0x42000000+(GPIOF_BASE+0x14-0x40000000)*32+n*4))

#define PAin(n) (*(volatile uint32_t *)(0x42000000+(GPIOA_BASE+0x10-0x40000000)*32+n*4))

static uint32_t g_key_event=0;//按键标志位

int main(void)

{

/* 打开端口A的硬件时钟(就是对硬件供电),默认状态下,所有时钟都是关闭 */

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);

/* 打开端口F的硬件时钟(就是对硬件供电),默认状态下,所有时钟都是关闭 */

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);

/* 打开系统配置syscfg时钟(就是对硬件供电),默认状态下,所有时钟都是关闭 */

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9|GPIO_Pin_10;//指定9 10号引脚

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT;//引脚工作在输出模式

GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;//速度越高,响应时间越短,但是功耗就越高,电磁干扰也越高

GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;//如果外部没有上拉电阻,就配置推挽输出模式

GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;//不需要使能上下拉电阻

GPIO_Init(GPIOF,&GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;//指定0号引脚

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN;//引脚工作在输入模式

GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;//速度越高,响应时间越短,但是功耗就越高,电磁干扰也越高

GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;//不需要使能上下拉电阻

GPIO_Init(GPIOA,&GPIO_InitStructure);

/* 将外部中断连接到指定的引脚,特别说明:引脚编号决定了使用哪个外部中断 */

SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);

/* 配置外部中断0 */

EXTI_InitStructure.EXTI_Line = EXTI_Line0; //指定外部中断0

EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//工作在中断模式

EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发:就是发现有从高电平到低电平的跳变,就向CPU中断请求处理

EXTI_InitStructure.EXTI_LineCmd = ENABLE;//允许外部中断0工作

EXTI_Init(&EXTI_InitStructure);

/* 通过NVIC管理外部中断0的中断请求:中断号、优先级、中断打开/关闭 */

NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//中断号

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;//抢占优先级

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;//响应优先级

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//中断打开

NVIC_Init(&NVIC_InitStructure);

PFout(9) =1;

PFout(10)=1;

while(1)

{

if(g_key_event)

{

delay_ms(50);

if(PAin(0)==0)

PFout(9)^=1;

//等待按键的释放

while(PAin(0)==0);

//释放时也消抖

delay_ms(50);

//等待按键的释放

while(PAin(0)==0);

g_key_event=0;

/* 打开EXTI0外部中断请求 */

NVIC_EnableIRQ(EXTI0_IRQn);

}

}

}

/* 中断服务函数 */

void EXTI0_IRQHandler(void)

{

/* 检测中断是否有触发 */

if(EXTI_GetITStatus(EXTI_Line0) == SET)

{

g_key_event=1;

/* 关闭EXTI0外部中断请求 */

NVIC_DisableIRQ(EXTI0_IRQn);

//清空标志位

EXTI_ClearITPendingBit(EXTI_Line0);

}

}

按键消抖简单方便,但是在裸机编程中,延时会占用CPU。在RTOS(实时操作系统)中vTaskDelay()可以通过睡眠延时进行任务调度,几乎不会占用CPU。

四、定时器消抖

定时器消抖的原理是基于时间延迟来确保按键状态的稳定性。当按键被按下或释放时,由于机械接触的不稳定性或电气噪声,可能会在短时间内产生多次快速的电平变化,这种现象称为抖动。定时器消抖的目的是等待这段不稳定的时间过去,然后确认按键的真实状态。以下是定时器消抖的详细步骤和原理:

检测按键状态:

在程序中,首先检测按键的当前状态,例如,检查GPIO引脚的电平是否表示按键被按下。

启动定时器:

如果检测到按键状态发生变化(例如,从未按下变为按下),则启动一个定时器,并设置一个适当的延时时间,通常这个时间足够长,可以覆盖大多数抖动事件,但又要足够短,以免影响用户体验,常见的延时时间为几十毫秒。

等待定时器超时:

程序等待定时器超时。在这个期间,CPU可以继续执行其他任务,或者进入低功耗模式等待定时器超时。

确认按键状态:

定时器超时后,再次检查按键状态。如果按键状态与初始检测时相同(例如,仍然是按下状态),则认为这是一个有效的按键动作,可以执行相应的处理逻辑。

处理按键事件:

如果确认按键动作有效,执行相应的按键处理程序,如切换LED状态、发送命令等。

清除定时器:

完成按键处理后,清除定时器中断标志,准备下一次按键检测。通过这种方法,定时器消抖可以有效滤除由于按键抖动引起的错误信号,确保只有在按键状态稳定后,才将其视为有效的用户输入。这种方法简单、可靠,且不会占用过多的CPU资源,因为它允许CPU在等待定时器超时期间执行其他任务。

代码实现

void key_init(void)

{

//使能AHB1总线上指定外设的硬件时钟,其实就是对外设进行供电 (如果该外设不使用,可以关闭其硬件时钟,降低功耗)

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);//打开中断控制时钟

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN;//输出模式

// GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;//推挽类型,Push Pull

GPIO_InitStructure.GPIO_Speed=GPIO_Speed_2MHz;//引脚工作速度

GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;//不使能上下拉电阻

//对GPIOF初始化

GPIO_Init(GPIOA,&GPIO_InitStructure);

/* 将外部中断连接到指定的引脚,特别说明:引脚编号决定了使用哪个外部中断 */

SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);

/* 配置外部中断0 */

EXTI_InitStructure.EXTI_Line = EXTI_Line0; //指定外部中断0

EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//工作在中断模式

EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发:就是发现有从高电平到低电平的跳变,就向CPU中断请求处理

EXTI_InitStructure.EXTI_LineCmd = ENABLE;//允许外部中断0工作

EXTI_Init(&EXTI_InitStructure);

/* 通过NVIC管理外部中断0的中断请求:中断号、优先级、中断打开/关闭 */

NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//中断号

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;//抢占优先级

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;//响应优先级

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//中断打开

NVIC_Init(&NVIC_InitStructure);

}

void tim2_init(void)

{

//使能TIM2的硬件时钟,就是对它供电

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

//配置TIM2的定时时间

TIM_TimeBaseStructure.TIM_Period = 10000/40-1;//用于设置自动重载寄存器的值,决定了定时时间,当前定时为1秒

TIM_TimeBaseStructure.TIM_Prescaler = 8400-1;//预分频值,将该值传递给预分频的寄存器,会自动加1,实际就是除以16800

//TIM_TimeBaseStructure.TIM_ClockDivision = 2;//当前stm32f407是不支持的,它是属于时钟分频,也是二次分频

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数,0 ->9999 就是1秒时间的到达

TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//配置TIM8的中断触发条件:更新中断(UI)

//配置TIM2的中断:优先级、IRQ通道要使能

NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

//使能TIM8工作

TIM_Cmd(TIM2,ENABLE);

}

int main()

{

led_init();

key_init();

tim2_init();

while(1)

{

}

}

void TIM2_IRQHandler(void)

{

//检测是否有更新中断

if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)

{

if(PAin(0)==0)

{

PFout(9)^= 1;

}

/* 关闭定时器2 */

TIM_Cmd(TIM2,DISABLE);

//清空标志位,告诉CPU当前中断不需要再服务,已经完成处理

TIM_ClearITPendingBit(TIM2,TIM_IT_Update);

}

}

void EXTI0_IRQHandler(void)

{

/* 检测中断是否有触发 */

if(EXTI_GetITStatus(EXTI_Line0) == SET)

{

//清空标志位

TIM_ClearITPendingBit(TIM2,TIM_IT_Update);

//关闭定时器

TIM_Cmd(TIM2,DISABLE);

//清空当前计数值

TIM_SetCounter(TIM2,0);

//重新启动定时器

TIM_Cmd(TIM2,ENABLE);

//清空标志位

EXTI_ClearITPendingBit(EXTI_Line0);

}

}

定时器消抖不会占用CPU,但是在使用时会占用多一个定时器资源,因此,在实际应用中,根据实际需求进行取舍

相关维度

hive时间类型 有哪些种类

hive时间类型 有哪些种类

2025年蓝牙音响测评:Colorfire七彩虹与其他品牌的超重低音音箱对比分析

2025年蓝牙音响测评:Colorfire七彩虹与其他品牌的超重低音音箱对比分析

royal是什么意思

royal是什么意思

导语的意思

导语的意思