基于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;
}
写在最后
实现跳跃流水灯的关键代码都已给出剩下的就是将他们整理一下,同时对两个定时器进行初始化操作即可.
本人大一第一次写博客,如有不足欢迎指出讨论

浙公网安备 33010602011771号