Bootstrap

蓝桥杯单片机第十五届模拟题1解析-满分代码

第十五届4T模拟赛1可以说是4T模拟赛题目中的很难,很有含金量的一届了,本次模拟赛题中挖了很多坑。

满分测评

在这里插入图片描述
其中我总结了一下本赛题的重难点

1.数据输入

本赛题要求每次按键输入都在数码管最后一位,然后每次输入在让上一次输入的数据左移一位,直到数据输入完成,再按下按键不能继续输入数据;
在这里插入图片描述

1. 按键左移

按键左移其实不是在按键上做文章,而是在数码管显示来控制输入左移。SMG_Buff[7-i] = pasword[pasword_Index-i-1]让数码管在最后一位显示的数据对应数组的第一位数据然后按键继续输入指针也随着增加,数码管显示慢慢左移,对应的数据也同样,这样就实现了数据左移。

if(pasword_Index == 0)//当数据没有输入时将数码管熄灭
{
	for(n=3;n<8;n++)
		SMG_Buff[n] = 10;
}
for(i=0;i<pasword_Index;i++)			
	SMG_Buff[7-i] = pasword[pasword_Index-i-1];//数据左移

按下按键不能继续输入数据:在按键输入加个限制条件,当输入完成后就不满足键盘输入条件了也就不能继续输入了。

//防止按键输入完成后继续输入
if(Key_Down >= 1 && Key_Down <= 10 && pasword_Index < 4)
{
	pasword[pasword_Index] = Key_Down - 1;
	pasword_Index++;
}

2.记录界面

要求显示的是输入四位数据的起始时间(看好是起始时间不是输入完成的时间),也就是用户进入输入页面后第一次按下键盘时的时间:
在这里插入图片描述
解决办法:我用了个标志位,标志位初始值就是0(用标志位是为了防止下一次按键输入然后继续采集时间)。若数据还等于零也就是键盘还没有按下就不能进行采集,键盘第一次按下后并且标志位等于零的时候才进行记录时间,记录完时间后,立马将标志位置一。

//只有第一次数据按下后才能采集
if(pasword[0] != 10 && pasword_Flag == 0)
	{
		for(j=0;j<3;j++)
			ucRtc_MAX[j] = ucRtc[j];
		pasword_Flag = 1;
	}

3.EEPROM存储功能

EEPROM应该算是本赛题中难点了
在这里插入图片描述
在这里插入图片描述
题目中要求EEPROM每个地址分别存放数据,写入EEPROM的数据是有要求的,简单理解就是写入EEPROM的数据是十六进制的,要提前进行进制转换。
当然时钟底层如果写入的是十六进制的话存入EEPROM的数据就不用进行进制转换,但我使用的是十进制的所以需要进制转换一下。
解释一下pasword_input = pasword[0]*1000 + pasword[1]*100 + pasword[2]*10 + pasword[3]; 是将存放输入数据的数组转换成十进制;
EEPROM_Data[2] = pasword_input >> 8又因为EEPROM的数据是十六进制的所以这句话是将十进制转换成二进制,每四位二进制对应一位十六进制;
pasword_input & 0x00ff这句话的意思就是将高位清零低位取出来;

if(SMG_Disp_Mode == 2)//进入记录页面	保存EEPROM
{					
	pasword_input = pasword[0]*1000 + pasword[1]*100 + pasword[2]*10 + pasword[3];	
	EEPROM_Data[0] = ((ucRtc_MAX[0]/16*10) + (ucRtc_MAX[0]%16));
	EEPROM_Data[1] = ((ucRtc_MAX[1]/16*10) + (ucRtc_MAX[1]%16));
	EEPROM_Data[2] = pasword_input >> 8;//取出高位
	EEPROM_Data[3] = pasword_input & 0x00ff;//取出低位
	EEPROM_Write(EEPROM_Data,0,4);
}

4.关于记录指示灯

要在每次进入记录界面保存数据前进行数据判断,若当前输入的数据大于上次的数据LED_Flag置一。

	LED_Flag = (pasword_input > pasword_input_old);//LED4标志位
	pasword_input_old = pasword_input;//赋值
	
	ucLED[3] = LED_Flag;

5.还有个坑就是题目没要求返回输入界面可以继续输入数据,但是测评的时候会要求返回输入界面可以继续输入数据并且可以记录时间数据;
每次进入记录界面时将数据清除就即可

	memset(pasword,10,4);
	pasword_Index = 0;
	pasword_Flag = 0;

以上就是本赛题的重难点,对比以前每届模拟题来说相对较难。

完整代码

以下为本赛题完整代码,其他基本的底层就不放在这里了。以前的文章底层都是全的。
IIC.c

#include <STC15F2K60S2.H>
#include <intrins.H>
#include <IIC.H>

#define DELAY_TIME 5	//宏定义

sbit SDA = P2^1;
sbit SCL = P2^0;

void IIC_Delay(unsigned char i)
{
	do{_nop_();}
	while(i--);
}

void IIC_Start(void)//总线启动条件
{
	SDA=1;
	SCL=1;
	IIC_Delay(DELAY_TIME);
	SDA=0;
	IIC_Delay(DELAY_TIME);
	SCL=0;
}
void IIC_Stop(void)//总线停止条件
{
	SDA=0;
	SCL=1;
	IIC_Delay(DELAY_TIME);
	SDA=1;
	IIC_Delay(DELAY_TIME);
}
void IIC_SendAck(bit ackbit)//发送应答
{
	SCL=0;
	SDA = ackbit;
	IIC_Delay(DELAY_TIME);
	SCL=1;
	IIC_Delay(DELAY_TIME);
	SCL=0;
	SDA=1;
	IIC_Delay(DELAY_TIME);
}	
bit IIC_WaitAck(void)//等待应答
{
	bit ackbit;
	
	SCL=1;
	IIC_Delay(DELAY_TIME);
	ackbit=SDA;
	SCL=0;
	IIC_Delay(DELAY_TIME);
	
	return ackbit;
}
void IIC_SendByte(unsigned char Byt)//通过IIC总线发送数据
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		SCL = 0;
		IIC_Delay(DELAY_TIME);
		if(Byt&0x80)SDA=1;
		else SDA=0;
		IIC_Delay(DELAY_TIME);
		SCL=1;
		Byt<<=1;
		IIC_Delay(DELAY_TIME);
	}
	SCL=0;
}
unsigned char IIC_RecByte(void)//IIC总线上读取数据
{
	unsigned char i,da;
	for(i=0;i<8;i++)
	{
		SCL=1;
		IIC_Delay(DELAY_TIME);
		da<<=1;
		if(SDA)da|=1;
		SCL=0;
		IIC_Delay(DELAY_TIME);
	}
	return da;
}
/*自己写*/
unsigned char Ad_Read(unsigned char addr)//addr为入口参数		作用:填写地址
{
	unsigned char temp;
	IIC_Start();//启动单总线
	IIC_SendByte(0x90);//发送一个字节		准备开启写入
	IIC_WaitAck();//等待应答
	IIC_SendByte(addr);//发送一个字节
	IIC_WaitAck();//等待应答

	IIC_Start();//启动单总线
	IIC_SendByte(0x91);//发送一个字节		开启写入
	IIC_WaitAck();//等待应答
	temp=IIC_RecByte();
	IIC_SendAck(1);
	IIC_Stop();
	return temp;
}
void Da_Write(unsigned char dat)
{
	IIC_Start();//启动单总线
	IIC_SendByte(0x90);//发送一个字节		准备开启写入
	IIC_WaitAck();//等待应答
	IIC_SendByte(0x41);//发送一个字节		准备开启写入
	IIC_WaitAck();//等待应答
	IIC_SendByte(dat);//发送一个字节
	IIC_WaitAck();//等待应答
	IIC_Stop();
}
/*自己写*/
//函数名:EEPROM
//入口参数;

void EEPROM_Write(unsigned char* EEPROM_String,unsigned char addr,unsigned char num)
{
	IIC_Start();//开始信号
	IIC_SendByte(0xA0);//选择EEPROM芯片,确定写入模式			0xA0写入		0xA1读取
	IIC_WaitAck();//等待应答
	
	IIC_SendByte(addr);//写入要储存的数据地址
	IIC_WaitAck();//等待应答
	
	while(num--)
	{
		IIC_SendByte(*EEPROM_String++);//将写入的信息写入
		IIC_WaitAck();//等待应答
		IIC_Delay(200);
	}
	IIC_Stop();//停止信号
}
void EEPROM_Read(unsigned char* EEPROM_String,unsigned char addr,unsigned char num)
{
	IIC_Start();//开始信号
	IIC_SendByte(0xA0);//选择EEPROM芯片,确定写入模式			0xA0写入		0xA1读取
	IIC_WaitAck();//等待应答
	
	IIC_SendByte(addr);//写入要储存的数据地址
	IIC_WaitAck();//等待应答
	
	IIC_Start();//开始信号
	IIC_SendByte(0xA1);//选择EEPROM芯片,确定写入模式			0xA0写入		0xA1读取
	IIC_WaitAck();//等待应答
	
	while(num--)
	{
		*EEPROM_String++ = IIC_RecByte();
		if(num) IIC_SendAck(0);
		else IIC_SendAck(1);
	}
	IIC_Stop();//停止信号
}

DS1302.c

#include <STC15F2K60S2.H>
#include <intrins.H>
#include <DS1302.H>

sbit SCK = P1^7;
sbit SDA = P2^3;
sbit RST = P1^3;

void DS302_Write(unsigned char temp)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		SCK = 0;
		SDA = temp&0x01;
		temp>>=1;
		SCK = 1;
	}
}
void DS1302_Write_Byte(unsigned char position,unsigned char dat)//写入
{
	RST=0; _nop_();
	SCK=0; _nop_();
	RST=1; _nop_();
	DS302_Write(position);
	DS302_Write(dat);
	RST=0;
}
unsigned char DS1302_Read_Byte(unsigned char position)//读取
{
	unsigned char i,temp=0x00;
	RST=0; _nop_();
	SCK=0; _nop_();
	RST=1; _nop_();
	DS302_Write(position);
	
	for(i=0;i<8;i++)
	{
		SCK=0;
		temp>>=1;
		if(SDA)
			temp|=0x80;
		SCK=1;
	}
	RST=0; _nop_();
	SCK=0; _nop_();
	SCK=1; _nop_();
	SDA=0; _nop_();
	SDA=1; _nop_();
	
	return temp;
}



void Set_Rtc(unsigned char ucRtc[3])
{
	DS1302_Write_Byte(0x8e,0x00);//打开WP;WP拉低
	
	DS1302_Write_Byte(0x84,ucRtc[0]);//小时
	DS1302_Write_Byte(0x82,ucRtc[1]);
	DS1302_Write_Byte(0x80,ucRtc[2]);
	
	DS1302_Write_Byte(0x8e,0x80);
}
void Read_Rtc(unsigned char ucRtc[3])
{
	ucRtc[0]= DS1302_Read_Byte(0x85);//数组读取小时
	ucRtc[1]= DS1302_Read_Byte(0x83);//数组读取分钟
	ucRtc[2]= DS1302_Read_Byte(0x81);//数组读取秒钟
}

main.c

#include <STC15F2K60S2.H>
#include <LED.H>
#include <SMG.H>
#include <Init.H>
#include <Key.H>
#include <IIC.H>
#include <DS1302.H>
#include <string.H>

/*全局变量声明区*/
unsigned char Key_Slow_Down;//按键减速专用变量
unsigned int SMG_Slow_Down;//数码管减速专用变量
unsigned char Key_Val,Key_Down,Key_Up,Key_Old;
unsigned char SMG_Buff[]={10,10,10,10,10,10,10,10};//数码管显示数据储存数组
unsigned char SMG_point[]={0,0,0,0,0,0,0,0};//小数点储存数组
unsigned char ucLED[]={0,0,0,0,0,0,0,0};//LED显示数据存储数组
unsigned char SMG_Pos;//数码管扫描辅助变量
unsigned char LED_Pos;//LED扫描辅助变量

unsigned char ucRtc[]={0x23,0x09,0x59};
unsigned char ucRtc_MAX[3];//输入第一位起始时间
/*界面切换*/
unsigned char SMG_Disp_Mode;//0-时间界面	1-输入界面	2-记录界面

/*输入界面*/
unsigned char pasword[4] = {10,10,10,10};//输入数据
unsigned char pasword_Index;//输入数据指针
bit pasword_Flag = 0;

/*重点:EEPROM*/
unsigned int pasword_input;//输入数据整数
unsigned int pasword_input_old = 10000;//输入数据整数
unsigned char EEPROM_Data[4];
bit LED_Flag;
/*按键控制函数*/
void Key_Prco()
{
	if(Key_Slow_Down)return;
	Key_Slow_Down = 1;
	
	Key_Val = Key_Read();//按键获取
	Key_Down = Key_Val&(Key_Val^Key_Old);//检测下降沿
	Key_Up = ~Key_Val&(Key_Val^Key_Old);//检测下降沿
	Key_Old = Key_Val;//辅助扫描变量
	
	if(SMG_Disp_Mode == 1)
	{
		if(Key_Down >= 1 && Key_Down <= 10 && pasword_Index < 4)//需要左移
		{
			pasword[pasword_Index] = Key_Down - 1;
			pasword_Index++;
		}
	}
	switch(Key_Down)
	{
		case 11:
			if(++SMG_Disp_Mode == 3)
				SMG_Disp_Mode = 0;
			if(pasword_Index == 4)//数据输入完成
			{
				if(SMG_Disp_Mode == 2)//进入记录页面	保存EEPROM
				{					
					pasword_input = pasword[0]*1000 + pasword[1]*100 + pasword[2]*10 + pasword[3];	
					EEPROM_Data[0] = ((ucRtc_MAX[0]/16*10) + (ucRtc_MAX[0]%16));
					EEPROM_Data[1] = ((ucRtc_MAX[1]/16*10) + (ucRtc_MAX[1]%16));
					EEPROM_Data[2] = pasword_input >> 8;//取出高位
					EEPROM_Data[3] = pasword_input & 0x00ff;//取出低位
					EEPROM_Write(EEPROM_Data,0,4);
					
					LED_Flag = (pasword_input > pasword_input_old);//LED4标志位
					pasword_input_old = pasword_input;//赋值
					
					memset(pasword,10,4);
					pasword_Index = 0;
					pasword_Flag = 0;
				}
			}
		break;
		case 12://清除按键
			if(SMG_Disp_Mode == 1)
			{
				pasword_Flag = 0;
				pasword_Index = 0;
				memset(pasword,10,4);
				memset(ucRtc_MAX,0,3);
			}
		break;
	}
}
void SMG_Prco()
{
	unsigned char j;
	unsigned char i;
	unsigned char n;
	if(SMG_Slow_Down)return;
	SMG_Slow_Down = 1;
	/*信息读取区*/
	Read_Rtc(ucRtc);
	/*信息处理区*/
	if(pasword[0] != 10 && pasword_Flag == 0)//只有第一次数据按下后才能采集
	{
		for(j=0;j<3;j++)
			ucRtc_MAX[j] = ucRtc[j];
		pasword_Flag = 1;
	}
	
	switch(SMG_Disp_Mode)
	{
		case 0://0-时间界面	
			SMG_Buff[0]=ucRtc[0]/16;
			SMG_Buff[1]=ucRtc[0]%16;
		SMG_Buff[2]=11;           
			SMG_Buff[3]=ucRtc[1]/16;
			SMG_Buff[4]=ucRtc[1]%16;
		SMG_Buff[5]=11;           
			SMG_Buff[6]=ucRtc[2]/16;
			SMG_Buff[7]=ucRtc[2]%16;
		break;
		case 1://1-输入界面	
			SMG_Buff[0]=12;
			SMG_Buff[1]=SMG_Buff[2]=SMG_Buff[3]=10;
			
			if(pasword_Index == 0)//当数据没有输入时将数码管熄灭
			{
				for(n=3;n<8;n++)
					SMG_Buff[n] = 10;
			}
			for(i=0;i<pasword_Index;i++)			//数据左移
				SMG_Buff[7-i] = pasword[pasword_Index-i-1];
		
		break;
		case 2://2-记录界面
			SMG_Buff[0]=13;
			SMG_Buff[1]=SMG_Buff[2]=10;
			
			SMG_Buff[3]=ucRtc_MAX[0]/16;
			SMG_Buff[4]=ucRtc_MAX[0]%16;
			SMG_Buff[5]=11;
			SMG_Buff[6]=ucRtc_MAX[1]/16;
			SMG_Buff[7]=ucRtc_MAX[1]%16;
		break;
	}
}
/*其他显示函数*/
void LED_Prco()
{
	unsigned char i;
	for(i=0;i<3;i++)
		ucLED[i] = (i == SMG_Disp_Mode);
	ucLED[3] = LED_Flag;
	
	ucLED[4]=ucLED[5]=ucLED[6]=ucLED[7]=0;
}
/*中断函数*/
void Timer0Init(void)		//1毫秒@12.000MHz
{
	TMOD &= 0xF0;		//设置定时器模式
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	
	EA=1;
	ET0=1;
}

/*中断服务函数*/
void Timer0() interrupt 1
{
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	
	if(++Key_Slow_Down == 10)Key_Slow_Down = 0;
	if(++SMG_Slow_Down == 500)SMG_Slow_Down = 0;
	if(++SMG_Pos == 8)SMG_Pos = 0;
	if(++LED_Pos == 8)LED_Pos = 0;
	SMG_Disp(SMG_Pos,SMG_Buff[SMG_Pos],SMG_point[SMG_Pos]);
	LED_Disp(LED_Pos,ucLED[LED_Pos]);
	
}
/*主函数*/
void main()
{
	Timer0Init();
	Set_Rtc(ucRtc);
	while(1)
	{
		Key_Prco();
		SMG_Prco();
		LED_Prco();
	}
}
;