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