独立按键检测的方法有:
1、轮询法
2、外部中断法
3、定时器中断法
4、定时器中断加状态机
按键消抖:
一、软件消抖
1、延时消抖与二次判断,延时一般用10ms即可。轮询法与外部中断法使用。外部中断边沿触发不消抖的话会有多次触发的现象。
2、利用定时器中断间隔来实现消抖。
二、硬件消抖
最简单的就是并联一个电容,一般使用104(0.1uf)电容。
使用定时器中断的方法,不用使用延时或者死循环,减少了CPU的占用,适用于大程序。定时器中断取10ms。
下面是状态机的程序,实际使用时,在定时器中断中调用,每10ms进行一次调用来检测判断。
(1)按键抬起时返回键值
u8 key_state_0 = 0; //初始状态
u8 key_state_1 = 1; //键按下确认状态
u8 key_state_2 = 2; //等待释放状态
//按键抬起时返回键值
unsigned char read_key(void)
{
static u8 key_state=key_state_0; //状态
u8 key_press0; //按键0电平
u8 key_press1; //按键1电平
u8 key_press2; //按键2电平
u8 key_return = 0; //返回键值
//读取电平值
key_press0 = GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3); // 读按键0电平
key_press1 = GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4); // 读按键1电平
key_press2 = GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_5); // 读按键2电平
//判断
switch (key_state)
{
case key_state_0: // 按键初始态
if (!key_press0 || !key_press1 || !key_press2) //如果检测到任意一个按键按下,转为按键确认状态
{
key_state = key_state_1;
}
break;
case key_state_1: // 按键确认态
if (!key_press0) //如果是按键0按下
{
key_return = 1; // 更新键值
}
else if(!key_press1)//如果是按键1按下
{
key_return = 2; // 更新键值
}
else if(!key_press2)//如果是按键2按下
{
key_return = 3; // 更新键值
}
key_state = key_state_2;// 状态转换到键释放状态
break;
case key_state_2: //键释放状态 // 如果按键还在按下,还保留在该状态,直到释放时返回键值并返回初始状态
if (key_press0 && key_press1 && key_press2) //如果都抬起了
{
key_state = key_state_0; //返回初始状态
return key_return; //抬起时返回按键值
}
break;
}
return 0;
}
(2)按键按下时返回键值,并且只触发一次
u8 key_state_0 = 0; //按键的初始状态
u8 key_state_1 = 1; //按键按下确认状态
u8 key_state_2 = 2; //待释放状态
//按键按下时返回键值
unsigned char read_key(void)
{
static u8 key_state=0; //状态位
u8 key_press0; //按键0电平
u8 key_press1; //按键1电平
u8 key_press2; //按键2电平
u8 key_return = 0; //按键返回值
//读取电平值
key_press0 = GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3); // 读按键0电平
key_press1 = GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4); // 读按键1电平
key_press2 = GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_5); // 读按键2电平
//判断
switch (key_state)
{
case key_state_0: // 按键初始态
if (!key_press0 || !key_press1 || !key_press2) //如果检测到任意一个按键按下,转为按键确认状态
{
key_state = key_state_1;
}
break;
case key_state_1: // 按键确认态
if (!key_press0) //如果是按键0按下
{
key_return = 1; //更新键值
return key_return; //立即返回键值
}
else if(!key_press1)//如果是按键1按下
{
key_return = 2;
return key_return;
}
else if(!key_press2)//如果是按键2按下
{
key_return = 3;
return key_return;
}
key_state = key_state_2;// 状态转换到键释放态
break;
case key_state_2: //键释放态 // 如果按键还在按下,还保留在该状态
if (key_press0 && key_press1 && key_press2) //如果抬起了则返回初始状态
{
key_state = key_state_0;
}
break;
}
return 0;
}
(3)具有长按和短按检测功能,达到长按要求立即返回键值,没有达到长按要求则返回短按键值,只触发一次
u8 key_state_0 = 0; //按键的初始状态(状态1)
u8 key_state_1 = 1; //按键的按下确认状态(状态2)
u8 key_state_2 = 2; //按键的抬起状态或者长按状态(状态3)
//没有达到长按要求时,返回短按键值,达到长按要求时,返回长按键值
unsigned char read_key(void)
{
static u8 key_state=0; //状态位
static u8 i=0; //长按计数值
u8 key_return; //键值
u8 key_press0; //按键0电平
u8 key_press1; //按键1电平
u8 key_press2; //按键2电平
//读取当前电平
key_press0 = GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3); // 读按键0电平
key_press1 = GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4); // 读按键1电平
key_press2 = GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_5); // 读按键2电平
//判断
switch (key_state)
{
case key_state_0: // 按键初始态
if(!key_press0 || !key_press1 || !key_press2) //如果检测到任意一个按键第一次按下,则转为按键确认状态
{
key_state = key_state_1;
}
break;
case key_state_1: // 按键确认态
if (!key_press0) //如果确认按键0按下
{
key_return = 1; //设置键值
}
else if(!key_press1)//如果确认按键1按下
{
key_return = 2; // 设置键值
}
else if(!key_press2)//如果确认按键2按下
{
key_return = 3; //设置键值
}
key_state = key_state_2;// 状态转换到第三状态
break;
case key_state_2: //该状态可以实现长按和短按不同的功能
if (!key_press0) //如果按键0还在按下
{
i++; //计数
if(i==2) //达到长按要求,则返回初始状态并直接返回按键值
{
key_return=4;
return key_return;
}
}
else if(!key_press1)//如果是按键1按下
{
i++;
if(i==2)
{
key_return=5;
return key_return;
}
}
else if(!key_press2)//如果是按键2按下
{
i++;
if(i==2)
{
key_return=6;
return key_return;
}
}
else //抬起了【一次按键完成】,处理长按过时与没有达到长按要求
{
key_state = key_state_0; //返回初始状态
if(i<2) //没有达到长按要求,则返回短按键值;
{
i=0;
return key_return;
}
else//大于2说明长按时间过长,返回0即可
{
i=0;
}
}
break;
}
return 0;
}
如果不使用状态机,只使用定时器中断记录符合要求电平的次数,也可以实现检测按键长按与短按。当检测到符合电平时就连加1,当检测到不符合要求的电平时就清零。
有不对的或者好的方法欢迎指正,谢谢啦