7.1  DS1302时钟简单分析

★ 实时时钟计算年、月、日、时、分、秒、星期,直到 2100 年,并有闰年调节功能
★ 31 x 8 位 通用暂存 RAM
★ 串行输入输出使管脚数最少
★ 2.0V 至 5.5V 宽电压范围操作
★ 在 2.0V 时工作电流小于 300nA
★ 读写时钟或 RAM 数据时有单字节或多字节(脉冲串模式)数据传送方式
★ 8 管脚 DIP 封装或可选的 8 管脚表面安装 SO 封装
★ 简单的 3 线接口
★ 与 TTL 兼容 (VCC = 5V)
★ 可选的工业温度范围:-40°C to +85°C
★ 与 DS1202 兼容


DS1302 涓流充电计时芯片包含一个实时时钟/日历和 31 字节的静态 RAM.通过简单的串行接口与微处理器通讯.这个实时时钟/日历提供年月日,时分秒信息.对于少于 31 天的月份月末会自动调整,还有闰年校正.由于有一个 AM/PM 指示器,时钟可以工作在 12 小时制或者 24小时制。使用同步串行通讯简化了 DS1302 与微处理器的接口。与时钟/RAM 通讯只需要三根线: CE,I/O (数据线), and SCLK (串行时钟). 数据输出输入时钟/RAM 一次1字节或者在脉冲串中多管脚定义

DS1302涓流充电计时芯片达 31 字节. DS1302 被设计工作在非常低的电能下,在低于 1μW 时还能保持数据和时钟信息.DS1302 是 DS1202 的后继者. 除了 DS1202 的基本计时功能以外, DS1302 有额外特点比如,双管脚主电源和备用电源,可编程涓流充电器 VCC1,还附加 7 字节的暂存器.

电路上面我们简单分析一下,

  1. C4和R9构成RC滤波电路,可以提供稳定的电源,
  2. X1为32.768KHZ
  3. 电容C6、C8最好是22-30pF
  4. RC就是我们开发板的电池,这里我们在设计时没有加上二极管,这里最好可以加上一个二极管,防止电流反灌到电池,可以起到保护电路的作用。
  5. R10、R11、R14是上拉电阻可以提供更稳定的信号,防止单片机无法拉高。

7.2  DS1302驱动程序

ds1302.c

/**********************************
包含头文件
**********************************/
#include "ds1302.h"
/**********************************
变量定义
**********************************/
//初始时间2023年9月5号15点15分30秒 星期二
char time_buf[8] = {0x20,0x23,0x09,0x05,0x15,0x15,0x30,0x02};
/****
*******向DS1302写入一字节数据
*****/
void ds1302_write_byte(uchar addr, uchar d) 
{
	uchar i;

	DS1302_RST=0;
	_nop_();	
	DS1302_SCK=0;//CLK低电平
	_nop_();
	DS1302_RST=1;//RST由低到高变化
	_nop_();	
	//写入目标地址:addr
	addr = addr & 0xFE;   //最低位置零,寄存器0位为0时写,为1时读
	for(i = 0; i < 8; i ++)
	{
		if (addr & 0x01)
		{
			DS1302_DAT=1;
		}
		else
		{
			DS1302_DAT=0;
		}
		DS1302_SCK=1;     			 		//产生时钟
		DS1302_SCK=0;
		addr = addr >> 1;
	}	
	//写入数据:d
	for(i = 0; i < 8; i ++)
	{
		if (d & 0x01)
		{
			DS1302_DAT=1;
		}
		else
		{
			DS1302_DAT=0;
		}
		DS1302_SCK=1;    					//产生时钟
		DS1302_SCK=0;
		d = d >> 1;
	}
	DS1302_RST=0;								//停止DS1302总线
	_nop_();
}
/****
*******从DS1302读出一字节数据
*****/
uchar ds1302_read_byte(uchar addr)
{
	uchar i,temp;

	DS1302_RST=0;
	_nop_();	
	DS1302_SCK=0;//CLK低电平
	_nop_();
	DS1302_RST=1;//RST由低到高变化
	_nop_();
	//写入目标地址:addr
	addr = addr | 0x01;   //最低位置高,寄存器0位为0时写,为1时读
	for (i = 0; i < 8; i ++)
	{
		if (addr & 0x01)
		{
			DS1302_DAT=1;
		}
		else
		{
			DS1302_DAT=0;
		}
		DS1302_SCK=1;
		DS1302_SCK=0;
		addr = addr >> 1;
	}	
	//输出数据:temp
	for (i = 0; i < 8; i ++)
	{
		temp = temp >> 1;
		if (DS1302_DAT)
		{
			temp |= 0x80;
		}
		else
		{
			temp &= 0x7F;
		}
		DS1302_SCK=1;
		DS1302_SCK=0;
	}	
	DS1302_RST=0;								//停止DS1302总线
	_nop_();	
	DS1302_SCK=1;
	return temp;
}

/****
*******向DS302写入时钟数据
*****/
void ds1302_write_time(void) 
{
	ds1302_write_byte(ds1302_control_add,0x00);							//关闭写保护 
	ds1302_write_byte(ds1302_sec_add,0x80);									//暂停时钟 
	ds1302_write_byte(ds1302_charger_add,0xa9);	    			//涓流充电 
	ds1302_write_byte(ds1302_year_add,time_buf[1]);					//年 
	ds1302_write_byte(ds1302_month_add,time_buf[2]);				//月 
	ds1302_write_byte(ds1302_date_add,time_buf[3]);					//日 
	ds1302_write_byte(ds1302_hr_add,time_buf[4]);						//时 
	ds1302_write_byte(ds1302_min_add,time_buf[5]);					//分
	ds1302_write_byte(ds1302_sec_add,time_buf[6]);					//秒
	ds1302_write_byte(ds1302_day_add,time_buf[7]);					//周 
	ds1302_write_byte(ds1302_control_add,0x80);							//打开写保护     
}

/****
*******从DS302读出时钟数据
*****/
void ds1302_read_time(void)  
{
	time_buf[1]=ds1302_read_byte(ds1302_year_add);					//年 
	time_buf[2]=ds1302_read_byte(ds1302_month_add);					//月 
	time_buf[3]=ds1302_read_byte(ds1302_date_add);					//日 
	time_buf[4]=ds1302_read_byte(ds1302_hr_add);						//时 
	time_buf[5]=ds1302_read_byte(ds1302_min_add);						//分 
	time_buf[6]=(ds1302_read_byte(ds1302_sec_add))&0x7f;		//秒,屏蔽秒的第7位,避免超出59
	time_buf[7]=ds1302_read_byte(ds1302_day_add);						//周 	
}

ds1302.h

#ifndef _DS1302_H_
#define _DS1302_H_
/**********************************
包含头文件
**********************************/
#include "main.h"
/**********************************
PIN口定义
**********************************/
sbit DS1302_RST=P1^4;				//ds1302复位引脚P1.4
sbit DS1302_DAT=P1^3;				//ds1302数据引脚P1.5
sbit DS1302_SCK=P1^2;				//ds1302时钟引脚P1.6
/**********************************
重定义关键词
**********************************/
#define ds1302_sec_add				0x80				//秒数据地址
#define ds1302_min_add				0x82				//分数据地址
#define ds1302_hr_add				0x84				//时数据地址
#define ds1302_date_add				0x86				//日数据地址
#define ds1302_month_add			0x88				//月数据地址
#define ds1302_day_add				0x8a				//星期数据地址
#define ds1302_year_add				0x8c				//年数据地址
#define ds1302_control_add		        0x8e				//控制数据地址
#define ds1302_charger_add		        0x90 				//涓流充电地址
#define ds1302_clkburst_add		        0xbe				//分页地址
/**********************************
变量定义
**********************************/
extern char time_buf[8];
/**********************************
函数声明
**********************************/
void ds1302_write_time(void);       			//向DS302写入时钟数据
void ds1302_read_time(void);        			//从DS302读出时钟数据
#endif

main.c

/**********************************
包含头文件
**********************************/
#include "main.h"
#include "lcd1602.h"
#include "ds1302.h"
/**********************************
变量定义
**********************************/
uchar dis_buf[20];
uint nian;
uint yue,ri,shi,fen,miao,xingqi;
/**********************************
函数名: usleep
传递参数: ms
返回参数: 无
函数作用: 延时1ms函数
**********************************/
void usleep(unsigned int ms)
{
  unsigned int m,n;
	for(m=ms;m>0;m--)
	for(n=110;n>0;n--);
}
/**********************************
函数名: main
传递参数:无
返回参数:无
函数作用:主函数
**********************************/
void main(void)
{
  lcd1602_init();    //LCD1602初始化
  lcd1602_clear();   //清屏
//  ds1302_write_time(); //预设初始时间
  while(1) 
  {
    ds1302_read_time();
    nian =  time_buf[1]/0x10*10 + time_buf[1]%0x10;
    yue = time_buf[2]/0x10*10 + time_buf[2]%0x10;
    ri = time_buf[3]/0x10*10 + time_buf[3]%0x10;
    shi = time_buf[4]/0x10*10 + time_buf[4]%0x10;
    fen = time_buf[5]/0x10*10 + time_buf[5]%0x10;
    miao = time_buf[6]/0x10*10 + time_buf[6]%0x10;
    xingqi = time_buf[7]/0x10*10 + time_buf[7]%0x10;
    
    sprintf(dis_buf,"20%.2d-%.2d-%.2d",nian,yue,ri);
		lcd1602_show_string(0,0,dis_buf);
    sprintf(dis_buf,"%.2d:%.2d:%.2d",shi,fen,miao);
    lcd1602_show_string(4,1,dis_buf);
    if(xingqi == 1)
    {
      lcd1602_show_string(12,0,"Mon ");			//显示星期一英文缩写
    }
    else if(xingqi == 2)
    {
      lcd1602_show_string(12,0,"Tues");			//显示星期二英文缩写
    }
    else if(xingqi == 3)
    {
      lcd1602_show_string(12,0,"Wed ");			//显示星期三英文缩写
    }
    else if(xingqi == 4)
    {
      lcd1602_show_string(12,0,"Thur");			//显示星期四英文缩写
    }
    else if(xingqi == 5)
    {
      lcd1602_show_string(12,0,"Fri ");			//显示星期五英文缩写
    }
    else if(xingqi == 6)
    {
      lcd1602_show_string(12,0,"Sat ");			//显示星期六英文缩写
    }
    else
    {
      lcd1602_show_string(12,0,"Sun ");			//显示星期天英文缩写
    }
  }
}

发表回复

后才能评论