单片机按键识别

本文探讨了单片机如何处理和识别按键的按下、弹起、长按三种状态,主要关注软件层面的信号处理,尤其是消抖技术。通过设置时间标志位和使用定时器来避免delay函数导致的程序堵塞,提高了单片机的运行效率。


本文主要针对单片机IO口读取到的按键信号,进行处理识别。识别按键的按下弹起长按三种状态,不讨论硬件层的配置问题。可应用于所有单片机的按键信号处理。


按键的简单介绍


按键的硬件主要以接地和接电源的方式。

下面这张图是接地的,当按键未按下时,key1电压为低电平,当按键按下时,key1电压为高电平。

接地按键

下面这张图是接电源的,当按键未按下时,key2电压为高电平,按下时为低电平。

按键接电源


按键信号处理


为了方便起见,不论按下时高电平还是低电平,我们在硬件层编写读取函数时,将按下的状态标志位1,弹起的状态标志位0。
下面以Key1PA0口和Key2PB2口为例:

u8_t HW_Read_Key(void)
{
	u8_t  nKeyBuff = 0;
	
	if (PA_IDR & 0x01)
	{
		nKeyBuff |= 0x01;
	}
	if (PB_IDR & 0x04)
	{
		nKeyBuff |= 0x02;
	}
	
  return nKeyBuff;
}

我们知道机械按键在按下和弹起时,会因为产生抖动,出现高低电平快速变化,因此我们必须进行消抖处理

普遍地做法是使用delay函数延迟50ms左右,但是delay这个延迟函数是for循环构成的,程序会堵塞在里面,不能及时的处理其他信息,且极大的减慢了单片机运行速度。

因此常用地做法是使用时间标志位,利用定时器定时,详细可以查看我的关于stm8s硬件配置的那篇文章。


本文编写了一个按键读取函数KeyReadInfor(),只需要在while(1)中执行这个函数就可以不断检测按键的信息了。

u8_t nKeyCrVal = 0; // 按键当前的值
u8_t nKeyPrVal = 0; // 按键上一次的值
u8_t nKeyPrDat = 0; // 按键50ms前的值
u8_t wTimeCnt = 0;  // 时间计时
typedef struct _KEYINFOR
{
    u8_t m_nKeyDn;
    u8_t m_nKeyUp;
    u8_t m_nKeykp;
}KEYINFOR;


void KeyReadInfor(KEYINFOR stKeyInfor)
{
    u8_t nMask = 0;     // 检测按键值变化
    u8_t nKeyDn = 0;    // 检测按键按下
    u8_t nKeyUp = 0;    // 检测按键弹起
    u8_t nKeyKp = 0;    // 检测按键长按
    if (bSysTime10ms) // bSysTime10ms为时间标志位,每10ms会置为True
    {
        bSysTime10ms = eFALSE;
        nKeyCrVal = HW_Read_Key(); // 读取按键的值
        if (nKeyCrVal == nKeyPrVal)
        {
            wTimeCnt++;
            if (wTimeCnt >= (2<<16)-1)
            {
                wTimeCnt = (2<<16)-1;
            }
            if (wTimeCnt == 5) // 50ms
            {
                nMask = nKeyCrVal ^ nKeyPrVal;
                nKeyDn = nKeyCrVal & nMask; 
                nKeyUp = nKeyPrVal & nMask;

            }
            else if (wTimeCnt == 200) //2s
            {
                nKeyKp = nKeyPrVal;
            }
        }
        else
        {
            wTimeCnt = 0;
            nKeyPrVal = nKeyCrVal;
        }
        stKeyInfor.m_nKeyDn = nKeyDn;
        stKeyInfor.m_nKeyUp = nKeyUp;
        stKeyInfor.m_nKeyKp = nKeyKp;
    }
}


main函数代码如下:

KEYINFOR s_stKeyInfor = {0};

while(1)
{
  /* 按键消息读取 */
  KeyReadInfor(s_stKeyInfor);
  /* 消息检测 */
  if (s_stKeyInfor.m_nKeyDn || s_stKeyInfor.m_nKeyUp || s_stKeyInfor.m_nKeyKp)
  {
    //消息处理,根据自己需求编写
    if (s_stKeyInfor.m_nKeyDn)
    {
      switch(s_stKeyInfor.m_nKeyDn)
      {
        case 0x01:
             break;
        
        case 0x02:
             break;
             
        default:
             break;
      }
      s_stKeyInfor.m_nKeyDn = 0;
    }
    
    if (s_stKeyInfor.m_nKeyUp)
    {
    
    }
    
    if (s_stKeyInfor.m_nKeyKp)
    {
    
    }
  }
}

需要注意的是,整个程序中,最好只有消息检测是无延迟的,以系统频率进行检测,其他程序均要有时间标志位延迟,否则当整个程序过大时,会导致单片机工作负载过大,从而使整个程序的循环变慢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值