【51单片机】按键操作(单个灯闪烁&&流水灯)

简介: 按键操作(单个灯闪烁&&流水灯)

⭐注意

由图片可知,要使用P3口

image.png

🍔同一个灯

🏳️‍🌈效果

image.pngled灯闪烁

#include <REGX52.H>
void Delay(unsigned int xms)    
{
  unsigned char i, j;
  while(xms){
    i = 2;
    j = 239;
    do
    {
        while (--j);
    } while (--i);
    xms--;
  }
}
void main()
{
  while(1)
  {
    if(P3_1==0)//按下
    {
      Delay(20);
      while(P3_1==0);//仍然按下
      Delay(20);
      P2_0=~P2_0;
    }
  }
}

着重分析下面的代码

while(1)
    {
        if(P3_1==0)
        {
            Delay(20);
            while(P3_1==0) ; 
            Delay(20);
            P2_0=~P2_0;
        }
    }

🏳️‍🌈问题分析

⭐注意

1.在这个程序里面,最好用类似于P3_1=0的代码,不要用类似于P2=0xFE的代码,因为后者很容易迷糊

(如果是流水灯,可以用P2=0xFE)

2.Delay()函数里面用的是自动延时代码,可以由软件自动生成,如果不明白是怎么肥四,可以参考下面这篇文章

【51单片机】使用STC烧录软件自动生成延时代码_在下 小吉的博客-CSDN博客

⭐P3_1=0,表示按下按键

⭐为什么while(P3_1==0);后面要加上分号呢?

在观察现象的时候发现,按一次松手,灯亮了,再按一次松手,灯灭了

所以,这样子主要是为了一直按着按键的时候,防止灯亮

(而且不能改为P3_1=0,因为这里必须是循环语句或者判断语句)

⭐两个Delay(20)的作用

如图

image.png

第一个Delay(20)是作用于前面抖动的部分

第二个Delay(20)是作用于后面抖动的部分(防止松手时影响灯的亮度)

⭐P2_0=~P2_0;

就是控制LED的开关状态,使其可以闪烁

🍔不同灯(显示二进制移位

🏳️‍🌈效果

image.png

多个led灯亮

🏳️‍🌈代码

#include <REGX52.H>
void Delay(unsigned int xms)    
{
  unsigned char i, j;
  while(xms){
    i = 2;
    j = 239;
    do
    {
        while (--j);
    } while (--i);
    xms--;
  }
}
void main()
{
  unsigned char LEDNum=0;
  while(1)
  {
    if(P3_1==0)
    {
      Delay(20);
      while(P3_1==0);
      Delay(20);
      LEDNum++;
      P2=~LEDNum;
    }
  }
}

🏳️‍🌈原因

请看下图

image.png

P2=~LEDNum;由于二进制数字的变化,所以会引起灯的变化

🍔流水灯(按一下按键,灯亮的位置才会变化)(移位)

🏳️‍🌈效果

image.png

WeChat_20230425133443

🏳️‍🌈代码

#include <REGX52.H>
unsigned char LEDNum;
void Delay(unsigned int xms)    
{
  unsigned char i, j;
  while(xms){
    i = 2;
    j = 239;
    do
    {
        while (--j);
    } while (--i);
    xms--;
  }
}
void main()
{
  while(1)
  {
    if(P3_1==0)
    {
      Delay(20);
      while(P3_1==0);
      Delay(20);
      if(LEDNum>=8) 
        LEDNum=0;
      P2=~(0x01<<LEDNum);
      LEDNum++;
    }
    if(P3_0==0)//多个按键操作
    {
      Delay(20);
      while(P3_0==0);
      Delay(20);
      if(LEDNum==0)//反方向的流水灯
        LEDNum=7;
      else
        LEDNum--;
      P2=~(0x01<<LEDNum);
    }
  }
}

🏳️‍🌈解释

首先 led=0x01,而且LEDNum=0,因为 LED 是低电平点亮 ,所以 (0x01<<LEDNum) 取反 后的结果是 0xFE, 对应二进制数为 1111 1110,即最低位为 0,因此最开始的 D1 指示灯会点亮 ,然后延时一段时间进入while循环, 由于要实现8个LED从D1->D8 循环点亮,因此可以使用 for 循环语句循环 8 次,每循环一次,点亮的小灯向右 移动一个即 P2 口输出的低电平要左移一位,因此可以使用 P2=~(0x01<<i);语句实现 。0x01<<LEDNum 表示每次 LEDNum 增加 1 次,0x01 中的 1 就移动多少位, 因为 1不会让 LED 点亮,需要取反后变为低电平 0 才能点亮,所以最后的结果需要

取反后给 P2 口

🍔流水灯(按一下,灯亮的位置自动改变)

🏳️‍🌈效果

image.png

按键流水灯

🏳️‍🌈代码例子&&解释

main.c

#include <REGX52.H>
#include "Timer0.h"
#include "Key.h"
#include <INTRINS.H>
unsigned char KeyNum,LEDMode;
void main()
{
  P2=0xFE;
  Timer0Init();
  while(1)
  {
    KeyNum=Key();   //获取独立按键键码
    if(KeyNum!=0)     //如果按键按下
    {
      //if(KeyNum==1) //如果K1按键按下
      //{
        LEDMode++;  //模式切换
        if(LEDMode>=2)LEDMode=0;
      //}
    }
  }
}
void Timer0_Routine() interrupt 1
{
  static unsigned int T0Count;
  TL0 = 0x18;   //设置定时初值
  TH0 = 0xFC;   //设置定时初值
  T0Count++;    //T0Count计次,对中断频率进行分频
  if(T0Count>=500)//分频500次,500ms
  {
    T0Count=0;
    if(LEDMode==0)      //模式判断
      P2=_crol_(P2,1);  //LED输出    
      //_crol_循环左移
    if(LEDMode==1)
      P2=_cror_(P2,1);
      //_cror_循环右移
  }
}

_crol_循环左移

_cror_循环右移

⭐使用定时器中断的原因(请看图片)

image.png

Timer0.c

#include <REGX52.H>
/**
  * @brief  定时器0初始化,1毫秒@12.000MHz
  * @param  无
  * @retval 无
  */
void Timer0Init(void)
{
  TMOD &= 0xF0;   //设置定时器模式
  TMOD |= 0x01;   //设置定时器模式
  TL0 = 0x18;   //设置定时初值
  TH0 = 0xFC;   //设置定时初值
  TF0 = 0;    //清除TF0标志
  TR0 = 1;    //定时器0开始计时
  ET0=1;
  EA=1;
  PT0=0;
}
/*定时器中断函数模板
void Timer0_Routine() interrupt 1
{
  static unsigned int T0Count;
  TL0 = 0x18;   //设置定时初值
  TH0 = 0xFC;   //设置定时初值
  T0Count++;
  if(T0Count>=1000)
  {
    T0Count=0;
  }
}
*/

🎆为什么主函数没有调用中断函数,但是中断函数为什么会执行呢

当调用Timer0Init()函数时,就会开启定时器0中断,并将中断服务函数的入口地址设置为Timer0_Routine()函数的地址(这个过程涉及到中断向量表的设置,一般由编译器完成)。而每当定时器0中断被触发时,就会跳转到Timer0_Routine()函数执行其中的代码。因此,即使主函数没有显式调用中断函数,中断函数也会被执行。

上面一段代码不明白的同学可以参考一下这一篇博客

【51单片机】使用STC烧录软件生成定时器的代码以及注意事项_在下小吉.的博客-CSDN博客

Timer0.h

#ifndef __TIMER0_H__
#define __TIMER0_H__
void Timer0Init(void);
#endif

Key.c

#include <REGX52.H>
#include "Delay.h"
/**
  * @brief  获取独立按键键码
  * @param  无
  * @retval 按下按键的键码,范围:0~4,无按键按下时返回值为0
  */
unsigned char Key()
{
  unsigned char KeyNumber=0;
  if(P3_1==0){Delay(20);while(P3_1==0);Delay(20);KeyNumber=1;}
  if(P3_0==0){Delay(20);while(P3_0==0);Delay(20);KeyNumber=2;}
  if(P3_2==0){Delay(20);while(P3_2==0);Delay(20);KeyNumber=3;}
  if(P3_3==0){Delay(20);while(P3_3==0);Delay(20);KeyNumber=4;}
  return KeyNumber;
}

image.png

因为图片中的就是P3口

所以最好使用P3口的4个引脚(P3_0~P3_3)

Key.h

#ifndef __KEY_H__
#define __KEY_H__
unsigned char Key();
#endif

Delay.c

void Delay(unsigned int xms)
{
  unsigned char i, j;
  while(xms--)
  {
    i = 2;
    j = 239;
    do
    {
      while (--j);
    } while (--i);
  }
}

上面一段代码不明白的同学可以参考一下这篇博客

【51单片机】使用STC烧录软件自动生成延时代码_在下小吉.的博客-CSDN博客

Delay.h

#ifndef __DELAY_H__
#define __DELAY_H__
void Delay(unsigned int xms);
#endif

相关文章
|
3月前
【51单片机】独立按键,每个按键不同功能,数码管数值的加减,控制流水灯模式,包含按键消抖,数码显示,流水灯
【51单片机】独立按键,每个按键不同功能,数码管数值的加减,控制流水灯模式,包含按键消抖,数码显示,流水灯
261 1
|
3月前
【51单片机】在LCD1602上显示时间:包含按键进行校准时间+闹钟功能:按键设置闹钟响铃时间,以及响铃的时间长度
【51单片机】在LCD1602上显示时间:包含按键进行校准时间+闹钟功能:按键设置闹钟响铃时间,以及响铃的时间长度
|
3月前
51单片机用汇编语言实现独立按键检测,每个按键有不同功能,包含按键消抖程序
51单片机用汇编语言实现独立按键检测,每个按键有不同功能,包含按键消抖程序
103 3
|
3月前
|
C语言
【51单片机】LCD1602显示字符串,时间、时间+按键校准、秒表计时的功能代码。
【51单片机】LCD1602显示字符串,时间、时间+按键校准、秒表计时的功能代码。
|
3月前
|
C语言
51单片机汇编语言流水灯代码
51单片机汇编语言流水灯代码
|
3月前
|
C语言
【51单片机】用汇编语言实现点灯、闪烁
【51单片机】用汇编语言实现点灯、闪烁
【51单片机】利用【时间延迟】的原理规避【按键抖动问题】
【51单片机】利用【时间延迟】的原理规避【按键抖动问题】
【51单片机】一文带你利用【Keil软件的模板功能】【自定义模板】简化操作(带图详解)
【51单片机】一文带你利用【Keil软件的模板功能】【自定义模板】简化操作(带图详解)
【51单片机】Kn独立按键控制【LED亮灭】【LED状态】【LED二进制式显示】【LED不断移位】(4)
【51单片机】Kn独立按键控制【LED亮灭】【LED状态】【LED二进制式显示】【LED不断移位】(4)
|
6月前
|
编译器 C语言 C++
【51单片机】LED的三个基本项目(LED点亮&LED闪烁&LED流水灯)(3)
【51单片机】LED的三个基本项目(LED点亮&LED闪烁&LED流水灯)(3)