其他分享
首页 > 其他分享> > 【51单片机快速入门指南】5.1:SPI与DS1302时钟芯片

【51单片机快速入门指南】5.1:SPI与DS1302时钟芯片

作者:互联网

目录

普中51-单核-A2
STC89C52
Keil uVision V5.29.0.0
PK51 Prof.Developers Kit Version:9.60.0.0
上位机:Vofa+ 1.3.10


硬知识

       摘自《普中 51 单片机开发攻略》、《DS1302中文手册》。

DS1302 简介

       DS1302 是 DALLAS 公司推出的涓流充电时钟芯片,内含有一个实时时钟/日历和 31 字节静态 RAM,通过简单的串行接口与单片机进行通信。实时时钟/日历电路提供秒、分、时、日、周、月、年的信息,每月的天数和闰年的天数可自动调整。时钟操作可通过AM/PM 指示决定采用 24 或 12 小时格式。DS1302 与 单片机之间能简单地采用同步串行的方式进行通信,仅需用到三根通信线:
       ①RES 复位
       ②I/O 数据线
       ③SCLK 串行时钟。
       时钟/RAM 的读/写数据以一个字节或多达 31 个字节的字符组方式通信。DS1302 工作时功耗很低,保持数据和时钟信息时功率小于 1mW。 DS1302 由 DS1202 改进而来增加了以下的特性:双电源管脚用于主电源和 备份电源供应,Vcc1 为可编程涓流充电电源,附加七个字节存储器。它广泛应用于电话、传真、便携式仪器以及电池供电的仪器仪表等产品领域下面。
       主要的性能指标:
★ 实时时钟具有能计算 2100 年之前的秒、分、时、日、星期、月、年的 能力,还有闰年调整的能力; ★ 31 个 8 位暂存数据存储 RAM;
★ 串行 I/O 口方式使得管脚数量最少;
★ 宽范围工作电压 2.0 ~ 5.5V;
★ 工作在 2.0V 时,电流小于 300nA;
★ 读/写时钟或 RAM 数据时有两种传送方式单字节传送和多字节传送字符组 方式;
★ 8 脚 DIP 封装或可选的 8 脚 SOIC 封装根据表面装配;
★ 简单 3 线接口;
★ 与 TTL 兼容 Vcc=5V;
★ 可选工业级温度范围-40~+85;
在这里插入图片描述

  1. VCC2:主电源引脚
  2. X1、X2:DS1302 外部晶振引脚,通常需外接 32.768K 晶振
  3. GND:电源地
  4. CE:使能引脚,也是复位引脚(新版本功能变)。
  5. I/O:串行数据引脚,数据输出或者输入都从这个引脚
  6. SCLK:串行时钟引脚
  7. VCC1:备用电源

DS1302 使用

       操作 DS1302 的大致过程,就是将各种数据写入 DS1302 的寄存器,以设置 它当前的时间的格式。然后使 DS1302 开始运作,DS1302 时钟会按照设置情况 运转,再用单片机将其寄存器内的数据读出。再用液晶显示,就是我们常说的简 易电子钟。所以总的来说 DS1302 的操作分 2 步(显示部分属于液晶显示的内容, 不属于 DS1302 本身的内容),但是在讲述操作时序之前,我们要先看看寄存器, DS1302 有一个控制寄存器、12 个日历、时钟寄存器和 31 个 RAM。
在这里插入图片描述
在这里插入图片描述

控制寄存器

       控制寄存器用于存放 DS1302 的控制命令字,DS1302 的 RST 引脚回到高电平后写入的第一个字节就为控制命令。它用于对 DS1302 读写过程进行控制,格式如下:
在这里插入图片描述
       上图是 DS1302 的寄存器样式,我们看到:

  1. 第 7 位永远都是 1;
  2. 第 6 位,1 表示 RAM,寻址内部存储器地址;0 表示 CK,寻址内部寄存器;
  3. 第 5 到第 1 位,为 RAM 或者寄存器的地址;
  4. 最低位,高电平表示 RD,即下一步操作将要“读”;低电平表示 W,即 下一步操作将要“写”。

在这里插入图片描述
在这里插入图片描述
       比如要读秒寄存器则命令为 1000 0001,反之写为 1000 0000。

日历/时钟寄存器

       DS1302 共有 12 个寄存器,其中有 7 个与日历、时钟相关,存放的数据为 BCD 码形式。格式如下:
在这里插入图片描述
在这里插入图片描述
       秒寄存器:低四位为秒的个位,高的次三位为秒的十位。最高位 CH 为 DS1302 的运行标志,当 CH=0 时,DS1302 内部时钟运行,反之 CH=1 时停止;
       小时寄存器:时寄存器。最高位为 12/24 小时的格式选择位,该位为 1 时表示 12 小时格式。当设置为 12 小时显示格式时,第 5 位的高电平表示下午 (PM);而当设置为 24 小时格式时,第 5 位位具体的时间数据。
       写保护寄存器:当该寄存器最高位 WP 为 1 时,DS1302 只读不写,所以要在往 DS1302 写数据之前确保 WP 为 0。
       慢充电寄存器(涓细电流充电)寄存器:我们知道,当 DS1302 掉电时,可以马上调用外部电源保护时间数据。该寄存器就是配置备用电源的充电选项的。 其中高四位(4 个 TCS)只有在 1010 的情况下才能使用充电选项;低四位的情 况与 DS1302 内部电路有关。

       在日历/时钟寄存器中都是以 BCD 码存放数据,BCD 码是通过 4 位二进制码来表示 1 位十进制中的 0~9 这 10 个数码。 如下所示:
在这里插入图片描述

DS1302 的读写时序

       在控制指令字输入后的下一个 SCLK 时钟的上升沿时,数据被写入 DS1302, 数据输入从低位(位 0)开始。同样,在紧跟 8 位的控制指令字后的下一个 SCLK 脉冲的下降沿读出 DS1302 的数据,读出数据时从低位 0 位到高位 7。其时序图如下所示:
在这里插入图片描述
       上图就是 DS1302 的三个时序:复位时序,单字节写时序,单字节读时序;
        CE(RST):复位时序,即在 RST 引脚产生一个正脉冲,在整个读写器件, RST 要保持高电平,一次字节读写完毕之后,要注意把 RST 返回低电平准备下次 读写周期;
       单字节读时序:注意读之前还是要先对寄存器写命令,从最低位开始写;可以看到,写数据是在 SCLK 的上升沿实现,而读数据在 SCLK 的下降沿实现。所以, 在单字节读时序中,写命令的第八个上升沿结束后紧接着的第八个下降沿就将要 读寄存器的第一位数据读到数据线上了!这个就是 DS1302 操作中最特别的地方。 当然读出来的数据也是最低位开始。
       单字节写时序:两个字节的数据配合 16 个上升沿将数据写入即可。

        程序注意事项:
        ★要记得在操作 DS1302 之前关闭写保护;
        ★注意用延时来降低单片机的速度以配合器件时序;
        ★DS1302 读出来的数据是 BCD 码形式,要转换成我们习惯的 10 进制,转换 方法在源程序里;
        ★读取字节之前,将 IO 设置为输入口,读取完之后,要将其改回输出口;
        ★在写程序的时候,建议实现开辟数组(内存空间)来集中放置 DS1302 的 一系列数据,方便以后扩展键盘输入。

电路设计

在这里插入图片描述

示例程序

       stdint.h【51单片机快速入门指南】1:基础知识和工程创建
       软件SPI程序见【51单片机快速入门指南】5:软件SPI
       串口部分见【51单片机快速入门指南】3.3:USART 串口通信

DS1302.c

#include "./Software_SPI/Software_SPI.h"
#include <STC89C5xRC.H>
#include "DS1302.h"

sbit DS1302_CS = P3^5;

//DS1302_CS拉高 移植时需修改
void DS1302_CS_H()
{
	DS1302_CS = 1;
}

//DS1302_CS拉低 移植时需修改
void DS1302_CS_L()
{
	DS1302_CS = 0;
}

//---存储顺序是秒分时日月周年---//
uint8_t gDS1302_TIME[7] = {0};

//---DS1302写入时分秒的地址命令---//
//---秒分时日月周年-------//
code uint8_t gWRITE_RTC_ADDR[7] = {0x1, 0x41, 0x21, 0x61, 0x11, 0x51, 0x31};

/*******************************************************************************
Num2BCD
数字转BCD码(两位数)
*******************************************************************************/
#define Num2BCD(value) (((value/10)<<4)|(value%10))

/*******************************************************************************
* 函 数 名       : reversed
* 函数功能		 : 倒序一个字节
*******************************************************************************/
uint8_t reversed(uint8_t raw)
{
	uint8_t i, value = 0;
	for(i = 0; i < 8; i++)
	{
		value <<= 1;
		value |= (raw & 0x01);
		raw >>= 1;
	}
	return value;
}

/*******************************************************************************
* 函 数 名       : ds1302_write_byte
* 函数功能		 : DS1302写单字节
* 输    入       : addr:地址/命令
				   dat:数据
* 输    出    	 : 无
*******************************************************************************/
void ds1302_write_byte(uint8_t addr,uint8_t dat)
{	
	DS1302_CS_L();
	DS1302_CS_H();

	SOFT_SPI_RW_MODE0(addr);
	SOFT_SPI_RW_MODE0(dat);

	DS1302_CS_L();
}

/*******************************************************************************
* 函 数 名       : ds1302_read_byte
* 函数功能		 : DS1302读单字节
* 输    入       : addr:地址/命令
* 输    出    	 : 读取的数据
*******************************************************************************/
uint8_t ds1302_read_byte(uint8_t addr)
{
	uint8_t temp  = 0;

	DS1302_CS_H();	
	SOFT_SPI_RW_MODE0(addr);
	temp = SOFT_SPI_RW_MODE0(0xff);
	DS1302_CS_L();

	return reversed(temp);	
}

/*******************************************************************************
* 函 数 名       : ds1302_init
* 函数功能		 : DS1302初始化时间
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void ds1302_init(uint8_t YY, uint8_t MM, uint8_t DD, uint8_t hh, uint8_t mm, uint8_t ss, uint8_t ww)
{
	uint8_t i;
	ds1302_write_byte(0x71, 0x0);
	gDS1302_TIME[0] = reversed(Num2BCD(ss));
	gDS1302_TIME[1] = reversed(Num2BCD(mm));
	gDS1302_TIME[2] = reversed(Num2BCD(hh));
	gDS1302_TIME[3] = reversed(Num2BCD(DD));
	gDS1302_TIME[4] = reversed(Num2BCD(MM));
	gDS1302_TIME[5] = reversed(Num2BCD(ww));
	gDS1302_TIME[6] = reversed(Num2BCD(YY));
	for(i = 0; i < 7; i++)
	{
		ds1302_write_byte(gWRITE_RTC_ADDR[i], gDS1302_TIME[i]);	
	}
	ds1302_write_byte(0x71, 0x1);	
}

/*******************************************************************************
* 函 数 名       : ds1302_read_time
* 函数功能		 : DS1302读取时间
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void ds1302_read_time(void)
{
	uint8_t i;
	for(i = 0; i < 7; i++)
	{
		gDS1302_TIME[i] = ds1302_read_byte(gWRITE_RTC_ADDR[i] | 0x80);	
	}	
}

DS1302.h

#ifndef DS1302_H_
#define DS1302_H_

#include "stdint.h"

#define Second()	((int16_t)(gDS1302_TIME[0]&0x0f)+((gDS1302_TIME[0]&0x70)>>4)*10)
#define Minute()	((int16_t)(gDS1302_TIME[1]&0x0f)+((gDS1302_TIME[1]&0x70)>>4)*10)
#define Hour()		((int16_t)(gDS1302_TIME[2]&0x0f)+((gDS1302_TIME[2]&0x30)>>4)*10)
#define Date()		((int16_t)(gDS1302_TIME[3]&0x0f)+((gDS1302_TIME[3]&0x30)>>4)*10)
#define Month()		((int16_t)(gDS1302_TIME[4]&0x0f)+((gDS1302_TIME[4]&0x10)>>4)*10)
#define Year()		((int16_t)(gDS1302_TIME[6]&0x0f)+((gDS1302_TIME[6]&0xF0)>>4)*10)
#define Week()		((int16_t)(gDS1302_TIME[5]&0x0f))

extern uint8_t gDS1302_TIME[];

void ds1302_write_byte(uint8_t addr,uint8_t dat);
uint8_t ds1302_read_byte(uint8_t addr);
void ds1302_init(uint8_t YY, uint8_t MM, uint8_t DD, uint8_t hh, uint8_t mm, uint8_t ss, uint8_t ww);
void ds1302_read_time(void);

#endif

测试程序

main.c

#include <STC89C5xRC.H>
#include "intrins.h"
#include "stdint.h"
#include "DS1302.h"
#include "USART.h"

void Delay1ms()		//@22.1184MHz
{
	unsigned char i, j;

	_nop_();
	i = 4;
	j = 146;
	do
	{
		while (--j);
	} while (--i);
}

void Delay_ms(int i)
{
	while(i--)
		Delay1ms();
}

void main(void)
{
	ds1302_init(21, 12, 3, 22, 42, 28, 5);	//配置为21年12月03日22时42分28秒 星期5
	USART_Init(USART_MODE_1, Rx_ENABLE, STC_USART_Priority_Lowest, 22118400, 115200, DOUBLE_BAUD_ENABLE, USART_TIMER_1);

	while(1)
	{	
		Delay_ms(1000);
		ds1302_read_time();
		printf("%d年%d月%d日%d时%d分%d秒星期%d\r\n", Year(), Month(), Date(), Hour(), Minute(), Second(), Week());
	}
}

实验现象

打开串口,可见如下数据:
在这里插入图片描述

标签:5.1,ds1302,gDS1302,寄存器,DS1302,uint8,SPI,TIME
来源: https://blog.csdn.net/weixin_44457994/article/details/121699178