STC学习:实时时钟

程序设计目标及程序运行效果说明
程序设计目标:通过DS1302芯片、晶振、电池和数码管实现实时时钟的数码管显示。
程序运行效果说明:将程序下载至芯片,数码管会出现实时的时钟,断开USB端口,不给实验板外部供电,时钟依然走秒。
程序相关电路及原理说明
1.原理说明
生活中大多数时钟大多是非实时的,只要把电池取下来时钟就将停止工作,或者当你的时钟电池耗尽时它将也停止工作。但是在掉电之后时钟将停止走秒,并丢失掉电前的时间。就像我们的老式洛基亚手机在拆卸电池之后再次安装电池开机会出现要求时间重置的界面,其无法保证手机在掉电之后依然维持时钟走秒。而实时时钟就很好的解决了这个难题。
DS1302是 DALLAS 公司推出的涓流充电时钟芯片内含有一个实时时钟/日历和 31 字节静态 RAM,外接32.768kHz晶振,为芯片提供计时脉冲,在电路板的纽扣电池(位于电路板左下方圆柱体)的持续供电下,实现DS1302的独立时间走动。我们可以直接对DS1302的寄存器进行读写,然后把时分秒等数据显示在实验板的数码管上面。
实时时钟的核心是晶振,晶振频率为32768 Hz。它为分频计数器提供精确的与低功耗的实基信号。它可以用于产生秒、分、时、日等信息。为了确保时钟长期的准确性,晶振必须正常工作,不能够收到干扰。
那实时时钟的晶振频率为什么是32768Hz? 实时时钟时间是以振荡频率来计算的。故它不是一个时间器而是一个计数器。而一般的计数器都是16位的。又因为时间的准确性很重要,故震荡次数越低,时间的准确性越低。所以必定是个高次数。32768 Hz = 2^15 即分频15次后为1Hz,周期 = 1s;经过工程师的经验总结32768 Hz,时钟最准确。
2.电路原理图及其工作原理
2.1 DS1302模块电路
在这里插入图片描述
DS1302的2、3引脚外接32.768kHz 的晶振,为芯片提供计时脉冲,通过秒寄存器的最高位控制晶振的工作状态,当为高时,停止工作;当为低时,晶振开始工作,实时模块自动计时。
RTC_SCLK引脚作为输入引脚,用于在串行接口上控制数据的输入与输出
RTC_IO引脚作为输入输出引脚,为实时时钟的数据线。
RTC_/RST引脚作为输入引脚,在读、写数据时必须置为高电平。该引脚有两个功能:第一,CE开始控制字访问移位寄存器的控制逻辑;其次,CE 提供结束单字节或多字节数据传输的方法,即:
在这里插入图片描述

DS1302对应的时序:单个字节读:在前8个SCLK时钟周期内,上升沿写入控制字,在后8个SCLK时钟周期内,下降沿读取数据字;均从最低位开始。
单个字节写:在前8 个 SCLK 时钟周期,上升沿写入控制字,在后 8 个 SCLK 时钟周期,上升沿写入数据字;均从最低位开始。
2.2 数码管显示电路
在这里插入图片描述
其中LED_SEL(P2.3)引脚决定E3的值:当LED_SEL为低时,使能74HC138译码器工作产生位选信号;LED共阴极,段选信号有效相应位置的LED管亮;通过定时器以一定频率扫描位选信号,结合段选信号进行数码管点亮,从而给人视觉上几个数码管几乎同时显示的效果。

用途
实时时钟不但可以作为家用,而且更可以在公共场合使用,如车站、码头、商场等场所,一个稳定可靠的时钟在我们的日常生活中具有很实际的意义,特别实在各种嵌入式系统中用于记录事件发生的时间和相关信息,如通信工程、电力自动化、工业控制等自动化程度高的领域的无人值守环境中。

代码如下:

#include"STC15F2K60S2.H"
#include"intrins.H"
#include"ctype.h"
#define uchar unsigned char 	   
#define uint unsigned int
//DS1302寄存器的定义
#define DS1302_second_write  0X80
#define DS1302_minutes_write  0X82
#define DS1302_hour_write  0X84
#define DS1302_date_write  0X86
#define DS1302_week_write  0X8A
#define DS1302_month_write  0X88
#define DS1302_year_write  0X8C
#define DS1302_second_read  0X81
#define DS1302_minutes_read  0X83
#define DS1302_hour_read  0X85
#define DS1302_date_read  0X87
#define DS1302_week_read  0X8B
#define DS1302_month_read  0X89
#define DS1302_year_read  0X8D
//位定义
sbit RTC_sclk=P1^5;//时钟控制引脚,控制数据的输入输出
sbit RTC_rst=P1^6;//CE引脚,读写时必须置高电平
sbit RTC_io=P5^4;//数据引脚
//显示的位定义
sbit led_sel=P2^3;
uchar wei[]={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07};	  //数码管位选
uchar duan[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//数码管段选
//定义时间结构体
typedef struct _systemtime_
{
   uchar second;
	 uchar minute;
	 uchar hour;
	 uchar day;
	 uchar week;
	 uchar  month;
   uchar year;
}systemtime;
systemtime t;
uchar i;
uchar temp;
int sec=0;
char flag_100mS=0;
//DS1302写一个字节的数据
void DS1302WriteByte(uchar dat)
{
	uchar i;
    RTC_sclk=0;//初始时钟线置0
	_nop_();
	_nop_();
	for(i=0;i<8;i++)//开始传输8位数据
	{
		RTC_io=dat&0x01;//取最低位
		_nop_();
	    _nop_();
		RTC_sclk=1;//时钟线拉高,制造上升沿,数据被传输
		_nop_();
	    _nop_();
		RTC_sclk=0;//时钟线拉低,为下一个上升沿作准备
		dat>>=1;//数据右移以为,准备传输下一位数据
	}
}
DS1302读一个字节的数据
uchar DS1302ReadByte()
{
	uchar i,dat;
	_nop_();
	_nop_();
	for(i=0;i<8;i++)
	{
		dat>>=1;//要返回的数据右移一位
		if(RTC_io==1)//当数据线为高时,证明该位数据为1
		dat|=0x80;
		RTC_sclk=1;
		_nop_();
	    _nop_();
		RTC_sclk=0;//制造下降沿
		_nop_();
	    _nop_();
	}
	return dat;//返回读取出的数据
}
//读相应地址中写一个字节的数据
uchar DS1302Read(uchar cmd)
{
	uchar dat;
	RTC_rst=0;//初始CE置0
	RTC_sclk=0;//初始时钟置0
	RTC_rst=1;//初始CE置1,传输开始
	DS1302WriteByte(cmd);//传输命令字
	dat=DS1302ReadByte();//读取得到的时间
	RTC_sclk=1;//时钟线拉高
	RTC_rst=0;//读取结束,CE置0,结束数据传输
	return dat;//返回得到的时间日期
}
//在相应地址写数据
void DS1302Write(uchar cmd,uchar dat)
{
	RTC_rst=0;//初始CE置0
	RTC_sclk=0;//初始时钟置0
	RTC_rst=1;//初始CE置1,传输开始
	DS1302WriteByte(cmd);//传输命令字,要写入的时间的地址
	DS1302WriteByte(dat);//写入修改的时间
	RTC_sclk=1;//时钟线拉高
	RTC_rst=0;//读取结束,CE=0,结束数据传输
}
//DS1302的时间值获取程序
systemtime GetDA1302()
{
	systemtime time;
	uchar realvalue;
	realvalue=DS1302Read(DS1302_second_read);
	time.second=((realvalue&0x70)>>4)*10+(realvalue&0x0f);
		realvalue=DS1302Read(DS1302_minutes_read);
	time.minute=((realvalue&0x70)>>4)*10+(realvalue&0x0f);
		realvalue=DS1302Read(DS1302_hour_read );
	time.hour=((realvalue&0x70)>>4)*10+(realvalue&0x0f);
		realvalue=DS1302Read(DS1302_date_read );
	time.day=((realvalue&0x70)>>4)*10+(realvalue&0x0f);
		realvalue=DS1302Read(DS1302_week_read);
	time.week=((realvalue&0x70)>>4)*10+(realvalue&0x0f);
		realvalue=DS1302Read(DS1302_month_read);
	time.month=((realvalue&0x70)>>4)*10+(realvalue&0x0f);
			realvalue=DS1302Read(DS1302_year_read);
	time.year=((realvalue&0x70)>>4)*10+(realvalue&0x0f);
	return time;
}
//DS1302初始化程序
void Init_DS1302()
{  	unsigned char hour,min,sec;
//    unsigned char code DataStr[]=__DATE__;	  //格式: "Jan 13 2017"   12字符(含结束符)
    unsigned char code DataStr[]=__TIME__;	  //格式:"09:12:04"	  9字符(含结束符)
    hour=((toint(DataStr[0]))<<4)+toint(DataStr[1]);
	min=((toint(DataStr[3]))<<4)+toint(DataStr[4]);
	sec=((toint(DataStr[6]))<<4)+toint(DataStr[7]);
	DS1302Write(0X8E,0X00);写保护关
	DS1302Write(DS1302_second_write,sec);
	DS1302Write(DS1302_minutes_write,min);
	DS1302Write(DS1302_hour_write,hour);
//    DS1302Write(DS1302_week_write,0X00);
//	DS1302Write(DS1302_month_write,0X00);
//	DS1302Write(DS1302_date_write,0X00);
//	DS1302Write(DS1302_year_write,0X00);
	temp=DS1302Read(DS1302_second_read)&0x7f;
	DS1302Write(DS1302_second_write,temp);
//	DS1302Write(0x90,0xa9);         //充电设置:允许充电,2个二极管,2k电
	DS1302Write(0X8E,0X80);//写保护置1
}
 void set_charge_DS1302()
{ 
	DS1302Write(0X8E,0X00); //写保护关
	DS1302Write(0x90,0xa9); //充电设置:允许充电,2个二极管,2k电
	DS1302Write(0X8E,0X80); //写保护置1
}
//系统初始化程序
void init()
{ 	P3=0xEF;				  //关蜂鸣器
	P2M0=0XFF;
	P2M1=0X00;
	P0M0=0XFF;
	P0M1=0X00;

	led_sel=0;//选通数码管
	TMOD=0X01;//定时器0,工作方式1
	EA=1;//打开总中断
	TH0=(65535-1000)/256;//设置定时初值
	TL0=(65535-1000)%256;
	TR0=1;//启动定时器
	ET0=1;//开启定时器中断
}
void time0() interrupt 1
{
	TH0=(65535-1000)/256;//设置定时初值
	TL0=(65535-1000)%256;
	EA=0;
	i++;
	if(++sec==100) {sec=0; flag_100mS=1; }
	if(i==8)
		i=0;
	led_sel=0;
	P0=0X00;
	P2=wei[i];
	switch(i)
	{
		case 0:P0=duan[t.hour/10];break;
		case 1:P0=duan[t.hour%10];break;
			case 3:P0=duan[t.minute/10];break;
		case 4:P0=duan[t.minute%10];break;
			case 6:P0=duan[t.second/10];break;
		case 7:P0=duan[t.second%10];break;
		default :P0=0x40;break;
	}
	EA=1;
}

void main()
{
	init();
	if(DS1302Read(DS1302_second_read)&0X80)	Init_DS1302();
	set_charge_DS1302();
	while(1)
	{
		if (flag_100mS==1)
		{
		 t=GetDA1302();
		 flag_100mS=0;
		}
	}
}
  • 12
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

布布要成为最负责的男人

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

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

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

打赏作者

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

抵扣说明:

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

余额充值