第八届蓝桥杯省赛单片机组试题——电子钟

1.底层驱动:
(1)onewire:

//onewire.h
#ifndef _ONEWIRE_H
#define _ONEWIRE_H

#include "reg52.h"

#define OW_SKIP_ROM 0xcc
#define DS18B20_CONVERT 0x44
#define DS18B20_READ 0xbe

//IC引脚定义
sbit DQ = P1^4;

//函数声明
void Delay_OneWire(unsigned int t);
void Write_DS18B20(unsigned char dat);
bit Init_DS18B20(void);
unsigned char Read_DS18B20(void);

#endif
//onewire.c
#include "onewire.h"
//单总线延时函数
void Delay_OneWire(unsigned int t)
{
  while(t--);
}

//DS18B20芯片初始化
bit Init_DS18B20(void)
{
	bit initflag = 0;
	//DQ = 1;
	//Delay_OneWire(5);
	DQ = 0;
	Delay_OneWire(200); 		//拉低总线480us以上
	DQ = 1;									//然后释放总线
	Delay_OneWire(20); 			//等待15~60us
	initflag = DQ;    			//读取DS18B20的复位应答信号
	Delay_OneWire(100);			//等待60~240us后释放总线
  
	return initflag;				//应答信号为低电平,表示复位成功
}

//通过单总线向DS18B20写一个字节
void Write_DS18B20(unsigned char dat)
{
	unsigned char i;
	for(i=0;i<8;i++)
	{
		DQ = 0;								//先将总线拉低10~15us
		DQ = dat&0x01;				//向总线写入数据
		Delay_OneWire(20);		//维持20~45us
		DQ = 1;								//释放总线
		dat >>= 1;						//发送下一个数据位
	}
	//Delay_OneWire(5);
}

//从DS18B20读取一个字节
unsigned char Read_DS18B20(void)
{
	unsigned char i;
	unsigned char dat;
  
	for(i=0;i<8;i++)
	{
		DQ = 0;								//先将总线拉低10~15us
		dat >>= 1;						//数据位右移
		DQ = 1;								//释放总线
		if(DQ)								//读取总线上的数据
		{
			dat |= 0x80;
		}	    
		Delay_OneWire(20);		//延迟45us左右,再读下一个数据位
	}
	return dat;
}

(2)SPI:

//SPI.h
#include
#include

sbit SCLK = P1^7; 
sbit RST = P1^3; 
sbit DSIO = P2^3; 

void DS1302_WriteByte(unsigned char addr, unsigned char dat);  //单字节写的时序
unsigned char DS1302_ReadByte(unsigned char addr);   //单字节读的时序在这里插入代码片
//SPI.c
#include
//单字节写的时序
void DS1302_WriteByte(unsigned char addr, unsigned char dat)
{
	unsigned char n;
   	RST = 0;
    _nop_();
    SCLK = 0;
    _nop_();
    RST = 1;
    _nop_();        

    for (n=0; n<8; n++)         //发送要写入数据的内存地址
    {
		DSIO = addr & 0x01;
		addr >>= 1;
		SCLK = 1;
		_nop_();
		SCLK = 0;
		_nop_();
	}
    for (n=0; n<8; n++)         //将指定内容写入该地址的内存
    {
		DSIO = dat & 0x01;
		dat >>= 1;
		SCLK = 1;
		_nop_();
		SCLK = 0;
		_nop_();
    }                 
	RST = 0;
    _nop_();
}

//单字节读的时序
unsigned char DS1302_ReadByte(unsigned char addr)
{
	unsigned char n,dat,tmp;
	RST = 0;
	_nop_();
	SCLK = 0;
	_nop_();
	RST = 1;
	_nop_();

    for(n=0; n<8; n++)         //发送要读出数据的内存地址
	{
		DSIO = addr & 0x01;
		addr >>= 1;
		SCLK = 1;
		_nop_();
		SCLK = 0;
		_nop_();
	}
	        
	for(n=0; n<8; n++)         //读出该地址内存的数据
	{
		tmp = DSIO;
		dat = (dat>>1) | (tmp<<7);
		SCLK = 1;
		_nop_();
		SCLK = 0;
		_nop_();
	}
	
	RST = 0;
	_nop_();
	SCLK = 1;
	_nop_();
	DSIO = 0;
	_nop_();
	DSIO = 1;
	_nop_();
	
	return dat;        
}

2.主程序:

#include
#include"onewire.h"
#include"spi.h"

bit led = 1;				 //LED亮灭
sbit L1 = P0^0;				 //定义L1
sbit S7 = P3^0;				 //定义独立按键S7
sbit S6 = P3^1;				 //定义独立按键S6
sbit S5 = P3^2;				 //定义独立按键S5
sbit S4 = P3^3;				 //定义独立按键S4
unsigned int T_dat;          //存放温度
unsigned char hour = 0;		 //闹钟时
unsigned char minute = 0;	 //闹钟分
unsigned char second = 0;	 //闹钟秒
unsigned char count = 0;	 //定时器0:20ms定时计数(闹钟)
unsigned char num = 0;       //定时器1:50ms定时计数(闪烁)
unsigned char clock = 0;     //时钟设置时分秒标志位
bit state = 1;               //时钟设置闪烁标志位
unsigned char alarm = 0;     //闹钟设置时分秒标志位
unsigned char stat = 0;      //闹钟闪烁标志位
unsigned char status = 0;    //数码管模式显示标志位

unsigned char code WRITE_RTC_ADDR[7] = 
	{0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};      //定义写操作的日历寄存器地址
unsigned char code READ_RTC_ADDR[7] = 
	{0x81,0x83,0x85,0x87,0x89,0x8b,0x8d};      //定义读操作的日历寄存器地址
unsigned char TIME[7] = 
	{0x50,0x59,0x23,0x00,0x00,0x00,0x00};      //定义日历时钟寄存器配置参数(秒、分、时、日、月、周、年)
unsigned char code SEG_code[19] = 
	{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
	 0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,
	 0xbf,0x7f,0xff};                          //定义共阳数码管段码内容:0~F,—,.,熄灭


/*====================================初始化====================================*/
//端口选择
void Select_HC138(unsigned char n)
{
	switch(n)
	{
		case 0:
			P2 = (P2 & 0x1f) | 0x00;
			break;
		case 4:
			P2 = (P2 & 0x1f) | 0x80;
			break;
		case 5:
			P2 = (P2 & 0x1f) | 0xa0;
			break;
		case 6:
			P2 = (P2 & 0x1f) | 0xc0;
			break;
		case 7:
			P2 = (P2 & 0x1f) | 0xe0;
			break;
	}
}

//关闭蜂鸣器、继电器和LED
void Init_system()
{
	Select_HC138(5);
	P0 = 0x00;
	Select_HC138(4);
	P0 = 0xff;
	Select_HC138(0);
}
/*==============================================================================*/


/*==================================数码管显示==================================*/
//数码管延时函数
void Delay_tube(unsigned char t)
{
	while(t--);
}

//数码管显示
void Show_tube(unsigned char position,unsigned char value)
{
	Select_HC138(7);
	P0 = 0xff;       //选通前先熄灭
	Select_HC138(6);
	P0 = 0x01 << position;
	Select_HC138(7);
	P0 = value;
}

//熄灭数码管
void Show_all()
{
	Select_HC138(7);
	P0 = 0xff;       //选通前先熄灭
	Select_HC138(6);
	P0 = 0xff;       //全部选通
}

//数码管动态显示(时间)
void Display_time()
{
	if((clock == 1)&&(state == 1))
	{
		Show_tube(0,SEG_code[18]);     //熄灭
		Show_tube(1,SEG_code[18]);     //熄灭
	}
	else
	{
		Show_tube(0,SEG_code[TIME[2]/16]);   //显示时的十位
		Delay_tube(100);
		Show_tube(1,SEG_code[TIME[2]%16]);   //显示时的个位
		Delay_tube(100);
	}									 

	Show_tube(2,SEG_code[16]);               //显示分隔符
	Delay_tube(100);

	if((clock == 2)&&(state == 1))
	{
		Show_tube(3,SEG_code[18]);     //熄灭
		Show_tube(4,SEG_code[18]);     //熄灭
	}
	else
	{
		Show_tube(3,SEG_code[TIME[1]/16]);   //显示分的十位
		Delay_tube(100);
		Show_tube(4,SEG_code[TIME[1]%16]);   //显示分的个位
		Delay_tube(100);
	}

	Show_tube(5,SEG_code[16]);               //显示分隔符
	Delay_tube(100);
	
	if((clock == 3)&&(state == 1))
	{
		Show_tube(6,SEG_code[18]);     //熄灭
		Show_tube(7,SEG_code[18]);     //熄灭
	}
	else
	{
		Show_tube(6,SEG_code[TIME[0]/16]);   //显示秒的十位
		Delay_tube(100);
		Show_tube(7,SEG_code[TIME[0]%16]);   //显示秒的个位
		Delay_tube(100);
	}
	
	Show_all();
}

//数码管动态显示(闹钟)
void Display_alarm()
{
	if((alarm == 1)&&(state == 1))
	{
		Show_tube(0,SEG_code[18]);     //熄灭
		Show_tube(1,SEG_code[18]);     //熄灭
	}
	else
	{
		Show_tube(0,SEG_code[hour/10]);   //显示时的十位
		Delay_tube(100);
		Show_tube(1,SEG_code[hour%10]);   //显示时的个位
		Delay_tube(100);
	}									 

	Show_tube(2,SEG_code[16]);               //显示分隔符
	Delay_tube(100);

	if((alarm == 2)&&(state == 1))
	{
		Show_tube(3,SEG_code[18]);     //熄灭
		Show_tube(4,SEG_code[18]);     //熄灭
	}
	else
	{
		Show_tube(3,SEG_code[minute/10]);   //显示分的十位
		Delay_tube(100);
		Show_tube(4,SEG_code[minute%10]);   //显示分的个位
		Delay_tube(100);
	}

	Show_tube(5,SEG_code[16]);               //显示分隔符
	Delay_tube(100);
	
	if((alarm == 3)&&(state == 1))
	{
		Show_tube(6,SEG_code[18]);     //熄灭
		Show_tube(7,SEG_code[18]);     //熄灭
	}
	else
	{
		Show_tube(6,SEG_code[second/10]);   //显示秒的十位
		Delay_tube(100);
		Show_tube(7,SEG_code[second%10]);   //显示秒的个位
		Delay_tube(100);
	}
	
	Show_all();
}
	 
//数码管动态显示(温度)
void Display_temp(unsigned char temp)
{
	Show_tube(5,SEG_code[temp/10]);     //显示温度的十位
	Delay_tube(100);

	Show_tube(6,SEG_code[temp%10]);     //显示温度的个位
	Delay_tube(100);

	Show_tube(7,SEG_code[12]);          //显示温度单位
	Delay_tube(100);

	Show_all();
}
/*==============================================================================*/


/*=====================================时钟=====================================*/
//时钟参数配置
void DS1302_config()
{
	unsigned char i;
	DS1302_WriteByte(0x8e,0x00);     //允许向内存写入数据
	for(i=0;i<7;i++)
	{
		//TIME[i] = ((TIME[i]/10<<4) | (TIME[i]&0x0f));		  //(10->16)
		DS1302_WriteByte(WRITE_RTC_ADDR[i],TIME[i]);   //低位先写
	}
	DS1302_WriteByte(0x8e,0x80);     //禁止向内存写入数据
}

//读取实时时间
void DS1302_Readtime()
{
	unsigned char i;
	for(i=0;i<7;i++)
	{
		TIME[i] = DS1302_ReadByte(READ_RTC_ADDR[i]);
		//TIME[i] = (((TIME[i]>>4)*10) | (TIME[i]%16));   //进制转换(16->10)
		//TIME[i] = (((TIME[i]>>4)*10) | (TIME[i]&0x0f));
		//不能写在这里,会出错!!!! 
	}
}
/*==============================================================================*/


/*===================================温度读取===================================*/
//温度延时函数
void Delay_temp(unsigned int time)
{
	while(time--)
	{
		Display_temp(T_dat);
	}
} 

//读取温度
void Read_temperature()
{
	unsigned char LSB,MSB;
	Init_DS18B20();         //复位
	Write_DS18B20(0xcc);    //跳过ROM操作
	Write_DS18B20(0x44);	//温度转换
	Delay_temp(1000);       //延时700ms,等待温度转换
	Init_DS18B20();         //复位
	Write_DS18B20(0xcc);    //跳过ROM操作
	Write_DS18B20(0xbe);    //读取RAM
	LSB = Read_DS18B20();   //读取温度数据的低8位(低位先读)
	MSB = Read_DS18B20();	//读取温度数据的高8位
	Init_DS18B20();			//复位,停止读取数据

	T_dat = 0x0000;			//长度为16位
	T_dat = MSB;
	T_dat <<= 8;
	T_dat |= LSB;           //整合温度 

	if((T_dat & 0xf800) == 0x0000)    //根据温度数据的高5位判断温度正负,如果温度为正
	{
		T_dat >>= 4;        //保留温度的整数部分
	}
}
/*==============================================================================*/


/*==================================闹钟(定时器0)===============================*/
//定时器0中断初始化
void Init_timer0()
{
	TMOD = 0x11;                    //同时选择定时器0和定时器1
	TH0 = (65535 - 20000) / 256;
	TL0 = (65535 - 20000) % 256;	//20ms定时
	
	EA = 1;                         //打开总中断
	ET0 = 1;                        //打开定时器0的中断
	TR0 = 0;                        //关闭定时器0
}	

//定时器0中断服务函数
void Service_timer0() interrupt 1
{
	TH0 = (65535 - 20000) / 256;
	TL0 = (65535 - 20000) % 256;
	count++;

	if(count % 10 == 0)   //定时每满0.2s
	{
		led = ~led;	
	}
		
	if(count == 250)      //定时满5s
	{
		count = 0;
		led = 1;
		Select_HC138(4);
		P0 = 0xff;        //关闭LED
		Select_HC138(0);
		TR0 = 0;        //关闭定时器0
	}
}
/*==============================================================================*/


/*====================================LED闪烁===================================*/
void LED_flash()
{
	if((TIME[2] == hour)&&(TIME[1] == minute)&&(second == TIME[0]))
	{
		TR0 = 1;
	}
	Select_HC138(4);
	P0 = 0xff;
	L1 = led;
	Select_HC138(0);
}
/*==============================================================================*/


/*===============================数码管闪烁(定时器1)============================*/
//定时器1中断初始化
void Init_timer1()
{
	TMOD = 0x11;  					//同时选择定时器0和定时器1
	TH1 = (65535 - 50000) / 256;
	TL1 = (65535 - 50000) % 256;	//50ms定时

	EA = 1;  						//打开总中断
	ET1 = 1;                 		//打开定时器1的中断
	TR1 = 0;						//关闭定时器1
}

//定时器1中断服务函数
void Service_timer1() interrupt 3
{
	TH1 = (65535 - 50000) / 256;
	TL1 = (65535 - 50000) % 256;
	num++;

	if(num == 10)     //间隔1s
	{
		state = ~state;
		num = 0;
	}	
}
/*==============================================================================*/


/*===================================独立按键===================================*/
//按键延时函数
void Delay_keys()
{
	unsigned char i,j;
	i = 108;
	j = 145;
	do
	{
		while(--j);
	}while(--i);
}

//按键处理
void Press_keys()
{
	if(S7 == 0)
	{
		Delay_keys();              //去抖动
		if(S7 == 0)	               //时钟设置
		{	
			TR0 = 0;
			led = 1;	
			if(alarm == 0)
			{
				switch(clock)
				{
					case 0:
						clock = 1;	   //设置时
						TR1 = 1;       //启动定时器1
						break;
					case 1:
						clock =2;	   //设置分
						break;
					case 2:
						clock = 3 ;	   //设置秒
						break;
					case 3:
						clock = 0;	   //返回时钟显示
						TR1 = 0;	   //关闭定时器1
						break;
				}
				while(S7 == 0)
				{
					Display_time();
				}
			}
		}
	}

	else if(S6 == 0)
	{
		Delay_keys();
		if(S6 == 0)				   //闹钟设置
		{
			TR0 = 0;
			led = 1;
			if(clock == 0)
			{   
				switch(alarm)
				{
					case 0:
						status = 1;	   //数码管显示闹钟
						alarm = 1;	   //设置时
						TR1 = 1;	   //启动定时器1
						break;
					case 1:
						alarm = 2;	   //设置分
						break;
					case 2:
						alarm = 3;	   //设置秒
						break;
					case 3:
						status = 0;    //返回时钟显示
						alarm = 0;	   
						TR1 = 0;	   //关闭定时器1
						break;
				}
				while(S6 == 0)
				{
					Display_alarm();
				}
			}
		}
	}

	else if(S5 == 0)
	{
		Delay_keys();
		if(S5 == 0)				  //时分秒加1操作
		{
			TR0 = 0;
			led = 1;
			if((status == 0)&&(clock != 0))	          //时钟调节状态
			{
				if(clock == 1)
				{		  
					TIME[2]++;		  //时加1
					if(TIME[2] == 24)
					{
						TIME[2] = 0;
					}
				}
				else if(clock == 2)
				{
					TIME[1]++;		  //分加1
					if(TIME[1] == 60)
					{
						TIME[1] = 0;
					}
				}
				else if(clock == 3)
				{
					TIME[0]++;        //秒加1
					if(TIME[0] == 60)
					{
						TIME[0] = 0;
					}
				}
				while(S5 == 0)
				{
					Display_time();	  //保证数码管动态显示
				}
				DS1302_config();    //重新配置日历时钟寄存器
			}
						
			else if((status == 1)&&(alarm != 0))	  //闹钟调节
			{
				if(alarm == 1)        //时加1
				{
					hour++;
					if(hour == 24)
					{
						hour = 0;
					}	
				}
				else if(alarm == 2)	  //分加1
				{
					minute++;
					if(minute == 60)
					{
						minute = 0;
					}
				}
				else if(alarm == 3)  //秒加1
				{
					second++;
					if(second == 60)
					{
						second = 0;
					}
				}
				while(S5 == 0)
				{
					Display_alarm();
				}
			}
		}
		while(S5 == 0);
	}

	else if(S4 == 0)
	{
		Delay_keys();
		if(S4 == 0)			   //时分秒减1操作、显示温度
		{
			TR0 = 0;
			led = 1;
			if((status == 0)&&(clock == 0))    //时钟显示状态下,按下显示温度
			{
				while(S4 == 0)
				{
					Display_temp(T_dat);
				}
			}
								
			else if((status == 0)&&(clock != 0))	 //时钟调节状态
			{
				if(clock == 1)
				{
					if(TIME[2] > 0)
					{
						TIME[2]--;		  //时减1	
					}
				}
				else if(clock == 2)
				{
					if(TIME[1] > 0)
					{
						TIME[1]--;		  //分减1
					}
				}
				else if(clock == 3)
				{
					if(TIME[0] > 0)
					{
						TIME[0]--;        //秒减1	
					}
				}
				while(S4 == 0)
				{
					Display_time();	  //保证数码管动态显示
				}
				DS1302_config();    //重新配置日历时钟寄存器
			}
						
			else if((status == 1)&&(alarm != 0))	  //闹钟调节状态
			{
				if(alarm == 1)        //时减1
				{
					if(hour > 0)
					{
						hour--;	
					}	
				}
				else if(alarm == 2)	  //分减1
				{
					if(minute > 0)
					{
						minute--;
					}
				}
				else if(alarm == 3)   //秒减1
				{
					if(second > 0)
					{
						second--;
					}
				}
				while(S4 == 0)
				{
					Display_alarm();
				}
			}
		}
	}
}
/*==============================================================================*/


/*====================================主函数====================================*/
void main()
{
	Init_system();
	Init_timer0();
	Init_timer1();
	Read_temperature();
	DS1302_config();
	while(1)
	{
		if((status == 0)&&(clock == 0))    //只在时钟显示时,读取当前时间
		{
			DS1302_Readtime();
		}
		switch(status)
		{
			case 0:
				Display_time();	   //显示时钟
				break;
			case 1:
				Display_alarm();   //显示闹钟
				break;
		}
		Press_keys();
		LED_flash();
	}
}
/*==============================================================================*/

你可能感兴趣的:(第八届蓝桥杯省赛单片机组试题——电子钟)