STC32G单片机开发教程(4):定时器中断和按键扫描实现多种判断功能

前言

在我的博客STC32G单片机的开发(2)里说明了定时器延时函数的编写,这边我们对其进行对中断函数的编写,相信接触过单片机的朋友也对中断的了解,我们这里对中断的概念就不一一介绍了,主要谈谈寄存器的一些使用,以及对定时器来一个补充说明(毕竟我们在延时函数只用定时器0),我们来说说其它定时器,毕竟STC32G单片机有5个定时器;

最近的话,思考了一下按键的扫描有感,自己找到并编写了一个不错的扫描方式实现单片机的代码,结合本文定时器一起进行讲解

寄存器

定时器寄存器

(1)定时器2相关寄存器

AUXR:这是老朋友了,在前几章我们一直相遇,这里我们配置它的启动位和12T分频位

T2L/T2H:重载值设置,注意这里TIM2是16位自动重载,所以我们计完一个周期的数就不用再手动重装了哈,但是想设置重载值记得要定时器关闭的时候再设置,这和TIM0和TIM1的设置规则是一样的哈

(2)定时器3/4相关寄存器

这里定时器3和4定时器基本锁死,我们就说其中一个。

T4T3M:这个寄存器和AUXR类似,这里定时器我们使用一个12T模式控制分频和定时器启动位就可

T3L/T3H以及T4L/T4H和TIM2,TIM1和TIM0一个道理,换汤不换药。

中断寄存器

我们这边为了不让定时器在初始化设置马上进行中断,我们先清空中断请求标志位。

这边在官方手册的位置在中断部分的内容,5个定时器都是一样的,只是3,4,5没用到TCON寄存器

这边的中断配置路线我们就看官方的手册来配置了,这里我就不一一介绍这些寄存器了。

学过51单片机的朋友都制动 每个中断回调函数名后都有个interrupt Num(Num是中断号)查询中断号我们也可以通过手册的中断列表查询

定时器中断初始化代码

那话就不多说我们配置一下定时器初始化代码

//初始化函数,设置为1ms中断
void TIM2_Init(void)
{
	AUXR|=0x04;//不分频 1T模式
	T2H=0xA2;
	T2L=0x40;
	T2IF=0;//清空标志位
	ET2=1;//使能定时器2中断
	EA=1;//使能全局中断
	AUXR|=0x10;//启动定时器2
}

void TIM3_Init(void)
{
	T4T3M |= 0x02;			//定时器时钟1T模式
	T3L = 0x40;				//设置定时初始值
	T3H = 0xA2;				//设置定时初始值
	T3IF=0; //清空标志位
	ET3=1;//使能定时器3中断
	EA=1;//使能全局中断
	T4T3M |= 0x08;			//启动定时器3
}


//中断回调函数
void TIM2_CB(void)interrupt 12
{

}
void TIM3_CB(void)interrupt 19
{

}

按键短按,长按和双击扫描

原理

这里难点在于双击的判断,我们也要做到一定的消抖,消抖顺序切记要对上,否者判断会出现一些问题的

1,(短按+长按)运用到3态状态机

流程大致为(1)按键按下->(2)消抖->(3)判断是否长按和短按

2,(短按+长按+双击)运用到7态状态机

流程大致为(1)按键按下->(2)消抖->(3)判断是否长按->(4)判断松开->(5)判断短按->

(6)消抖->(7)判断双击

代码

这边消抖的时间我们为了保守使用设置为20ms

这边的话,我之前使用到了STM32单片机来写过于是就直接复制上了,我也懒得再移植就是了,这里我们用到个结构题来存储各个按键的一些值

typedef struct Keys{
	uint8_t State;//状态
	uint8_t Short_Flag;//短按标志位
	uint8_t Long_Flag;//长按标志位
	uint8_t Double_Flag;//双击标志位
	uint8_t Count;//计数器,计数延时
	uint8_t Value;//存储按键按下为1还是0
	uint8_t Key_Up;//弹起标志位,防止反复触发
}Keys;

然后我们设置一个结构体数组变量

Keys Key[4];//设置可以扫描四个按键且互不干扰

然后 Coding Time:

短按+长按(STM32实现)少量注释和下列是相同道理的哈

void TIM8_TRG_COM_TIM14_IRQHandler(void)
{

	if(TIM_GetITStatus(TIM14,TIM_IT_Update)==SET)
	{
		Key[0].Value=!(uint8_t)KEY1;
		Key[1].Value=(uint8_t)KEY2;
		Key[2].Value=(uint8_t)KEY3;
		Key[3].Value=(uint8_t)KEY4;
		
		for(uint8_t i=0;i<4;i++)
		{
			switch(Key[i].State)
			{
				case 0:
				{
					if(Key[i].Value==0&&Key[i].Key_Up)
					{
						Key[i].State=1;
						Key[i].Key_Up=0;
					}
					else Key[i].State=0;	
					break;					
				}
				
				case 1:
				{
					if(Key[i].Value==0)Key[i].State=2;//消抖处理
					else Key[i].State=0;
					break;
				}
				
				case 2:
				{
					if(Key[i].Count<0xFF)	
						Key[i].Count++;
					if(Key[i].Value==1&&Key[i].Count<=40)//超过200毫秒
					{
						Key[i].Count=0;
						Key[i].Short_Flag=1;
						Key[i].State=0;
						
					}
					else if(Key[i].Value==0&&Key[i].Count>40)
					{
						Key[i].Count=0;
						Key[i].Long_Flag=1;
						Key[i].State=0;
					}
						
					break;
				}
				
			}
			if(Key[i].Value==1)Key[i].Key_Up=1;
		}		
		Time14++;
		TIM_ClearITPendingBit(TIM14,TIM_IT_Update);
	}
}

短按+长按+双击(STC32实现)

void TIM2_CB(void)interrupt 12  //1ms延时
{
	static uint8_t cnt=0;
	uint8_t i;
	if(cnt++==20)//20ms循环扫描一次按键
	{
		cnt=0;
		Key[0].Value=(uint8_t)KEY1;
		for(i=0;i<1;i++)
		{
			switch(Key[i].State)
			{
				case 0:
				{
					if(Key[i].Value==0&&Key[i].Key_Up)//Key_Up防止长按按下不放反复触发
					{
						Key[i].State=1;
						Key[i].Key_Up=0;
					}
					else Key[i].State=0;	
					break;					
				}
				
				case 1:
				{
					if(Key[i].Value==0)Key[i].State=2;//第一次消抖处理
					else Key[i].State=0;
					break;
				}
				
				case 2:
				{
					if(Key[i].Count<0xFF)	
						Key[i].Count++;//开始计数一个数二十毫秒
					if(Key[i].Value==1)//按键弹起进入按键扫描双击判断
					{
						Key[i].Count=0;
						Key[i].State=3;
						
					}
					else if(Key[i].Value==0&&Key[i].Count>35)//超过700毫秒
					{
						Key[i].Count=0;
						Key[i].Long_Flag=1;
						Key[i].State=0;
					}		
					break;
				}
				
				case 3://弹起消抖处理
				{
					if(Key[i].Value==1)Key[i].State=4;
					else Key[i].State=0;
					break;
				}
				
				case 4://判断长按短按
				{
					if(Key[i].Count<0xFF)	
						Key[i].Count++;//开始计数一个数二十毫秒
                    if(Key[i].Value==1&&Key[i].Count>10)//超过200毫秒
					{
						Key[i].Short_Flag=1;
						Key[i].Count=0;
						Key[i].State=0;
					}
                    else if(Key[i].Value==0)//二次按下消抖处理
					{
						Key[i].State=5;
						Key[i].Count=0;
					}
                    break;					
				}
				
				case 5://二次按下消抖处理
				{
					if(Key[i].Value==0)
						Key[i].State=6;	
					else Key[i].State=0;
					break;
				}
				case 6://弹起再做二次标志位,别问为什么,就是正常人快按两次都是弹起
				{
					if(Key[i].Value==1)
					{
						Key[i].Double_Flag=1;
						Key[i].State=0;	
					}
					break;
				}
			}
			if(Key[i].Value==1)Key[i].Key_Up=1;//判断是否弹起
		}		
	}
}

main函数:完成一次按键在main中判断做完事记得清除标志位哈

#include <STC32G.H>
#define LED1 P40
#define LED2 P41
#define LED3 P42
void main(void)
{
	P4M1=0x00;
	P4M0=0xFF;
	P40=0;
	P41=0;
	P42=0;
	Key_Init();
	while(1)
	{
		if(Key[0].Short_Flag==1)
		{
			LED1=!LED1;
			Key[0].Short_Flag=0;
		}
		if(Key[0].Long_Flag==1)
		{
			LED2=!LED2;
			Key[0].Long_Flag=0;
		}
		if(Key[0].Double_Flag==1)
		{
			LED3=!LED3;
			Key[0].Double_Flag=0;
		}
	}
}

实验现象

(STC32G)最后三个灯可仅靠一个按键来控制,按键200ms后没再按下判断为短按第一个灯亮,按键长按700ms后判断为第二个灯亮,按键200ms后再按下判断为双击第三个灯亮。

总结

要学会多思考,多应用,如果有错误不当的地方请大家指证哈

物联沃分享整理
物联沃-IOTWORD物联网 » STC32G单片机开发教程(4):定时器中断和按键扫描实现多种判断功能

发表评论