基于51单片机的跳跃流水灯的实现

声明:本文基于普中51开发板

        单片机型号为为STC89C52RC

要求:

流水灯:

基础       从左到右,从右到左,一颗一颗的亮、灭,对时间间隔无要求,需肉眼可见的变化。
提高       从 直接点亮 变为 渐亮渐暗的呼吸灯
              能用普通按键直接跳过多少个灯(如按下 按键1 跳过1个灯,按下 按键3 跳过3个灯)
              能即时响应按键的跳转

基础部分的解决

要实现循环流水灯的方法很多在这里采用_crol_函数位移的方法,但要注意_crol_函数是在intrins头文件中的(不要忘记了)

cror函数

关于_crol_函数的解释,只需要注意最前与最后两个字母

        第一个代表要移动的变量类型,第二个则是往哪个方向移动

        c代表char类的变量,l代表int类型,l为long型

        l为往左即(left),r为往右(right)

如果只是基础的问题解决只需用用for环来配置寄存器以点亮灯泡即可(循环流水灯)

代码如下,由于最新版的普中51开发板的LED部分是第八个灯在前

所以在让对P2寄存器赋初始值的时候需要注意低位在前,高位在后.

要解决这个问题低位在前,高位在后

我所知道的有两个方法

1.比较难想象的话可以把51单片机反过来,这样比较好想.

2.可以进行蝶式交换法使其成为正常的顺序.

基础部分代码

		P2=0xfe; 
		Delay(3);
		for(i=0;i<7;i++)//只需要移动7次即可,不然会移动到初始位置
		{
			P2=_crol_(P2,1);  
			Delay(3);
		}
		
		P2=0x7f;
		Delay(3);
		for(i=0;i<7;i++)
		{
		   P2=_cror_(P2,1);
		   Delay(3);
		}

Delayms函数代码

void delayms(unsigned cahr t)
{
    while(t--);
}

基础部分流水灯代码

这里采用江科大视频中的方法只需做微小改动即可

	  unsigned char arr[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};	
	  for(k=0;k<7;k++)
	  {
       for(Time=0;Time<100;Time++)
		   {
			    for(i=0;i<20;i++)
				{
                    P2=arr[k];
					delayms(Time);
					P2=0xff;
					delayms(100-Time);
				}
		   }
		  for(Time=100;Time>0;Time--)
		  {
			   for(i=0;i<20;i++)
			   {
						P2=arr[k];
						delayms(Time);
						P2=0xff;
						delayms(100-Time);
			   }
		  }
	  }
		
		 for(k=7;k>0;k--)
		{
          for(Time=0;Time<100;Time++)
          {
              for(i=0;i<20;i++)
              {
                    P2=arr[k];
                    delayms(Time);
                    P2=0xff;
                    delayms(100-Time);
              }
		  }
		  for(Time=100;Time>0;Time--)
		  {
			   for(i=0;i<20;i++)
			   {
							P2=arr[k];
							delayms(Time);
							P2=0xff;
							delayms(100-Time);
			   }
		   }
	   }

最后只要将上述代码都放到while(1)循环中不断进行即可

提高部分

代码总思路

题目要求我们能够即使响应按键的跳转说明我们要舍弃前面的做法,因为进入循环之后单片机就困在循环中了导致无法检测按键(因此不能用循环)

我们可以用一个数组来表示LED灯的各种状态(从左到右和从右到左的状态)

同时定义一个i来指向数组中的各个元素,在定时器中每隔一段时间就改变i的值,同时超过就取余清零

按键检测可以用定时器扫描的方法,每20毫秒扫描一次(与滤波原理类似)用定时器0

定时器1则用作流水灯部分的实现

每按下就让i加上按键的值,一旦i大于14就打开定时器1的开关

废话不多说直接上代码

代码部分

主函数与定时器中断

unsigned char Light[14]={0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD};
unsigned char i,j,k,Mode,Number;
void main()
{   
	Timer0_Init();
	Timer1_Init();
	while(1)
	{
		Number=Key();  //检测按键是否按下
		i+=Number;     //i的改变处
		if(i>=14)      //呼吸灯模式与普通模式的转变
		{              
			if(TR1==1) //定时器一的启动与停止
			{
				TR1=0; //定时器一启动就是呼吸灯模式
			}
			else if(TR1==0)
			{
				TR1=1;
			}
	    }		
		i%=14;         //先转换模式再清零
		P2=Light[i];   //P2口怎样都是要等于Light[i]
	}
}

void Timer0_Routine(void) interrupt 1 //中断程序代码1ms
{   //静态占据空间
	static unsigned int T0Count1=0,T0Count2=0;
	TL0=0x66;//这里要注意定义静态变量的前面不能放东西
	TH0=0xFC;//不然编译会报错
	T0Count1++;
	if(T0Count1>=1)//20ms
	{
		T0Count1=0;
		Key_Loop();//每隔20毫秒调用一次
	}
	T0Count2++;
	if(T0Count2>=800)    //执行i加一功能i指向的是现在被点亮的灯
	{
		T0Count2=0;      //清零计数值
		i++;             //数组元素移动
		
	}
}

void Timer1_Routine() interrupt 3 //100us微秒
{
	static unsigned int Compare,Count,Flag;
	TL1 = 0xAF;		        //设置定时初值
	TH1 = 0xFF;	  	     	//设置定时初值
	Count++;                //计数值加加
	Count%=100;             //计数值清零100us*100=10ms执行一次
	Compare%=100;           //比较值清零处10ms*100=1s执行一次
	
	if(Compare>=99)            
	{                       
		Flag=!Flag;		 	//方向标志位
		Compare=0;
		Count=0;
	}
	
	if(Flag==0)		     	//逐渐点亮的过程
	{   
		if(Count<Compare)   //相当于在10ms中有Count/Compare这么多是时间是点亮的
		{
			P2=Light[i];	
		}
		else if(Count>Compare)//相当于在10ms中有(100-Count)/Compare这么多是时间是点亮的
		{   
            P2=0xff;
			if(Count>=99)
			{
				Compare++;	//Compare/100逐渐变亮小于就点亮
			}
		}
	}
	
	else if(Flag==1)     	//逐渐熄灭的过程
	{   
		if(Count<Compare)
		{
			P2=0xff;
		}
		else if(Count>Compare)
		{   
            P2=Light[i];
			if(Count>=99)
			{
				Compare++;
			}
		}		
	}
}
按键扫描部分
#include <REGX52.H>
#include "Delay.h"

unsigned char Key_KeyNumber;//应用这个Key函数是在主函数
						    //避免了每次都刷新Key_KeyNumber的值,要使用时才会刷新
unsigned char Key(void)     //返回Key Number的值
{
	unsigned char Temp=0;	//先清零按键值再返回按键的值
	Temp=Key_KeyNumber;		//中间变量为按键值
    Key_KeyNumber=0;		//否则会一直返回上一次的值
	return Temp;	        
}
unsigned char Key_GetState()
{
	unsigned char KeyNumber=0;//未按下就返回0,i就不会加
	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;
}
void Key_Loop(void)
{   
	//这里是不能直接Key_KeyNumber=0;
	//因为会一直扫描
	static unsigned char NowState , LastState;
    LastState = NowState;
    NowState = Key_GetState();//上一个状态位低电平就是按下的时候
	if(LastState && !NowState)//现状态为零就是高电平状态也就是已经松手的时候
	{
		//等于上一个状态才能让主函数知道是哪一个按键按下在执行操作,因为现状态是零
		Key_KeyNumber=LastState;//此处检测的是松手状态
	}
}
//若要检测按下的状态只需像这样改动
	if(!LastState && NowState)
	{
		Key_KeyNumber=NowState;
	}

写在最后

实现跳跃流水灯的关键代码都已给出剩下的就是将他们整理一下,同时对两个定时器进行初始化操作即可.

本人大一第一次写博客,如有不足欢迎指出讨论

posted @ 2023-12-09 10:59  Jiang-Tang  阅读(86)  评论(0)    收藏  举报  来源