撼山岳—第九届之彩灯控制器

声明:代码确实写的不好,用的都是最笨的方法实现功能,不喜勿喷

第九届省赛题相对于其他几届难度还是有所增加的,第九届的题用到了AD模块、EEPROM模块、LED流水灯、独立按键以及数码管。题目中要求流水灯以四种方式来实现。

模式一:L1,L2,L3,............L8依次点亮

模式二:L8,L7,L6,............L1依次点亮

模式三:L1L8,L2L7,L3L6,L4L5依次点亮

模式四:L4L5,L3L6,L2L7,L1L8依次点亮
要求流水灯的时间间隔可调,时间范围为400-1200ms。

时间间隔的调控还是比较好解决的,直接用定时器控制流水灯是否开启的标志位就可以了。看一下具体代码:

	if(Mode1_flag == 1)
    {		
	    P2=0X80;
		P0 = ~(1< PWM)
	{
		P2 = 0X80;P0 = 0XFF;
		P2 = 0X00;
	}
	if(KeyS7_Open == 1)		//在S7按键按下的情况下
	{
		Mode1_num++;
		if(Mode1_num == Timer*10)//实现按照设定的时间间隔流水
		{
			Mode1_num = 0;
			Mode1_flag = 1;
		}		
	}

但是针对流水灯的四种模式的后两种我还是想来很久的,主要是陷入了移位的思维模式中没有跳出来,总是想用移位来实现,最后还是退而求其次用来数组的方法实现的。下面看一下模式3和模式4 的代码:
 

uchar tab1[4] = {0X7E,0XBD,0XDB,0XE7};	//LED从两端向中间流水
uchar tab2[4] = {0XE7,0XDB,0XBD,0X7E};	//LED从中间向两端流水
if(Mode1_flag == 1)
{				
	P2=0X80;
	P0 = tab1[i];		//两端向中间
	i +=1;
	if(i == 4)
	    i = 0;
	Mode1_flag = 0;			
}	
if(Mode1_flag == 1)
{										
	P2 = 0X80;
	P0 = tab2[i];		//中间向两端
	i +=1;
	if(i == 4)
	    i = 0;
	Mode1_flag = 0;			
}	

上面我说了了这么多还没有把题目粘出来让大家看一下,下面看一下题目的要求:

撼山岳—第九届之彩灯控制器_第1张图片

撼山岳—第九届之彩灯控制器_第2张图片

撼山岳—第九届之彩灯控制器_第3张图片

撼山岳—第九届之彩灯控制器_第4张图片

撼山岳—第九届之彩灯控制器_第5张图片

撼山岳—第九届之彩灯控制器_第6张图片

按键和数码管难度一直是一个样子,我们直接看这两个方面的代码:
 

#include "shuma.h"
u8 shu_tab[]={0XC0,0XF9,0XA4,0XB0,0X99,0X92,0X82,0XF8,0X80,0X90,0XBF,0XC6,0XFF};//0XC6是英文C
uchar yi,er,san,si,wu,liu,qi,ba;

//第一和第二个数码管
void Display1_2(uchar yi,uchar er)
{
		P2=0XC0;//打开位选573   U8
		P0=0X01;//选择第一个数码管
		P2=0XFF;//打开段选573   U7
		P0=shu_tab[yi];
		Delay1ms();Delay1ms();
		
		P2=0XC0;//打开位选573   U8
		P0=0X02;//选择第二个数码管
		P2=0XFF;//打开段选573   U7
		P0=shu_tab[er];
		Delay1ms();Delay1ms();
}	
//第四和第五个数码管
void Display4_5(uchar si,uchar wu)
{
		P2=0XC0;//打开位选573   U8
		P0=0X08;//选择第一个数码管
		P2=0XFF;//打开段选573   U7
		P0=shu_tab[si];
		Delay1ms();Delay1ms();
		
		P2=0XC0;//打开位选573   U8
		P0=0X10;//选择第一个数码管
		P2=0XFF;//打开段选573   U7
		P0=shu_tab[wu];
		Delay1ms();Delay1ms();
}
//第三和第六个数码管
void Display3_6(uchar san,uchar liu)
{
		P2=0XC0;//打开位选573   U8
		P0=0X04;//选择第三个数码管
		P2=0XFF;//打开段选573   U7
		P0=shu_tab[san];
		Delay1ms();Delay1ms();
		
		P2=0XC0;//打开位选573   U8
		P0=0X20;//选择第四个数码管
		P2=0XFF;//打开段选573   U7
		P0=shu_tab[liu];
		Delay1ms();Delay1ms();
}

//第七和第八个数码管
void Display7_8(uchar qi,uchar ba)
{
		P2=0XC0;//打开位选573   U8
		P0=0X40;//选择第一个数码管
		P2=0XFF;//打开段选573   U7
		P0=shu_tab[qi];
		Delay1ms();Delay1ms();
		
		P2=0XC0;//打开位选573   U8
		P0=0X80;//选择第一个数码管
		P2=0XFF;//打开段选573   U7
		P0=shu_tab[ba];
		Delay1ms();Delay1ms();
		P2=0XFF;//打开段选573   U7
		P0=0XFF;//关闭所有数码管段选
}


void Delay1ms()		//@11.0592MHz
{
	unsigned char i, j;

	_nop_();
	_nop_();
	_nop_();
	i = 11;
	j = 190;
	do
	{
		while (--j);
	} while (--i);
}

//第三和第四个数码管
void Display3_4(uchar san,uchar si)
{
		P2=0XC0;//打开位选573   U8
		P0=0X04;//选择第三个数码管
		P2=0XFF;//打开段选573   U7
		P0=shu_tab[san];
		Delay1ms();Delay1ms();
		
		P2=0XC0;//打开位选573   U8
		P0=0X08;//选择第四个数码管
		P2=0XFF;//打开段选573   U7
		P0=shu_tab[si];
		Delay1ms();Delay1ms();
}
//第五和第六个数码管
void Display5_6(uchar wu,uchar liu)
{
		P2=0XC0;//打开位选573   U8
		P0=0X10;//选择第一个数码管
		P2=0XFF;//打开段选573   U7
		P0=shu_tab[wu];
		Delay1ms();Delay1ms();
		
		P2=0XC0;//打开位选573   U8
		P0=0X20;//选择第一个数码管
		P2=0XFF;//打开段选573   U7
		P0=shu_tab[liu];
		Delay1ms();Delay1ms();
}
#include "key.h"
#include "shuma.h"
#include "eeprom.h"
uchar KeyS7_Open;				//实现按键S7的打开流水灯的功能
uchar KeyS6_Set;				//按下S6进入设置界面
uchar KeyS5_Add;				//在设置情况下,实现数值加一
uchar KeyS4_Dec;				//在设置情况下,实现数值减一
uchar LED_grade_flag;		//LED亮度等级显示的标志位
extern uchar LED_grade;
extern uchar Mode;	
extern uint Timer;	
extern uchar yi,er,san,si,wu,liu,qi,ba;
//独立按键扫描函数
void D_keyscan()
{
	if(P30==0)
	{
		Delay5ms();
		if(P30==0)					//按键S7
		{
			KeyS7_Open += 1;
			if(KeyS7_Open == 2)
				KeyS7_Open = 0;
		}
		while(!P30);
	}
	else if(P31==0)				//按键S6
	{
		Delay5ms();
		if(P31==0)
		{
			KeyS6_Set += 1;
			if(KeyS6_Set == 3)
				KeyS6_Set = 0;
			IICEE_write(0x00,Mode);					//将要锁存的数据写入eeprom
			Delay5ms();
			IICEE_write(0x01,Timer/100);		//将设置的时间间隔写入EEPORM
			Delay5ms();
		}
		while(!P31);
	}
	else if(P32==0)				//按键S5
	{
		Delay5ms();
		if(P32==0)
		{
			KeyS5_Add = 1;
		}
		while(!P32);
	}
	else if(P33==0)				//按键S4
	{
		Delay5ms();
		if(P33==0)
		{
			KeyS4_Dec = 1;	
			if(KeyS7_Open == 1)
			{
				LED_grade_flag = 1;
			}
				
		}		
		while(!P33)
		{
				if(LED_grade_flag == 1)		//实现按下S4显示亮度等级的标志位,松开不显示
				{
					yi = 12;er = 12;san = 12;si = 12;
					wu = 12; liu = 12;qi = 10;ba = LED_grade;
					Display1_2(yi,er);
					Display3_4(san,si);
					Display5_6(wu,liu);
					Display7_8(qi,ba);					
				}			
		}
		LED_grade_flag = 0;
	}
}

void Delay5ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 54;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}

AD这个模块的使用还是很简单的,题目要求把流水灯的亮度分为四个等级,我们用AD读取滑动变阻器的值,然后区间分割就可以了。

下面看一下代码:

#include "ad.h"
#include "eeprom.h"
//AD开始函数
void IICAD_statrt(void)
{	
	SDA = 1;
	_nop_();
	SCL = 1;
	_nop_();
	SDA = 0;
	_nop_();
	SCL = 0;
}
//AD停止函数
void IICAD_stop(void)
{
	SDA = 0;
	_nop_();
	SCL = 1;
	_nop_();
	SDA = 1;

}
//向AD写一个字节
void AD_writebyte(uchar dat)
{
	uchar i;
	for(i=0;i<8;i++)
	{
		SCL = 0;
		_nop_();
		SDA = dat&0x80;
		_nop_();
		SCL = 1;
		dat<<=1;
	}
	SCL = 0;
	_nop_();
}	

//应答函数
uchar ACK_AD(void)
{
	SCL = 1;
	IICAD_delay(5);
	if(SDA == 1)
	{
		SCL = 0;
		Delay100us();
		IICAD_stop();
		return 0;
	}
	else
	{
		SCL = 0;
		Delay100us();
		return 1;
	}
}
//从AD中读取数据
uchar IICAD_read(uchar add)
{
	uchar temp;
	IICAD_statrt();
	AD_writebyte(0x90);
	ACK_AD();
	AD_writebyte(add);
	ACK_AD();
	
	IICAD_statrt();
	AD_writebyte(0x91);
	ACK_AD();
	temp = IICAD_readbyte();
	IICAD_stop();
	//现在显示的是0-255,如果需要显示0-100可以加代码(temp = 0.39*temp;)
	return temp;
}
//从AD中读取一个字节
uchar IICAD_readbyte()
{
	uchar dat;
	uchar i;
	SCL = 0;
	_nop_();
	SCL = 1;
	_nop_();
	for(i=0;i<8;i++)
	{
		SCL = 1;
		dat<<=1;
		if(SDA)
		{
			dat |= 0x01;
		}
		SCL = 0;
	}
	return dat;
}

void IICAD_delay(uchar a)
{
	do
	{
		_nop_();
	}
	while(a--);
}
void Delay100us()		//@11.0592MHz
{
	unsigned char i, j;

	_nop_();
	_nop_();
	i = 2;
	j = 15;
	do
	{
		while (--j);
	} while (--i);
}

题目要求我们在重新上电之后,导入上次的模式和定时时间间隔,EEPROM就是实现的这个功能(建议大家在写入EEPROM之后,读取出来用数码管显示一下结果是否正确,然后再对读取出的数据进行处理)。下面看一下程序:

#include "eeprom.h"
//IIC开始函数
void IICEE_statrt(void)
{	
	SDA = 1;
	_nop_();
	SCL = 1;
	_nop_();
	SDA = 0;
	_nop_();
	SCL = 0;
}
//IIC停止函数
void IICEE_stop(void)
{
	SDA = 0;
	_nop_();
	SCL = 1;
	_nop_();
	SDA = 1;

}
//将地址和数据写进IIC
void IICEE_write(uchar add ,uchar dat)
{
	IICEE_statrt();
	EE_writebyte(0xa0);
	ACK_EE();
	EE_writebyte(add);
	ACK_EE();
	EE_writebyte(dat);
	ACK_EE();	
	IICEE_stop();
	Delay10ms();Delay10ms();//一定要加
}
//向IIC写一个字节
void EE_writebyte(uchar dat)
{
	uchar i;
	for(i=0;i<8;i++)
	{
		SCL = 0;
		_nop_();
		SDA = dat&0x80;
		_nop_();
		SCL = 1;
		dat<<=1;
	}
	SCL = 0;
	_nop_();
}	
void IICEE_delay(uchar a)
{
	do
	{
		_nop_();
	}
	while(a--);
}
//应答函数
uchar ACK_EE(void)
{
	SCL = 1;
	IICEE_delay(5);
	if(SDA == 1)
	{
		SCL = 0;
		IICEE_stop();
		return 0;
	}
	else
	{
		SCL = 0;
		return 1;
	}
}
//从IIC中读取数据
uchar IICEE_read(uchar add)
{
	uchar temp;
	IICEE_statrt();
	EE_writebyte(0xa0);
	ACK_EE();
	EE_writebyte(add);
	ACK_EE();
	
	IICEE_statrt();
	EE_writebyte(0xa1);
	ACK_EE();
	temp = IICEE_readbyte();
	IICEE_stop();
	Delay10ms();
	return temp;
}
//从IIC中读取一个字节
uchar IICEE_readbyte()
{
	uchar dat;
	uchar i;
	SCL = 0;
	_nop_();
	SCL = 1;
	_nop_();
	for(i=0;i<8;i++)
	{
		SCL = 1;
		dat<<=1;
		if(SDA)
		{
			dat |= 0x01;
		}
		SCL = 0;
	}
	return dat;
}

void Delay10ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 108;
	j = 145;
	do
	{
		while (--j);
	} while (--i);
}

最主要的功能实现函数,我都写在主函数中,下面看一下主函数代码:

#include "system.h"
#include "shuma.h"
#include "key.h"
#include "ad.h"
#include "eeprom.h"
void Timer0Init(void);
void Delay200ms();
extern uchar yi,er,san,si,wu,liu,qi,ba;
extern uchar KeyS7_Open;
extern uchar KeyS6_Set;
extern uchar KeyS5_Add;
extern uchar KeyS4_Dec;
extern uchar LED_grade_flag;
uchar tab1[4] = {0X7E,0XBD,0XDB,0XE7};	//LED从两端向中间流水
uchar tab2[4] = {0XE7,0XDB,0XBD,0X7E};	//LED从中间向两端流水

uchar AD_num;														//存储AD的值
uchar PWM;															//LED亮度等级控制位
uchar LED_grade;												//存储LED亮度等级的变量
uchar Mode=1;														//存储LED流水模式
uchar Timer_L=4;												//存储LED流水时间的低位间隔
uchar Timer_H=12;												//存储LED流水时间的高位间隔
uint Timer=400;														//存储LED流水总时间间隔
uchar Shuma_flag;												//数码管以0.8秒显示的标志位
uchar Mode1_flag;												//LED灯流水方式的标志位
uchar Init1_flag;												//防止计数出乱的的清零标志位
uchar Init2_flag;												//防止计数出乱的的清零标志位
uchar Init3_flag;												//防止计数出乱的的清零标志位
uchar i;																//实现流水灯
void main(void)
{

	Init();
	Timer0Init();
	Mode = IICEE_read(0x00);					//读取EEPROM中的模式值
	Delay5ms();
	Timer = IICEE_read(0x01)*100;			//读取EEPROM中的时间值
	Delay5ms();
	while(1)
	{
		D_keyscan();									//调用按键扫描函数
		AD_num = IICAD_read(0x03);		//调用AD函数
		/***************划分亮度等级*****************/
		if(AD_num < 80)		
		{
			PWM = 50;
			LED_grade = 1;
		}	
		else if((AD_num >= 80) && (AD_num < 150))
		{
			PWM = 80;
			LED_grade = 2;
		}		
		else if((AD_num >= 150) && (AD_num < 210))
		{
			PWM = 95;
			LED_grade = 3;
		}
		else if(AD_num >= 210)
		{
			PWM = 100;
			LED_grade = 4;		
		}	
		/****************按下按键S7实现流水灯的显示************************/
		if(KeyS7_Open == 1)//防止计数出乱的的清零标志位
		{
			if(Init3_flag == 0)
			{
				i = 0;
				Mode = Mode + 1;
				KeyS4_Dec = 0;
				KeyS5_Add = 0;
				Init3_flag = 1;				
			}
			if(Mode == 1)							//模式一
			{
				if(Mode1_flag == 1)
				{		
					P2=0X80;
					P0 = ~(1<>i);		//从右到左流水
					i +=1;
					if(i == 8)
						i = 0;
					Mode1_flag = 0;			
				}	
			}
			else if(Mode == 3)					//模式三
			{
				if(Mode1_flag == 1)
				{				
					P2=0X80;
					P0 = tab1[i];		//两端向中间
					i +=1;
					if(i == 4)
						i = 0;
					Mode1_flag = 0;			
				}	
			}
			else if(Mode == 4)					//模式四
			{
				if(Mode1_flag == 1)
				{
										
					P2 = 0X80;
					P0 = tab2[i];		//中间向两端
					i +=1;
					if(i == 4)
						i = 0;
					Mode1_flag = 0;			
				}	
			}			
		}
		else if(KeyS6_Set == 1)/*****************第一次按下S6按键实现模式的改变*********************/
		{
			if(Init1_flag == 0)
			{
				KeyS4_Dec = 0;
				KeyS5_Add = 0;
				Init1_flag = 1;
			}
			yi = 10;er = Mode;san = 10;si = 12;
			wu = Timer_H; liu = Timer_L;qi = 0;ba = 0; 
			if(Shuma_flag == 0)						//实现被选中的数码管以0.8秒的间隔闪烁
			{
				P2=0XC0;P0=0X07;
				P2=0XFF;P0=0XFF;		
			}	
			else
			{
				Display1_2(yi,er);
				Display3_4(san,si);	
			}
			if(KeyS5_Add == 1)						//按下S5按键实现数值的加一
			{
				KeyS4_Dec = 0;	
				Mode = Mode + 1;
				KeyS5_Add = 0;
				if(Mode == 5)								//实现四种模式的循环
					Mode = 1;
			}
			else if(KeyS4_Dec == 1)				//按下S4按键实现数值的减一
			{
				if(Mode >= 2)								//实现四种模式的循环
				{
					Mode = Mode - 1;
					KeyS4_Dec = 0;					
				}
				else
				{
					Mode = 1;
				}					
			}
			Display5_6(wu,liu);
			Display7_8(qi,ba);	
		}
		else if(KeyS6_Set == 2)/*****************第二次按下S6按键流水时间间隔数值的改变*********************/							
		{
			if(Init2_flag == 0)
			{
				KeyS4_Dec = 0;
				KeyS5_Add = 0;
				Init2_flag = 1;				
			}
			yi = 10;er = Mode;san = 10;si = 12;
			wu = Timer_H; liu = Timer_L;qi = 0;ba = 0; 
			if(Shuma_flag == 0)						//实现被选中的数码管以0.8秒的间隔闪烁
			{
				P2=0XC0;P0=0XE0;
				P2=0XFF;P0=0XFF;
				
			}	
			else
			{
				Display5_6(wu,liu);
				Display7_8(qi,ba);		
			}	
			if(KeyS5_Add == 1)							//按下S5按键实现数值的加一
			{
				
				KeyS5_Add = 0;
				if((Timer_L == 2) && (Timer_H == 1))//控制时间间隔的数值在要求的范围内
				{
					Timer_L = 2;
					Timer_H = 1;
					KeyS5_Add = 0;
				}
				else 
				{
					Timer_L = Timer_L + 1;
					if(Timer_L == 10)
					{
						Timer_L = 0;
						Timer_H = 1;
					}
				}
			}
			else if(KeyS4_Dec == 1)				//按下S4按键实现数值的减一
			{					
				KeyS4_Dec = 0;
				if(Timer_H == 1)						//控制时间间隔的数值在要求的范围内
				{
					if(Timer_L == 0)
					{
						Timer_L = Timer_L - 1;
						Timer_H = 12;
						Timer_L = 9;
					}
					else 
					{
						Timer_L = Timer_L - 1;
					}
				}
				else if(Timer_L >= 5)
				{
					Timer_L = Timer_L - 1;	
				}

			}
			if(Timer_H != 12)
				Timer = Timer_H*1000 + Timer_L*100;				//将设定的时间间隔保存
			else
				Timer = Timer_L*100;
			Display1_2(yi,er);
			Display3_4(san,si);					
		}
	}
}

void Timer0Init(void)		//100微秒@11.0592MHz
{
	AUXR |= 0x80;		//定时器时钟1T模式
	TMOD &= 0xF0;		//设置定时器模式
	TL0 = 0xAE;		//设置定时初值
	TH0 = 0xFB;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0 = 1;
	EA = 1;
}

void Timer0(void) interrupt 1
{
	static uchar num=0;
	uint Mode1_num;
	uint Shuma_Timer;
	
	TL0 = 0xAE;		//设置定时初值
	TH0 = 0xFB;		//设置定时初值
	num++;
	if(num == 100)
		num = 0;
	if(num > PWM)
	{
		P2 = 0X80;P0 = 0XFF;
		P2 = 0X00;
	}
	if(KeyS7_Open == 1)		//在S7按键按下的情况下
	{
		Mode1_num++;
		if(Mode1_num == Timer*10)//实现按照设定的时间间隔流水
		{
			Mode1_num = 0;
			Mode1_flag = 1;
		}		
	}
	if(KeyS6_Set != 0)		//如果S6按键按下,开始计时实现被选中的数码管以0.8秒的时间间隔闪烁
	{
		Shuma_Timer++;
		if(Shuma_Timer == 8000)
		{	
			Shuma_flag = 1;
		}
		else if(Shuma_Timer == 16000)
		{
			Shuma_Timer = 0;
			Shuma_flag = 0;
		}		
	}		
}	

最后还是把我写的完整的工程上传一下,以供大家参考https://download.csdn.net/my

你可能感兴趣的:(蓝桥杯)