首页 > 其他分享 >#STM32#定时器扫描按键消抖#按键控制LED灯亮灭#标准库

#STM32#定时器扫描按键消抖#按键控制LED灯亮灭#标准库

时间:2024-10-13 13:18:15浏览次数:3  
标签:TIM2 灯亮 LED NVIC TIM 按键 GPIO

一.机械按键抖动

在按下按键后金属弹片会来回震动影响I/O口的电平变化,影响检测和判断操作。

通常抖动时间为:5ms~10ms

影响:在不加消除抖动的情况下按下按键LED灯可能会出现失灵的情况,因为这时的判断按键情况通常是判断电平的高低,由于电平不停的发转,所以呀很难判断此时是否是被按下。

二.消除抖动的方法

 1.软件延时消抖

按下按键后我们Delay一会儿跳过这个抖动时间过去,这样就轻松的解决了这个问题。不过这样会出现cpu等待的情况,这个时候CPU干不了其他事情,就这样干等着。明显有点屈才了。

2.定时器扫描按键消抖

让硬件自己隔段时间就去看看按键的状态,要是符合情况再让CPU去处理。这样就起到不断扫描的作用。

 

三.代码的实现(基于标准库)

1.变量的定义

// 按键状态变量
volatile uint8_t key_pressed = 0;  // 0: 未按下, 1: 短按, 2: 长按
volatile uint32_t key_timer = 0;   // 按键计时器

// LED状态变量
volatile uint8_t led_state = 0;    // 0: LED灭, 1: LED亮

 volatile关键字是一种类型修饰符,用于告诉编译器某个变量的值可能会在程序外部被意外地改变。使用volatile修饰的变量,编译器在每次使用时都会重新从内存中读取它的值,而不是使用缓存中的值。

2.定时器中断(Timer_Init)

定时器为1ms中断 ,频率为1000,可以通过改变ARR与PSC的值来调整中断频率。

这里的APB1是低速外设总线最大频率为36*10^6Hz.

#include "stm32f10x.h"


/**
  * 函    数:定时中断初始化
  * 参    数:无
  * 返 回 值:无
  */
void Timer_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟,TIM是外设时钟
	
	/*配置时钟源*/
	TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	/*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数器模式,选择向上计数,计数的方式
	TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;				//计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 3600 - 1;				//预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			//重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);				//将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元	
	
	/*中断输出配置*/
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);						//清除定时器更新标志位
																//TIM_TimeBaseInit函数末尾,手动产生了更新事件
																//若不清除此标志位,则开启中断后,会立刻进入一次中断
																//如果不介意此问题,则不清除此标志位也可
	
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);					//开启TIM2的更新中断,开启了更新中断到NVIC的通路
	
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
																//即抢占优先级范围:0~3,响应优先级范围:0~3
																//此分组配置在整个工程中仅需调用一次
																//若有多个中断,可以把此代码放在main函数内,while循环之前
																//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				//选择配置NVIC的TIM2线,TIM2_IRQn是定时器2在NUVIC里的通道//中断号
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//指定NVIC线路的抢占优先级为2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
	
	/*TIM使能*/
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行,使能计数器
}


/* 定时器中断函数,可以复制到使用它的地方
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)	    //获取中断标志位
	{
		
            //可写操作
            

		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);         //清楚标志位
	}
}
*/

3.I/O口的配置 (IO_Init)

IO_Init函数用于初始化按键和LED引脚。按键配置为上拉输入模式,LED配置为推挽输出模式。

   

#include "stm32f10x.h"

// 定义按键和LED引脚
#define KEY_PIN GPIO_Pin_1
#define KEY_PORT GPIOB
#define LED_PIN GPIO_Pin_1
#define LED_PORT GPIOA


// 初始化按键和LED引脚
void IO_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;

    // 使能GPIOA和GPIOB时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);

    // 配置按键引脚为输入模式,上拉
    GPIO_InitStructure.GPIO_Pin = KEY_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(KEY_PORT, &GPIO_InitStructure);

    // 配置LED引脚为输出模式
    GPIO_InitStructure.GPIO_Pin = LED_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(LED_PORT, &GPIO_InitStructure);

    // 初始化LED状态
    GPIO_ResetBits(LED_PORT, LED_PIN);  // LED灭,高电平
}

 4.中断函数进行操作(TIM2_IRQHandler)

  • 如果按键按下时间超过200ms,则认为是长按;否则,如果之前的状态是高电平(未按下),并且当前检测到低电平(按下),则认为是短按。
  • TIM2_IRQHandler函数是TIM定时器的中断服务函数,用于实现按键扫描、消抖处理和LED控制。
  • 使用key_timer变量来计时按键按下的时间。
  • 当按键释放时,如果之前检测到按键按下(无论是短按还是长按),则切换LED的状态。
/**
  * 函    数:TIM2中断函数
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
  *           函数名为预留的指定名称,可以从启动文件复制
  *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
  */
void TIM2_IRQHandler(void){
	
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){			//判断是否是TIM2的更新事件触发的中断
	
		static uint8_t last_key_state = 1;  					// 初始化为1,因为按键默认是高电平(未按下)
   
      // 读取按键状态
        uint8_t current_key_state = GPIO_ReadInputDataBit(KEY_PORT, KEY_PIN);
        
        // 消抖处理
        if (current_key_state == 0) {  			// 按键按下(低电平)
            key_timer++;
        
            if (key_timer >= 200) {  			// 假设长按时间为200ms
                key_pressed = 2;  				// 长按
            }
            else if (last_key_state == 1) {  	// 确保不是抖动
                key_pressed = 1;  				// 短按
            }
        }
        else {
            // 按键释放
            if (key_pressed == 1 || key_pressed == 2) {
                // 如果之前检测到按键按下(无论是短按还是长按),则处理按键事件
                key_pressed = 0;  				// 重置按键状态
        
                // 切换LED状态
                led_state = !led_state;
                if (led_state) {
                    GPIO_SetBits(LED_PORT, LED_PIN);  	// LED亮
                }
                else {
                    GPIO_ResetBits(LED_PORT, LED_PIN);  // LED灭
                }
            }
        
            key_timer = 0;  							// 重置计时器
        }
        
        last_key_state = current_key_state;  			// 更新上一次按键状态
	}
}

5.主函数

main函数的主循环中,不需要添加额外的按键处理逻辑,因为所有的按键处理和LED控制都已经在中断服务函数中完成了。 

int main(void)
{
	IO_Init();        
	Timer_Init();
	
	while(1){
		
        //可以有其他的操作

	}	
}

6.结束语

  • 示例代码中的消抖时间和长按时间可能需要根据实际硬件进行调整。
  • 在实际应用中,可能需要添加更多的按键处理逻辑或功能。
  • 确保STM32的标准外设库已经正确包含在项目中,并且已经配置了正确的系统时钟(SystemCoreClock)。

标签:TIM2,灯亮,LED,NVIC,TIM,按键,GPIO
From: https://blog.csdn.net/HFDTBS123/article/details/142895052

相关文章

  • Walter Russell's 'The Universal One' - Detailed Summary
    1.宇宙统一理论Russell试图提出一个全面的理论来解释宇宙的所有方面。他认为:所有自然现象,无论是物质的、能量的还是精神的,都遵循相同的基本原理。宇宙是一个统一的、有机的整体,其中所有部分都相互关联和相互依存。他的理论试图涵盖从原子结构到星系形成,从生命起源到意......
  • Qt全屏显示和按键ESC退出全屏
    1.需求描述将播放窗口全屏显示,将工具栏边框等隐藏在后面;然后退出全屏通过ESC返回按钮;2.实现方法定义一个全屏的容器类,继承于QWidget;全屏时,去掉窗口的父窗口,然后全屏显示,不去掉父窗口指针无法全屏;按下ESC按钮后,通过keyPressEvent(QKeyEvent*pEvent);函数,将父窗口重新设置回去;......
  • 聊聊git push到远程服务器出现RPC failed问题
    前言最近小组成员跟我说,他git提交不了代码。我问了下原因,他说他代码一提交就会报代码语言:txt复制error:RPCfailed;HTTP413curl22TherequestedURLreturnederror:413fatal:theremoteendhungupunexpectedlyfatal:theremoteendhungupunexpected......
  • Audio Knowledge
     LinksforreferenceNyquistfrequencyAudiobitdepthSample-rate&Sample-widthWAVfileformatSamplemethodIMA_ADPCM ......
  • PatentGPT: A Large Language Model for Patent Drafting Using Knowledgebased Fine-
    本文是LLM系列文章,针对《PatentGPT:ALargeLanguageModelforPatentDraftingUsingKnowledgebasedFine-tuningMethod》的翻译。PatentGPT:一种使用基于知识的微调方法进行专利起草的大型语言模型摘要1引言2相关工作3提出的方法4实验5基准测试6总结......
  • GeoKR系列--Geographical Knowledge-Driven Representation Learning for Remote Sens
    一、abstract1.绝大多数遥感图像仍未标注,想要充分利用这些未标注的图像,本文提出了一种基于地理知识驱动的表示学习方法,使得提升遥感图像的网络性能+减少对标注数据的需求。2.本文将全球地表覆盖产品和与每张遥感图像相关的地理位置视为地理知识,为了消除遥感图像与地理知识之......
  • JDK12~17的新特性:Switch增强,优化NPE,文本块,instanceof增强,record,sealed
    JDK14switch语句的增强:类似lambda的语法糖,不需要再写break了。提供yield实现返回值其中switch类型匹配属于预览,正常情况下是关闭的publicclassEnhanceSwitch{publicstaticvoidmain(String[]args){oldVersion();newVersion();}pri......
  • LED显示驱动/高亮数显屏驱动芯片VK16K33A 采用SOP28封装形式,可支持16SEGx8GRID的点阵L
    VK16K33A是一种带按键扫描接口的数码管或点阵LED驱动控制专用芯片,邱婷:188-2366-8825内部集成有数据锁存器、键盘扫描、LED驱动模块等电路。数据通过I2C通讯接口与MCU通信。SEG脚接LED阳极,GRID脚接LED阴极,可支持16SEGx8GRID的点阵LED显示面板。最大支持13×3的按键。内置上电......
  • SC02E —— 2按键带自校正功能的容性触摸感应器
    SC02E是2按键带自校正功能的容性触摸感应器,高性能,低功耗,最低工作电压1.65V,待机2uA,有超强稳定性和抗干扰能力。触摸按键相比传统按键,不仅结构设计简单美观,而且防水、抗干扰、一致性好,在节约成本的同时实现产品科技感和质感的提升,所以广泛应用在家居家电、健康医疗、消费电子等领......
  • Error: Failed to download metadata for repo ‘update‘
    安装Rsync时报错:通常原因为从openEuler的官方仓库更新元数据时遇到超时或连接失败的问题。更换镜像源#移除旧的镜像源[[email protected]]#mv/etc/yum.repos.d/openEuler.repo/etc/yum.repos.d/openEuler.repo.bak#替换国产openEuler镜像源[root@nginx1yum......