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

🎊专栏【51单片机

🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。

🎆音乐分享【如愿

大一同学小吉,欢迎并且感谢大家指出我的问题🥰

目录

⭐注意

🍔同一个灯 

🏳️‍🌈效果

🏳️‍🌈代码

🏳️‍🌈问题分析

⭐注意

⭐P3_1=0,表示按下按键 

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

⭐两个Delay(20)的作用

⭐P2_0=~P2_0;

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

🏳️‍🌈效果

🏳️‍🌈代码

🏳️‍🌈原因

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

🏳️‍🌈效果

🏳️‍🌈代码

🏳️‍🌈解释 

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

🏳️‍🌈效果

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

main.c 

Timer0.c

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

Timer0.h

Key.c

Key.h

Delay.c

Delay.h


 

⭐注意

由图片可知,要使用P3口

73305b94a54d4c769381ce4387198b58.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()
{
	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)的作用

如图

922e53b552254051a02cec1a5cdbe25d.png

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

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

⭐P2_0=~P2_0;

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

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

🏳️‍🌈效果

多个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;
		}
	}
}

🏳️‍🌈原因

请看下图

d68ea58396b6478a8cdf4d375248e2c4.png

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

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

🏳️‍🌈效果

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 口

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

🏳️‍🌈效果

按键流水灯

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

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_循环右移 

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

8250547df0a54fb092118392b949daf5.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;
}

73305b94a54d4c769381ce4387198b58.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

🥰如果大家有不明白的地方,或者文章有问题,欢迎大家在评论区讨论,指正🥰  

 

  • 15
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

在下小吉.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值