51单片机-独立按键

206 阅读6分钟

按键介绍

  • 轻触按键:相当于是一种电子开关,按下时开关接通,松开时开关断开,实现原理是通过轻触按键内部的金属弹片受力弹动来实现接通和断开

独立按键

  • 内部结构:
image-20220113152440031.png
  • 独立按键在开发板中的连接

image-20220113153120180.png

独立按键控制LED亮灭

因为按键一端连接的是GND,又因为单片机引脚默认是高电平,所以按下按键引脚检测到的就成了低电平。

//按住亮 松开灭
#include <REGX52.H>
main()
{
    while(1)
    {
        if(P3_1==0)//如果P_3引脚是低电平
            P2_0=0;
        else
            P2_0=1;
    }
}

但是按键存在抖动

按键的抖动

对于机械开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开,所以在开关闭合及断开的瞬间会伴随一连串的抖动

image-20220113154316370.png

为了防止这一现象,我们可以利用程序消抖

因为按键按下、弹起的时候存在抖动

所以我们在按键按下、弹起前一段20ms的延时

从而起到消除抖动的作用

//按一下亮 再按一下灭
#include <REGX52.H>void Delay(unsigned int xms)        //@11.0592MHz
{
    unsigned char i, j;
    while(xms)
    {
        i = 180;
        j = 73;
        do
        {
            while (--j);
        } while (--i);
        xms--;
    }
}
​
void mian()
{
    while(1)
    {
        if(P3_1==0)
        {   
            Delay(20);
            while(P3_1==0);//直到松手,弹片弹起
            Delay(20);
            
            P2_0=~P2_0;//取反~0=1 ~1=0
        }
    }
}
​
/*也可以怎么写
if(P3_1==0)
{
    Delay(20);
    if(P3_1==0) P2_0=~P2_0; 
    while(P3_1==0);         //松手检测,一直按住不会一直执行
}   */

按键控制LED显示二进制

P2默认状态是1111 1111

要是二级制

1就是P2=1111 1110

2就是P2=1111 1101

以此类推即可

我们可以每按下一次 P2-- ;来实现LED显示二进制

//按键控制LED显示二进制,按一下变化一次
#include <REGX52.H>void Delay(unsigned int xms)        //@11.0592MHz
{
    unsigned char i, j;
    while(xms)
    {
        i = 180;
        j = 73;
        do
        {
            while (--j);
        } while (--i);
        xms--;
    }
}
void main()
{
    while(1)
    {
        if(P3_1==0)
        {
        Delay(20);
        while(P3_1==0);//直到松手,弹片弹起
        Delay(20);
        
        P2--;
        }
    }
}

独立按键控制LED移位

#include <REGX52.H>
void Delay(unsigned int xms)        //@11.0592MHz
{
    unsigned char i, j;
    while(xms)
    {
        i = 180;
        j = 73;
        do
        {
            while (--j);
        } while (--i);
        xms--;
    }
}
​
unsigned char lednum;
void main()
{
    P2=~0x01; //1111 1110
    while(1)
    {
        if(P3_1==0)
        {
            Delay(20);
            while(P3_0==0);
            Delay(20);
            
            lednum++;
            if(lednum>=8)//最后一个灯向右,回到第一个灯
                lednum=0;           
            P2=~(0x01<<lednum);         
        }
        
        if(P3_0==0)
        {
            Delay(20);
            while(P3_0==0);
            Delay(20);
            
            if(lednum==0)//第一个灯向左,到最后一个灯
                lednum=8;
            lednum--;
            P2=~(0x01<<lednum);         
        }
        
    }
}

利用定时器扫描按键

利用定时器定时扫描按键的状态,从而判断按键是否按下

#include <REGX52.H>
#include "Delay.h"unsigned char Key_KeyNumber;
​
/**
  * @brief  获取按键键码
  * @param  无
  * @retval 按下按键的键码,范围:0,1~4,0表示无按键按下
  */
unsigned char Key(void)
{
    unsigned char Temp=0;
    Temp=Key_KeyNumber;
    Key_KeyNumber=0;
    return Temp;
}
​
/**
  * @brief  获取当前按键的状态,无消抖及松手检测
  * @param  无
  * @retval 按下按键的键码,范围:0,1~4,0表示无按键按下
  */
unsigned char Key_GetState()
{
    unsigned char KeyNumber=0;
    
    if(P3_1==0){KeyNumber=1;}
    if(P3_0==0){KeyNumber=2;}
    if(P3_2==0){KeyNumber=3;}
    if(P3_3==0){KeyNumber=4;}
    
    return KeyNumber;
}
​
/**
  * @brief  按键驱动函数,在中断中调用
  * @param  无
  * @retval 无
  */
void Key_Loop(void)
{
    static unsigned char NowState,LastState;
    LastState=NowState;             //按键状态更新
    NowState=Key_GetState();        //获取当前按键状态
    //如果上个时间点按键按下,这个时间点未按下,则是松手瞬间,以此避免消抖和松手检测
    //如果上个时间点按键未按下,这个时间点按下,则是按下瞬间,也可以避免消抖和松手检测
    if(LastState==1 && NowState==0)
    {
        Key_KeyNumber=1;
    }
    if(LastState==2 && NowState==0)
    {
        Key_KeyNumber=2;
    }
    if(LastState==3 && NowState==0)
    {
        Key_KeyNumber=3;
    }
    if(LastState==4 && NowState==0)
    {
        Key_KeyNumber=4;
    }
}
​
​
//主函数中这样调用
unsigned char KeyNum;
void main()
{
    Timer0_Init();
    while(1)
    {
        KeyNum=Key();
        if(KeyNum==1)           //K1按键按下
        {
            //K1按键按下后的操作
        }
    }
    
    
void Timer0_Routine() interrupt 1
    {
    static unsigned int T0Count1,
    TL0 = 0x18;     //设置定时初值
    TH0 = 0xFC;     //设置定时初值
    T0Count1++;
    if(T0Count1>=20)
    {
        T0Count1=0;
        Key_Loop(); //20ms调用一次按键驱动函数
    }
​

关于static

在 C 语言中,static 关键字不仅可以用来修饰变量,还可以用来修饰函数。在使用 static 关键字修饰变量时,我们称此变量为静态变量。

静态变量的存储方式与全局变量一样,都是静态存储方式。但这里需要特别说明的是,静态变量属于静态存储方式,属于静态存储方式的变量却不一定就是静态变量。例如,全局变量虽然属于静态存储方式,但并不是静态变量,它必须由 static 加以定义后才能成为静态全局变量。

考虑到可能会有不少读者对静态变量作用不太清楚,本节就来详细讨论一下它的主要作用。

隐藏与隔离的作用

上面已经阐述过,全局变量虽然属于静态存储方式,但并不是静态变量。全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,全局变量在各个源文件中都是有效的。

如果我们希望全局变量仅限于在本源文件中使用,在其他源文件中不能引用,也就是说限制其作用域只在定义该变量的源文件内有效,而在同一源程序的其他源文件中不能使用。这时,就可以通过在全局变量之前加上关键字 static 来实现,使全局变量被定义成为一个静态全局变量。这样就可以避免在其他源文件中引起的错误。也就起到了对其他源文件进行隐藏与隔离错误的作用,有利于模块化程序设计。

保持变量内容的持久性

有时候,我们希望函数中局部变量的值在函数调用结束之后不会消失,而仍然保留其原值。即它所占用的存储单元不释放,在下一次调用该函数时,其局部变量的值仍然存在,也就是上一次函数调用结束时的值。这时候,我们就应该将该局部变量用关键字 static 声明为“静态局部变量”。

当将局部变量声明为静态局部变量的时候,也就改变了局部变量的存储位置,即从原来的栈中存放改为静态存储区存放。这让它看起来很像全局变量,其实静态局部变量与全局变量的主要区别就在于可见性,静态局部变量只在其被声明的代码块中是可见的。

对某些必须在调用之间保持局部变量的值的子程序而言,静态局部变量是特别重要的。如果没有静态局部变量,则必须在这类函数中使用全局变量,由此也就打开了引入副作用的大门。使用静态局部变量最好的示例就是实现统计次数的功能。

参考:

static变量及其作用,C语言static变量详解 (biancheng.net)