【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

相关文章
|
5天前
|
数据格式
用C51单片机制作LED流水的灯
用C51单片机制作LED流水的灯
52 0
【51单片机】一文带你利用【Keil软件的模板功能】【自定义模板】简化操作(带图详解)
【51单片机】一文带你利用【Keil软件的模板功能】【自定义模板】简化操作(带图详解)
|
5天前
|
编译器 C语言 C++
【51单片机】LED的三个基本项目(LED点亮&LED闪烁&LED流水灯)(3)
【51单片机】LED的三个基本项目(LED点亮&LED闪烁&LED流水灯)(3)
定时器+按键控制LED流水灯模式+定时器时钟——“51单片机”
定时器+按键控制LED流水灯模式+定时器时钟——“51单片机”
|
5天前
|
C语言
独立按键控制LED亮灭、独立按键控制LED状态、独立按键控制LED显示二进制、独立按键控制LED移位——“51单片机”
独立按键控制LED亮灭、独立按键控制LED状态、独立按键控制LED显示二进制、独立按键控制LED移位——“51单片机”
|
5天前
|
C语言
点亮一个LED+LED闪烁+LED流水灯——“51单片机”
点亮一个LED+LED闪烁+LED流水灯——“51单片机”
|
5天前
|
C语言
###51单片机学习-----如何通过C语言运用延时函数设计LED流水灯
###51单片机学习-----如何通过C语言运用延时函数设计LED流水灯
85 0
|
8月前
|
芯片
单片机 (MCU) 如何才能不死机之 IIC 操作
单片机 (MCU) 如何才能不死机之 IIC 操作
|
5天前
|
缓存 编译器 索引
单片机中按键检测函数详细分析经典
单片机中按键检测函数详细分析经典
94 0
|
8月前
STM32F0单片机快速入门七 串口(UART)操作从轮询到中断
STM32F0单片机快速入门七 串口(UART)操作从轮询到中断
STM32F0单片机快速入门七 串口(UART)操作从轮询到中断