51单片机入门:DS1302时钟

51单片机内部含有晶振,可以实现定时/计数功能。但是其缺点有:精度往往不高、不能掉电使用等。 我们可以通过DS1302时钟芯片来解决以上的缺点。


DS1302时钟芯片

功能:DS1302是一种低功耗实时时钟芯片,内部有自动的计时功能,其范围包括:年、月、日、时、分、秒、星期。(且每个月的天数和闰年的天数可以内部自动调整)。

结构以及引脚定义

常见的DS1302时钟芯片有DIP(直插式)和SO(表贴式)两种封装 。

|-------|---------------------------|
| 引脚名称 | 功能 |
| X1、X2 | DS1302外部晶振引脚,通常接32.768K晶振 |
| VCC1 | 备用电源,在主电源使用时可被充电 |
| VCC2 | 主电源供应管脚(与单片机共用一个电源) |
| GND | 电源地 |
| CE | 使能端,也是复位引脚,在进行读写时CE要保持高电平 |
| I/O | 串行数据输入或输出 |
| SCLK | 串行时钟 |
[引脚定义]

通常通过单片机直接与CE、I/O、SCLK这3个引脚相连,控制其高低电平,进而控制DS1302时钟芯片。

DS1302电路图

其中,C2和C3为旁路电容,目的是消除晶振起振时产生的电感干扰。对于此电路,没有接入备用电池,可自行将外部备用电源接入VCC1。


DS1302内部寄存器

控制寄存器

功能:通过给控制寄存器数据,来决定:进入年月日等具体哪一个寄存器、进行读/写操作。

上图为控制寄存器样式。

最高位一直为1;

第6位:1表示RAM,寻址内部存储器;0为CK,寻址内部寄存器。

第5位~第1位:为年月日等日历寄存器的地址。

最低位:1表示下一步操作为"读";0表示下一步操作为"写"。

日历/时钟寄存器

上图为年月日等相关寄存器的地址(绿框内)、功能等。以下对其进行详细说明:

|--------|---------------|--------------|----|-------|------|----|----|----|----|
| 寄存器名称 | 取值范围 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
| 秒寄存器 | 00~59 | CH | 秒的十位 ||| 秒的个位 ||||
| 分寄存器 | 00~59 | 0 | 分的十位 ||| 分的个位 ||||
| 小时寄存器 | 1~12或 00~23 | 12小时制/ 24小时制 | 0 | AM/PM | Hour | 小时的个位 ||||
| 日寄存器 | 01~31 | 0 | 0 | 日的十位 || 日的个位 ||||
| 月寄存器 | 01~12 | 0 | 0 | 0 | 1或0 | 月的个位 ||||
| 星期寄存器 | 01~07 | 0 | 0 | 0 | 0 | 星期几 ||||
| 年寄存器 | 01~99 | 20XX年的十位 |||| 年的个位 ||||
| 写保护寄存器 | | WP | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
[相关寄存器的位说明]

需特别注意以下部分:

秒寄存器:CH为DS1302的运行标志,当CH=0时,DS1302内部才能工作;当CH=1时,DS1302停止工作。

写保护寄存器:当WP为1时,DS1302只读不写。所以进行写操作时要确保WP为0。


DS1302初始时间设置

举个例子,如果想将2024.05.04---周7---23:59:49这个时间写入DS1302内部,具体操作如下:

1、对0x8E地址操作,关闭写保护;

2、对秒寄存器0x80操作,写入0x49;

3、对分寄存器0x82操作,写入0x59;

4、对时寄存器0x84操作,写入0x23;

......以此类推,最后对0x8E地址操作,打开写保护。

BCD码

BCD码:用4位二进制数来表示1位十进制数。

例如:0001 0011表示13、1000 0101表示85、0001 1010不合法。

日历/时钟寄存器内部 都是以BCD码来存放数据。因此要设置DS1302的时间,要写入BCD码格式。同时读取数据时,读到的也是BCD码,使用时需转换为对应十进制。

|---|------|---|------|
| 0 | 0000 | 5 | 0101 |
| 1 | 0001 | 6 | 0110 |
| 2 | 0010 | 7 | 0111 |
| 3 | 0011 | 8 | 1000 |
| 4 | 0100 | 9 | 1001 |

转换公式:

读写时序

前面提到:单片机与DS1302相连的线有3根,分别为:CE、SCLK和I/O引脚;如何通过这3根线进行数据写入和读出呢?

CE:初始化使其为低电平,在整个读写过程中,要保持高电平;一次读写操作完毕后,再回到低电平。

cpp 复制代码
//  功能:DS1302初始化
void DS1302_Init(){
	DS1302_CE=0;    //使能端置0
	DS1302_SCLK=0;  //时钟脉冲置0
}

单字节写入 :(先关闭写保护)当CE为高电平时,通过单片机控制SCLK产生脉冲,每一个上升沿,I/O线的数据就进入控制寄存器;当控制寄存器配置完成后,紧接着的I/O线数据会在上升沿时进入对应地址的寄存器。

cpp 复制代码
/**
   *@breaf DS1302单字节写入函数
   *@param command:写入命令字,包含要写入寄存器的地址;
   *@param Data:将要写入的数据内容;
   *@retval 无
   */
void  DS1302_WriteBety(unsigned char command,Data)
{
			unsigned char i;
			DS1302_CE=1;//使能位置高电平;


//设置控制寄存器
			for(i=0;i<8;i++)//控制寄存器数据需要通过IO线一个一个写入控制寄存器;低位先写入
		{
				DS1302_IO=command&(0x01<<i);
				DS1302_SCLK=1;  //给一个上升沿
				DS1302_SCLK=0;
		}


//设置相关年月日寄存器
				for(i=0;i<8;i++)//数据写入
		{
				DS1302_IO=Data&(0x01<<i);
				DS1302_SCLK=1;
				DS1302_SCLK=0;
		}
			DS1302_CE=0;	
}

单字节读出 :当CE为高电平时,通过单片机控制SCLK产生脉冲,每一个上升沿,I/O线的数据就进入控制寄存器;当控制寄存器配置完成后,紧接着对应地址的寄存器数据会在下降沿时进入I/O线。

cpp 复制代码
/**
   *@breaf  DS1302单字节读出函数
   *@param command:写入控制指令的指令,包含要读出寄存器的地址;
   *@retval Data:读出的数据;
   */
unsigned char   DS1302_ReadBety(unsigned char command)
{
	unsigned i,Data=0X00;
	    command|=0X01;//写入指令与读出指令只在最后一位相差1,故在此利用或运算消除;
			DS1302_CE=1;//使能位置高电平;
		for(i=0;i<8;i++)//控制寄存器数据需要通过IO线一个一个写入控制寄存器;低位先写入
		{
				DS1302_IO=command&(0x01<<i);
				DS1302_SCLK=0;	
				DS1302_SCLK=1;
		}
				DS1302_IO=0;
			for(i=0;i<8;i++)//数据读出
		{
					DS1302_SCLK=1;
					DS1302_SCLK=0;			
					if(DS1302_IO)  
					{Data |=(0X01<<i);}
		}
		DS1302_CE=0;
		return Data;
}

注意数据输入从低位开始。


代码设计

main.c

cpp 复制代码
#include <REGX52.H>
#include "lcd1602.h"//包含LCD1602头文件
#include "DS1302.h"//包含DS1302头文件
void main ()//定义主函数
{
	
		LCD_Init();	//LCD1602初始化
		DS1302_Init();//DS1302初始化
		LCD_ShowString(1,1,"  -  -  ");//设置年月日格式
		LCD_ShowString(2,1,"  :  :  ");//设置时分秒格式
		DS1302_SetTime();//设置时间,通过数组进行设置
	
		while(1)
		{
		  		DS1302_ReadTime();//读取内部时间
					LCD_ShowNum(1,1,DS1302_Time[0],2);//显示年
					LCD_ShowNum(1,4,DS1302_Time[1],2);//显示月
					LCD_ShowNum(1,7,DS1302_Time[2],2);//显示日
					LCD_ShowNum(2,1,DS1302_Time[3],2);//显示时
					LCD_ShowNum(2,4,DS1302_Time[4],2);//显示分
					LCD_ShowNum(2,7,DS1302_Time[5],2);//显示秒
					LCD_ShowNum(2,13,DS1302_Time[6], 1);//显示星期
    	}
}

DS1302.c

cpp 复制代码
#include <REGX52.H>
 
//引脚定义;
sbit  DS1302_SCLK=P3^6;
sbit  DS1302_IO=P3^4;
sbit  DS1302_CE=P3^5;
 
//对应寄存器位置定义
#define  DS1302_SECOND		0X80
#define  DS1302_MINUTE  	0X82
#define  DS1302_HOUR		0X84
#define  DS1302_DATE        0X86
#define  DS1302_MONTH		0X88
#define  DS1302_DAY         0X8A
#define  DS1302_YEAR        0X8C
#define  DS1302_WP          0X8E 
 
//定义数组,用于存放设定的时间,年月日时分秒星期
unsigned char DS1302_Time[]={23,10, 28,19,00,59,6};//顺序:年月日时分秒星期
/**
   *@breaf DS1302初始化
   *@param无
   *@retval无
   */
void  DS1302_Init()
{
	DS1302_CE=0;//将使能位置0,低电平;
	DS1302_SCLK=0;//将时钟位置0,低电平;
}
 
/**
   *@breaf DS1302单字节写入函数
   *@param command:写入控制指令的指令,包含要写入寄存器的地址;
	 *@param Data:将要写入的数据内容;
   *@retval 无
   */
void  DS1302_WriteBety(unsigned char command,Data)
{
			unsigned char i;
			DS1302_CE=1;//使能位置高电平;
			for(i=0;i<8;i++)//控制寄存器数据需要通过IO线一个一个写入控制寄存器;低位先写入
		{
				DS1302_IO=command&(0x01<<i);//相当于把第1--7位置0,只留第0位,如果第0位是0,则为0;反之则为1;
				DS1302_SCLK=1;
				DS1302_SCLK=0;
		}
		
				for(i=0;i<8;i++)//数据写入
		{
				DS1302_IO=Data&(0x01<<i);//相当于把第1--7位置0,只留第0位,如果第0位是0,则为0;反之则为1;
				DS1302_SCLK=1;
				DS1302_SCLK=0;
		}
			DS1302_CE=0;	
}
 
/**
   *@breaf  DS1302单字节读出函数
   *@param command:写入控制指令的指令,包含要读出寄存器的地址;
   *@retval Data:读出的数据;
   */
unsigned char   DS1302_ReadBety(unsigned char command)
{
	unsigned i,Data=0X00;
	    command|=0X01;//写入指令与读出指令只在最后一位相差1,故在此利用或运算消除;
			DS1302_CE=1;//使能位置高电平;
		for(i=0;i<8;i++)//控制寄存器数据需要通过IO线一个一个写入控制寄存器;低位先写入
		{
				DS1302_IO=command&(0x01<<i);//相当于把第1--7位置0,只留第0位,如果第0位是0,则为0;反之则为1;
				DS1302_SCLK=0;	
				DS1302_SCLK=1;
		}
				DS1302_IO=0;
			for(i=0;i<8;i++)//数据读出
		{
					DS1302_SCLK=1;
					DS1302_SCLK=0;			
					if(DS1302_IO)  
					{Data |=(0X01<<i);}
		}
		DS1302_CE=0;
		return Data;
}
 
 
/**
   *@breaf 向DS1302内设定时间
   *@param无
   *@retval无
   */
void DS1302_SetTime()
{
		DS1302_WriteBety(DS1302_WP,0x00);//操作 DS1302 之前,关闭写保护,不然指令无法进入控制寄存器;
		DS1302_WriteBety(DS1302_YEAR, DS1302_Time[0]/10*16+DS1302_Time[0]%10);//写入年,并将10进制转化BCD码;
		DS1302_WriteBety(DS1302_MONTH, DS1302_Time[1]/10*16+DS1302_Time[1]%10);//写入月,并将10进制转化BCD码;
		DS1302_WriteBety(DS1302_DATE, DS1302_Time[2]/10*16+DS1302_Time[2]%10);//写入日,并将10进制转化BCD码;
		DS1302_WriteBety(DS1302_HOUR, DS1302_Time[3]/10*16+DS1302_Time[3]%10);//写入时,并将10进制转化BCD码;
		DS1302_WriteBety(DS1302_MINUTE, DS1302_Time[4]/10*16+DS1302_Time[4]%10);//写入分,并将10进制转化BCD码;
		DS1302_WriteBety(DS1302_SECOND, DS1302_Time[5]/10*16+DS1302_Time[5]%10);//写入秒,并将10进制转化BCD码;
		DS1302_WriteBety(DS1302_DAY, DS1302_Time[6]/10*16+DS1302_Time[6]%10);//写入星期,并将10进制转化BCD码;
		DS1302_WriteBety( DS1302_WP,0x80);//写入结束,开启写保护;
}
 
/**
*@breaf 读取DS1302内时间
*@param无
*@retval无
*/
void DS1302_ReadTime()	
{
	unsigned char Temp;//定义变量,用于暂时存储BCD码
    Temp=DS1302_ReadBety(DS1302_YEAR);//读取年BCD码;
		DS1302_Time[0]=Temp/16*10+Temp%16;//BCD码转十进制;
	 Temp=DS1302_ReadBety(DS1302_MONTH);//读取月BCD码;
		DS1302_Time[1]=Temp/16*10+Temp%16;//BCD码转十进制;
	 Temp=DS1302_ReadBety(DS1302_DATE);//读取日BCD码;
		DS1302_Time[2]=Temp/16*10+Temp%16;//BCD码转十进制;
	 Temp=DS1302_ReadBety(DS1302_HOUR);//读取小时BCD码;
		DS1302_Time[3]=Temp/16*10+Temp%16;//BCD码转十进制;
	 Temp=DS1302_ReadBety(DS1302_MINUTE);//读取分钟BCD码;
		DS1302_Time[4]=Temp/16*10+Temp%16;//BCD码转十进制;
	 Temp=DS1302_ReadBety(DS1302_SECOND);//读取秒BCD码;
		DS1302_Time[5]=Temp/16*10+Temp%16;//BCD码转十进制;
	 Temp=DS1302_ReadBety(DS1302_DAY);//读取星期BCD码;
		DS1302_Time[6]=Temp/16*10+Temp%16;//BCD码转十进制;
}

DS1302.h

cpp 复制代码
#ifndef __DS1302_H__
#define __DS1302_H__
 
extern unsigned char DS1302_Time[];//声明设置时间的数组
void  DS1302_Init();//声明初始化函数
void  DS1302_WriteBety(unsigned char command,Data);//声明时间写入函数
unsigned char   DS1302_ReadBety(unsigned char command);//声明时间读出函数
void DS1302_SetTime();//声明设置内部时间函数
void DS1302_ReadTime();//声明读取内部时间函数

#endif
相关推荐
hollq3 小时前
STM32F103RCT6+STM32CubeMX+keil5(MDK-ARM)+Flymcu实现串口重定向
arm开发·stm32·嵌入式硬件
小鱼儿电子4 小时前
17-基于STM32的宠物饲养系统设计与实现
stm32·嵌入式硬件·物联网·宠物·宠物饲养系统
小莞尔6 小时前
【51单片机】【protues仿真】基于51单片机四层电梯系统
单片机·嵌入式硬件
CFZPL6 小时前
使用江科大串口发送函数发送freertos的vTaskList出现跑飞
单片机
F133168929576 小时前
WD5030A,24V降5V,15A 大电流,应用于手机、平板、笔记本充电器
stm32·单片机·嵌入式硬件·51单片机·硬件工程·pcb工艺
易享电子7 小时前
基于单片机电器断路器保护器系统Proteus仿真(含全部资料)
单片机·嵌入式硬件·fpga开发·51单片机·proteus
爱倒腾的老唐10 小时前
01、如何学习单片机
单片机·嵌入式硬件·学习
点灯小铭10 小时前
基于单片机的夹具压力控制系统设计
单片机·嵌入式硬件·mongodb·毕业设计·课程设计
雾削木16 小时前
stm32解锁芯片
javascript·stm32·单片机·嵌入式硬件·gitee
璞致电子18 小时前
fpga开发板ZYNQ 璞致 PZ7010/7020 邮票孔核心板简介-ZYNQ7000系列小系统学习板
linux·嵌入式硬件·学习·fpga开发·fpga·fpga开发板·xilinx开发板