51单片机嵌入式开发:18、STC89C52RC嵌入式DS1302实时时钟实验及数码管显示


STC89C52RC嵌入式DS1302实时时钟实验及数码管显示


1 概述

1.1 DS1302简介

DS1302是一种经典的实时时钟芯片,由Dallas Semiconductor(现在是Maxim Integrated)生产。它是一款低成本、低功耗的实时时钟模块,适用于许多嵌入式系统和电子设备中,用于提供实时时钟和日期功能。

DS1302 可慢速充电实时时钟芯片包含实时时钟/日历和 31 字节的非易失性静态 RAM。它经过一 个简单的串行接口与微处理器通信。实时时钟/日历可对秒,分,时,日,周,月,和年进行计数,对 于小 于 31 天的月,月末的日期自动进行调整,还具有闰年校正的功能。时钟可以采用 24 小时格式 或带 AM(上午)/PM(下午)的 12 小时格式。31 字节的 RAM 可以用来临时保存一些重要数据。 使用同步串行通信, 简化了 DS1302 与微处理器的通信。与时钟/RAM 通信仅需 3 根线:(1)RST (复位),(2)I/O(数据线)和(3)SCLK(串行时钟)。数据可以以每次一个字节的单字节形式或多 达 31 字节的多字节形式传输。DS1302 能在非常低的功耗下工作,消耗小于 1µW 的功率便能保存 数据和时钟信息。

在这里插入图片描述

1.2 DS1302功能和特点

实时时钟功能: DS1302提供了秒、分、时、日、月、年等实时时钟信息,并能够自动处理闰年。
低功耗: DS1302具有低功耗特性,通常可以通过外部电池来提供备用电源,以保持时钟的持续运行。
SPI接口: DS1302通过简单的3线SPI接口与微控制器或其他设备进行通信。
32字节RAM: DS1302集成了32字节的RAM,可以用于存储临时数据或者配置信息。
电压范围: DS1302能够在较宽的电压范围内工作,通常在2V至5.5V之间。
精度: DS1302的时钟误差非常小,通常在几秒内每月。

1.3 DS1302工作原理

DS1302通过与微控制器的SPI通信来读取和设置时间信息。它通常连接到微控制器的GPIO引脚,并通过特定的协议(如时钟脉冲和数据线)来进行通信。通过读取和写入特定的寄存器,可以控制DS1302的各种功能。

1.4 DS1302应用领域

嵌入式系统: DS1302常被用于嵌入式系统中,为设备提供实时时钟功能。
时钟模块: DS1302可以用于制作各种时钟模块,如数字时钟、温度计时钟等。
计时器和提醒器: DS1302可以用于创建计时器、闹钟和提醒功能。
数据记录: 由于具有RAM存储功能,DS1302还可用于数据记录应用,如温度记录器等。
总的来说,DS1302是一个功能丰富、易于使用的实时时钟芯片,适用于许多需要时间记录和计时功能的应用场景。

2 DS1302设计原理

2.1 引脚说明

在这里插入图片描述

电路连接示意图:

在这里插入图片描述

2.2 寄存器说明及使用

(1)命令cmd字节说明

通常我们将第六位为0,不作为RAM使用,将其作为实时时钟的专用芯片。

在这里插入图片描述

(2)命令汇总

将寄存器地址看成命令即可:

在这里插入图片描述

3 DS1302读写软件设计

DS1302通过与微控制器的SPI通信来读取和设置时间信息。所以基本的通讯方式是以SPI半双工主从方式获取和操作DS1302内部时间数据。

3.1 读程序

根据手册读流程:

在这里插入图片描述
在这里插入图片描述

数据读代码设计如下:
// 从DS1302读取一个字节

unsigned char DS1302_ReadByte(void)
{
    unsigned char i, dat = 0;

    for (i = 0; i < 8; i++)
    {
        dat >>= 1;               // 右移一位,准备接收下一位数据
        if (DS1302_IO)           // 读取数据位
            dat |= 0x80;         // 如果数据位为1,将最高位置1
        DS1302_SCLK = 1;        // 拉高时钟线,传输数据
        DS1302_SCLK = 0;        // 将时钟线拉低,准备传输下一位
    }
    return dat;     // 返回读取到的数据
}

// 从DS1302读取一个字节的数据

unsigned char DS1302_Read(unsigned char addr)
{
	unsigned char temp,i;
	DS1302_RST=0;             //停止工作
	DS1302_SCLK=0;  
	DS1302_RST=1;  
	
	for (i = 0; i < 8; i++)
    {
        DS1302_IO = addr & 0x01; // 传输地址的最低位
        addr >>= 1;              // 右移一位,准备传输下一位
        DS1302_SCLK = 1;        // 拉高时钟线,传输数据
        DS1302_SCLK = 0;        // 将时钟线拉低,准备传输下一位
    }
	temp = DS1302_ReadByte();
	DS1302_RST=0;
	DS1302_SCLK=1;     //停止工作
	
    return temp; // 读取地址和数据,地址最高位置0表示读操作
}

3.2 写程序

根据手册写流程:
在这里插入图片描述在这里插入图片描述

软件代码设计如下:
// 向DS1302写入一个字节

void DS1302_WriteByte(unsigned char addr, unsigned char dat)
{
    unsigned char i;

    for (i = 0; i < 8; i++)
    {
        DS1302_IO = addr & 0x01; // 传输地址的最低位
        addr >>= 1;              // 右移一位,准备传输下一位
        DS1302_SCLK = 1;        // 拉高时钟线,传输数据
        DS1302_SCLK = 0;        // 将时钟线拉低,准备传输下一位
    }

    for (i = 0; i < 8; i++)
    {
        DS1302_IO = dat & 0x01; // 传输数据的最低位
        dat >>= 1;              // 右移一位,准备传输下一位
        DS1302_SCLK = 1;       // 拉高时钟线,传输数据
        DS1302_SCLK = 0;       // 将时钟线拉低,准备传输下一位
    }
}

// 向DS1302写入一个字节的数据

void DS1302_Write(unsigned char addr, unsigned char dat)
{
	DS1302_RST=0;    //停止工作
	DS1302_SCLK=0;                                 
	DS1302_RST=1;   //重新工作
	DS1302_WriteByte(addr | 0x80, dat); // 写入地址和数据,地址最高位置1表示写操作

	DS1302_RST=0;
	DS1302_SCLK=1;
}

4 软件工程代码

工程示意图:

在这里插入图片描述

//main.c文件

#include "includes.h"



/******************************************************************/
/*                    微秒延时函数  //10us                         */
/******************************************************************/
void delay_us(unsigned int us)//delay us
{
	while(us--)
	{
	}
}

/******************************************************************/
/*                    微秒延时函数                                */
/******************************************************************/
void delay_ms(unsigned int Ms)//delay us
{
	while(Ms--)
	{
		delay_us(100);
	}
}





/*------------------------------------------------
                    主函数
------------------------------------------------*/
void main (void)
{
//	unsigned char sec, min, hour;
//	
//    DS1302_Write(0x80, 0x00); // 

    // 设置时间
    DS1302_Write(0x80, 0); // 秒
    DS1302_Write(0x82, 0x25); // 分钟
    DS1302_Write(0x84, 0x19); // 小时

    while (1)
    {
        // 读取时间
        sec = DS1302_Read(0x81); // 秒
        min = DS1302_Read(0x83); // 分钟
        hour = DS1302_Read(0x85); // 小时

        // 在这里可以进行对时间的处理或其他操作

        delay_ms(1);
		sys_keynum_ledon(sec/16,6);
        delay_ms(1);
		sys_keynum_ledon(sec%16,7);
        delay_ms(1);
		sys_keynum_ledon(10,5);
        delay_ms(1);
		
		sys_keynum_ledon(min/16,3);
        delay_ms(1);
		sys_keynum_ledon(min%16,4);
        delay_ms(1);
		sys_keynum_ledon(10,2);
        delay_ms(1);
		
		sys_keynum_ledon(hour/16,0);
        delay_ms(1);
		sys_keynum_ledon(hour%16,1);
		
    }
}

//includes.h文件

#ifndef __INCLUDES_H__
#define __INCLUDES_H__

//#include<reg52.h> 

#include<intrins.h> //汇编指令_nop_
#include<stdio.h> 	//标准输入输出

//_nop_(); 产生一条NOP指令
//作用:对于延时很短的,要求在us级的,采用“_nop_”函数,这个函数相当汇编NOP指令,延时几微秒。
//NOP指令为单周期指令,可由晶振频率算出延时时间。

//8051 为每个机器周期 12 时钟
//对于12M晶振,延时1uS。
//11.0592M晶振,延时1.0851uS。

//对于延时比较长的,要求在大于10us,采用C51中的循环语句来实现。


//包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
#include "STC89C5xRC_RDP.h"

//应用层头文件
//#include "c51_gpio.h"
#include "c51_ledtube.h"
//#include "c51_key.h"
//#include "c51_timer.h"
//#include "c51_exit.h"
//#include "c51_lcd1602.h"
//#include "c51_iic.h"
//#include "c51_tx1838.h"
//#include "c51_uart.h"
//#include "c51_28byj48.h"
#include "c51_ds1302.h"


//extern void delay(unsigned int cnt);
extern void delay_us(unsigned int us);//delay us;
extern void delay_ms(unsigned int Ms);//delay Ms;


///******************************************************************/
///*                    微秒延时函数  //10us                         */
///******************************************************************/
//void delay_us(unsigned int us)//delay us
//{
//	while(us--)
//	{
//	}
//}

///******************************************************************/
///*                    微秒延时函数                                */
///******************************************************************/
//void delay_ms(unsigned int Ms)//delay us
//{
//	while(Ms--)
//	{
//		delay_us(100);
//	}
//}

///*------------------------------------------------
//                    延时子程序
//------------------------------------------------*/
//void delay(unsigned int cnt) 
//{
// while(--cnt);
//}

#endif

//c51_ledtube.c文件

#include "includes.h"

// 显示段码值01234567,可对应原理图查看显示不同图形对应的引脚高点电平配置状态
unsigned char const EL[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,\
		                  	 0x77,0x7c,0x39,0x5e,0x79,0x71};//0-F

code unsigned char ledmap[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x40};


///********************************************************
//函数名称:sys_ledtube_on1
//函数功能:点亮一个数码管全为亮起来
//入口参数:
//出口参数:
//修    改:
//内    容:
//********************************************************/
//void sys_ledtube_on1(void)
//{
//	//根据原理图,将P0口全部输出高电平,P2选择0号数码管
//	P0=0xFF;//取显示数据,段码
//	P2=0;  	//取位码
//}

///********************************************************
//函数名称:sys_ledtube_on2
//函数功能:显示一组数据
//入口参数:
//出口参数:
//修    改:
//内    容:
//********************************************************/
//static unsigned char ledtube_cnt = 0;

//void sys_ledtube_on2(void)
//{
//	ledtube_cnt++;
//	if(ledtube_cnt>7)
//	{
//		ledtube_cnt = 0;
//	}
//	P0 = 0x00;				//防止切换数码管瞬间有虚影出现
//	P2 = 0x00;
//	P0 = ledmap[ledtube_cnt];	//取显示数据,段码
//	P2 = ledtube_cnt;  		//取位码
//	
//	//根据人眼适应虚影缓冲时间为50ms左右
//	//我们调整delay在500以下可以看到明显的看起来是一串数据一起显示
//	delay(100); 			
//}


///********************************************************
//函数名称:sys_keynum_ledon
//函数功能:显示按键数值
//入口参数:按键数值
//出口参数:
//修    改:
//内    容:
//********************************************************/
void sys_keynum_ledon(unsigned char num,unsigned char pn)
{
	//根据原理图,将P0口全部输出高电平,P2选择0号数码管
	P0 = 0x00;		//防止切换数码管瞬间有虚影出现
	P2 = pn;  		//取位码
	P0 = ledmap[num];	//取显示数据,段码
}

//c51_ledtube.h文件

#ifndef __C51_LEDTUBE_H__
#define __C51_LEDTUBE_H__


extern unsigned char const EL[];

//extern void sys_ledtube_on1(void);
//extern void sys_ledtube_on2(void);

extern void sys_keynum_ledon(unsigned char num,unsigned char pn);


#endif

//c51_ds1302.c文件

#include "includes.h"

unsigned char sec, min, hour;

// 向DS1302写入一个字节
void DS1302_WriteByte(unsigned char addr, unsigned char dat)
{
    unsigned char i;

    for (i = 0; i < 8; i++)
    {
        DS1302_IO = addr & 0x01; // 传输地址的最低位
        addr >>= 1;              // 右移一位,准备传输下一位
        DS1302_SCLK = 1;        // 拉高时钟线,传输数据
        DS1302_SCLK = 0;        // 将时钟线拉低,准备传输下一位
    }

    for (i = 0; i < 8; i++)
    {
        DS1302_IO = dat & 0x01; // 传输数据的最低位
        dat >>= 1;              // 右移一位,准备传输下一位
        DS1302_SCLK = 1;       // 拉高时钟线,传输数据
        DS1302_SCLK = 0;       // 将时钟线拉低,准备传输下一位
    }
}

// 从DS1302读取一个字节
unsigned char DS1302_ReadByte(void)
{
    unsigned char i, dat = 0;

    for (i = 0; i < 8; i++)
    {
        dat >>= 1;               // 右移一位,准备接收下一位数据
        if (DS1302_IO)           // 读取数据位
            dat |= 0x80;         // 如果数据位为1,将最高位置1
        DS1302_SCLK = 1;        // 拉高时钟线,传输数据
        DS1302_SCLK = 0;        // 将时钟线拉低,准备传输下一位
    }
    return dat;     // 返回读取到的数据
}

// 向DS1302写入一个字节的数据
void DS1302_Write(unsigned char addr, unsigned char dat)
{
	DS1302_RST=0;    //停止工作
	DS1302_SCLK=0;                                 
	DS1302_RST=1;   //重新工作
	DS1302_WriteByte(addr | 0x80, dat); // 写入地址和数据,地址最高位置1表示写操作

	DS1302_RST=0;
	DS1302_SCLK=1;
}

// 从DS1302读取一个字节的数据
unsigned char DS1302_Read(unsigned char addr)
{
	unsigned char temp,i;
	DS1302_RST=0;             //停止工作
	DS1302_SCLK=0;  
	DS1302_RST=1;  
	
	for (i = 0; i < 8; i++)
    {
        DS1302_IO = addr & 0x01; // 传输地址的最低位
        addr >>= 1;              // 右移一位,准备传输下一位
        DS1302_SCLK = 1;        // 拉高时钟线,传输数据
        DS1302_SCLK = 0;        // 将时钟线拉低,准备传输下一位
    }
	temp = DS1302_ReadByte();
	DS1302_RST=0;
	DS1302_SCLK=1;     //停止工作
	
    return temp; // 读取地址和数据,地址最高位置0表示读操作
}

//c51_ds1302.h文件

#ifndef __C51_DS1302_H__
#define __C51_DS1302_H__


sbit DS1302_SCLK = P3^6; // DS1302的时钟引脚

sbit DS1302_IO = P3^4;   // DS1302的数据引脚

sbit DS1302_RST = P3^5;  // DS1302的复位引脚

extern unsigned char sec, min, hour;

// 函数声明
extern void DS1302_WriteByte(unsigned char addr, unsigned char dat);

extern unsigned char DS1302_ReadByte(void);

extern void DS1302_Write(unsigned char addr, unsigned char dat);

extern unsigned char DS1302_Read(unsigned char addr);



#endif

5 总结

5.1 注意事项

(1)DS1302获取和设置的时间数值采用BCD吗方式,形式上10进制的16进制数据,比如获取数值为0x16,则其数值就是十进制的16。在解析和设置时注意发送接收数据格式。
(2)DS1302有一个停止,当芯片断电后,DS1302会停止不再计时 ,“秒”寄存器第7位为1,当DS1302停止时,读出来的秒为一直是0x80,则此时需要对秒寄存器最高位设置为0,重新启动。
DS1302_Write(0x80, 0x00);

5.2 应用总结

DS1302实时时钟芯片在许多电子设备和嵌入式系统中具有广泛的应用价值。以下是DS1302实时时钟在各种应用中的重要作用和价值:

  1. 实时时钟功能:
    DS1302提供了秒、分、时、日、月、年等实时时钟信息,可以用于跟踪时间并记录时间戳。在需要时间记录、数据同步或定时操作的应用中,DS1302可以提供准确的时间基准。
  2. 定时器和闹钟功能:
    通过DS1302,您可以实现定时器、闹钟和提醒功能,使设备能够在特定时间点执行预定的任务或发出提醒。
  3. 数据记录和时间戳:
    DS1302内置的32字节RAM可以用于存储临时数据或配置信息,适用于数据记录应用(如温度记录器),同时可以为数据添加时间戳。
  4. 电源失效保护:
    DS1302通常可以通过外部电池提供备用电源,以保持实时时钟在主电源中断时的持续运行。这种功能对于需要持续时间记录和时间跟踪的应用非常重要。
  5. 低功耗设计:
    DS1302具有低功耗特性,适用于要求节能设计的电子设备,同时可以延长电池寿命。
  6. 嵌入式系统应用:
    DS1302广泛应用于嵌入式系统、智能家居设备、温控器、时钟模块等各类电子产品中,为这些设备提供时间管理和实时时钟功能。
  7. 开发教学和实验:
    由于DS1302易于使用和集成,它也经常被用于教学和实验项目中,帮助学生学习嵌入式系统和时钟控制的基础知识。
    综上所述,DS1302实时时钟芯片在各种应用中扮演着关键角色,为设备提供准确的时间记录、定时功能和数据管理,同时具备低功耗设计和备用电源支持,使其在电子领域中具有重要的应用和价值。

在这里插入图片描述在这里插入图片描述

  • 17
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值