stm32一个简单易懂的按键防抖代码(标准库)
本文介绍了一种基于STM32标准库的软件按键防抖实现方法。通过分析机械按键的抖动特性(5-10ms),采用时间差判断法进行消抖处理:在主循环中记录按键中断时间戳,当两次触发间隔超过20ms时执行有效动作,并通过双重条件判断确保抖动期间的正常响应。硬件方面配置PA4为上拉输入并映射到EXTI4中断线,采用双沿触发方式。与硬件RC滤波相比,该方案具有成本低、灵活性高的优势,特别适合资源受限的嵌入式系统
引言
- 简述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预测抖动模式等)
更多推荐


所有评论(0)