蓝桥杯之单片机设计与开发(23)——2018_第九届_蓝桥杯_省赛——“彩灯控制器”

2019年3月12日更新

在连续多次单字节读取E22PROM时,不需要延时。


搞完所有模块,开始写真题。

1、题目解读

 

看到这个题,大致要知道用到了哪些模块。

LED、数码管、AD、E2PROM、独立按键。

蓝桥杯之单片机设计与开发(23)——2018_第九届_蓝桥杯_省赛——“彩灯控制器”_第1张图片

蓝桥杯之单片机设计与开发(23)——2018_第九届_蓝桥杯_省赛——“彩灯控制器”_第2张图片

蓝桥杯之单片机设计与开发(23)——2018_第九届_蓝桥杯_省赛——“彩灯控制器”_第3张图片

 蓝桥杯之单片机设计与开发(23)——2018_第九届_蓝桥杯_省赛——“彩灯控制器”_第4张图片

2、代码

在这里我直接把所有代码给贴上来吧,程序可能写的很麻烦,大家参考一下就行了

也可以在这下载https://download.csdn.net/download/xiaomo_haa/10991680

main.c

#include 
#include "sys.h"

u8 time_led[] = {0x11, 0x04, 0x04, 0x04, 0x04};				//LED流转间隔 *100
u8 Mode_led = 1;
u8 pwm = 4;

u8 Mode_Option = 0;			//设置模式位  0无/1流转方式/2流转间隔
u8 led_lighting = 0;		//LED亮度
bit flag_led = 0;				//LED工作标志
bit flag_800ms = 1;
bit display_pwm = 0;

void main(void)
{
	u8 flag_dat = 0;
	
	All_init();
	Timer0Init();
	Timer1Init();
	flag_dat = Read_E2PROM(0x00);			//读取E2PROM写入标志位
	if(flag_dat == 0x11)
	{
		time_led[0] = flag_dat;
		time_led[1] = Read_E2PROM(0x01);
		time_led[2] = Read_E2PROM(0x02);
		time_led[3] = Read_E2PROM(0x03);
		time_led[4] = Read_E2PROM(0x04);
	}
	LED_work(0xff);
	EA = 1;
	while(1)
	{
		Key_press();
	}
}

sys.c

#include "sys.h"

void All_init(void)
{
	P2 = (P2 & 0x1f) | 0x80;	//打开Y4C
	P0 = 0xff;		//关闭LED
	P2 = (P2 & 0x1f) | 0xc0;	//打开Y6C
	P0 = 0x00;		//关闭所有数码管
	P2 = (P2 & 0x1f) | 0xa0;	//打开Y5C
	P0 = 0x00;		//关闭蜂鸣器和继电器
	P2 = P2 & 0x1f;						//关闭所有573	
}

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

void Timer0(void) interrupt 1
{
	static u16 T0count1 = 0, T0count2 = 0;
	static u8 index = 0, e2_addr = 0x00;
	
	T0count2 ++;
	
	if(Mode_Option > 0)			//800ms数码管闪烁
	{
		T0count1 ++;
		if(T0count1 >= 400)
		{
			T0count1 = 0;
			flag_800ms = ~flag_800ms;
		}
	}
	
	if((T0count2 % 5 == 0) && (T0count2 < 30))	//每10ms时写一次E2PROM
	{
		switch(index)
		{
			case 0: Write_E2PROM(0x00, time_led[e2_addr]); break;
			case 1: Write_E2PROM(0x01, time_led[e2_addr]); break;
			case 2: Write_E2PROM(0x02, time_led[e2_addr]); break;
			case 3: Write_E2PROM(0x03, time_led[e2_addr]); break;
			case 4: Write_E2PROM(0x04, time_led[e2_addr]); break;
			default : break;
		}
		index ++;
		e2_addr ++;
		
		if(index >= 5)
		{
			index = 0;
			e2_addr  = 0;
		}
	}
	else if(T0count2 >= 30)		//60ms时读取一次AD值
	{
		T0count2 = 0;
		pwm = Read_AIN(0x03);
	}

	Key_Scan();
	Smg_show();
	Smg_Scan();
}

void Timer1Init(void)		//[email protected]
{
	AUXR |= 0x40;		//定时器时钟1T模式
	TMOD &= 0x0F;		//设置定时器模式
	TL1 = 0xAE;		//设置定时初值
	TH1 = 0xFB;		//设置定时初值
	TF1 = 0;		//清除TF1标志
	TR1 = 0;		//定时器1暂停计时
	ET1 = 1;
}

void Timer1(void) interrupt 3
{
	static u8 dat;
	static u16 T1count1 = 0;
	static u8 mode_backup = 0;
	static u8 index = 0, T1count2 = 0;
	static u8 mode_index = 1;
	u16 temp = 0;
	u8 hightime = 0;
	
	T1count1 ++;
	T1count2 ++;
	
	T1count2 &= 0x0f;			//最大计数到15
	
	temp = 1000 * time_led[mode_index];		//流转时间间隔
	hightime = pwm * pwm;								//高电平时间
	
	if(T1count1 >= temp)
	{
		T1count1 = 0;
		
		if(mode_index == 1)
		{
			if(mode_backup != mode_index)
			{
				dat = 0x7f;
				mode_backup = mode_index;
			}
			dat = _crol_(dat, 1);			//模式1
			if(dat == 0x7f)
				mode_index ++;
		}
		
		else if(mode_index == 2)
		{
			if(mode_backup != mode_index)
			{
				dat = 0xfe;
				mode_backup = mode_index;
			}
			dat = _cror_(dat, 1);			//模式2
			if(dat == 0xfe)
				mode_index ++;
		}
		
		else if(mode_index == 3)
		{
			if(mode_backup != mode_index)
			{
				index = 0;
				mode_backup = mode_index;
			}
			switch(index)
			{
				case 0: dat = 0x7e; break;		//0111 1110
				case 1: dat = 0xbd; break;		//1011 1101
				case 2: dat = 0xdb; break;
				case 3: dat = 0xe7; break;
				default : break;
			}
			index ++;
			index &= 0x03;
			if(dat == 0xe7)
				mode_index ++;
		}
		
		else if(mode_index == 4)
		{
			if(mode_backup != mode_index)
			{
				index = 0;
				mode_backup = mode_index;
			}
			switch(index)
			{
				case 0: dat = 0xe7; break;		//1110 0111
				case 1: dat = 0xdb; break;		//1011 1101
				case 2: dat = 0xbd; break;
				case 3: dat = 0x7e; break;
				default : break;
			}
			index ++;
			index &= 0x03;
			if(dat == 0x7e)
				mode_index = 1;
		}
	}
	
	if(T1count2 < hightime)			//PWM
		LED_work(dat);
	else
		LED_work(0xff);
}

sys.h

#ifndef _SYS_H_
#define _SYS_H_

//头文件包含
#include 
#include 
#include "iic.h"

typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;

//外部变量
extern u8 time_led[];				//LED流转间隔 *100
extern u8 Mode_led;
extern u8 pwm;
extern u8 Mode_Option;			//设置模式位  0无/1流转方式/2流转间隔
extern u8 led_lighting;			//LED亮度
extern bit flag_led;				//LED工作标志
extern bit flag_800ms;
extern bit display_pwm;

extern u8 KeySta[];			//键值存储区
extern u8 Keybackup[];

//管脚定义

//函数声明
void All_init(void);
void Timer0Init(void);
void Timer1Init(void);

void Smg_Scan(void);
void Smg_show();
void Smg_show();
void LED_work(u8 dat);

void Key_Scan(void);
void Key_drive(u8 key);
void Key_press(void);

#endif


key.c

#include "sys.h"

u8 KeySta[] = {1, 1, 1, 1};			//键值存储区
u8 Keybackup[] = {1, 1, 1, 1};	//键值备份区

sbit S4 = P3^3;
sbit S5 = P3^2;
sbit S6 = P3^1;
sbit S7 = P3^0;

//按键扫描函数,在定时器中断里调用
void Key_Scan(void)
{
	static u8 Keybuff[] = {0xff, 0xff, 0xff, 0xff};	//按键缓冲区
	u8 i = 0;
	
	Keybuff[0] = (Keybuff[0] << 1) | S7;
	Keybuff[1] = (Keybuff[1] << 1) | S6;
	Keybuff[2] = (Keybuff[2] << 1) | S5;
	Keybuff[3] = (Keybuff[3] << 1) | S4;
	
	for(i = 0; i < 4; i++)
	{
		if(Keybuff[i] == 0xff)				//按键松开
			KeySta[i] = 1;
		else if(Keybuff[i] == 0x00)		//按键按下
			KeySta[i] = 0;
		else				//键值不稳定
		{}
	}
}

void Key_drive(u8 key)
{	
	if(key == 0)
	{
		if(TR1 == 0)
			TR1 = 1;
		else
			TR1 = 0;
	}
	
	else if(key == 1)
	{
		Mode_Option ++;
		if(Mode_Option >= 3)
			Mode_Option = 0;
	}
	
	else if(key == 2)
	{
		if(Mode_Option == 1)
		{
			Mode_led ++;
			if(Mode_led >= 4)				//模式最大4
				Mode_led = 4;
		}
		else if(Mode_Option == 2)
		{
			time_led[Mode_led] ++;
			if(time_led[Mode_led] >= 12)		//最大1200ms
				time_led[Mode_led] = 12;
		}
	}
	
	else if(key == 3)
	{
		if(Mode_Option == 1)
		{
			Mode_led --;
			if(Mode_led <= 1)				//模式最小1
				Mode_led = 1;
		}
		
		else if(Mode_Option == 2)
		{
			time_led[Mode_led] --;
			if(time_led[Mode_led] <= 4)		//最小400ms
				time_led[Mode_led] = 4;
		}
	}
}

//检测按键是否按下,在main函数调用
void Key_press(void)
{
	u8 i;
	
	for(i = 0; i < 4; i ++)
	{
		if(KeySta[i] != Keybackup[i])
		{
			if(Keybackup[i] != 0)		//按键松开时操作
				Key_drive(i);
			Keybackup[i] = KeySta[i];
		}
	}
	
	if(Mode_Option == 0)
	{
		if(KeySta[3] == 0)
			display_pwm = 1;
		else
			display_pwm = 0;
	}
}


display.c

#include "sys.h"

u8 code Nixie[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8,
										0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e,
										0xff, 0xbf};		//16 17
u8 Nixiebuff[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
u8 smg1, smg2, smg3, smg4, smg5, smg6, smg7, smg8;

//数码管扫描
void Smg_Scan(void)
{
	static u8 index = 0;
	
	P2 = (P2 & 0x1f) | 0xc0;	//打开Y6C
	P0 = 0x01 << index;
	
	P2 = (P2 & 0x1f) | 0xe0;	//打开Y7C
	P0 = 0xff;
	P0 = Nixiebuff[index];
	
	index ++;
	index &= 0x07;
}

//更新数码管值
void Smg_show()
{
	if(Mode_Option == 0)		//不是设置模式不显示
	{
		if(display_pwm == 0)
			smg1 = smg2 = smg3 = smg4 = smg5 = smg6 = smg7 = smg8 = 16;
		else
		{
			smg1 = smg2 = smg3 = smg4 = smg5 = smg6 = 16;
			smg7 = 17;
			smg8 = pwm % 10;
		}
	}
	else if(Mode_Option == 1)
	{
		if(flag_800ms == 1)		//显示
		{
			smg1 = 17;
			smg2 = Mode_led % 10;
			smg3 = 17;
			smg4 = 16;
			smg5 = time_led[Mode_led] / 10;
			smg6 = time_led[Mode_led] % 10;
			smg7 = 0;
			smg8 = 0;
		}
		else				//不显示,实现闪烁
		{
			smg1 = 16;
			smg2 = 16;
			smg3 = 16;
			smg4 = 16;
			smg5 = time_led[Mode_led] / 10;
			smg6 = time_led[Mode_led] % 10;
			smg7 = 0;
			smg8 = 0;
		}
	}
	else if(Mode_Option == 2)
	{
		if(flag_800ms == 1)		//显示
		{
			smg1 = 17;
			smg2 = Mode_led % 10;
			smg3 = 17;
			smg4 = 16;
			smg5 = time_led[Mode_led] / 10;
			smg6 = time_led[Mode_led] % 10;
			smg7 = 0;
			smg8 = 0;
		}
		else				//不显示,实现闪烁
		{
			smg1 = 17;
			smg2 = Mode_led % 10;
			smg3 = 17;
			smg4 = 16;
			smg5 = 16;
			smg6 = 16;
			smg7 = 16;
			smg8 = 16;
		}
	}

	Nixiebuff[0] = Nixie[smg1];
	Nixiebuff[1] = Nixie[smg2];
	Nixiebuff[2] = Nixie[smg3];
	Nixiebuff[3] = Nixie[smg4];
	Nixiebuff[4] = Nixie[smg5];
	Nixiebuff[5] = Nixie[smg6];
	Nixiebuff[6] = Nixie[smg7];
	Nixiebuff[7] = Nixie[smg8];
}


//LED显示
void LED_work(u8 dat)
{
	P2 = (P2 & 0x1f) | 0x80;
	P0 = dat;
	P2 = P2 & 0x1f;
}


iic.c

#include "sys.h"

#define somenop {_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();}    


#define SlaveAddrW 0xA0
#define SlaveAddrR 0xA1

//总线引脚定义
sbit SDA = P2^1;  /* 数据线 */
sbit SCL = P2^0;  /* 时钟线 */


//总线启动条件
void IIC_Start(void)
{
	SDA = 1;
	SCL = 1;
	somenop;
	SDA = 0;
	somenop;
	SCL = 0;	
}

//总线停止条件
void IIC_Stop(void)
{
	SDA = 0;
	SCL = 1;
	somenop;
	SDA = 1;
}

//应答位控制
void IIC_Ack(bit ackbit)
{
	if(ackbit) 
	{	
		SDA = 0;
	}
	else 
	{
		SDA = 1;
	}
	somenop;
	SCL = 1;
	somenop;
	SCL = 0;
	SDA = 1; 
	somenop;
}

//等待应答
bit IIC_WaitAck(void)
{
	SDA = 1;
	somenop;
	SCL = 1;
	somenop;
	if(SDA)    
	{   
		SCL = 0;
		IIC_Stop();
		return 0;
	}
	else  
	{ 
		SCL = 0;
		return 1;
	}
}

//通过I2C总线发送数据
void IIC_SendByte(unsigned char byt)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{   
		if(byt&0x80) 
		{	
			SDA = 1;
		}
		else 
		{
			SDA = 0;
		}
		somenop;
		SCL = 1;
		byt <<= 1;
		somenop;
		SCL = 0;
	}
}

//从I2C总线上接收数据
unsigned char IIC_RecByte(void)
{
	unsigned char da;
	unsigned char i;
	
	for(i=0;i<8;i++)
	{   
		SCL = 1;
		somenop;
		da <<= 1;
		if(SDA) 
		da |= 0x01;
		SCL = 0;
		somenop;
	}
	return da;
}

/*******************************************************************************
* 函数名	:Read_AIN
* 输入值	:unsigned char chn
* 返回值	:unsigend char dat
* 作者		:小默haa
* 时间		:2019年2月25日
* 功能描述:读取PCF8591AIN采集数据
* 备注		:chn为PCF8591的通道
*******************************************************************************/
unsigned char Read_AIN(unsigned char chn)
{
	unsigned char dat, val, ad_pwm;
	EA = 0;
	IIC_Start();						//IIC总线起始信号							
	IIC_SendByte(0x90);			//PCF8591的写设备地址		
	IIC_WaitAck();  		    //等待从机应答		
	IIC_SendByte(chn); 			//写入PCF8591的控制字节		
	IIC_WaitAck();  				//等待从机应答						
	IIC_Stop(); 						//IIC总线停止信号					
	
	IIC_Start();						//IIC总线起始信号									
	IIC_SendByte(0x91); 	  //PCF8591的读设备地址		
	IIC_WaitAck(); 			    //等待从机应答		
	dat = IIC_RecByte();	  //读取PCF8591通道3的数据 			
	IIC_Ack(0); 						//产生非应答信号				
	IIC_Stop(); 						//IIC总线停止信号		
	val = (dat * 50) / 255;
	
	if((val >= 38) && (val <= 50))
		ad_pwm = 4;
	else if((val >= 26) && (val < 38))
		ad_pwm = 3;
	else if((val >= 14) && (val < 26))
		ad_pwm = 2;
	else if((val >= 0) && (val < 14))
		ad_pwm = 1;
	
	EA = 1;
	
	return ad_pwm;	
}


void Write_E2PROM(unsigned char add, unsigned char dat)
{
	EA = 0;
  IIC_Start();
  IIC_SendByte(0xa0);	//发送器件地址
  IIC_WaitAck();
  IIC_SendByte(add);	//发送操作地址
  IIC_WaitAck();
  IIC_SendByte(dat);	//写一字节
  IIC_WaitAck();
  IIC_Stop();
  somenop;
	EA = 1;
}

unsigned char Read_E2PROM(unsigned char add)
{
  unsigned char d;
	
	IIC_Start();
	IIC_SendByte(0xa0); 	//发送器件地址
	IIC_WaitAck();
	IIC_SendByte(add);		//发送要操作的地址
	IIC_WaitAck();
	IIC_Stop();
	
	IIC_Start();
	IIC_SendByte(0xa1);		//发送读操作
	IIC_WaitAck();
	d = IIC_RecByte();	//读一字节
	IIC_Ack(0);
	IIC_Stop();
	return d;
}

iic.h

#ifndef _IIC_H
#define _IIC_H

//函数声明
void IIC_Start(void); 
void IIC_Stop(void);  
void IIC_Ack(bit ackbit); 
void IIC_SendByte(unsigned char byt); 
bit IIC_WaitAck(void);  
unsigned char IIC_RecByte(void); 
unsigned char Read_AIN(unsigned char chn);
void Write_E2PROM(unsigned char add, unsigned char dat);
unsigned char Read_E2PROM(unsigned char add);

#endif

 

 

 

 

 

 

你可能感兴趣的:(蓝桥杯,第十届蓝桥杯)