蓝桥杯单片机第八届省赛客观题+程序+易错知识点

1.首先所需要的底层

1.1  初始化函数
/*.h文件*/
#ifndef _INIT_H
#define _INIT_H
#include <stc15f2k60s2.h>
void System_Init();

#endif

/*.c文件*/
#include <init.h>

void System_Init()
{
	P0 = 0xff;//关闭LED
	P2 = P2 & 0x1f | 0x80;
	P2 &= 0x1f;	
	
	P0 = 0xff;//关闭其他外设
	P2 = P2 & 0x1f | 0xa0;
	P2 &= 0x1f;

}
1.2  Led函数
/*.h文件*/
#ifndef _LED_H
#define _LED_H
#include <stc15f2k60s2.h>
void Led_Disp(unsigned char addr,enable);
#endif

/*.c文件*/
#include <led.h>
void Led_Disp(unsigned char addr,enable)//LED显示函数
{
	static unsigned char temp = 0x00;
	static unsigned char temp_old = 0xff;
  
  if(enable)
	  temp |= (0x01 << addr);
  else
    temp &= ~(0x01 << addr);
  if(temp != temp_old)
	{
    P0 = ~temp;
		P2 = P2 & 0x1f | 0x80;
		P2 &= 0x1f;
		temp_old = temp;
	}		
}

}
1.3  Key函数(这里使用独立按键)
/*.h文件*/
#ifndef _KEY_H
#define _KEY_H
#include <stc15f2k60s2.h>
unsigned char Key_Read();

#endif

/*.c文件*/
#include <key.h>

unsigned char Key_Read()
{
	unsigned char temp = 0;
	if(P33 == 0) temp = 4;
	if(P32 == 0) temp = 5;
	if(P31 == 0) temp = 6;
	if(P30 == 0) temp = 7;
	return temp;
}
1.4  数码管函数
/*.h文件*/
#ifndef _SEG_H
#define _SEG_H
#include <stc15f2k60s2.h>
void Seg_Disp(unsigned char wela,dula,point);

#endif

/*.c文件*/

#include <seg.h>

code unsigned char Seg_Wela[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
code unsigned char Seg_Dula[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0xbf,0xc6};//0-9 10-熄灭 11 - 12-C

void Seg_Disp(unsigned char wela,dula,point)
{
	P0 =0xff;
	P2 = P2 & 0x1f | 0xe0;
	P2 &= 0x1f;	
	
	P0 = Seg_Wela[wela];
	P2 = P2 & 0x1f | 0xc0;
	P2 &= 0x1f;
	
	P0 = Seg_Dula[dula];
	if(point)
		P0 &= 0x7f; 
	P2 = P2 & 0x1f | 0xe0;
	P2 &= 0x1f;
}
1.5  RTC(DS1302)
/*.h文件*/
#ifndef _DS1302_H
#define _DS1302_H
void Write_Ds1302(unsigned  char temp);
void Write_Ds1302_Byte( unsigned char address,unsigned char dat );
unsigned char Read_Ds1302_Byte ( unsigned char address );
void Ds1302_Set(unsigned char *ucRct);
void Ds1302_Read(unsigned char *ucRct);
#endif


/*.c文件*/
#include <ds1302.h>
#include <reg52.h>
#include <intrins.h>
sbit SCK = P1^7;
sbit SDA = P2^3;
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 Ds1302_Set(unsigned char *ucRct)
{
	unsigned char i;
	Write_Ds1302_Byte(0x8e,0x00);
	for(i = 0;i < 3;i++)
	   Write_Ds1302_Byte(0x84 - 2*i,ucRct[i]);
	Write_Ds1302_Byte(0x8e,0x80);
}
void Ds1302_Read(unsigned char *ucRct)
{
	unsigned char i;
	for(i = 0;i < 3;i++)
	   ucRct[i] = Read_Ds1302_Byte(0x85 - 2*i);
}
1.5  Onewire(DS18B20)
/*.h文件*/
#ifndef _ONEWIRE_H
#define _ONEWIRE_H
void Delay_OneWire(unsigned int t);
void Write_DS18B20(unsigned char dat);
unsigned char Read_DS18B20(void);
bit init_ds18b20(void);
float rd_temperature();
void Delay4us();

#endif


/*.c文件*/
#include <reg52.h>
#include <onewire.h>
#include <intrins.h>
sbit DQ = P1^4;
//

void Delay4us()		//@12.000MHz
{
	unsigned char i;

	_nop_();
	_nop_();
	i = 9;
	while (--i);
}

void Delay_OneWire(unsigned int t)  
{
	unsigned char i;
	while(t--){
		for(i=0;i<12;i++);
	}
}

//
void Write_DS18B20(unsigned char dat)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		DQ = 0;
		DQ = dat&0x01;
		Delay_OneWire(5);
		DQ = 1;
		dat >>= 1;
	}
	Delay_OneWire(5);
}

//
unsigned char Read_DS18B20(void)
{
	unsigned char i;
	unsigned char dat;
  
	for(i=0;i<8;i++)
	{
		DQ = 0;
		dat >>= 1;
		DQ = 1;
		Delay4us();
		if(DQ)
		{
			dat |= 0x80;
		}	    
		Delay_OneWire(5);
	}
	return dat;
}

//
bit init_ds18b20(void)
{
  	bit initflag = 0;
  	
  	DQ = 1;
  	Delay_OneWire(12);
  	DQ = 0;
  	Delay_OneWire(80);
  	DQ = 1;
  	Delay_OneWire(10); 
    initflag = DQ;     
  	Delay_OneWire(5);
  
  	return initflag;
}

float rd_temperature()
{
	unsigned char low,high;
	init_ds18b20();
	Write_DS18B20(0xcc);//跳过rom
	Write_DS18B20(0x44);//温度转化
	
	init_ds18b20();
	Write_DS18B20(0xcc);//跳过rom
	Write_DS18B20(0xbe);//读取温度
	
	low = Read_DS18B20();
	high = Read_DS18B20();
	
	return((high << 8) | low) / 16.0;
}

2.主程序

/*头文件*/
#include <STC15F2K60S2.H>
#include <Init.h>
#include <Seg.h>
#include <Key.h>
#include <Led.h>
#include <onewire.h>
#include <intrins.h>
#include <ds1302.h>

/*变量定义区*/
unsigned char Key_Slow_Down;
unsigned int Seg_Slow_Down;
unsigned char Key_Val,Key_Old,Key_Up,Key_Down;
unsigned char Seg_Buf[8] = {10,10,10,10,10,10,10,10};
unsigned char Seg_Pos;
unsigned char Seg_Point[8] = {0,0,0,0,0,0,0,0};
unsigned char ucLed[8] = {0,0,0,0,0,0,0,0};
unsigned char ucRct[3] = {0x23,0x59,0x50};
unsigned char ucRtc_Set[3];
unsigned char ucRtc_Set_Index;//设置调节指针
unsigned char Alerm_Disp[3];
unsigned char Alerm_Set[3];
bit Change_Mode;//0-时间显示 1-温度显示
unsigned char Timer_Mode;//0-时钟显示  1-时钟设置 2-闹钟设置
unsigned char temperature;//实时温度数据
bit Seg_Star_Flag;//0-灭 1-亮
unsigned char Timer_200Ms;//0.2秒为间隔
bit Alerm_Flag;//0-闹钟不提醒 1-闹钟开始提醒
bit Led_Star_Flag;
unsigned int Timer_5000Ms;
	
/*处理函数*/
void Key_Proc()
{
	unsigned char i;
	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(Alerm_Flag == 1)
	{
		if(Key_Down != 0)
			Alerm_Flag = 0;
		return;
	}
	
	if(Timer_Mode == 0)
	{
		if(Key_Old == 4)
			Change_Mode = 1;
		else
      Change_Mode = 0; 					
	}
	
 switch(Key_Down)
 {
	 case 7:
		 if(Change_Mode == 0)
		 {
			 
			 if(Timer_Mode == 0)
			 {
				 for(i=0;i<3;i++)
				   ucRtc_Set[i] = ucRct[i];
				 Timer_Mode = 1;	 
			 }
			 else if(Timer_Mode == 1)
			 {
				 if(++ucRtc_Set_Index == 3)
				 {
					 ucRtc_Set_Index = Timer_Mode = 0;
					 Ds1302_Set(ucRtc_Set);
				 }
			 }
	  }
	 break;
	 case 6:
		 if(Change_Mode == 0)
		 {
			 if(Timer_Mode == 0)
				 Timer_Mode = 2;
			 else if(Timer_Mode == 2)
			 {
				 if(++ucRtc_Set_Index == 3)
				 {
					 ucRtc_Set_Index = Timer_Mode = 0;
					 for(i=0;i<3;i++)
					  Alerm_Set[i] = Alerm_Disp[i];
					  for(i=0;i<3;i++)
					    Alerm_Disp[i] = 0;
				 }
			 }
		 }
	 break;
	 case 5:
		 if(Change_Mode == 0)
		 {
			 if(Timer_Mode == 1)//时钟设置模式
			 {
				 if((++ucRtc_Set[ucRtc_Set_Index]) %16 == 0x0a)
					 ucRtc_Set[ucRtc_Set_Index] += 6;//为了方便转化为10  0x0a + 6 = 0x10
				 if(ucRtc_Set[ucRtc_Set_Index] == (ucRtc_Set_Index?0x60:0x24))
					    ucRtc_Set[ucRtc_Set_Index] = ucRtc_Set_Index?0x59:0x23;
			 }
			 else if(Timer_Mode == 2)
			 {
				 if((++Alerm_Disp[ucRtc_Set_Index]) %16 == 0x0a)
					 Alerm_Disp[ucRtc_Set_Index] += 6;//为了方便转化为10  0x0a + 6 = 0x10
				 if(Alerm_Disp[ucRtc_Set_Index] == (ucRtc_Set_Index?0x60:0x24))
					    Alerm_Disp[ucRtc_Set_Index] = ucRtc_Set_Index?0x59:0x23;				 
			 }
		 }
	 break;
	 case 4:
		 if(Change_Mode == 0)
		 {
			 if(Timer_Mode == 1)//时钟设置模式
			 {
				 if((--ucRtc_Set[ucRtc_Set_Index]) %16 == 0x0f)
					 ucRtc_Set[ucRtc_Set_Index] -= 6;//为了方便转化为10  0x0f - 6 = 0x10
				 if(ucRtc_Set[ucRtc_Set_Index] == 0xf9)
					    ucRtc_Set[ucRtc_Set_Index] = 0x00;
			 }
			 else if(Timer_Mode == 2)
			 {
				 if((--Alerm_Disp[ucRtc_Set_Index]) %16 == 0x0f)
					 Alerm_Disp[ucRtc_Set_Index] -= 6;//为了方便转化为10  0xa0 + 6 = 0x10
				 if(Alerm_Disp[ucRtc_Set_Index] == 0xf9)  // 0-7 = f9
					    Alerm_Disp[ucRtc_Set_Index] = 0x00;				 
			 }
		 }
	 break;
		 
 }
}


void Seg_Proc()
{
	unsigned char i;
	if(Seg_Slow_Down) return;
	Seg_Slow_Down = 1;
	Ds1302_Read(ucRct);
	temperature = rd_temperature();
	Seg_Star_Flag = ucRct[2] %16%2;
	if(Change_Mode)//处于温度显示格式
	{
		for(i=0;i<5;i++)
		  Seg_Buf[i] = 0;
		Seg_Buf[5] = temperature/10%10;
		Seg_Buf[6] = temperature%10;
		Seg_Buf[7] = 12;
	}
	else//处于时间显示界面
	{
		switch(Timer_Mode)
		{
			case 0://时间显示界面
				for(i=0;i<3;i++)
				{
					Seg_Buf[3*i] = ucRct[i] /16%16;
					Seg_Buf[3*i+1] = ucRct[i] %16;
				}
			  Seg_Buf[2] = Seg_Buf[5] = 11;
			break;
			case 1:
				for(i=0;i<3;i++)
				{
					Seg_Buf[3*i] = ucRtc_Set[i] /16%16;
					Seg_Buf[3*i+1] = ucRtc_Set[i] %16;
				}
				Seg_Buf[2] = Seg_Buf[5] = 11;
				Seg_Buf[3*ucRtc_Set_Index] = Seg_Star_Flag?ucRtc_Set[ucRtc_Set_Index] /16%16:10;//对应的位闪烁
				Seg_Buf[3*ucRtc_Set_Index+1] = Seg_Star_Flag?ucRtc_Set[ucRtc_Set_Index] %16:10;		
			break;
			case 2:
				for(i=0;i<3;i++)
				{
					Seg_Buf[3*i] = Alerm_Disp[i] /16%16;
					Seg_Buf[3*i+1] = Alerm_Disp[i] %16;
				}
				Seg_Buf[2] = Seg_Buf[5] = 11;
				Seg_Buf[3*ucRtc_Set_Index] = Seg_Star_Flag?Alerm_Disp[ucRtc_Set_Index] /16%16:10;
				Seg_Buf[3*ucRtc_Set_Index+1] = Seg_Star_Flag?Alerm_Disp[ucRtc_Set_Index] %16:10;
			break;
	 }
	}
 
}



/*显示函数*/
void Led_Proc()
{
	if(ucRct[0]==Alerm_Set[0] && ucRct[1]==Alerm_Set[1] && ucRct[2]==Alerm_Set[2])
		Alerm_Flag =1;
	ucLed[0] = Led_Star_Flag;
}


/*定时器0初始化函数*/
void Timer0Init(void)		//1ms@12.000MHz
{
	AUXR &= 0x7F;		
	TMOD &= 0xF0;		
	TL0 = 0x18;		
	TH0 = 0xFC;		
	TF0 = 0;		
	TR0 = 1;		
	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]);
	if(Alerm_Flag == 1)
	{
		if(++Timer_200Ms == 200)
		{
			Timer_200Ms = 0;
			Led_Star_Flag ^= 1;
		}
		if(++Timer_5000Ms == 5000)
		{
			Timer_200Ms = Alerm_Flag = 0;
			Timer_5000Ms = 0;
		}
	}
	else
		Led_Star_Flag = 0;

}




/*主函数*/
void main()
{
	System_Init();
	Timer0Init();
	Ds1302_Set(ucRct);
	while(1)
	{
		Key_Proc();
		Seg_Proc();
		Led_Proc();
	}
}



3.难点+易错点分析
3.1 首先要注意到时钟显示界面只需要一个ucRct数组即可,时钟设置界面只需要一个ucRtc_Set数据即可,只要每次再进入时钟设置时,将时钟显示的值赋值给时钟设置值,再对时钟设置值进行调整即可
			 if(Timer_Mode == 0)
			 {
				 for(i=0;i<3;i++)
				   ucRtc_Set[i] = ucRct[i];
				 Timer_Mode = 1;	 
			 }

同时设置完时钟之后,不需要把时钟设置值赋值给时钟显示值,只需要将时钟设置放进DS1302芯片就可以了

			 else if(Timer_Mode == 1)
			 {
				 if(++ucRtc_Set_Index == 3)
				 {
					 ucRtc_Set_Index = Timer_Mode = 0;
					 Ds1302_Set(ucRtc_Set);
				 }
			 }
3.2  在选中哪一段,那一段就闪烁和选中哪一段,那一段就显示有异曲同工之妙
/*选中哪一段那一段就显示*/
Seg_Buf[3*i] = ucRct[i] /16%16;
Seg_Buf[3*i+1] = ucRct[i] %16;
				

/*选中哪一段那一段就闪烁*/
Seg_Buf[3*ucRtc_Set_Index] = Seg_Star_Flag?ucRtc_Set[ucRtc_Set_Index] /16%16:10;
Seg_Buf[3*ucRtc_Set_Index+1] = Seg_Star_Flag?ucRtc_Set[ucRtc_Set_Index] %16:10;	
3.3  在进行时钟设置时,加减时钟设置值时,是以16进制进行的,所以要进行特殊的处理,要了解十进制和16进制的关系 ;0x09+1=0x0a而不是0x10,那我们应该将他变为0x10,因为0x10/16 = 10
所以我们要计算0x10-0x0a=6;同理减的时候0x10-1=0x0f;我们要把它变成0x09,0x0f-0x09 = 6所以我们要抓住这一点,最后减到0x00再做减法会一下子减掉7,0x00 - 7 = ffffff f9;我们只取f9
	 case 5:
		 if(Change_Mode == 0)
		 {
			 if(Timer_Mode == 1)//时钟设置模式
			 {
				 if((++ucRtc_Set[ucRtc_Set_Index]) %16 == 0x0a)
					 ucRtc_Set[ucRtc_Set_Index] += 6;//为了方便转化为10  0x0a + 6 = 0x10
				 if(ucRtc_Set[ucRtc_Set_Index] == (ucRtc_Set_Index?0x60:0x24))
					    ucRtc_Set[ucRtc_Set_Index] = ucRtc_Set_Index?0x59:0x23;
			 }
			 else if(Timer_Mode == 2)
			 {
				 if((++Alerm_Disp[ucRtc_Set_Index]) %16 == 0x0a)
					 Alerm_Disp[ucRtc_Set_Index] += 6;//为了方便转化为10  0x0a + 6 = 0x10
				 if(Alerm_Disp[ucRtc_Set_Index] == (ucRtc_Set_Index?0x60:0x24))
					    Alerm_Disp[ucRtc_Set_Index] = ucRtc_Set_Index?0x59:0x23;				 
			 }
		 }
	 break;
	 case 4:
		 if(Change_Mode == 0)
		 {
			 if(Timer_Mode == 1)//时钟设置模式
			 {
				 if((--ucRtc_Set[ucRtc_Set_Index]) %16 == 0x0f)
					 ucRtc_Set[ucRtc_Set_Index] -= 6;//为了方便转化为10  0x0f - 6 = 0x10
				 if(ucRtc_Set[ucRtc_Set_Index] == 0xf9)
					    ucRtc_Set[ucRtc_Set_Index] = 0x00;
			 }
			 else if(Timer_Mode == 2)
			 {
				 if((--Alerm_Disp[ucRtc_Set_Index]) %16 == 0x0f)
					 Alerm_Disp[ucRtc_Set_Index] -= 6;//为了方便转化为10  0xa0 + 6 = 0x10
				 if(Alerm_Disp[ucRtc_Set_Index] == 0xf9)  // 0-7 = f9
					    Alerm_Disp[ucRtc_Set_Index] = 0x00;				 
			 }
		 }
	 break;
3.4  在闹钟提醒的时候只要有按键按下就会停止提醒,但是这个时候可能会按到S6和S7,那么他就会执行S6和S7对应的功能,这里我们不能让他执行对应功能,所以我们在进行处理完之后可以给他一个return,不让他再进行下面按键的操作而直接跳出按键操作函数
if(Alerm_Flag == 1)
{
	if(Key_Down != 0)
		Alerm_Flag = 0;
	return;
}

程序优化链接:

https://mp.csdn.net/mp_blog/creation/editor/137479776

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Liuzp36

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值