前言
很多人学单片机的时候马马虎虎,许多例程都是知其然不知其所以然。这样其实是害了自己,拿来就用自然很方便,但如果不懂其中的原理,只要功能要求稍微变一点,估计你就蒙圈了。毕竟技巧是基于扎实的基础之上的!
一、明确需求
用户基本操作定义:
1。短按操作:按键按下,按下时间<1s,属于一次短按操作
2。长按操作:按键按下,按下时间>1s,属于一次长按操作
在正常0.5s内无按键操作为启始按键扫描条件下,扫描按键将产生以下3种按键事件:
1。长按事件:任何1次出现的长按操作都属于长按事件
2。单击事件:1次短按操作后,间隔0.5内没有短按操作
3。双击事件:2次短按操作间隔时间<0.5s,则2次短按操作为1次双击事件,且2次短按都取消
特别操作情况定义:
1。短按操作和长按操作间隔<0.5s,以及,长按操作和短按操作间隔<0.5s,均不产生双击事件
2。连续n次(n为奇数)短按操作,且间隔均<0.5s,产生(n-1)/2次双击事件+1次单击事件
3。连续n次(n为偶数)短按操作,且间隔均<0.5s,产生n/2次双击事件
对按键操作者的建议:
由于按键的多功能性质,建议操作者每次在单击/长按/双击按键事件发生后,隔0.5s后再进行下一次的按键操作。因为在特别操作情况下,程序是保证按定义进行判断和处理的,主要是怕操作者自己记不清楚导致操作失误。
对软件设计者的要求:
1。应该全面进行分析,给出严格定义和判断条件,如上所示。如果自己都不清楚,你的设计出的系统就不稳定,不可靠。
2。在1的基础上,编写出符合要求的程序,并进行全面测试。
二,实现过程
1.首先在key.h定义声明结构体,及其所需的函数
typedef struct
{
uint8_t KeyIO;//按键IO
uint8_t KeyIOFilterCnt;//滤波次数
uint8_t KeyPressNum;//按键次数
uint8_t KeyPressTime;//按键按下时间
uint8_t KeyPressOverTime;//超时时间
uint8_t KeyPressFlag;//按键按下标志位
uint8_t keyEffectNum;//按键次数
uint8_t KeyLongPressTime;//长按时间
uint8_t KeyShortPressTime;//短按时间
} KEY_MELEMENT;
//--------------------------flag--------------------------------
#define G_F(VALX,VALY) ((VALX)&(VALY)) //GET_FLAG
#define C_F(VALX,VALY) ((VALX)&=(~(VALY))) //CLEAR_FLAG
#define S_F(VALX,VALY) ((VALX)|=(VALY)) //SET_FLAG
//---------------------KeyPressFlag-----------------------------
#define KeyPress 0x01
#define KeyShotPress 0x02
#define KeyLongPress 0x04
#define TwoKeyPress 0x08
//------------------------TwoKeyFlag---------------------------
#define TwoKeyAllPress 0x01
#define TwoKeyPressEffect 0x01
//------------------------KEYIO-------------------------
//根据自己的单片机型号去声明按键IO
#define KEY1IO ((gpio_porta_read()>>5)&0x01) //PA5
#define KEY2IO ((gpio_porta_read()>>6)&0x01) //PA6
//声明函数
void Function_KeyInit(void);//初始化
void Function_OneKeyRead(KEY_MELEMENT * Key);//扫描单个按键
void Function_TwoKeyRead(KEY_MELEMENT *KeyI , KEY_MELEMENT *KeyII);//扫描两个按键
void Function_KeyHandle(void);//按键处理
2.在Key.c中实现函数功能
2.1初始化按键IO
#include "Key.h"
//有多少个按键就声明多少个结构体
KEY_MELEMENT Key1IO;
KEY_MELEMENT Key2IO;
/*******************************************************
*
* 按键端口初始化
*
*******************************************************/
void Function_KeyInit(void)
{
//====================按键引脚初始化==================
//根据自己的单片机型号初始化按键IO
system_set_port_mux(GPIO_PORT_A, GPIO_BIT_5, PORTA5_FUNC_A5);
gpio_set_dir(GPIO_PORT_A, (GPIO_BIT_5), GPIO_DIR_IN);
system_set_port_pull(GPIO_PA5, false);
system_set_port_mux(GPIO_PORT_A, GPIO_BIT_6, PORTA6_FUNC_A6);
gpio_set_dir(GPIO_PORT_A, (GPIO_BIT_6), GPIO_DIR_IN);
system_set_port_pull(GPIO_PA6, false);
//====================按键结构体初始化==================
Key1IO.KeyIOFilterCnt = 0;
Key1IO.KeyPressNum = 0;
Key1IO.KeyPressTime = 0;
Key1IO.KeyPressOverTime = 0;
Key1IO.KeyPressFlag = 0;
Key1IO.keyEffectNum = 0;
//长按,短按时间可根据自己的需求进行调节
Key1IO.KeyLongPressTime = 60; //时间MS = KeyLongPressTime * 扫描函数周期
Key1IO.KeyShortPressTime = 20;//时间MS = KeyShortPressTime * 扫描函数周期
Key2IO.KeyIOFilterCnt = 0;
Key2IO.KeyPressNum = 0;
Key2IO.KeyPressTime = 0;
Key2IO.KeyPressOverTime = 0;
Key2IO.KeyPressFlag = 0;
Key2IO.keyEffectNum = 0;
Key2IO.KeyLongPressTime = 50;
Key2IO.KeyShortPressTime = 20;
}
2.2按键扫描函数
2.2.1单按键扫描函数
/*******************************************************
KeyIO IO口寄存器
KeyPressNum 为按键次数。 KeyPressNum = 0xFF为长按生效
KeyPressFlag bit0=1按键按下 bit1=0按键松开
*******************************************************/
void Function_OneKeyRead(KEY_MELEMENT * Key)//扫描单个按键
{
Key->keyEffectNum = 0;
if(Key->KeyIO==0)//低电平生效 //if(Key->KeyIO==1)//高电平生效
{
if(Key->KeyIOFilterCnt<2)
{
Key->KeyIOFilterCnt++;
}
else
{
Key->KeyPressOverTime = 0;
S_F(Key->KeyPressFlag,KeyPress);
if(G_F(Key->KeyPressFlag,TwoKeyPress)==0)
{
if(G_F(Key->KeyPressFlag,KeyLongPress)==0)
{
Key->KeyPressTime++;
if(Key->KeyPressTime > Key->KeyLongPressTime)
{
Key->KeyPressTime = 0;
S_F(Key->KeyPressFlag,KeyLongPress);
C_F(Key->KeyPressFlag,KeyShotPress);
Key->KeyPressNum = 0;
Key->keyEffectNum = 0xff;
}
else
{
S_F(Key->KeyPressFlag,KeyShotPress);
}
}
else
{
Key->KeyPressTime = 0;
C_F(Key->KeyPressFlag,KeyShotPress);
}
}
else
{
Key->KeyPressTime = 0;
C_F(Key->KeyPressFlag,KeyShotPress);
C_F(Key->KeyPressFlag,KeyLongPress);
Key->keyEffectNum = 0;
Key->KeyPressNum = 0;
}
}
}
else
{
if(Key->KeyIOFilterCnt>0)
{
Key->KeyIOFilterCnt--;
}
else
{
Key->KeyPressTime = 0;
C_F(Key->KeyPressFlag,KeyLongPress);
C_F(Key->KeyPressFlag,KeyPress);
if(G_F(Key->KeyPressFlag,KeyShotPress)!=0)
{
C_F(Key->KeyPressFlag,KeyShotPress);
Key->KeyPressNum++;
Key->KeyPressOverTime = 0;
}
else
{
if(Key->KeyPressNum!=0)
{
if(++Key->KeyPressOverTime > Key->KeyShortPressTime)
{
Key->KeyPressOverTime = 0;
Key->keyEffectNum = Key->KeyPressNum;
Key->KeyPressNum = 0;
}
}
}
}
}
}
2.2.2双按键长按扫描函数
void Function_TwoKeyRead(KEY_MELEMENT *KeyI , KEY_MELEMENT *KeyII)
{
static uint8_t TwoKeyPressCnt=0;
C_F(TwoKeyFlag,TwoKeyPressEffect);
if((G_F(KeyI->KeyPressFlag,KeyPress)!=0)&&(G_F(KeyII->KeyPressFlag,KeyPress)!=0))//按下
{
S_F(KeyI->KeyPressFlag,TwoKeyPress);
S_F(KeyII->KeyPressFlag,TwoKeyPress);
if(G_F(TwoKeyFlag,TwoKeyAllPress)==0)
{
if(TwoKeyPressCnt<187)//长按的时间
{
TwoKeyPressCnt++;
}
else
{
TwoKeyPressCnt = 0;
S_F(TwoKeyFlag,TwoKeyAllPress);
S_F(TwoKeyFlag,TwoKeyPressEffect);
}
}
}
else
{
TwoKeyPressCnt = 0;
if((G_F(KeyI->KeyPressFlag,KeyPress)==0)&&(G_F(KeyII->KeyPressFlag,KeyPress)==0))
{
C_F(TwoKeyFlag,TwoKeyAllPress);
C_F(KeyI->KeyPressFlag,TwoKeyPress);
C_F(KeyII->KeyPressFlag,TwoKeyPress);
}
}
}
3.按键处理
/********************************************
*
*按键处理
*
*********************************************/
void Function_KeyHandle(void)
{
//key1键处理
if(Key1IO.keyEffectNum==Long_Key)
{
}
else if(Key1IO.keyEffectNum==Once_Key)
{
}
else if(Key1IO.keyEffectNum==Double_Key)
{
}//需要实现多击功能,自行拓展
//key2键处理
if(Key2IO.keyEffectNum==Long_Key)
{
}
else if(Key2IO.keyEffectNum==Once_Key)
{
}
else if(Key2IO.keyEffectNum==Double_Key)
{
}//需要实现多击功能,自行拓展
//双按键长按处理
if(G_F(TwoKeyFlag,TwoKeyAllPress)!=0)
{
}
}
4.实际运用
void Main(void)
{
if(TimeData.InterruptFlag_1ms != 0)
{
TimeData.InterruptFlag_1ms = 0;//1MS中断标志位
TimeData.InterruptCnt_1ms++;//时间轮询
switch(TimeData.InterruptCnt_1ms & 0x07)8MS
{
case 0x00:
break;
case 0x02:
break;
case 0x04:
break;
case 0x06:
break;
default:
break;
}
switch(TimeData.InterruptCnt_1ms & 0x0f)//16MS
{
case 0x01:
Key1IO.KeyIO = KEY1IO;
Key2IO.KeyIO = KEY2IO;
Function_OneKeyRead(&Key1IO);
Function_OneKeyRead(&Key2IO);
Function_TwoKeyRead(&Key1IO,&Key2IO);
break;
case 0x05:
Function_KeyHandle();
break;
case 0x09:
break;
case 0x0d:
break;
default:
break;
}
switch(TimeData.InterruptCnt_1ms & 0x1f)//32ms
{
case 0x03:
break;
case 0x07:
break;
case 0x0b:
break;
case 0x0f:
break;
case 0x13:
break;
case 0x17:
break;
case 0x1b:
break;
case 0x1f:
break;
default:
break;
}
}
}
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/m0_52596850/article/details/144758072
|
|