51单片机编程应用(C语言):独立按键

目录

1.独立按键介绍

2.独立按键控制LED亮灭

1.1按下时LED亮,松手LED灭(按一次执行亮灭)

1.2首先按下时无操作,松手时LED亮(再按下无操作,所以LED亮),松手LED灭(松手时执行取反操作)(按两次执行亮灭)

1.3.独立按键控制LED按二进制递增亮

1.4.两个独立按键控制LED移位,左移,右移

1.5:一个独立按键控制流水灯方向

方法一:一个代码编程所有:

方法2:模块化编程,


1.独立按键介绍

独立按键的原理图:

所有单片机I/O口上电时都是默认高电平,独立按键没有按下,就是高电平,按下时,外部接地时强下拉,与地相接,I/O就会变为低电平,这里讲一下什么是I/O口,I/O口也叫input/output口,输入输出口,cpu可以给寄存器写值,比如上一节LED灯,CPU写值给寄存器,,由于单片机内部是弱上拉,所以要加驱动器加强电流,最后连接到I/O口,寄存器通过数据总线把值给I/O口此时I/O口就是output,input刚好就是相反的操作,比如I/O口读取外部信息,把值给寄存器,CPU再识别寄存器的值,

讲一下编程的命名问题,前面我们讲了sbit,对单个I/O口命名,就像一个人一样,要有名字,不过称呼一个人是不是也可以取外号啊,C51编程就给了这样的命名规则,

#include <REGX52.H>这个函数就相当于父母一样,里面对I/O口八位或单个I/O都命名了,

我们用单个I/O口就直接用P0_0,P0_0就像父母给的名字一样,但是也可以外号称呼,sbit LED=P2^0,此时LED就是外号,LED和P2_0都可以指向P2^0口(看成一个人)

记住:sbit LED=P2_0;是错的,把名字给外号显然不是喊你,P2^1=0;也是错的,赋值就像别人要称呼你,称呼你喊你的名字,肯定不是叫哎,这个人吧

延时消抖

主要原因还是单片机运算速度太快了,us级别,这些抖动他是可以识别出来的, 比如上面这个图,你按一下,由于有抖动,出现高-低--高--低--高--低...这样的话单片机会误以为你按了5/6下,

实际上你就按了一下,所以这样的操作就会与人们期望的不符,所以我们延时一段时间来消除抖动,按下与松手都加个延时,当然也可以加一个电路消抖动,(有点麻烦)

2.独立按键控制LED亮灭

1.1按下时LED亮,松手LED灭(按一次执行亮灭)

最终代码如下:

cpp 复制代码
#include <REGX52.H>
sbit LED=P2^0;
void Delay1ms(unsigned int xms)		//@12.000MHz
{
	unsigned char i, j;
  while(xms)
	{
	i = 2;
	j = 239;
	do
	{
		while (--j);
	} while (--i);
	xms--;
  }
}

void main()
{
	
	while(1)
	{
		if(P3_1==0)//按下
		{
			Delay1ms(20);//消抖,真正的按下
			P2_0=0;
		}
		else//松手
		{
            Delay1ms(20);
			LED=0;
		}
	}
}

1.2首先按下时无操作,松手时LED亮(再按下无操作,所以LED亮),松手LED灭(松手时执行取反操作)(按两次执行亮灭)

代码如下:

cpp 复制代码
#include <REGX52.H>
sbit LED=P2^0;
void Delay1ms(unsigned int xms)		//@12.000MHz
{
	unsigned char i, j;
  while(xms)
	{
	i = 2;
	j = 239;
	do
	{
		while (--j);
	} while (--i);
	xms--;
  }
}

void main()
{
	
	while(1)
	{
		if(P3_1==0)
		{
			Delay1ms(20);//消抖,确定按下
		}
		else
		{
			Delay1ms(20);//消抖,确定松手
			LED=~LED;
			while(P3_1==1);
		}
	}
}

这段代码的现象是没有按之前,LED从1变为0;LED亮, 如果没有按独立按键,LED就一直亮,按下独立按键,没有任何反应,松手LED从0变为1,LED灭,一直是灭的状态,假如没有while(P3_1==1);这句,现象是LED一直在闪烁,无论按不按,都是闪烁,为什么呢,P3_1默认为高电平,一上电,我们没有按开关,执行else语句,LED=~LED,10101010.......延迟20ms的闪烁,按了就是多了延迟,只是增加了闪烁时间不同而已,有了这句话之后,松手的话,死循环,一直不执行,保持着了LED的状态,而不要让他一直变,

最后按一下松手执行下面操作,

亮----灭-----亮------灭.....

当然下面代码可以执行下面现象.

灭-----亮------灭.....亮........

cpp 复制代码
void main()
{
	
	while(1)
	{
		if(P3_1==0)
		{
			Delay1ms(20);
		    while(P3_1==0);
			Delay1ms(20);
			LED=~LED;
		}
	}
}

没有按下,不执行任何操作,保持原有状态 ,按下时,一直按着的话,就什么都不操作,执行while语句,一旦松手,跳出while语句,LED取反,再跳出if语句

1.3.独立按键控制LED按二进制递增亮

引入中间变量LEDNum,为什么呢,因为P2 ++,上电默认是不是1111 1111,加1变为0000 0000,全部亮,加一0000 0001,刚好与我们的预想的相反,而P2取反,就会1111 1111,变为0000 0000,取反you变为1111 1111,无法达到我们的效果,而引入中间变量LEDNum,LEDNum把值存起来,再给P2赋值就完美的解决了这一点,比如char类型刚好一个字节,存8位,

按下一次按键后LEDNum从0000 0000加加后变为0000 0001,取反1111 1110,给P2口,刚好第一个灯亮,而且LEDNum的值不会因为赋给P2而变成P2的值,还是 0000 0001,加一0000 0010 取反 1111 1101 给P2,刚好点亮第二个灯,依次循环,实现了用灯表示二进制,

cpp 复制代码
#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;
      }
	
    }
}

1.4.两个独立按键控制LED移位,左移,右移

方法一:

0000 0001 0x01<<0

0000 0010 0x01<<1

0000 0100 0x01<<2

0000 1000 0x01<<3

..............

0 1 2 3 4定义为LEDNum,每按一下加一,加到7,LEDNum回到0,加个if语句。if(LEDNum>=8),LEDNum=0,因为LEDNum=7时,继续执行i++,

P2口应该与上面写的是反的,再取反操作。

代码如下

cpp 复制代码
#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);
		}
	}
}
 

注意:LEDNum是无符号型,最大值 255,LEDNum--的话,减到0,再减一的话,变成255,这就是越界。所以减到0我们重新让他变为7,

方法二:

每按一次独立按键,P2的数值变化如下

P2: 1111 1111 上电时, 我们用左移操作时,最低位是不是补0,我们在或上0x01,补成1

1111 1110 按一次按键 P2左移1位 ,此时P2=1111 1110

1111 1101 按两次按键 P2左移1位后或上0000 0001,

1111 1011 按三次按键 P2左移1位后或上0000 0001

...............

cpp 复制代码
#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()
{   
		P2=0xFE;
	    while(1)
	    {
		if(P3_1==0)
		{
			Delay(20);
			while(P3_1==0);
			Delay(20);
			P2=P2<<1|0x01;
			if(P2==0xFF)
	        {
				P2=0xFE;
	        }
				
		}
		
		if(P3_0==0)
		{
			Delay(20);
			while(P3_0==0);
			Delay(20);
			P2=P2>>1|0x80;
			if(P2==0xFF)
	        {
				P2=0x7F;
	        }
		}
	}
}
 

1.5:一个独立按键控制流水灯方向

代码如下:

方法一:一个代码编程所有:
cpp 复制代码
#include <REGX52.H>
#include <INTRINS.H>  //导入头文件
unsigned char LEDNum;
unsigned int count;
void Delay1ms(unsigned int xms)		//@11.0592MHz   //延时函数
{
	unsigned char i, j;
	while (xms--)
	{
		i = 2;
		j = 199;
		do
		{
			while (--j);
		} while (--i);
 
	}
}
 
void main()
{
	while(1)
	{
		if(P3_1==0)
		{
			Delay1ms(20);
			while(P3_1==0);
			Delay1ms(20); //软件消抖
			P2=0xFE;
			Delay1ms(500);
			LEDNum = 0xFE;
			while(1)
			{
				while(count == 0) //当count为0时进入此循环
				{
					LEDNum =_crol_(LEDNum,1);
					P2 = LEDNum;
					Delay1ms(500);//1
					if(P3_1==0)
					{
						Delay1ms(20);
						while(P3_1==0);
						Delay1ms(20);
						count = 1; //再次按下K1改变count值使进入逻辑右移
					}
				}
				while(count == 1) //当count为1时进入此循环
				{
					LEDNum =_cror_(LEDNum,1);
					P2 = LEDNum;
					Delay1ms(500);
					if(P3_1==0)
					{
						Delay1ms(20);
						while(P3_1==0);
						Delay1ms(20);
						count = 0; //再次按下K1改变count值使进入逻辑左移

					}
				}
			}			
		}
	}
}
方法2:模块化编程,

后面我们专门写一期怎么模块化编程,下面针对这个题进行一次模块化编程,涉及内容有中断,定时器,后面所以讲到了会一一再解释这里的所有代码的具体含义。

main.c

cpp 复制代码
#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_循环右移
	}
}

Timer0.c :

cpp 复制代码
#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;
		
	}
}
*/

Timer0.h :

cpp 复制代码
#ifndef __TIMER0_H__
#define __TIMER0_H__
 
void Timer0Init(void);
 
#endif

Key.c :

cpp 复制代码
#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;
}

Key.h

cpp 复制代码
#ifndef __KEY_H__
#define __KEY_H__
 
unsigned char Key();
 
#endif

Delay.c :

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

Delay.h :

cpp 复制代码
#ifndef __DELAY_H__
#define __DELAY_H__
 
void Delay(unsigned int xms);
 
#endif
相关推荐
LS_learner2 分钟前
树莓派(ARM64 架构)Ubuntu 24.04 (Noble) 系统 `apt update` 报错解决方案
嵌入式硬件
来自晴朗的明天43 分钟前
16、电压跟随器(缓冲器)电路
单片机·嵌入式硬件·硬件工程
4311媒体网1 小时前
C语言操作符全解析 C语言操作符详解
java·c语言·jvm
钰珠AIOT1 小时前
在同一块电路板上同时存在 0805 0603 不同的封装有什么利弊?
嵌入式硬件
代码游侠1 小时前
复习——Linux设备驱动开发笔记
linux·arm开发·驱动开发·笔记·嵌入式硬件·架构
二年级程序员1 小时前
一篇文章掌握“顺序表”
c语言·数据结构
傻乐u兔2 小时前
C语言进阶————指针4
c语言·开发语言
历程里程碑2 小时前
Linux22 文件系统
linux·运维·c语言·开发语言·数据结构·c++·算法
2601_949146539 小时前
C语言语音通知接口接入教程:如何使用C语言直接调用语音预警API
c语言·开发语言