蓝桥杯单片机组第十五届模拟题一

1.题目

一、基本要求

使用大赛组委会统一提供的四梯/国信长天单片机竞赛实训平台,完成本试题的程序设计与调试。程序编写、调试完成后,选手需通过考试系统提交以准考证号命名的hex文件。不符合以上文件提交要求的作品将被评为零分或者被酌情扣分。

硬件设置:

将IAP15F2K61S2单片机内部振荡器频率设定为12MHz。

键盘工作模式跳线J5配置为KBD矩阵按键模式。

扩展方式跳线J13配置为IO模式。

请注意:选手需严格按照以上要求配置竞赛板,编写和调试程序,不符合以上配置要求的作品将被评为零分或者被酌情扣分。

二、硬件框图

图1 系统硬件框图

三、功能描述

3.1 基本功能描述

1)通过读取DS1302 RTC芯片,获取时间数据;

2)通过EEPROM实现数据记录功能;

3)通过LED指示灯完成试题要求的状态指示功能;

4)通过数码管、按键完成试题要求的数据显示和界面切换功能。

3.2 显示功能

1)时间界面

时间界面如图2所示,显示内容包括时、分、秒数据和间隔符“-” 时、分、秒数据固定占 2 位显示宽度,不足 2 位补 0。

图2 时间界面

2)输入界面

输入界面如图3所示,由标识符(C)和一个4位数据组成,4位数据通过4位数码管显示,每输入一位数据,数码管显示向左移动一位,直到完成4位数据的输入。

图3 输入界面

每次重新进入输入界面时,默认显示4位数据的4位数码管处于熄灭状态。

3)记录界面

记录界面如图4所示,由标识符(E)和输入4位数据的起始时和分数据以及时、分数据的间隔符(-)。

图4 记录界面

4)显示要求

按照题目要求的界面格式和切换方式进行设计。

数码管显示无重影、闪烁、过暗、亮度不均匀等严重影响显示效果的缺陷。

3.3 按键功能

1)功能说明

① S4:定义为“切换”,按下S4按键,切换“时间界面”、“输入界面”和“记录界面”。

图5 界面切换顺序

② S5:定义为“清除”,在输入界面下,按下S5,清除当前输入的数据,显示输入数据的4位数码管全部熄灭。

③ S6、S10、S14、S18、S9、S13、S17、S8、S12、S16分别对应数值“0-9”,在“输入界面”下,按下对应按键,实现一位对应数据的输入,显示格式及要求如图3所。若当前4位数据输入完成,按键继续输入不影响当前已输入完成的4位数据。

2)按键要求

按键应做好消抖处理,避免出现一次按键动作导致功能多次触发。

按键动作不影响数码管显示等其他功能。

按键 S5、S6、S10、S14、S18、S9、S13、S17、S8、S12、S16仅在“输入界面”有效。

3.4 记录功能

每次进入记录界面时,将4位数据和输入数据的起始时间保存到E2PROM,存储位置要求如下:

输入数据起始时间(时):E2PROM内部地址0;

输入数据起始时间(分):E2PROM内部地址1;

输入数据(高字节):E2PROM内部地址2;

输入数据(低字节):E2PROM内部地址3;

注意:

E2PROM写入操作发生在切换到记录界面时,其它时间不写入。

输入数据以HEX编码写入E2PROM,例如数据数据为1234,E2PROM内部地址2中应保存04H, 内部地址3中保存D2H。

3.5 LED指示灯功能

1、界面指示灯

1)时间界面下,指示灯 L1 点亮,否则指示灯 L1 熄灭。

2)输入界面下,指示灯 L2 点亮,否则指示灯 L2 熄灭。

3)记录界面下,指示灯 L3 点亮,否则指示灯 L3 熄灭。

2、记录指示灯

若本次记录的4位数据较上一次记录的4位数据大,则指示灯L4点亮,否则指示灯熄灭。

3.6 初始状态

请严格按照以下要求设计作品的上电初始状态。

1)处于时间界面

2)指示灯L1点亮,其余指示灯熄灭。


2.代码

2.1 main.c

main.c
/**************头文件声明区*******************/
#include <STC15F2K60S2.H>
#include <ds1302.h>
#include <iic.h>
#include <Init.h>
#include <Key.h>
#include <Seg.h>
#include <Led.h>

/***************变量定义区*********************/
/****键盘专用******/
unsigned char Key_Slow_Down;//10
unsigned char Key_Val,Key_Down,Key_Up,Key_Old;

/****数码管专用****/
unsigned int Seg_Slow_Down;//500
unsigned char Seg_Pos;
unsigned char Seg_Buf[8]={10,10,10,10,10,10,10,10};
unsigned char Seg_Point[8]={0,0,0,0,0,0,0,0};

/****Led专用******/
unsigned char ucLed[8]={0,0,0,0,0,0,0,0};

/****界面专用*****/
unsigned char Seg_Disp_Mode;//界面标志位:0-时间界面;1-输入界面;2-记录界面
//时间界面
unsigned char ucRtc[3]={0x24,0x09,0x59};
//输入界面
bit Input_Flag;//判断是否输入数据 1-输入数据 0-没有输入数据
unsigned char Input[4]={10,10,10,10};//输入数组,默认为10(熄灭状态)
unsigned char Input_Index;//输出数组标志位 0-3
unsigned int Value_Old;//记录上一次数码管输入的值
unsigned int Value;//输入的值

//记录界面
unsigned char ucRtc_Data[3]={0x00,0x00,0x00};
unsigned char Hour,Minute;//记录时-分
unsigned char high,low;//记录Value

/****************函数处理区********************/

//清除数码管数据并将数码管(4-7)熄灭
void Clear_Data()
{
	unsigned char i;
	for(i=4;i<8;i++)
	{
		Seg_Buf[i]=10;
		Seg_Point[i]=0;
		Input[i-4]=10;
	}
	Input_Index=0;//恢复Input_Index的值
}



//记录按下的时间
void Remeber_Time(void)
{
	unsigned char i;
	for(i=0;i<3;i++)
		ucRtc_Data[i]=ucRtc[i];
}

//按键处理函数
void Key_Proc()
{
	if(Key_Slow_Down) return;
	Key_Slow_Down=1;
	
	//消抖
	Key_Val=Key_Read();
	Key_Down=Key_Val&(Key_Old^Key_Val);
	Key_Up=~Key_Val&(Key_Old^Key_Val);
	Key_Old=Key_Val;
	
	if(Key_Down>=1&&Key_Down<=10&&Input_Index<=3)//输入值在0-9且数组在0-3
	{
		if(Seg_Disp_Mode==1)
		{
			
			Input[Input_Index]=Key_Down-1;
			//记录按下第一个数子的时间
			if(Input_Index==0) Remeber_Time();
			Input_Index++;
			//数码管显示
			Input_Flag=1;
		}
	}
	
	switch(Key_Down)
	{
		//S4
		case 11:
			if(++Seg_Disp_Mode==3) 
				Seg_Disp_Mode=0;
			
			//令输入界面数码管清零
			if(Seg_Disp_Mode==0) 
			{
				Value_Old=Value;//记录下上一次的值
				Input_Flag=0;
				Input_Index=0;//令数据重新等于0
			}
			//记录输入界面输入的数据
			else if(Seg_Disp_Mode==2) 
			{
				Value=Input[0]*1000+Input[1]*100+Input[2]*10+Input[3];
				high=(unsigned char)(Value>>8);
				low=(unsigned char)(Value&0x00ff);
				//16进制要变成10进制
			    Hour=ucRtc_Data[0]/16*10+ucRtc_Data[0]%16;
			    Minute=ucRtc_Data[1]/16*10+ucRtc_Data[1]%16;
				
				Write_EEPROM(&Hour,0,1);
				Write_EEPROM(&Minute,1,1);
				Write_EEPROM(&high,2,1);
				Write_EEPROM(&low,3,1);
				
			}
		break;
		
		//S5
		case 12:
			if(Seg_Disp_Mode==1) 
			{
				Clear_Data();
				Value=0;//此时输入数据为0
			}
		break;
		
	}
	
}

//数码管清理函数
void Clear_Seg()
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		Seg_Buf[i]=10;
		Seg_Point[i]=0;
	}
}

//数码管处理函数
void Seg_Proc()
{
	if(Seg_Slow_Down) return;
	Seg_Slow_Down=1;
	
	Read_Rtc(ucRtc);//实时获取时间
	switch(Seg_Disp_Mode)
	{
		/****时间界面******/
		case 0:
			Clear_Seg();
			Seg_Buf[0]=ucRtc[0]/16;
			Seg_Buf[1]=ucRtc[0]%16;
			Seg_Buf[2]=11;//-
			Seg_Buf[3]=ucRtc[1]/16;
			Seg_Buf[4]=ucRtc[1]%16;
			Seg_Buf[5]=11;//-
			Seg_Buf[6]=ucRtc[2]/16;
			Seg_Buf[7]=ucRtc[2]%16;
		break;
		/****输入界面******/
		case 1:
			Clear_Seg();
			Seg_Buf[0]=12;//C
			if(Input_Flag==1)
			{
				if(Input_Index-1==0)
					Seg_Buf[7]=Input[0];
				else if(Input_Index-1==1)
				{
					Seg_Buf[6]=Input[0];
					Seg_Buf[7]=Input[1];
				}
				else if(Input_Index-1==2)
				{
					Seg_Buf[5]=Input[0];
					Seg_Buf[6]=Input[1];
					Seg_Buf[7]=Input[2];
				}
				else
				{
					Seg_Buf[4]=Input[0];
					Seg_Buf[5]=Input[1];
					Seg_Buf[6]=Input[2];
					Seg_Buf[7]=Input[3];
				}
			}
		break;
		/****记录界面******/
		case 2:
			Clear_Seg();
			Seg_Buf[0]=13;//E
			Seg_Buf[3]=ucRtc_Data[0]/16;
			Seg_Buf[4]=ucRtc_Data[0]%16;
			Seg_Buf[5]=11;//-
			Seg_Buf[6]=ucRtc_Data[1]/16;
			Seg_Buf[7]=ucRtc_Data[1]%16;
		break;
	}
}

//其他处理函数
void Led_Proc()
{
	unsigned char i;
	for(i=0;i<3;i++)
		ucLed[i]=(i==Seg_Disp_Mode);
	
	ucLed[3]=(Value>Value_Old)?1:0;
}


//定时器0初始化
void Timer0Init(void)		//1毫秒@12.000MHz
{
	AUXR &= 0x7F;		//定时器时钟12T模式
	TMOD &= 0xF0;		//设置定时器模式
	TL0 = 0x18;		//设置定时初始值
	TH0 = 0xFC;		//设置定时初始值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	
	ET0=1;
	EA=1;
}

void Timer0Sever()interrupt 1
{
	if(++Key_Slow_Down==10) Key_Slow_Down=0;
	if(++Seg_Slow_Down==500) Seg_Slow_Down=0;
	
	if(++Seg_Pos==8) Seg_Pos=0;
	
	Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos],Seg_Point[Seg_Pos]);
	Led_Disp(Seg_Pos,ucLed[Seg_Pos]);
		
}


/****************main()函数********************/
void main()
{	
	Timer0Init();
	System_Init();
    Set_Rtc(ucRtc);

	while(1)
	{
		Key_Proc();
		Seg_Proc();
		Led_Proc();
	}
}

2.2 底层代码

2.2.1 DS1302

ds1302.c
/*	# 	DS1302代码片段说明
	1. 	本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
	2. 	参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
		中对单片机时钟频率的要求,进行代码调试和修改。
*/		
#include <ds1302.h>
#include <STC15F2K60S2.H>
#include <intrins.h>

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


//
void Write_Ds1302(unsigned  char temp) 
{
	unsigned char i;
	for (i=0;i<8;i++)     	
	{ 
		SCK = 0;
		SDA = temp&0x01;
		temp>>=1; 
		SCK=1;
	}
}   

//
void Write_Ds1302_Byte( unsigned char address,unsigned char dat )     
{
 	RST=0;	_nop_();
 	SCK=0;	_nop_();
 	RST=1; 	_nop_();  
 	Write_Ds1302(address);	
 	Write_Ds1302(dat);		
 	RST=0; 
}

//
unsigned char Read_Ds1302_Byte ( unsigned char address )
{
 	unsigned char i,temp=0x00;
 	RST=0;	_nop_();
 	SCK=0;	_nop_();
 	RST=1;	_nop_();
 	Write_Ds1302(address);
 	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)
{
	unsigned char i;
	Write_Ds1302_Byte(0x8e,0);//关闭写保护
	for(i=0;i<3;i++)
		Write_Ds1302_Byte(0x84-2*i,ucRtc[i]);
	Write_Ds1302_Byte(0x8e,1);//打开写保护	
}

void Read_Rtc(unsigned char *ucRtc)
{
	unsigned char i;
	for(i=0;i<3;i++)
		ucRtc[i]=Read_Ds1302_Byte(0x85-2*i);
}

2.2.2 EEPROM

iic.c
/*	#   I2C代码片段说明
	1. 	本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
	2. 	参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
		中对单片机时钟频率的要求,进行代码调试和修改。
*/
#include <iic.h>
#include <intrins.h>

sbit scl=P2^0;
sbit sda=P2^1;
#define DELAY_TIME	5

//
static void I2C_Delay(unsigned char n)
{
    do
    {
        _nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();_nop_();_nop_();		
    }
    while(n--);      	
}

//
void I2CStart(void)
{
    sda = 1;
    scl = 1;
	I2C_Delay(DELAY_TIME);
    sda = 0;
	I2C_Delay(DELAY_TIME);
    scl = 0;    
}

//
void I2CStop(void)
{
    sda = 0;
    scl = 1;
	I2C_Delay(DELAY_TIME);
    sda = 1;
	I2C_Delay(DELAY_TIME);
}

//
void I2CSendByte(unsigned char byt)
{
    unsigned char i;
	
    for(i=0; i<8; i++){
        scl = 0;
		I2C_Delay(DELAY_TIME);
        if(byt & 0x80){
            sda = 1;
        }
        else{
            sda = 0;
        }
		I2C_Delay(DELAY_TIME);
        scl = 1;
        byt <<= 1;
		I2C_Delay(DELAY_TIME);
    }
	
    scl = 0;  
}

//
unsigned char I2CReceiveByte(void)
{
	unsigned char da;
	unsigned char i;
	for(i=0;i<8;i++){   
		scl = 1;
		I2C_Delay(DELAY_TIME);
		da <<= 1;
		if(sda) 
			da |= 0x01;
		scl = 0;
		I2C_Delay(DELAY_TIME);
	}
	return da;    
}

//
unsigned char I2CWaitAck(void)
{
	unsigned char ackbit;
	
    scl = 1;
	I2C_Delay(DELAY_TIME);
    ackbit = sda; 
    scl = 0;
	I2C_Delay(DELAY_TIME);
	
	return ackbit;
}

//
void I2CSendAck(unsigned char ackbit)
{
    scl = 0;
    sda = ackbit; 
	I2C_Delay(DELAY_TIME);
    scl = 1;
	I2C_Delay(DELAY_TIME);
    scl = 0; 
	sda = 1;
	I2C_Delay(DELAY_TIME);
}

void Read_EEPROM(unsigned char* EEPROM_string,unsigned char addr,unsigned char num)
{
	I2CStart();
	I2CSendByte(0xA0);
	I2CWaitAck();
	I2CSendByte(addr);
	I2CWaitAck();
	
	I2CStart();
	I2CSendByte(0xA1);
	I2CWaitAck();
	
	while(num--)
	{
		 *EEPROM_string++=I2CReceiveByte();
		if(num) I2CSendAck(0);
		else I2CSendAck(1);	
	}
	I2CStop();
}

void Write_EEPROM(unsigned char* EEPROM_string,unsigned char addr,unsigned num)
{
	I2CStart();
	I2CSendByte(0xA0);
	I2CWaitAck();
	I2CSendByte(addr);
	I2CWaitAck();
	
	while(num--)
	{
		I2CSendByte(*EEPROM_string++);
		I2CWaitAck();
		I2C_Delay(200);
	}
	
	I2CStop();
}

2.3 其他(Key.c)

关于数码管,Led灯代码可以参考我第十四届的代码,下面是我按键的代码

Key.c
#include <STC15F2K60S2.H>
#include <Key.h>

unsigned char Key_Read(void)
{
	unsigned char temp=0;
	P44=0;P42=1;P35=1;P34=1;
	if(P33==0) temp=11;
	if(P32==0) temp=12;
	if(P31==0) temp=1;
	P44=1;P42=0;P35=1;P34=1;
	if(P33==0) temp=8;
	if(P32==0) temp=5;
	if(P31==0) temp=2;
	P44=1;P42=1;P35=0;P34=1;
	if(P33==0) temp=9;
	if(P32==0) temp=6;
	if(P31==0) temp=3;
	P44=1;P42=1;P35=1;P34=0;
	if(P33==0) temp=10;
	if(P32==0) temp=7;
	if(P31==0) temp=4;
	
	return temp;
}

3.后话

 代码仅供参考,欢迎大家指出不足,一起交流

  • 57
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值