嵌入式按键驱动,支持短按、长按、双击(中断方式)

 目录:

1:概述
2:stm32外部中断配置

3:code


概述:

1:本篇利用按键中断输入方式,当按键触发,中断处理函数置按键扫描标志位为1,开始按键键值扫描,完成后,主程序根据键值(短按、长按、双击),完成相应的动作;

2:当K1按键短按:LED1闪烁;当K1按键长按,LED2闪烁;当K1按键双击,LED3闪烁;

3:优点:相比于轮询扫描按键键值的方式,按键响应速度更快,节约CPU资源;

4:参考资料:stm32中文参考手册(EXIT的有关说明)、Cortex-M3权威指南(NVIC的有关说明)

5:开发板:STM32f103ZERT  奋斗开发板V5 开发环境:KEIL MDK5

6:工程下载:


stm32外部中断配置

1: 设置中断分组

设置中断优先级之前,一定要设置中断分组,本代码中有sysclk中断和按键外部中断,设置中断中断分组为2,2位主优先级和2位次优先级,需设置内部sysclk中断的优先级高于按键中断的优先级,内部中断使用NVIC_SetPriority()函数设置,具体函数参考core_cm3.h文件,外部中断优先级在NVIC_Init()函数中设置;

2:完成外部中断线路映射

使用GPIO_EXTILineConfig()函数完成中断线路的映射;

3:NVIC寄存器初始化

完成对应中断的使能和优先级设定,使用 NVIC_Init()库函数完成初始化。注意:一定要给NVIC_InitStructure结构体的成员赋值,因为NVIC_InitTypeDef结构体是在函数内部定义的,并且没有赋初值,如果不设置的话,结构体中的成员使用编译器默认的初值,有可能会出现错误,因为中断优先级很重要,如果按键中断的优先级高于sysclk中断,按键处理函数中的按键防抖会死循环,具体见下面程序;

4:EXIT寄存器初始化

使能响应的外部中断,并且设置中断的触发方式,使用EXTI_Init()函数完成初始化;

5:程序进行了2次防抖,一次是在按键中断处理函数中,一次是在主程序按键扫描中;


code

main.c

#include "stm32f10x.h"
#include "Key_Board.h"
#include "Rcc_Driver.h"
#include "Led_Driver.h"
#include "Sysclk_Driver.h"

volatile unsigned int led_flash_timing = 0;
volatile unsigned int get_keyvalue_timing = 0;
bool led1_flag = 0;
bool led2_flag = 0;
bool led3_flag = 0;

unsigned char i = 0;

int main()
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);     //设置中断优先级分组
	RCC_PeriphClock_Config();                           //外设时钟初始化
	SysTick_Init(INT_1MS,SysTick_CLKSource_HCLK_Div8);  //sysclk定时器初始化
	Key_Init();                                         //K1按键初始化
	Led_Init();                                         //LED初始化
	
	while(1)
	{
		if((get_keyvalue_flag == 1) && (get_keyvalue_timing > 20)) //间隔20ms扫面一次,进行按键防抖
		{
			get_keyvalue_timing = 0;
			Get_Key_Value();
			Key_Func();
		}
			
		if(led_flash_timing > 150)
		{
			led_flash_timing = 0;
			if(led3_flash_flag == 1)   //LED3闪烁
			{
				led3_flag = !led3_flag;
				if(led3_flag==1)
				{
					LED3_ON;
				}
				else
				{
					LED3_OFF;
				}
			}
			else
				LED3_OFF;
			
			if(led2_flash_flag == 1)    //LED2闪烁
			{
				led2_flag = !led2_flag;
				if(led2_flag==1)
				{
					LED2_ON;
				}
				else
				{
					LED2_OFF;
				}
			}
			else
				LED2_OFF;
			
			if(led1_flash_flag==1) //LED1闪烁
			{
				led1_flag = !led1_flag;
				if(led1_flag==1)
				{
					LED1_ON;
				}
				else
				{
					LED1_OFF;
				}
			}
			else
				LED1_OFF;
		}
	}
}

Key_Board.c

#include "Key_Board.h"

volatile unsigned int delay_timing = 0;

/*记录按键按下时间 时间小于1.5S,按键为短按;时间大于1.5S,按键为长按*/
volatile unsigned int key1_timing = 0;

/*记录两次短按之间的时间间隔*/
volatile unsigned int key1_doublepress_timing = 0;

/*定义按键键值结构体*/
Key_Value k1_value;

/*K1按键短按标志位*/
bool k1_shortpress_happen = 0;

///*K1按键长按标志位*/
//bool k1_longpress_happen = 0;

/*K1按键双击标志位*/
bool k1_doublepress_happen = 0;

/*led1闪烁时间标志位,主函数检测到此标志位为1时,led1开始闪烁,否则,结束闪烁*/
bool led1_flash_flag = 0;

/*3个led全部闪烁标志位,主函数检测到此标志为1时,三个led灯开始同时闪烁,否则结束闪烁*/
bool led2_flash_flag = 0;

/*流水灯事件标志位*/
bool led3_flash_flag = 0;

/*获取键值标志*/
volatile bool get_keyvalue_flag = 0;

/*按键初始化
*k1=GPIOC5 设置GPIOC5为上拉输入
*/
void Key_Init(void)
{
	EXTI_InitTypeDef   EXTI_InitStructure;
	NVIC_InitTypeDef   NVIC_InitStructure;
	
	/*按键对应GPIO口初始化*/
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Pin  = GPIO_Pin_5;
	GPIO_Init(GPIOC, &GPIO_InitStruct);
	
	/*按键中断初始化*/
	/*设置中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	/*完成外部线路映射*/
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource5);
	
	/*NVIC寄存器初始化*/
	NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;//外部中断5
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断
  NVIC_Init(&NVIC_InitStructure);//配置
	
	/*EXIT寄存器初始 配置EXTI_Line0*/
  EXTI_InitStructure.EXTI_Line = EXTI_Line5;//LINE5
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断事件
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能LINE5
  EXTI_Init(&EXTI_InitStructure);//初始化
}

/*按键中断处理函数*/
void EXTI9_5_IRQHandler()
{
	delay_timing = 0;
	while(delay_timing<10);   //延时防抖,如果sysclk的中断优先级低于按键的中断优先级,程序会死掉
	if(K1 == 0)
	{
		get_keyvalue_flag = 1;
	}
	EXTI_ClearITPendingBit(EXTI_Line5);
}

/*获得键值*/
void Get_Key_Value(void)
{
	if(K1 == 0)         //当K1按键按下
	{
		if(k1_shortpress_happen==0)
		{
			k1_shortpress_happen = 1;      //开始一次按键键值扫描
			key1_timing = 0;               //按键按下计时变量清0,开始计时,此值每1ms加1,sysclk中断函数中实现自加
		}
		else
		if(k1_shortpress_happen==1)
		{
			if(key1_timing > 1500)         //按键按下时间超过1.5S,长按事件发生,置k1_value.long_press为1
			{
				k1_value.long_press = 1;
				k1_shortpress_happen = 0;    //按键短按标志位置0
			}
		}
	}
	
	if(K1==1)         //当K1按键抬起
	{
		if(k1_shortpress_happen==1)      //按键抬起后,k1_shortpress_happen等于1,说明按键为短按,不是长按
		{
			k1_shortpress_happen = 0; 
			if(k1_doublepress_happen==0)
			{
				k1_doublepress_happen = 1;    //按键双击标志位置1,等待确认按键是否为双击
				key1_doublepress_timing = 0;  //开始计时,同样1ms自增加1
			}
			else
			{
				if(key1_doublepress_timing < 500)  //第一次短按发生后,在500ms时间内,发生第二次短按,完成一次双击,跟新按键键值
				{
					k1_doublepress_happen = 0;
					k1_value.double_press = 1;
				}
			}
		}
		else
		if(k1_doublepress_happen == 1)         //第一次短按后,等待500ms,如未再发生短按,跟新按键短按键值
		{
			if(key1_doublepress_timing > 500)
			{
				k1_doublepress_happen = 0;
				k1_value.short_press = 1;
			}
		}
	}
}

/*按键事件处理函数 根据键值 进行相应的事件处理*/
void Key_Func(void)
{
		if(k1_value.short_press == 1)
		{
			k1_value.short_press = 0;
			led1_flash_flag = !led1_flash_flag;
			get_keyvalue_flag = 0;          
		}
		
		if(k1_value.long_press == 1)
		{
			k1_value.long_press = 0;
			led2_flash_flag = !led2_flash_flag;
			get_keyvalue_flag = 0;
		}
		
		if(k1_value.double_press == 1)
		{
			k1_value.double_press = 0;
			led3_flash_flag = !led3_flash_flag;
			get_keyvalue_flag = 0;
		}
}








你可能感兴趣的:(短按,长按,双击,stm32按键中断,stm32)