1、DS1302简介
(1)详情查看数据手册。
(2)管角描述
管教 | 名称 | 功能 |
---|---|---|
1 | Vcc2 | 双供电配置中的主电源供电引脚 |
2 | X1 | 与标准的32.768kHz晶振相连。用于ds1302记时。 |
3 | X2 | |
4 | GND | 电源地 |
5 | CE | 输入信号,CE信号在读写时必须保持高电平 |
6 | I/O | 输入/推挽输出I/O,是三线接口的双向数据管脚 |
7 | SCLK | ds1302与单片机通信的时钟信号 |
8 | Vcc1 | 电源引脚 |
(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 charextern 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; //开启定时器1ES = 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]);}
}
七、实验现象
通过串口打印时间,如下图: