引言

  • 简述STM32标准库的应用背景及按键输入在嵌入式系统的重要性
  • 提出按键抖动问题及其对系统稳定性的影响
  • 说明文章目标:介绍基于标准库的按键防抖实现方法

按键抖动原理分析

  • 机械按键的物理特性导致抖动现象
  • 抖动的典型时间特征(如持续5-10ms)
  • 抖动对信号采集的干扰表现(如误触发、多次触发)

硬件防抖与软件防抖对比

  • 硬件防抖的常见方法(如RC滤波电路)
  • 软件防抖的优势(成本低、灵活性高)
  • 适用场景分析(资源受限 vs 高实时性需求)

接下来是主函数的代码

#include "stm32f10x.h"
#include "exit.h"
#include "delay.h"

long long  time[1000];
long long counter = 0;
extern int press ;
int i = 0;
int choose;
uint8_t now_pin ;
long long last_time;

int main(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//使能中断事件时钟
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设定优先级组为2
    delay_init(72);//初始化延迟函数,主频72Mhz
    GPIO_D_output(GPIO_Pin_0);//初始化pd0
    GPIO_A_input(GPIO_Pin_4);//初始化pa4
    NVIC_Configuration();//初始化,配置中断事件优先级
    EXTI_initialization();//配置中断线路为line4
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_Pin_4);//将pa4映射到exti_4中断线路上
    GPIO_SetBits(GPIOD , GPIO_Pin_0);//初始pd0为高电平
    while(1)
{
    delay_ms(1);//延迟1ms用作计时器
    counter ++;//计数+1
    if (i >= 1000) i = 0;//循环使用该数组
    if (press == 1)//当中断时
    {
    time [i] = counter;//将时间给数组
    i++;
    choose = time[i - 1] - time[i - 2];// 计算两者时间差
    if (choose >= 20 )//20ms以上进行相应
        {
        now_pin = GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_4);//读取按钮当前电平
        switch (now_pin)//
            {
            case (Bit_RESET):
                GPIO_SetBits(GPIOD, GPIO_Pin_0);//低电平开灯
                break;
            case (Bit_SET):
                GPIO_ResetBits(GPIOD, GPIO_Pin_0);//高电平关灯
                break;
            }
        }
    }
    if (press == 0)
    {
        last_time = counter;
        if ((last_time - time[i - 1]) >= 20 && (time[ i - 1] - time[i - 2]) > 20)
        {
            now_pin = GPIO_ReadInputDataBit(GPIOA , GPIO_Pin_4);//读取按钮当前电平
        switch (now_pin)//
            {
            case (Bit_RESET):
                GPIO_SetBits(GPIOD, GPIO_Pin_0);//低电平开灯
                break;
            case (Bit_SET):
                GPIO_ResetBits(GPIOD, GPIO_Pin_0);//高电平关灯
                break;
            }
        }
    }
    press = 0;
    }
}

核心思想

1.主要是利用两次按钮之间的间隔识别(放在主循环里)

2.这样一来就会有一个问题,有效动作如果和颤抖仅间隔20ms咋办,于是我使用了第二个if判断,使得在上述情况下,仍然能够让灯进行工作

3.本文适用于高电平关灯低电平开灯,如果要应用的话记得自行修改

exit.c的文件说明

1.对于初始化来说,我选用的是初始化为推挽输出,代码如下

/*********************************************
* @功能 : 重置对应PDx端口为推挽输出
* @参数1 : PDx所对应的GPIO_Pin_x端口
* @返回值 : 无
* @概述 : 初始化PDx端口为推挽输出,50Mhz
***********************************************/
void GPIO_D_output(uint16_t GPIO_Pin)
{
        GPIO_InitTypeDef GPIO_InitStructure;    //定义容器
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin;    //设定引脚
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);    //使能d组时钟
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  // 推挽输出模式
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    //设定时钟频率
        GPIO_Init(GPIOD, &GPIO_InitStructure);    //写入配置
}

2.对于nvic与exit线路配置,由于我用的是pa4作为输入,所以用如下代码

/*********************************************
* @功能 : 重置对应PAx端口为输入上拉
* @参数1 : PAx所对应的GPIO_Pin_x端口
* @返回值 : 无
* @概述 : 初始化PAx端口为上拉输入,50Mhz
***********************************************/
void GPIO_A_input(uint16_t GPIO_Pin)
{
        GPIO_InitTypeDef GPIO_InitStructure;    //定义容器
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin;    //设定引脚
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);    //使能a组时钟
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;  // 下拉输入模式
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    //设定时钟频率
        GPIO_Init(GPIOA, &GPIO_InitStructure);    //写入配置
}

/*********************************************
* @功能 : 初始化外设nvic
* @返回值 : 无
* @概述 : 初始化nvic
***********************************************/
void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;    //定义容器
    NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;    // 配置EXTI4中断优先级
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;    //设定抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;    //  设定子优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;    // 使能exti4中断线路
    NVIC_Init(&NVIC_InitStructure);    //传入数据
}

/*********************************************
* @功能 : 初始化外设exit4
* @返回值 : 无
* @概述 : 初始化exit4
***********************************************/
void EXTI_initialization(void)
{
    EXTI_InitTypeDef EXTI_InitStructure;    //定义容器
    EXTI_InitStructure.EXTI_Line = EXTI_Line4;    //指定要配置line4
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;    //定义为中断
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;    //设置为双沿
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;    //使能exit线路
    EXTI_Init(&EXTI_InitStructure);    //传入数据
}

3.中断函数展示(自己定义一个press全局变量然后extern到主函数)

/*********************************************
* @功能 : 中断函数,改变中断标志位
* @返回值 : 无
* @概述 : 改变中断标志位
***********************************************/
void EXTI4_IRQHandler(void)
{
EXTI_ClearITPendingBit(EXTI_Line4);//清除中断表示
press = 1;//按键被按下
}

防抖算法优化方向

  • 动态调整防抖阈值(适应不同按键特性)

测试与验证方法

  • 逻辑分析仪抓取波形验证防抖效果
  • 压力测试(快速连续触发、长按等场景)
  • 资源占用统计(CPU利用率、内存消耗)

总结与扩展思考

  • 对比不同方法的适用性总结
  • 延伸应用(如旋钮编码器防抖处理)
  • 未来改进方向(AI预测抖动模式等)

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐