【51单片机实现】多彩流水灯效果

目录

任务要求:

1、按键 1、2、3、4 按下,使 8 个 LED 实现下面对应的模式 1、 2、        3、4,上电默认每种模式流水灯的流转时间间隔为 500ms。

1)模式1:按照L1、L2……L8的顺序,从左到右循环点亮。

2)模式2:按照L8、L7……L1的顺序,从右刀座循环点亮。

3)模式3:从两边向中间点亮(  (L1,L8)->(L2,L7)->(L3,L6)->(L4,L5)  )

4)模式4:从中间向两边点亮(  (L4,L5)->(L3,L6)->(L2,L7)->(L1,L8)  )

2、按键5按下流水灯的流转时间间隔增加100ms,超过1200ms从400ms开始,用定时器控制时间

3、代码简洁,注释简单易懂。

实现思路:

按键部分

定时器部分

LED处理部分

函数部分

程序源码


任务要求:

1、按键 1、2、3、4 按下,使 8 个 LED 实现下面对应的模式 1、 2、        3、4,上电默认每种模式流水灯的流转时间间隔为 500ms。

1)模式1:按照L1、L2……L8的顺序,从左到右循环点亮。

2)模式2:按照L8、L7……L1的顺序,从右刀座循环点亮。

3)模式3:从两边向中间点亮(  (L1,L8)->(L2,L7)->(L3,L6)->(L4,L5)  )

4)模式4:从中间向两边点亮(  (L4,L5)->(L3,L6)->(L2,L7)->(L1,L8)  )

2、按键5按下流水灯的流转时间间隔增加100ms,超过1200ms从400ms开始,用定时器控制时间

3、代码简洁,注释简单易懂。

实现思路:

程序大体框架如下图:

 还没学过Visio画图,第一次尝试,轻喷~

按键部分

我们先把按键放在定时器里刷新,识别到几号按键按下,就对应LED灯按照第几个模式点亮,按键1按下就是模式1,按键2按下就是模式2……模式1234转换可以用一个全局变量实现,我代码中本变量名称为Light_Mode

 主函数要循环判断Light_Mode变量的数值,所以写一个无参数无返回值的LED处理的函数,先根据按键返回值,给Light_Mode变量赋相应的状态值,再根据这个状态值,实现四种不同模式的点灯。

定时器+中断

定时时间10ms,因为51单片机定时时间上限大约是70ms,按键消抖的部分大概是10ms,所以按键在定时器中以10ms时间扫描一次它的状态,每10ms扫描一次它的状态,就会滤除硬件抖动的部分,LED要求的500ms定时,可以在这个10ms基础上累加计数,就是每10ms计数变量自加1,计数变量==50的时候,就是500ms了

要求2 要改变LED闪烁时间间隔 ,那就再定义一个全局变量 unsigned int  SpaceT = 500;
名称SpaceT,初值500ms
按键5 按一下SpaceT变量自加100,超过1200,给SpaceT赋值400

定时器0初始化代码

void Timer0_Init(void)		//1毫秒@11.0592MHz
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x66;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0=1;
	EA=1;
	PT0=0;	
}

定时器0的中断

void Timer0_Routine() interrupt 1  //定时器0的中断函数
{
	static unsigned char Button; //按键扫描时间变量 10ms
	static unsigned int T0Count;

	TL0 = 0x66;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	Button++;
	T0Count++;

	if(T0Count >= SpaceT){
		i++;
		T0Count = 0;	//软件复位
	}

	if(Button >= 10){
		MatrixKey_Loop();
		Button = 0;
	}

}

LED处理

上图是普中科技的51开发板 原理图上说明P2端口对应了8个发光二极管,从P20~P27

如果想点亮LED1(图示D1),就写P2_0 = 0; 

或者对P2整个端口赋值,P2 = 0xFE; 十六进制的0xFE转化二进制后为1111 1110,只有第一个LED点亮。

如果想先点亮第二个LED,那就P2 = 0xFD;        0xFD代表1111 1101,只有第二个灯点亮

如果想实现流水灯的效果,那就给P2端口依次赋值如下八个数
1111 1110

1111 1101

1111 1011

1111 0111

1110 1111

1101 1111

1011 1111

0111 1111

不难发现,只有0向前移位,一次移动移位,由此可以想到位运算中的移位运算符

 参考书 C primer plus

书上指出,左移运算符,高位舍弃,低位补0(补0的那些位LED就会亮起),所以不符合我们的要求

由于高位舍弃,低位补0,如果我们找到和上面八个数相反的数,再给它取反,就得到了想要的效果

留点空余时间思考一下

……

我们可以对0x01 (0000 0001)移位,左移

左移1次 0000 0010 (高位舍弃,低位补0), 取反 1111 1101

左移2次 0000 0100                                       取反 1111 1011

……

变量 i 也要定义为全局变量,因为 i 要在中断函数里改变

for(i=0;i<8;)	//i变量在定时器里加加,控制闪烁时间间隔
{
	P2 = ~(0x01<<i);
}

模式2流水

for(i=0;i<8;)	//i变量在定时器里加加,控制闪烁时间间隔
{
	P2 = ~(0x80>>i);

}

模式3

for(i=0;i<4;)
{
	P2 = ~((0x01<<i) | (0x80>>i));//两边向中间 亮灯
}

模式4

for(i=0;i<4;)
{
	P2 = ~((0x10<<i) | (0x08>>i));
}

程序源码

本题要求简单,所以我就建了两个模块化的代码

Timer0.c

#include <REGX52.H>
#include "Timer0.h"


//定时器0初始化模板  1毫秒@11.0592MHz
void Timer0_Init(void)		//1毫秒@11.0592MHz
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x66;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0=1;
	EA=1;
	PT0=0;	
}

Timer0.h

#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0_Init(void);		//1毫秒@11.0592MHz

#endif

MatrixKeyT.c

 矩阵按键模块化的代码在之前的文章有发过,需要的童鞋们可以去翻阅一下

#include <REGX52.H>
#include "MatrixKeyT.h"

unsigned char Key_KeyNumber;


/**
  *@brief   名称:矩阵键盘扫描,获取按键键码
  *@param   参数:无  (放在主函数while里 赋值给一个 代表键码的变量)
  *@retval返回值:KeyNumber 0代表无按键按下,1~16代表键码
  */
unsigned char Key(void)
{
	unsigned char Temp = 0;
	Temp = Key_KeyNumber;	//赋值给暂存变量
	Key_KeyNumber = 0;		//清零
	return Temp;			//返回暂存变量
}


/**
*@brief   名称:矩阵键盘读取按键键码, 内部函数(不需要外部调用的)
  *@param   参数:无
  *@retval返回值:KeyNumber 0代表无按键按下,1~16代表键码
  */
unsigned char MatrixKey_GetState()
{
	unsigned char KeyNumber=0;
	
	P1=0xFF;
	 P1_3=0;
	if(P1_7==0 && P1_3==0){KeyNumber=1;}
	if(P1_6==0 && P1_3==0){KeyNumber=5;}
	if(P1_5==0 && P1_3==0){KeyNumber=9;}
	if(P1_4==0 && P1_3==0){KeyNumber=13;}
	
	P1=0xFF;
	P1_2=0;
	if(P1_7==0 && P1_2==0){KeyNumber=2;}
	if(P1_6==0 && P1_2==0){KeyNumber=6;}
	if(P1_5==0 && P1_2==0){KeyNumber=10;}
	if(P1_4==0 && P1_2==0){KeyNumber=14;}
	
	P1=0xFF;
	P1_1=0;
	if(P1_7==0 && P1_1==0){KeyNumber=3;}
	if(P1_6==0 && P1_1==0){KeyNumber=7;}
	if(P1_5==0 && P1_1==0){KeyNumber=11;}
	if(P1_4==0 && P1_1==0){KeyNumber=15;}
	
	P1=0xFF;
	P1_0=0;
	if(P1_7==0 && P1_0==0){KeyNumber=4;}
	if(P1_6==0 && P1_0==0){KeyNumber=8;}
	if(P1_5==0 && P1_0==0){KeyNumber=12;}
	if(P1_4==0 && P1_0==0){KeyNumber=16;}
	
	return KeyNumber;
}


void MatrixKey_Loop(void)
{
	static unsigned char NowState,LastState;
	LastState = NowState;				//按键状态更新
	NowState  = MatrixKey_GetState();		//获取当前按键状态
	if(LastState == 1 && NowState == 0) {Key_KeyNumber=1;}
	if(LastState == 2 && NowState == 0) {Key_KeyNumber=2;}
	if(LastState == 3 && NowState == 0) {Key_KeyNumber=3;}
	if(LastState == 4 && NowState == 0) {Key_KeyNumber=4;}
	if(LastState == 5 && NowState == 0) {Key_KeyNumber=5;}
	if(LastState == 6 && NowState == 0) {Key_KeyNumber=6;}
	if(LastState == 7 && NowState == 0) {Key_KeyNumber=7;}
	if(LastState == 8 && NowState == 0) {Key_KeyNumber=8;}
	if(LastState == 9 && NowState == 0) {Key_KeyNumber=9;}
	if(LastState ==10 && NowState == 0) {Key_KeyNumber=10;}
	if(LastState ==11 && NowState == 0) {Key_KeyNumber=11;}
	if(LastState ==12 && NowState == 0) {Key_KeyNumber=12;}
	if(LastState ==13 && NowState == 0) {Key_KeyNumber=13;}
	if(LastState ==14 && NowState == 0) {Key_KeyNumber=14;}
	if(LastState ==15 && NowState == 0) {Key_KeyNumber=15;}
	if(LastState ==16 && NowState == 0) {Key_KeyNumber=16;}
}

MatrixKeyT.h

#ifndef __MATRIXKEYT_H__
#define __MATRIXKEYT_H__

unsigned char Key(void);
void MatrixKey_Loop(void);
	
#endif

main.c

#include <REGX52.H>
#include "Timer0.h"
#include "MatrixKeyT.h"

unsigned char i;//循环变量
unsigned char KeyNumer;
unsigned int  SpaceT = 500;//初值400ms
unsigned char Light_Mode;

void LED_Func();

void main()
{	
	Timer0_Init();

	while(1)
	{
		KeyNumer = Key();
		LED_Func();
	
	}
}

void LED_Func()
{
	if	   (KeyNumer == 1) Light_Mode=1;
	else if(KeyNumer == 2) Light_Mode=2;
	else if(KeyNumer == 3) Light_Mode=3;
	else if(KeyNumer == 4) Light_Mode=4;
	else if(KeyNumer == 5)
	{
		SpaceT += 100;
	}
	if(SpaceT>1200)
		SpaceT = 400;
	
	if(Light_Mode == 1)
	{
		for(i=0;i<8;)	//i变量在定时器里加加,控制闪烁时间间隔
		{
			P2 = ~(0x01<<i);
//				if(Light_Mode != 1)	//for循环,按键键码数值改变,但是模式没及时改变,此处有个bug
//					break;	//要小写
		}
	}
	
	else if(Light_Mode == 2)
	{
		for(i=0;i<8;)	//i变量在定时器里加加,控制闪烁时间间隔
		{
			P2 = ~(0x80>>i);
//				if(Light_Mode != 2)
//					break;
		}
	}
	
	else if(Light_Mode == 3)
	{
		for(i=0;i<4;)
		{
			P2 = ~((0x01<<i) | (0x80>>i));//两边向中间 亮灯
		}
	}
	
	else if(Light_Mode == 4)
	{
		for(i=0;i<4;)
		{
			P2 = ~((0x10<<i) | (0x08>>i));
		}
	}
}

void Timer0_Routine() interrupt 1  //定时器0的中断函数
{
	static unsigned char Button; //按键扫描时间变量 10ms
	static unsigned int T0Count;

	TL0 = 0x66;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	Button++;
	T0Count++;

	if(T0Count >= SpaceT){
		i++;
		T0Count = 0;	//软件复位
	}

	if(Button >= 10){
		MatrixKey_Loop();
		Button = 0;
	}

}

LED流水实现部分有个BUG:因为我写的是for循环,虽然这时候按下按键,变量Light_Mode已经被重新赋值了,所以不能立刻跳出循环,执行下一个流水灯效果。之前有测试过跳出那个for循环,但是没有实现过,有兴趣的小伙伴可以尝试修改一下bug。

不过有一个解决方案,把每一个流水灯状态否存放在数组里,依次实现,这样就避免使用for循环~

补充一个模式1流水灯的方法

/*   void forward(void)函数说明
第一次给P1端口赋值 0xfe = 1111 1110
延时200ms
1111 1110<<1    1111 1100     最后一个跟着亮了,给最后一位清零,即灭,“或”0x01,变成1111 1101 ,下次赋值本数
第二次赋值后,延时200ms
1111 1101<<1    1111 1010    也要把最后一位清零,|0x01,变成1111 1011……	
*/

void forward(void)
{
	unsigned char TempOut = 0xfe,j;
	for (j=0;j<8;j++)
	{
		P1 = TempOut;
		Delay_ms(200);
		TempOut = (TempOut << 1) | 0x01;
		
	}
}

小生文采拙劣,多多包涵!

欢迎大家讨论!

物联沃分享整理
物联沃-IOTWORD物联网 » 【51单片机实现】多彩流水灯效果

发表评论