STM32学习笔记(一)stm32c8t6实现按键的单击、连击、长按。

记录一下自己学习的过程

1.硬件连接

用的是c8t6的最小系统板,通过面包板连接按键,将PB11口用作按键输入。同时还连接了一块oled的屏方便查看现象。

STM32学习笔记(一)stm32c8t6实现按键的单击、连击、长按。_第1张图片

2.代码部分

核心思想和代码总体框架来自博客:(7条消息) stm32【按键处理:单击、连击、长按】_Elven-C的博客-CSDN博客_stm32 按键中断多次进入最佳解决办法作了一些简化。

首先是头文件部分

#ifndef __KEY_H
#define __KEY_H

#include "sys.h"
#include "delay.h"
#include "OLED.h"

#define KEY_NULL   0    //无事件
#define KEY_LONG   1    //长按事件
#define KEY_SHORT  2    //短按事件
#define KEY_DOUBLE 3    //连按事件

#define KEY_DOWN   1    //按键按下状态
#define KEY_UP     0    //按键松手状态
#define KEY_NONE   2    //按键初始状态
 
#define KEY_CONTINUE 50 //按下的最长时间
#define KEY_IDLE     40 //松手最长时间
#define KEY_PIN    PBin(11)//PB11用作按键输入

void Key_Init(void);
int Key_Scan(void);
void Key_Process(void);

#endif

 Key.c部分

struct KEY
{
    u8 key_prevent;    //前一次按键事件
	u8 key_event;      //当前按键事件
	u8 key_state;      //按键状态 按下或松开
	u8 key_cnt;        //按键按下的次数
	u8 key_continue;   //按键按下的时间
	u8 key_idle;       //按键松手的时间
	u8 key_flag;       //按键状态发生改变的标志
	u8 key_event_flag; //产生一次按键事件的标志
}key={KEY_NULL,KEY_NONE,0,0,0,0,0};//结构体初始化

GPIO和定时器的配置

void Exit_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	EXTI_InitTypeDef EXTI_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO,ENABLE);

	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;						//配置为上拉输入
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource11);
	EXTI_DeInit();
	EXTI_InitStructure.EXTI_Line = EXTI_Line11;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; 
    //上升下降沿中断,这样按下或松手就都能触发中断
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStructure); 

}

void TIM2_Config(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	TIM_TimeBaseStructure.TIM_Prescaler= 71;//预分频					
    TIM_TimeBaseStructure.TIM_Period=9999; //相当于每10ms进入一次中断
	TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;	// 计数器计数模式,向上计数
	// 初始化定时器
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

	TIM_ARRPreloadConfig(TIM2, ENABLE);			//使能TIM重载寄存器ARR
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);		// 清除计数器中断标志位
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);	// 开启计数器中断
	TIM_Cmd(TIM2, DISABLE);						// 关闭定时器的时钟,等待使用
}

//配置中断
void NVIC_Exit_GPIO_Config(void)
{
	NVIC_InitTypeDef	NVIC_InitStructure;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}

 void NVIC_TIM2_Config(void)
{
	NVIC_InitTypeDef	NVIC_InitStructure;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn  ;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}

中断函数 

void EXTI15_10_IRQHandler()
{
	Delay_ms(20);                 //按键消抖
	if(KEY_PIN==0)                //KEY_PIN==0代表按键按下
	{
		key.key_flag=1;           //代表按键状态发生改变的标志
		key.key_state=KEY_DOWN;   //按键状态
		key.key_continue=0;       //按键按下的时间清零
	}
	else                          //else的情况就是按键松手  
	{
		key.key_flag=1;            
		key.key_state=KEY_UP;
		key.key_idle=0;           按键松手的时间清零
	}
	EXTI_ClearITPendingBit(EXTI_Line11);    //清除中断标志
}


void TIM2_IRQHandler(void)        //定时器中断每隔10ms进入一次中断
{
	Key_Process();                //每隔10ms调用一次
	TIM_ClearITPendingBit(TIM2 , TIM_IT_Update);//清除中断标志位
}

Key_Process();

void Key_Process(void)
{
		switch(key.key_state)
		{
			case KEY_DOWN://按键按下进入
			{
				if(key.key_continue=KEY_CONTINUE)
				{
					if(key.key_cnt>1)//这个if判断是防止连按之后在长按会被判定为一次长按
					{
						OLED_ShowString(4,1,"DOUBLE");//这个if判断把连按之后的长按判断为连按
						OLED_ShowNum(2,1,key.key_cnt,2);//的一部分
                        key.key_event=KEY_DOUBLE;
                        key.key_prevent=key.key_event;
					}
					else//正常的长按
					{key.key_event=KEY_LONG;    //当按下的时间超出了设定的值后判断为长按
					key.key_event_flag=1;        //产生了一次按键事件
						key.key_prevent=key.key_event;
					OLED_ShowString(4,1,"LONG  ");    //打印到OLED屏上观察
					}

				}
				if(key.key_flag)//按键按下就会进入这个if判断
				{
					key.key_flag=0;//清除按键状态发生改变的标志位
					if(key.key_idle=KEY_IDLE)//大于松手时间,代表按键事件产生
				{
                    //这个if判断防止长按被识别为单击,应为这一部分是靠按下次数来判断
                    //单击或长按,而单击和长按按下次数都为1,但长按的按键事件一定会判定为
                    //长按,通过key.key_event判断当前是长按还是单击
					if(key.key_cnt==1 && key.key_event!=KEY_LONG)
					{
						key.key_event=KEY_SHORT;
                        key.key_prevent=key.key_event;
						OLED_ShowString(4,1,"SHORT ");
						OLED_ShowNum(2,1,key.key_cnt,2);
					}
					else if(key.key_cnt>1)//按下次数为>1次的情况
					{
						if(key.key_prevent==KEY_LONG)
                        //这个if判断防止前一次的长按被当作连按的一部分
							key.key_cnt--;//减去长按的一次计数
						if(key.key_cnt==1)//一次说明为单击
						{
							key.key_event=KEY_SHORT;
                            key.key_prevent=key.key_event;
						OLED_ShowString(4,1,"SHORT ");
						OLED_ShowNum(2,1,key.key_cnt,2);
						}
						else//大于1次为连击
						{
							key.key_event=KEY_DOUBLE;
						OLED_ShowString(4,1,"DOUBLE");
						OLED_ShowNum(2,1,key.key_cnt,2);//oled输出连击次数
						}
					}
				}
				break;
			}
		}
		
	}
void Key_Init(void)
{
	Exit_GPIO_Config();
	TIM2_Config();
	NVIC_Exit_GPIO_Config();
	NVIC_TIM2_Config();
	TIM_Cmd(TIM2, ENABLE);	//使能时钟
}	

main.c部分 

#include "Key.h"

int main()
{
	SystemInit();
	OLED_Init();
	OLED_ShowString(1,1,"Ready:");
	Key_Init();
	while(1)
	{

	}
}

测试

测试

你可能感兴趣的:(stm32,单片机,学习)