十二、51单片机之DS1302(SPI)

1、DS1302简介

(1)详情查看数据手册。

(2)管角描述

管教名称功能
1Vcc2双供电配置中的主电源供电引脚
2X1与标准的32.768kHz晶振相连。用于ds1302记时。
3X2
4GND电源地
5CE输入信号,CE信号在读写时必须保持高电平
6I/O输入/推挽输出I/O,是三线接口的双向数据管脚
7SCLKds1302与单片机通信的时钟信号
8Vcc1电源引脚

(3)ds1302采用SPI数字接口。

(4)ds1302内部存储着一个时间点信息(年月日时分秒周几),可读可写,上电时间自动走。

2、RTC相关知识

(1)RTC指real time clock,实时时钟的意思。

(2)时间点和时间段,时间点是xx年xx月xx日xx时xx分xx秒,时间段是一定长度的时间。

(3)RTC用于提供时间点,定时器用于提供时间段。

(4)RTC存在形式:单片机内部集成或单片机外部扩展。

3、SPI接口

(1)分为三线或者四线。

(2)三线:CE、SCLK、I/O。

(3)四线:CE、SCLK、输入、输出。

4、DS1302的时间格式

4.1、8421BCD码

(1)ds1302读出的时间是按照8421BCD码表示的。

(2)8421BCD码是一种数字编码,很像10进制和16进制的结合。

(3)8421BCD码看起来很像10进制(29往下是30而不是2A)。

(3)BCD码本质上又是16进制(BCD码的21在计算机中就是0x21)。

(4)BCD码用4位二进制数来表示十进制数中的0~9这10个数码。

4.2、BCD码的意义

(1)计算机喜欢16进制,而人喜欢10进制,BCD码综合了两者的考量。

4.3、ds1302年份

(1)ds1302直接读出的年(BCD编码)+2000就是当前的年份。

(2)譬如读出的BCD码16,对应0x16所以就是2016年。

4.4、十进制转换为8421BCD码

(1)十进制84转为8421BCD码为0x84。

(2)计算方式(84/10)*16+84%10

5、代码出现的问题及解决

问题: 串口打印时间时会出现一些FF。

解决方法:

(1)硬件上在IO线上设置10K的电阻做弱上拉处理。

(2)如果没有做弱上拉,也有办法解决。在代码的读取寄存器时序之后,加一个将IO置为低电平的代码进去,就可以了。(见如下代码ds1302.c中Ds1302ReadByte函数)。

6、代码

(1)ds1302代码

ds1302.c文件

#include "ds1302.h"
#include <reg52.h>
#include <intrins.h>


sbit DS_IO	 = P3^4;
sbit DS_CE   = P3^5;
sbit DS_SCLK = P3^6;
/*DS1302写入和读取时分秒的地址命令*/
uchar code  Read_RTC_Addr[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8B, 0x8D};
uchar code Write_RTC_Addr[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8A, 0x8C};
/*存储顺序是秒分时日月周年,存储格式是用BCD码*/
uchar TIME[7] = {0, 50, 0x21, 0x16, 0x03, 0x04, 0x23};


/*
 *功能:向ds1302写命令(地址+数据)
 *参数:
		addr :地址
		value:数据
 *返回值:无
 */
void DS1302_WriteByte(unsigned char addr, unsigned char value)
{
	unsigned char i = 0;
	DS_CE = 0;
	_nop_();
	DS_SCLK = 0;
	_nop_();
	DS_CE = 1;
	_nop_();
	
	for(i = 0; i < 8; i++)
	{
		DS_IO = (addr >> i) & 0x01;		/*SPI由低位开始传输*/
		DS_SCLK=1;
		_nop_();
		DS_SCLK=0;
		_nop_();
	}
	
 for(i = 0; i < 8; i++)
	{
		DS_IO = (value >> i) & 0x01;
		DS_SCLK = 1;
		_nop_();
		DS_SCLK = 0;
		_nop_();
	}
	
	DS_CE = 0;
	_nop_();
}


/*
 *功能  : 读取一个地址的数据
 *参数  : addr
 *返回值: Data
 */
unsigned char Ds1302ReadByte(unsigned char addr)
{
	unsigned char i    = 0;
	unsigned char Data = 0;
	
	DS_CE = 0;
	_nop_();
	DS_SCLK = 0;
	_nop_();
	DS_CE = 1;
	_nop_();
	

	for(i = 0; i < 8; i++)
	{
		DS_IO = (addr >> i) & 0x01;    /*数据从低位开始传送*/
		DS_SCLK = 1;                   /*数据在上升沿时,DS1302读取数据*/
		_nop_();                       /*延时*/
		DS_SCLK = 0;                   /*DS1302下降沿时,放置数据*/
		_nop_();
	}
	
	for(i = 0; i < 8; i ++)          /*读取8位数据*/
	{
		if(DS_IO == 1)
		{
			Data |= (0x01 << i);         /*从最低位开始接收*/
		}
		DS_SCLK = 1;
		_nop_();
		DS_SCLK = 0;                   /*DS1302下降沿时,放置数据*/
		_nop_();
	}

	DS_CE = 0;
	_nop_();	
	
	DS_IO = 0;   /*为了解决FF出现的问题*/
	return Data;	
}

/*
 *功能:读取时钟信息
 *参数:无
 *返回值:无
 */
void Ds1302ReadTime()
{
	unsigned char i = 0;
	for(i = 0; i < 7; i++)	/*读取7个字节的时钟信号:分秒时日月周年*/
	{
		TIME[i] = Ds1302ReadByte(Read_RTC_Addr[i]);
	}
}	


/*
 *功能:设置初始时间
 *参数:无
 *返回值:无
 */
void DS1392SetTime(void)
{
	unsigned char i = 0;
	DS1302_WriteByte(0x8E,0x00);  /*关闭写保护*/
	for(i = 0; i < 7; i++)
	{
		DS1302_WriteByte(Write_RTC_Addr[i],TIME[i]);  /*TIME为8421BCD码]*/
	}
	DS1302_WriteByte(0x8E,0x80);  /*打开写保护*/
}

ds1302.h文件

#ifndef _DS1302_H_
#define _DS1302_H_


#define uchar unsigned char
	

extern uchar TIME[7];


void DS1302_WriteByte(unsigned char addr, unsigned char value);  /*向ds1302写命令(地址+数据)*/  
unsigned char Ds1302ReadByte(unsigned char addr);                /*读取一个地址的数据*/
void Ds1302ReadTime();                                           /*读取时钟信息*/
void DS1392SetTime(void);                                        /*设置初始时间*/

#endif

(2)串口代码

drv_uart.c文件

#include "drv_uart.h"
#include <reg52.h>
#include <intrins.h>

/*
 *功能:串口初始化函数,8数据位,1停止位,无校验位,波特率4800
 *参数:无
 *返回值:无
 */
void UartInit(void)
{
	SCON = 0x50;  //串口工作在模式1,8位数据位,允许串行接收
	PCON = 0x80;  //波特率加倍
	TMOD = 0x20;  //设置T1为模式2 
	TH1 = 243;    //波特率4800	,TH1 = 晶振频率/12/32/波特率
	TL1 = 243;    //8位自动重装,意识是TH1用完了之后下一个周期TL1会自动重装到TH1去。
	TR1 = 1;      //开启定时器1
	ES = 1;       //打开串口中断
	EA = 1;       //打开总中断
}


/*
 *功能:通过串口发送一个字节数据
 *参数:需要发送的内容
 *返回值:无
 */
void UartSendByte(unsigned char Dat)
{
	SBUF = Dat;      //准备好需要发送的一个字节
	while(TI == 0);  //确认串口发送没有再忙,while循环需要加超时判断
	TI = 0;					 //软件复位TI标志位
}


void Delay500ms()		//@12.000MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 4;
	j = 205;
	k = 187;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

drv_uart.h文件

#ifndef __DRV_UART_H__
#define __DRV_UART_H__


/*函数声明*/
void UartInit(void);                  /*串口初始化函数,8数据位,1停止位,无校验位,波特率4800*/
void UartSendByte(unsigned char Dat); /*通过串口发送一个字节数据*/
void Delay500ms();	


#endif

(3)main.c代码

#include <reg52.h>
#include "ds1302.h"
#include "drv_uart.h"


/*函数声明*/
void Uart_PrintTime(void);

void main()
{
	UartInit();      // 串口初始化
	DS1392SetTime(); // 设置初始时间
	while(1)
	{
		Ds1302ReadTime();     /*读取时钟信息*/
		Uart_PrintTime();     /*通过串口打印时间*/
		//UartSendByte('D');
		Delay500ms();
	}
}


/*
 *功能:通过串口打印时间
 *参数:无
 *返回值:无
 */
void Uart_PrintTime(void)
{
	unsigned char i = 0;	/*用于for循环*/
	for(i = 0; i < 7; i++)
	{
		UartSendByte(TIME[i]);
	
	}
}

七、实验现象

通过串口打印时间,如下图:

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

代码织梦师小猪

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

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

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

打赏作者

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

抵扣说明:

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

余额充值