第八届蓝桥杯单片机——电子时钟

第八届蓝桥杯单片机——电子时钟_第1张图片
第八届蓝桥杯单片机——电子时钟_第2张图片
第八届蓝桥杯单片机——电子时钟_第3张图片
这是今天二刷的一道题目,其中用的了ds1302和onewire协议。
底层代码就不放了,前面讲模块的文章也有,用的是官方的底层驱动。
下面直接上主函数,在程序中讲解

#include 
#include "ds1302.h"
#include "onewire.h"
#define uchar unsigned char
#define uint unsigned int
uchar code adress[]={
     0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//数码管显示地址
uchar code display[]={
     0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0x40,0x39};
//共阴数码管显示编码表,在stc中的范例程序->实验箱->数码管显示中有
uchar Trg,cont,key_count=0,key_flag=0;//键盘有关
uchar shi,fen,miao,set_num,m,send_num;//时间有关
uchar nz_shi_ture=0,nz_fen_ture=0,nz_miao_ture=0;//闹钟参数
uchar nz_shi=0,nz_fen=0,nz_miao=0;//闹钟显示
uchar i=0,buff[8];//显示有关
uchar flag=0;//启动停止标志位
uchar tempe;//温度
uint display_count=0;//控制闪烁
uchar display_flag=1;
uchar sz_mode=0,nz_mode=0;//模式控制
uchar led_flag=0,LED_f=1,LED;
uint led_count=0;//led控制
void init()//初始化关蜂鸣器,继电器,led
{
     
	P2=0xa0;P0=0x00;P2=0x00;
	P2=0x80;P0=0xff;P2=0x00;
}
void set_time(uchar shi,uchar fen,uchar miao)//设置初始时间
{
     
	Write_Ds1302_Byte(0x8e,0);//关写保护
	Write_Ds1302_Byte(0x84,(shi/10)*16+shi%10);//记住时分秒的写地址 0x84,0x82,0x80;
	Write_Ds1302_Byte(0x82,(fen/10)*16+fen%10);
	Write_Ds1302_Byte(0x80,(miao/10)*16+miao%10);
	Write_Ds1302_Byte(0x8e,0x80);//开写保护
}
void read_time()//读取时间,读取前关中断
{
     
	EA=0;
	shi=Read_Ds1302_Byte(0x85);//记住时分秒的读取地址
	fen=Read_Ds1302_Byte(0x83);
	miao=Read_Ds1302_Byte(0x81);
	EA=1;
}
void stop()//停止功能,这个非常重要
{
     
	Write_Ds1302_Byte(0x8e,0);
	if(miao<0x80)//停止
	{
     
		Write_Ds1302_Byte(0x80,miao|0x80);//高位置1
		flag=1;
	}
	else//启动
	{
     
		Write_Ds1302_Byte(0x80,miao&0x7f);//高位值0
		flag=0;
	}
	Write_Ds1302_Byte(0x8e,0x80);
}
uchar read_tempe()//读温度
{
     
	uint temp;
	uchar tempe;
	uchar high,low;
	init_ds18b20();
	Write_DS18B20(0xcc);
	Write_DS18B20(0x44);
	Delay_OneWire(200);
	
	init_ds18b20();
	Write_DS18B20(0xcc);
	Write_DS18B20(0xbe);
	
	low=Read_DS18B20();
	high=Read_DS18B20();
	
	temp=high&0x0f;
	temp<<=8;
	temp=temp|low;
	tempe=(uchar)(temp*0.0625);//精度是0.0625摄氏度
	
	return tempe;
}
void read_key()//独立键盘读取
{
     
	uchar readData=P3^0xff;
	Trg=readData&(readData^cont);
	cont=readData;
}
void key_function()//按键功能
{
     
	if(Trg&0x01||Trg&0x02||Trg&0x04||Trg&0x08)//任意键关闹钟
	{
     
		led_flag=0;
		LED_f=1;
		led_count=0;
	}
	if(Trg&0x01)//s7,时钟设置
	{
     
		if(nz_mode==0)
			sz_mode++;
		if(sz_mode==1)
			stop();
		if(sz_mode==4)
		{
     
			stop();
			sz_mode=0;
		}
	}
	if(Trg&0x02)//s6,闹钟设置
	{
     
		if(sz_mode==0)
			nz_mode++;
		if(nz_mode==4)
		{
     
			nz_mode=0;
			nz_shi_ture=nz_shi;//这个是更新闹钟的比较参数,和显示参数,就是,你在改变闹钟后要完全返回后,参数才生效
			nz_fen_ture=nz_fen;
			nz_miao_ture=nz_miao;
			LED_f=1;
		}
	}
	if(Trg&0x04)//s5,加
	{
     
		if(sz_mode!=0&&nz_mode==0&&flag==1)//时钟暂停时,调节时钟
		{
     	//选择当前要调节是时分秒
			if(sz_mode==1)//时
				m=0x84;
			else if(sz_mode==2)
				m=0x82;
			else if(sz_mode==3)//秒
				m=0x80;
			EA=0;set_num=Read_Ds1302_Byte(m+1);EA=1;//读取当前的时间
			send_num=(set_num/16&0x07)*10+set_num%16+1;//BCD转十进制之后加一,注意这个&0x07,是秒特有的,为了不分类,时分也可以
			//注意边界属性
			if(m==0x84)
				send_num=send_num%24;
			else
				send_num=send_num%60;
				
			Write_Ds1302_Byte(0x8e,0x00);//关写保护
			if(m==0x80)//秒的时候BCD码要|0x80,高位置1,不然会出错
				Write_Ds1302_Byte(m,(send_num/10*16+send_num%10)|0x80);
			else
				Write_Ds1302_Byte(m,send_num/10*16+send_num%10);
			Write_Ds1302_Byte(0x8e,0x80);//开写保护
		}
		if(nz_mode!=0&&sz_mode==0)//增加闹钟
		{
     
			if(nz_mode==1)
				nz_shi=(nz_shi+1)%24;
			if(nz_mode==2)
					nz_fen=(nz_fen+1)%60;
			if(nz_mode==3)
				 nz_miao=(nz_miao+1)%60;
		}
	}
	if(Trg&0x08)//s4减,类似于加,注意边界属性,不能减出来负数
	{
     
		if(sz_mode!=0&&nz_mode==0&&flag==1)
		{
     
			if(sz_mode==1)//ʱ
				m=0x84;
			else if(sz_mode==2)
				m=0x82;
			else if(sz_mode==3)//Ãë
				m=0x80;
			EA=0;set_num=Read_Ds1302_Byte(m+1);EA=1;
			send_num=(set_num/16&0x07)*10+set_num%16;
			if(send_num==0)
			{
     
				if(m==0x84)send_num=23;
				else send_num=59;
			}
			else
				send_num=send_num-1;
			Write_Ds1302_Byte(0x8e,0x00);
			if(m==0x80)//
				Write_Ds1302_Byte(m,(send_num/10*16+send_num%10)|0x80);
			else
				Write_Ds1302_Byte(m,send_num/10*16+send_num%10);
			Write_Ds1302_Byte(0x8e,0x80);
		}
		if(nz_mode!=0&&sz_mode==0)
		{
     
			if(nz_mode==1)
			{
     
				if(nz_shi==0)nz_shi=23;
				else nz_shi--;
			}
			if(nz_mode==2)
			{
     
				if(nz_fen==0)nz_fen=59;
				else nz_fen--;
			}
			if(nz_mode==3)
			{
     
				if(nz_miao==0)nz_miao=59;
				else nz_miao--;
			}
		}
	}
	
}
void set_buff()//设置显示数组
{
     
	if(cont&0x08&&sz_mode==0&&nz_mode==0)//长按 s4 在时钟显示界面,显示温度
	{
     
		tempe=read_tempe();
		buff[0]=10;
		buff[1]=10;
		buff[2]=10;
		buff[3]=10;
		buff[4]=10;
		buff[5]=tempe/10;
		buff[6]=tempe%10;
		buff[7]=12;//c
	}
	else if(nz_mode==0)//时钟界面
	{
     
		if(sz_mode==1)//时闪烁
		{
     
			if(display_flag==1)
			{
     
				buff[0]=shi/16;buff[1]=shi%16;
			}
			else
			{
     
				buff[0]=10;buff[1]=10;
			}
		}
		else
		{
     
			buff[0]=shi/16;buff[1]=shi%16;
		}
		if(sz_mode==2)//分闪烁
		{
     
			if(display_flag==1)
			{
     
				buff[3]=fen/16;buff[4]=fen%16;
			}
			else
			{
     
				buff[3]=10;buff[4]=10;
			}
		}
		else
		{
     
				buff[3]=fen/16;buff[4]=fen%16;
		}
		if(sz_mode==3)//秒闪烁
		{
     
			if(display_flag==1)
			{
     
				buff[6]=miao/16&0x07;buff[7]=miao%16;
			}
			else
			{
     
				buff[6]=10;buff[7]=10;
			}
		}
		else
		{
     
			buff[6]=miao/16&0x07;buff[7]=miao%16;
		}
		buff[2]=11;buff[5]=11;
	}
	else if(nz_mode!=0)
	{
     
		if(nz_mode==1)//闹钟时闪烁
		{
     
			if(display_flag==1)
			{
     
				buff[0]=nz_shi/10;buff[1]=nz_shi%10;
			}
			else
			{
     
				buff[0]=10;buff[1]=10;
			}
		}
		else
		{
     
			buff[0]=nz_shi/10;buff[1]=nz_shi%10;
		}
		if(nz_mode==2)//分闪烁
		{
     
			if(display_flag==1)
			{
     
				buff[3]=nz_fen/10;buff[4]=nz_fen%10;
			}
			else
			{
     
				buff[3]=10;buff[4]=10;
			}
		}
		else
		{
     
			buff[3]=nz_fen/10;buff[4]=nz_fen%10;
		}
		if(nz_mode==3)//秒闪烁
		{
     
			if(display_flag==1)
			{
     
				buff[6]=nz_miao/10;buff[7]=nz_miao%10;
			}
			else
			{
     
				buff[6]=10;buff[7]=10;
			}
		}
		else
		{
     
			buff[6]=nz_miao/10;buff[7]=nz_miao%10;
		}
		buff[2]=11;buff[5]=11;
	}
}
void set_led()//设置led
{
     
	if(shi==nz_shi_ture&&fen==nz_fen_ture&&miao==nz_miao_ture)
	{
     
		led_flag=1;
	}
	if(led_flag==1)
	{
     
		if(LED_f==1)
			LED=0x01;
		else
			LED=0x00;
	}
	else
		LED=0x00;
}
void Timer0Init(void)		//[email protected]
{
     
	EA=1;
	ET0=1;
	AUXR |= 0x80;		//?????1T??
	TMOD &= 0xF0;		//???????
	TL0 = 0xCD;		//??????
	TH0 = 0xD4;		//??????
	TF0 = 0;		//??TF0??
	TR0 = 1;		//???0????
}
void time0() interrupt 1
{
     
	if(++key_count==10)//扫描键盘
		key_flag=1;
	if(++display_count==1000)//控制1s闪烁
	{
     
		display_flag=~display_flag;
		display_count=0;
	}
	if(led_flag==1)//led闪烁
	{
     
		led_count++;
		if(led_count%200==0&&led_count<=5000)//5s内,0.2s闪烁
			LED_f=~LED_f;
		if(led_count>5000)//超过5s 关闹钟
		{
     
			led_flag=0;
			LED_f=0;
			led_count=0;
		}
	}
	//数码管显示
	P2=0xc0;P0=adress[i];P2=0x00;
	P2=0xe0;P0=~display[buff[i]];P2=0x00;
	i++;if(i==8)i=0;
}
void main()
{
     
	init();//初始化
	read_tempe();//读一下温度
	Timer0Init();//定时器0初始化
	set_time(23,59,55);//设置初始时间
	while(1)
	{
     
		if(key_flag==1)//按键按下
		{
     
			key_flag=0;
			key_count=0;
			read_key();//读键盘
			key_function();//键盘功能
		}
		read_time();//读温度
		set_led();//设置led
		set_buff();//设置显示数组
		P2=0x80;P0=~LED;P2=0x00;
	}
}

这篇博客是清明节写的,当时只写了一半,现在过了一个星期补了回来,发现有大问题。当时,因为第二天要模拟考,没写完,但是第二天正好考到了时钟,是一道国赛题,其中就有时钟显示,和调节时间,当时因为矩阵键盘的原因,一直无法让选中的时分秒,间隔一秒闪烁,耽误了很长时间,大概有两个小时时钟模块还没写好,决定放弃,因为下面还有好几个模块,幸好下面的模块比较简单,不到两个小时全写完了。
因为下面有个调节参数要用到键盘的模块时,发现了是矩阵键盘的问题,我一开始写的会一直读矩阵键盘,后来我灵光一现,换成Trg 判断,和独立键盘差不多,因为两个的读取原理一样的。键盘的问题解决了。但是时钟的暂停不下了。暂停可以按下时间就会走的非常快。改了好久,不行,于是放弃,一度以为是stop()函数记错了。可是就那么几行,不可能错的。。。而且时钟的加减都没问题。最终还是没有发现。最后就改成了,时钟边走边调节时间。
考后发参考代码才知道!
要改底层驱动!!!!要改底层驱动!!!!要改底层驱动!!!!
不改也可以,但是读取和改时间就没那么简单了,要注意读取的时间。
下面来对照改一下驱动

void Write_Ds1302(unsigned  char temp)//写字节
{
     
	unsigned char i;
	for (i=0;i<8;i++)     	
	{
      
		SCK=0;
		SDA=temp&0x01;
		temp>>=1; 
		SCK=1;
		SCK=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;
		SCK=0;//改,新增
	} 
 	RST=0;	_nop_();
 	//SCK=0;	_nop_();//删去
	//SCK=1;	_nop_();//删去
	SDA=0;	_nop_();
	SDA=1;	_nop_();
	return (temp);			
}

这两个子函数要改一下。所以说要记得东西又增加了。
此为还有一个个坑就是。onewire的驱动竟然也要改!!!onewire的驱动竟然也要改!!!onewire的驱动竟然也要改!!!
之前几场模拟没用的采集温度,昨天做了一个有采集温度的国赛题,第一个模块就是采集温度,也幸好我用了从实验室拷贝的驱动,我之前模块题的时候把驱动改了都不记得了,之前做的好几个省赛题,直接拿之前的驱动,所以一直不知道。幸好做了遇到的这个问题。如果不改,温度读不出。第一怀疑的read_temp()是不是写错了。对照了一下,没有!!也是突然想到了换底层驱动,结果就是竟然可以了。对照了一下发现驱动果然不一样,就一点点不一样——延时函数!!!

void Delay_OneWire(unsigned int t)  //STC89C52RC
{
     
	t=t*10;//增加,这个要乘10
	while(t--);
}

很迷吧。快考试了,因为之前十年没考过超声波和串口,现在预测说可能考。要抓紧复习啊。

你可能感兴趣的:(单片机,蓝桥杯)