51单片机与DS1302动态数码管时钟(简单项目_已实现)

使用DS1302,51单片机,138译码器,74HC573增加驱动能力,DS18B20显示温度。

#include<reg51.h>
#include<intrins.h>
unsigned char DisplayData[8];
/*定义按键 调时间用的*/
sbit K1 = P3^0;sbit K11 = P3^3;
sbit K2 = P3^1;sbit K22 = P3^4;
sbit K3 = P3^2;sbit K33 = P3^5;
sbit K4 = P3^6;
/*138译码器的三个输入端*/
sbit AA = P2^0;              
sbit BB = P2^1;
sbit CC = P2^2;
/*四个LED灯控制*/
sbit LED1 = P2^4;
sbit LED2 = P2^5;
sbit LED3 = P2^6;
sbit LED4 = P2^7;
/*DS1302引脚设定*/
sbit IO = P0^1;					//数据输入和输出引脚
sbit CE = P0^0;         //复位引脚,高电平启用数据传输
sbit Clock = P0^2;			//时钟输入引脚
/*DS18B20数据线*/
sbit Data = P0^3;
#ifndef __TEMP_H_
#define __TEMP_H_
//---重定义关键词---//
#ifndef uchar
#define uchar unsigned char
#endif

#ifndef uint 
#define uint unsigned int
#endif

//--声明全局函数--//
void Delay1ms(uint );
uchar Ds18b20Init();
void Ds18b20WriteByte(uchar com);
uchar Ds18b20ReadByte();
void  Ds18b20ChangTemp();
void  Ds18b20ReadTempCom();
int Ds18b20ReadTemp();

#endif
/*DS1302中的数据以BCD码存储,每4位2进制表示一位十进制
下面Time数组中的第一个0x00就表示0十秒0秒;0x15表示24小时
形式的下午15点*/
unsigned char Time[] = {0x30,0x33,0x21,0x23,0x01,0x03,0x22};//初始化时间秒,分,时,日,月,周,年
char Read_Cmd[7] = {0x81,0x83,0x85,0x87,0x89,0x8b,0x8d}; 
char Write_Cmd[7] = {0x80,0x82,0x84,0x86,0x88,0x8a,0x8c};
/*数码管0-F段码值*/
unsigned char Seg_Code[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
//--定义使用的IO口--//
/*38译码器的输入端定义*/
//--声明全局函数--//
void Delay1ms(uint);
uchar Ds18b20Init();
void Ds18b20WriteByte(uchar com);
uchar Ds18b20ReadByte();
void  Ds18b20ChangTemp();
void  Ds18b20ReadTempCom();
int Ds18b20ReadTemp();

/*******************************************************************************
* 函 数 名         : Delay1ms
* 函数功能		   : 延时函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/

void Delay1ms(uint y)
{
	uint x;
	for( ; y>0; y--)
	{
		for(x=20; x>0; x--);
	}
}
/*******************************************************************************
* 函 数 名         : Ds18b20Init
* 函数功能		   : 初始化
* 输    入         : 无
* 输    出         : 初始化成功返回1,失败返回0
*******************************************************************************/

uchar Ds18b20Init()
{
	uchar i;
	Data = 0;			 //将总线拉低480us~960us
	i = 70;	
	while(i--);//延时642us
	Data = 1;			//然后拉高总线,如果DS18B20做出反应会将在15us~60us后总线拉低
	i = 0;
	while(Data)	//等待DS18B20拉低总线
	{
		Delay1ms(1);
		i++;
		if(i>5)//等待>5MS
		{
			return 0;//初始化失败
		}
	
	}
	return 1;//初始化成功
}

/*******************************************************************************
* 函 数 名         : Ds18b20WriteByte
* 函数功能		   : 向18B20写入一个字节
* 输    入         : com
* 输    出         : 无
*******************************************************************************/

void Ds18b20WriteByte(uchar dat)
{
	uint i, j;
	for(j=0; j<8; j++)
	{
		Data = 0;	     	  //每写入一位数据之前先把总线拉低1us
		i++;
		Data = dat & 0x01;  //然后写入一个数据,从最低位开始
		i=6;
		while(i--); //延时68us,持续时间最少60us
		Data = 1;	//然后释放总线,至少1us给总线恢复时间才能接着写入第二个数值
		dat >>= 1;
	}
}
/*******************************************************************************
* 函 数 名         : Ds18b20ReadByte
* 函数功能		   : 读取一个字节
* 输    入         : com
* 输    出         : 无
*******************************************************************************/


uchar Ds18b20ReadByte()
{
	uchar byte, bi;
	uint i, j;	
	for(j=8; j>0; j--)
	{
		Data = 0;//先将总线拉低1us
		i++;
		Data = 1;//然后释放总线
		i++;
		i++;//延时6us等待数据稳定
		bi = Data;	 //读取数据,从最低位开始读取
		/*将byte左移一位,然后与上右移7位后的bi,注意移动之后移掉那位补0。*/
		byte = (byte >> 1) | (bi << 7);						  
		i = 4;		//读取完之后等待48us再接着读取下一个数
		while(i--);
	}				
	return byte;
}
/*******************************************************************************
* 函 数 名         : Ds18b20ChangTemp
* 函数功能		   : 让18b20开始转换温度
* 输    入         : com
* 输    出         : 无
*******************************************************************************/

void  Ds18b20ChangTemp()
{
	Ds18b20Init();
	Delay1ms(1);
	Ds18b20WriteByte(0xcc);		//跳过ROM操作命令		 
	Ds18b20WriteByte(0x44);	    //温度转换命令
//	Delay1ms(100);	//等待转换成功,而如果你是一直刷着的话,就不用这个延时了
   
}
/*******************************************************************************
* 函 数 名         : Ds18b20ReadTempCom
* 函数功能		   : 发送读取温度命令
* 输    入         : com
* 输    出         : 无
*******************************************************************************/

void  Ds18b20ReadTempCom()
{	

	Ds18b20Init();
	Delay1ms(1);
	Ds18b20WriteByte(0xcc);	 //跳过ROM操作命令
	Ds18b20WriteByte(0xbe);	 //发送读取温度命令
}
/*******************************************************************************
* 函 数 名         : Ds18b20ReadTemp
* 函数功能		   : 读取温度
* 输    入         : com
* 输    出         : 无
*******************************************************************************/

int Ds18b20ReadTemp()
{
	int temp = 0;
	uchar tmh, tml;
	Ds18b20ChangTemp();			 	//先写入转换命令
	Ds18b20ReadTempCom();			//然后等待转换完后发送读取温度命令
	tml = Ds18b20ReadByte();		//读取温度值共16位,先读低字节
	tmh = Ds18b20ReadByte();		//再读高字节
	temp = tmh;
	temp <<= 8;
	temp |= tml;
	return temp;
}
void delay(int a,int b)
{
	int i,j;
	for(i = 0;i <= a;i ++)
	{
		for(j = 0;j <= b;j ++)
		{}
	}
}
void DigDisplay()
{
	AA=0;BB=0;CC=0;P1 = DisplayData[2];delay(5,10);P1 = 0x00;
	AA=1;BB=0;CC=0; P1 = DisplayData[3];delay(5,10);P1 = 0x80;delay(5,10);P1 = 0x00;
	AA=1;BB=1;CC=0;P1 = DisplayData[4];delay(5,10);P1 = 0x00;
	AA=0;BB=0;CC=1;P1 = DisplayData[5];delay(5,10);P1 = 0x00;
	AA=0;BB=1;CC=1;P1 = 0x39;delay(5,10);P1 = 0x00;//显示C
	LED1 = 1;LED2 = 1;LED3 = 0;LED4 = 1;
}

void LcdDisplay(int temp) 	 //lcd显示
{
  float tp;  
	if(temp< 0)				//当温度值为负数
  {
		DisplayData[0] = 0x40; 
		//因为读取的温度是实际温度的补码,所以减1,再取反求出原码
		temp=temp-1;
		temp=~temp;
		tp=temp;
		temp=tp*0.0625*100+0.5;	
		//留两个小数点就*100,+0.5是四舍五入,因为C语言浮点数转换为整型的时候把小数点
		//后面的数自动去掉,不管是否大于0.5,而+0.5之后大于0.5的就是进1了,小于0.5的就
		//算由?.5,还是在小数点后面。
 
  }
 	else
  {			
		DisplayData[0] = 0x00;
		tp=temp;//因为数据处理有小数点所以将温度赋给一个浮点型变量
		//如果温度是正的那么,那么正数的原码就是补码它本身
		temp=tp*0.0625*100+0.5;	
		//留两个小数点就*100,+0.5是四舍五入,因为C语言浮点数转换为整型的时候把小数点
		//后面的数自动去掉,不管是否大于0.5,而+0.5之后大于0.5的就是进1了,小于0.5的就
		//算加上0.5,还是在小数点后面。
	}
	DisplayData[1] = Seg_Code[temp / 10000];
	DisplayData[2] = Seg_Code[temp % 10000 / 1000];
	DisplayData[3] = Seg_Code[temp % 1000 / 100];
	DisplayData[4] = Seg_Code[temp % 100 / 10];
	DisplayData[5] = Seg_Code[temp % 10];
	DigDisplay();
}
/*向DS1320发送命令,告知对其进行的操作是读还是写,和读写的是时分秒等的哪一位*/
void Ds1302_Cmd(char Cmd)
{
	char n;
	CE = 0;_nop_();
	Clock = 0;_nop_();
	CE = 1;_nop_();
	for(n = 0;n <= 7;n ++)//发送八位命令代码,已保存在上面的数组中
	{
		IO = Cmd & 0x01;
		Cmd = Cmd >> 1;
		Clock = 1;          //上升沿读取数据
		_nop_();
		Clock = 0;          
		_nop_();
	}
}
/*向DS1320中写入8位数据*/
void WriteDatToDs1320(char Dat)
{
	char n;
	for(n = 0;n <= 7;n ++)
	{
		IO = Dat & 0x01;
		Dat = Dat >> 1;
		Clock = 1;          //上升沿读取数据
		_nop_();
		Clock = 0;          //时钟重置为0
		_nop_();
	}
	CE = 0;               //传输数据结束
	_nop_();
}
/*从DS1320接收8位数据*/
char ReadDatFromDs1320()		//数据是从最低位开始接收,因此要进行下列移位操作
{
	char n;
  unsigned char Data_Temp,Data; /*这里注意使用无符号字符类型,否则移位会发生错误!!!
	具体原因可能是C51的编译器关于移位是逻辑右移还是算术右移的问题,我也搞不懂*/
	for(n = 0;n <= 7;n ++)
	{
		Data_Temp = IO;
		Data = ((Data >> 1) | (Data_Temp << 7));
		/*Data右移1位,空出最高位(为0);Data_Temp为接收到的一
		位数据将其左移7位,这样就与Data最高位对齐,在进行“或”运
		算,将数据传给Data,再进行下一次循环,这样先接收的数据就
		逐渐移到了低位,最后接收的数据就到了高位。*/
		Clock= 1;_nop_();
		Clock= 0;					//下降沿放置数据.
		_nop_();
	}
	/*DS1320复位稳定时间*/
	CE = 0;								//关闭数据传输
	_nop_();
	Clock = 1;	
	_nop_();
  IO = 0;	
	_nop_();
	IO = 1;
	_nop_();
	return Data;
}
/*从DS1320中读取全部时间,要进行7次读数据操作,每次读取一个参数*/
void ReadTimeFromDs1320()
{
	unsigned char n;
	for(n = 0;n <= 6;n ++)
	{
		Ds1302_Cmd(Read_Cmd[n]);      //先发送读的命令
		Time[n] = ReadDatFromDs1320();//读取8位数据
	}
}
/*初始化DS1320中的时间为Time数组中的时间,将预先设定的全部时间
写到DS1320中,由于有7个参数,因此要循环7次*/
void Ds1320_Init()
{
	int n;
	Ds1302_Cmd(0x8e);
	WriteDatToDs1320(0x00);//这两句话是关闭写保护
	/*像DS1320中写入:秒,分,时,日,月,周,年七组8位数据*/
	for(n = 0;n <= 6;n ++)
	{
		Ds1302_Cmd(Write_Cmd[n]);WriteDatToDs1320(Time[n]);
		/*例如:当n = 1时,先发送写秒的命令,再发送秒的数据
		命令和数据都提前保存在了数组中,具体命令代码查阅数
		据手册,如下图*/
	}
	Ds1302_Cmd(0x8e);WriteDatToDs1320(0x80);//打开写保护.
}
//扫描数码管显示时间程序
void Display(int Hour1,int Hour2,int Min1,int Min2,int Sec1,int Sec2)
{
	int i,j;
	for(i=0;i<8;i++)
	{
		switch(i)	 //位选,选择点亮的数码管,
		{
			case(0):AA=0;BB=0;CC=0;P1 = Seg_Code[Hour1];break;//显示第0位			
			case(1):AA=1;BB=0;CC=0;P1 = Seg_Code[Hour2];break;//显示第1位
			case(2):AA=0;BB=1;CC=0;P1 = 0X40;break;//显示第2位
			case(3):AA=1;BB=1;CC=0;P1 = Seg_Code[Min1];break;//显示第3位
			case(4):AA=0;BB=0;CC=1;P1 = Seg_Code[Min2];break;//显示第4位
			case(5):AA=1;BB=0;CC=1;P1 = 0X40;break;//显示第5位
			case(6):AA=0;BB=1;CC=1;P1 = Seg_Code[Sec1];break;//显示第6位
			case(7):AA=1;BB=1;CC=1;P1 = Seg_Code[Sec2];break;//显示第7位
		}
		delay(10,10);
		P1 = 0X00;
	}
}
void main()
{
	int i,j,number;
	int Hour1,Hour2,Min1,Min2,Sec1,Sec2;
	int kk1,kk2;
	int flag = 0;
	int temp,temp1;
	K1 = 1;K2 = 2;K3 = 1;K4 = 1;
	K11 = 0;K22 = 0;K33 = 0;
/*	Ds1320_Init();     初始化函数,初次上电需要写入时间,然后注释掉这一行,
	再次上电就会读取芯片中的时间了,再次断电上电就能显示正确的时间了*/
	ReadTimeFromDs1320();//读取时间并存储到Time数组中,这里写在循环外是为了达到LED闪烁的效果
	Hour1 = Time[2]/16;  //小时的十位,Time[2]对16取余,因为是16进制
	Hour2 = Time[2]&0x0f;//保留低四位的小时个位数
	Min1 = Time[1]/16;
	Min2 = Time[1]&0x0f;
	Sec1 = Time[0]/16;
	Sec2 = Time[0]&0x0f;
	Display(Hour1,Hour2,Min1,Min2,Sec1,Sec2);
	kk1 = Sec2;
	while(1)
	{
		ReadTimeFromDs1320();//读取时间并存储到Time数组中
		Hour1 = Time[2]/16;  //小时的十位,Time[2]对16取余,因为是16进制
		Hour2 = Time[2]&0x0f;//保留低四位的小时个位数
		Min1 = Time[1]/16;
		Min2 = Time[1]&0x0f;
		Sec1 = Time[0]/16;
		Sec2 = Time[0]&0x0f;
		Display(Hour1,Hour2,Min1,Min2,Sec1,Sec2);
		kk2 = Sec2;
		if(kk2!=kk1)     //让LED每秒闪烁一次
		{
			LED1 = ~LED1;
			LED2 = ~LED2;
			LED3 = ~LED3;
			LED4 = ~LED4;  
			kk1 = kk2;
			flag ++;
		}
		/******************按键检测*****以下*************/
		/*按键检测,调整小时数,k1,K2同时按下有效,目的是防止误触*/
		if((K2 == 0)&&(K1==0))
		{
			delay(30,10);
			if((K2 == 0)&&(K1==0))
			{
				Time[2] ++;
				if(((Time[2]&0xf0)==0x20)&&(((Time[2]&0x0f)==0x04)))
				{
					Time[2] = 0x00;
					Ds1320_Init();
				}
				else if(((Time[2]&0xf0)==0x10)&&(((Time[2]&0x0f)==0x0a)))
				{
					Time[2] = 0x20;
					Ds1320_Init();					
				}
				else if(((Time[2]&0xf0)==0x00)&&(((Time[2]&0x0f)==0x0a)))
				{
					Time[2] = 0x10;
					Ds1320_Init();					
				}				
				else
				{
					Ds1320_Init();
				}
			}
			delay(300,20);
		}	
		/*按键检测,调整分钟数,k3,K4同时按下有效*/
		/*逻辑判断过程如下:若个位+1得10,则判断十位是否是5,是5的话个位10位都归零。十位不是5的话,十位+1,个位归零*/
		if((K3 == 0)&&(K4==0))
		{
			delay(30,10);
			if((K3 == 0)&&(K4==0))
			{
				Time[1] ++;
				if((Time[1]&0x0f)==0x0a)
				{
					temp1 = Time[1];
					temp1 = temp1 >> 4;
					temp1 ++;
					if((temp1&0x0f) == 6)
					{
						Time[1] = 0x00;
						Ds1320_Init();
						delay(300,20);
					}
					else
					{
						temp1 = Time[1];
						temp1 = temp1 >> 4;
						temp1 ++;
						temp1 = temp1 << 4;
						Time[1] = temp1 & 0xf0;
						Ds1320_Init();
						delay(300,20);
					}
				}
				else
				{
					Ds1320_Init();
					delay(300,20);
				}
			}			
		}		
		/********************按键检测***以上**************************/
		if(flag == 10)
		{
			temp = Ds18b20ReadTemp();
			for(i = 0;i <= 10;i ++)
			{
				for(j = 0;j <= 25;j ++)
				{
					LcdDisplay(temp);
				}
			}
			flag = 0;
			LED1 = 0;LED2 = 0;LED3 = 0;LED4 = 0;//控制LED灯
		}
	}
}

原理图如下:

 说明:1、原理图中去掉了原本的闪烁LED。与程序不太相符,其他部分一致。程序仍可以使用。

            2、74HC573芯片用IC插座代替了。其他也可以代替,自行替换即可。

            3、PCB板原来为USB电源插口,原理图中换成了DC圆形插口。

            4、PCB板没有DP的电阻,实测如果不加电阻,DP和其他数码段无法同时点亮。不加电阻

只能用程序实现扫描显示小数点线效果。

            5、PCB起初没有放置DS18B20后来自己焊接。

成品效果图:

焊接技术辣鸡┭┮﹏┭┮。

注意事项:1、LED要加限流电阻,1K左右即可,不然太亮,不美观。

                  2、焊盘不要设置到数码管的四个角上,不然不好安放。

                  3、选用138译码器和共阴极数码管可能会偏暗,可以选共阳极更好点。

                  4、数码管DP加一个1K左右电阻即可,不然DP和其他数码段无法同时点亮。其他的不

加电阻好像也没事,经过测试没烧坏。

                  5、DS1302的晶振最好选尺寸大一点的,不然不好焊接,焊接不好或者误触,都会导

致走时异常的快或者慢。

 

 

 

 

  • 3
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值