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后再按下判断为双击第三个灯亮。
总结
要学会多思考,多应用,如果有错误不当的地方请大家指证哈