STM32中断+定时器实现单击、多击和长击(二)

​​​​​​STM32中断+定时器实现长按、短按(一)_空宇尤樱的博客-CSDN博客

上一节用实现了按键的长、短按键,这一节在原本的功能上加一个多击功能,可以实现双击、三击、四击等等,下面开始吧。

单片机:STM32F103C8T6

长、短按原理

按键定时器运行只有这三种操作。

按下按键  -->  使能定时器:按下按键后就对定时器使能,启动定时器开始计时。

松开按键  -->  失能定时器:松开按键后就对定时器失能,关闭定时器停止计时,计算按下时间。

定时器  -->  失能定时器:当按键按下时,定时器的计数器溢出超过200次,就是2s的时间,就失能定时器,实现长按按键。

短按:按下按键 . 使能定时器  -->  松开按键 . 失能定时器

长按:按下按键 . 使能定时器  -->  定时器 . 失能定时器

按下按键使能定时器开始计时,松开按键失能定时器结束计时,判断此次按键是否为有效按键,长短按等。

多击原理

按下按键  -->  失能定时器、使能定时器:按下按键后就对定时器失能清除溢出次数,再使能定时器,启动定时器开始计时。

松开按键  -->  失能定时器、使能定时器:松开按键后就对定时器失能,关闭定时器停止计时,计算按下时间,确认是误触还是短按;再定时器使能,启动定时器开始计时,等待下次按键的到来,或者不再有按键按下。

定时器  -->  失能定时器:当按键按下时,定时器的计数器溢出超过200次,就是2s的时间,就失能定时器,实现长按按键,当定时器的计数器溢出次数超过50次,也就是0.5s的时间,且此刻没有按键按下,失能定时器结束计时,此次按键操作结束。

单击、多击:按下按键 . 使能定时器  -->  松开按键 . 失能定时器  -->  松开按键 . 使能定时器  -->  按下按键 . 失能定时器

长击:按下按键 . 使能定时器  --> 定时器 . 失能定时器

按下按键使能定时器计时开始,松开按键失能定时器结束计时,判断此次按键是否有效,再次使能定时器,等待下次按键到来,在0.5s内无按键按下,失能定时器结束计时。

按键配置

按键初始化还是常规配置,中断服务函数新增全局变量g_Key_Number,用于记录按键按下次数,每按下一次有效按键自增一次。

//#include "key.c"

void KEY_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;					//定义GPIO结构体
	EXTI_InitTypeDef EXTI_InitStructure;					//定义EXTI(外部中断)结构体
	NVIC_InitTypeDef NVIC_InitStructure;					//定义NVIC(嵌套向量中断)结构体
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); 	//开启时钟
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;				//上拉输入
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;					//P13引脚
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			//输出频率
	GPIO_Init(GPIOA, &GPIO_InitStructure);						//结构体配置完成初始化
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); //选择EXTI的信号源
    EXTI_InitStructure.EXTI_Line = EXTI_Line0;					//选择EXIT事件线
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;			//EXTI为中断模式
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;//双边沿中断
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;					//使能中断
    EXTI_Init(&EXTI_InitStructure);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置优先级分组:抢占优先级和子优先级
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;			//中断源
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//使能中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//子优先级
	NVIC_Init(&NVIC_InitStructure);
	
}

uint8_t g_Key_Number = 0;			//按键按下次数
void EXTI0_IRQHandler(void)
{
	static uint8_t Keystate = 0;	//按键状态 0 -- 松开  1 -- 按下
	
	if(EXTI_GetITStatus(EXTI_Line0) != RESET) {
		if(!KEY_STATA && Keystate) {
			
			Keystate = 0;			//松开按键
			TIM_Cmd(TIM2, DISABLE);	//关闭定时器
			if(g_Time_Count < 5) {
				//按键抖动、
				printf("...press, Time_Count = %d\n", g_Time_Count);
			}
			else if((g_Time_Count > 5) && (g_Time_Count < 200)) {
				//短按、松开后响应
				printf("stort press!\n");
				g_Key_Number++;		//按键按下次数自增
			}
			TIM_Cmd(TIM2, ENABLE);	//启动定时器
		}
		else if(KEY_STATA && !Keystate){
			TIM_Cmd(TIM2, DISABLE);	//关闭定时器
			Keystate = 1;			//按下按键
			g_Time_Count = 0;		//清空定时器溢出次数
			TIM_Cmd(TIM2, ENABLE);	//启动定时器
		}
		EXTI_ClearITPendingBit(EXTI_Line0);	//清除中断标志
	}
}
//#include "key.h"

#define KEY_STATE	!!(GPIOA->IDR & 0x0001)
#define KEY_STATA	!!GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)

extern uint8_t g_Key_Number;			//外部声明 -- 给"time.c"中使用
void KEY_Init(void);

定时器配置

长按与短按的响应都放在定时器中断服务函数,按键松开0.5s内无按键按下,则表示短按,按键按下2s,则表示长按;虽然都是计时,但按键的状态不一样,在判断处加上同时满足按键状态,即可得到单击、多击和长按。

//#include "time.c"

void TIME_Init(uint16_t psc, uint16_t arr)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义TIM时基结构
	NVIC_InitTypeDef NVIC_InitStructure;							//定义NVIC(嵌套向量中断)结构体
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启低速总线时钟
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟分割
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//向上计数
	TIM_TimeBaseInitStructure.TIM_Period = arr - 1;					//自动重装载值
	TIM_TimeBaseInitStructure.TIM_Prescaler = psc - 1;				//预分频系数
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);							//清除TIM的挂起标志
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);						//使能指定的TIM中断,允许更新中断
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);					//配置优先级分组:抢占优先级和子优先级
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;					//中断源
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;					//使能中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;		//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;				//子优先级
	NVIC_Init(&NVIC_InitStructure);
	
//	TIM_Cmd(TIM2, ENABLE);	//启动定时器
}

uint16_t g_Time_Count;//TIM溢出次数 -- 10ms  	Tout(溢出时间) = (ARR+1)(PSC+1)/Tclk(时钟分割)
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) {
		g_Time_Count++;
		
		if ((g_Time_Count >= 50) && !KEY_STATA) {//按键松开0.5s后, 计算按下次数
			TIM_Cmd(TIM2, DISABLE);	//关闭定时器
			g_Time_Count = 0;		//清空定时器溢出次数
			printf("The number of key presses:%d\n", g_Key_Number);
			g_Key_Number = 0;		//清空按键按下次数
		}
		
		if ((g_Time_Count >= 200) && KEY_STATA) {//按键按下2s后, 确定为长按
			TIM_Cmd(TIM2, DISABLE);	//关闭定时器
			g_Time_Count = 0;		//清空定时器溢出次数
			g_Key_Number = 0;		//清空按键按下次数(防止多按后又长按按键)
			printf("long press!\n");
		}
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}
//#include "time.h"

extern uint16_t g_Time_Count;
void TIME_Init(uint16_t psc, uint16_t arr);

主函数

//#include "main.c"

int main(void)
{
	LED_Init();
	KEY_Init();
	TIME_Init(72, 10000);
	Usart_Init();
	LED_ON;
	printf("KEY + TIM\n");
	
	while (1) {
		
		delay_ms(1000);
	}
}

运行效果:

源码:

 链接:https://pan.baidu.com/s/1or2NC853GW2ojSg-iuA4jA?pwd=vsag 
提取码:vsag

  • 4
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值