DS1302
DS1302芯片简介
-
DS1302是一种实时时钟芯片,专门用来显示时间的流动
-
特点:精度高,不占用CPU的时间,断电后有电池供电,不会丢失数据。
芯片引脚图
芯片内部结构图
芯片寄存器(重要)
Address Byte 一字节=8位
第7位:默认1
第6位:值1是RAM 值0是CK
第1~5位:地址位
第0位:值1是RD(读read) 值0是WR(写write)
Address Byte下面两种情况
0~7位分别是:1000 0000=80h 按照RTC表格则是:写秒。
0~7位分别是:1000 0001=81h 按照RTC表格则是:读秒。
可以更改Address Byte来控制 读或写 年月日时分秒。
时序定义
时序如下
1.CE在读写过程中为高电平:1,不读写则为低电平:0.
2.发送命令字,先发送第1个字节的Address Byte的最低位,SCLK给上升沿。最低位就被写入单片机。SCLK清零
3.A0写入,SCLK给上升沿,A0写入单片机………………最高位(第8位)写入。
4.(根据第一个字节来 读或写 年月日……)开始发送第2个字节的D0,SCLK给上升沿,D0写入单片机…………最高位D7发送,D7写入单片机。SCLK置零,CE置零
-
SCLK上升沿为写 ,下降沿为读
-
时钟芯片只操作D0~D7
读命令比写命令的脉冲少1个,不一样
上面一共15个脉冲(读)
下面一共16个脉冲(写)
所以上面(READ)经过操作(在第8个后面卡脉冲),但是在循环里面,
- 在上面16位中(READ),前8个脉冲,每个脉冲SCLK值先0后1,存入Command.(Command的8位被存到IO中)
后8个脉冲,每个脉冲SCLK值先1后0,读出IO的每一位赋给Data,也就是读出Command.(相当于第8个脉冲用了2次,达到“卡脉冲” 目的)
unsigned char DS1302_ReadByte(unsigned char Command)
{
char Data=0x00,i=0;
DS1302_CE=1;
i=0;
while(i<=7)
{
DS1302_IO=Command&(0x01<<i); //依次存入0~7位Command
DS1302_SCLK=0; //SCKL先0后1
DS1302_SCLK=1;
i++;
}
i=0; //i清零
while(i<=7)
{
DS1302_SCLK=1; //SCKL先1后0(多卡1个脉冲)
DS1302_SCLK=0; //清零(存完一位)
if(DS1302_IO)
{Data=Data|(0x01<<i);} //读出第i位数据到Data
}
DS1302_CE=0; //读完后CE清零
DS1302_IO=0;
return Data; //返回读出的数据
}
BCD码
- BCD码(Binary Coded Decimal),用4位二进制数来表示1位十进制数
- 例:0001 0011表示13,1000 0101表示85,0001 1010不合法
- 在十六进制中的体现:0x13表示13,0x85表示85,0x1A不合法
- BCD码转十进制:DEC=BCD/16*10+BCD%16; (2位BCD)
- 十进制转BCD码:BCD=DEC/10*16+DEC%10; (2位BCD)
DS1302中以BCD形式存时间,而我们常用的是十进制,读和写要进行不同的转换。
10-1显示时钟
代码结构图
显示效果(自动刷新时间,每秒变化一次)
main函数
#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Delay.h"
void main()
{
LCD_Init();
DS1302_Init();
DS1302_SetTime(); //把Time数组内的时间写入DS1302
LCD_ShowString(1,3,"-");
LCD_ShowString(1,6,"-");
LCD_ShowString(2,3,":");
LCD_ShowString(2,6,":");
while(1)
{
DS1302_ReadTime(); //从DS1302读新的时间
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);
}
}
DS1302.c 代码
#include <REGX52.H>
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;
#define DS1302_YEAR 0x8C //宏定义年月日时分秒星期
#define DS1302_MONTH 0x88
#define DS1302_DATE 0x86
#define DS1302_HOUR 0x84
#define DS1302_MINUTE 0x82
#define DS1302_SECOND 0x80
#define DS1302_DAY 0x8A
#define DS1302_WP 0x8E
unsigned char DS1302_Time[]={23,6,2,9,7,50,5}; //时间数组:年月日时分秒星期
/**
* @brief 往AS1302芯片写入命令字Command和数据Data
* @param Command命令字,Data数据
* @retval 无
*/
void DS1302_WriteByte(unsigned char Command,Data)
{
int i=0;
DS1302_CE=1;
while(i<=7)
{
DS1302_IO=Command&(0x01<<i); //依次存入0~7位Command
DS1302_SCLK=1; //存入一位
DS1302_SCLK=0; //清零(存完一位)
i++;
}
i=0; //i 清零
while(i<=7)
{
DS1302_IO=Data&(0x01<<i); //依次存入第i位(共7个)Data
DS1302_SCLK=1; //存入一位
DS1302_SCLK=0; //清零(存完一位)
i++;
}
DS1302_CE=0; //清零(CE写完一个字节)
}
/**
* @brief 读出数据
* @param Command命令字
* @retval Data数据
*/
unsigned char DS1302_ReadByte(unsigned char Command)
{
char Data=0x00,i=0;
Command|=0x01; //使用宏定义,最后一位置1,写变读
Command|=0x01;
DS1302_CE=1;
i=0;
while(i<=7)
{
DS1302_IO=Command&(0x01<<i); //依次存入0~7位Command
DS1302_SCLK=0; //SCKL先0后1
DS1302_SCLK=1;
i++;
}
i=0; //i清零
while(i<=7)
{
DS1302_SCLK=1; //SCKL先1后0(多卡1个脉冲)
DS1302_SCLK=0; //清零(存完一位)
if(DS1302_IO){Data|=(0x01<<i);}
i++;
}
DS1302_CE=0; //读完后CE清零
DS1302_IO=0;
return Data; //返回读出的数据
}
//初始化
void DS1302_Init()
{
DS1302_CE=0;
DS1302_SCLK=0;
}
/**
* @brief 把Time数组里面的日期写入DS1302芯片
* @param 无
* @retval 无
*/
void DS1302_SetTime()
{
DS1302_WriteByte(DS1302_WP,0x00);
DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10); //十进制需要转换为BCD码,DS1302芯片是BCD存储
DS1302_WriteByte(DS1302_MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);
DS1302_WriteByte(DS1302_DATE,DS1302_Time[2]/10*16+DS1302_Time[2]%10);
DS1302_WriteByte(DS1302_HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);
DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);
DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);
}
/**
* @brief 把DS1302芯片的时间读到Time[]数组中
* @param 无
* @retval 无
*/
void DS1302_ReadTime()
{
unsigned char Temp; //从DS1302读出的是BCD,需要把BCD转换成十进制
Temp=DS1302_ReadByte(DS1302_YEAR);
DS1302_Time[0]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_MONTH);
DS1302_Time[1]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_DATE);
DS1302_Time[2]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_HOUR);
DS1302_Time[3]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_MINUTE);
DS1302_Time[4]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_SECOND);
DS1302_Time[5]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_DAY);
DS1302_Time[6]=Temp/16*10+Temp%16;
}
LCD1602.c 代码
#include <REGX52.H>
//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
//函数定义:
/**
* @brief LCD1602延时函数,12MHz调用可延时1ms
* @param 无
* @retval 无
*/
void LCD_Delay()
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
/**
* @brief LCD1602写命令
* @param Command 要写入的命令
* @retval 无
*/
void LCD_WriteCommand(unsigned char Command)
{
LCD_RS=0;
LCD_RW=0;
LCD_DataPort=Command;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602写数据
* @param Data 要写入的数据
* @retval 无
*/
void LCD_WriteData(unsigned char Data)
{
LCD_RS=1;
LCD_RW=0;
LCD_DataPort=Data;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602设置光标位置
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @retval 无
*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
if(Line==1)
{
LCD_WriteCommand(0x80|(Column-1));
}
else if(Line==2)
{
LCD_WriteCommand(0x80|(Column-1+0x40));
}
}
/**
* @brief LCD1602初始化函数
* @param 无
* @retval 无
*/
void LCD_Init()
{
LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
LCD_WriteCommand(0x01);//光标复位,清屏
}
/**
* @brief 在LCD1602指定位置上显示一个字符
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @param Char 要显示的字符
* @retval 无
*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
LCD_SetCursor(Line,Column);
LCD_WriteData(Char);
}
/**
* @brief 在LCD1602指定位置开始显示所给字符串
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串
* @retval 无
*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=0;String[i]!='\0';i++)
{
LCD_WriteData(String[i]);
}
}
/**
* @brief 返回值=X的Y次方
*/
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
/**
* @brief 在LCD1602指定位置开始显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~65535
* @param Length 要显示数字的长度,范围:1~5
* @retval 无
*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @brief 在LCD1602指定位置开始以有符号十进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:-32768~32767
* @param Length
* @retval 无
*/
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
unsigned char i;
unsigned int Number1;
LCD_SetCursor(Line,Column);
if(Number>=0)
{
LCD_WriteData('+');
Number1=Number;
}
else
{
LCD_WriteData('-');
Number1=-Number;
}
for(i=Length;i>0;i--)
{
LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @brief 在LCD1602指定位置开始以十六进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~0xFFFF
* @param Length 要显示数字的长度,范围:1~4
* @retval 无
*/
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i,SingleNumber;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
SingleNumber=Number/LCD_Pow(16,i-1)%16;
if(SingleNumber<10)
{
LCD_WriteData(SingleNumber+'0');
}
else
{
LCD_WriteData(SingleNumber-10+'A');
}
}
}
/**
* @brief 在LCD1602指定位置开始以二进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~1111 1111 1111 1111
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
}
}