1.DS1302定时器流程图
2.DS1302芯片介绍
DS1302是由美国DALLAS公司推出的具有涓细电流充电能力的低功耗实时时钟芯片。它可以对年、月、日、周、时、分、秒进行计时且具有闺年补偿等多种功能。
DS1302引脚定义和应用电路
DS1302内存结构框图
寄存器定义
3.时序定义
配置八位寄存器
由上图时序编译DS1302的写入函数
void DS1302_WriteByte(unsigned char Command,Data) //写入数据,送入字节Command和数据Data
{
unsigned char i;
DS1302_CE=1; //给CE写入高电平
for(i=0;i<8;i++)
{
DS1302_IO=Command&(0x01<<i); //将Command里的第i位赋予IO,依次左移
DS1302_SCLK=1; //根据前8个脉冲循环写入8次高低电平
DS1302_SCLK=0;
}
for(i=0;i<8;i++) //将Data里的第i位赋予IO,依次左移
{
DS1302_IO=Data&(0x01<<i);
DS1302_SCLK=1;
DS1302_SCLK=0;
}
DS1302_CE=0;
}
注意READ的SCLK的时序,相比于WRITE的SCLK方波,READ少了一个,并且从第八个开始与前面的高低位相反,故编译时要着重注意
unsigned char DS1302_ReadByte(unsigned char Command) //读取字节
{
unsigned char i,Data=0x00;
Command|=0x01; //将传入Command的地址第一位置为1,变成READ的地址
DS1302_CE=1;
for(i=0;i<8;i++)
{
DS1302_IO=Command&(0x01<<i);
DS1302_SCLK=0; //这里采用先低后高的置位,使得结束时刚好落在SCLK脉冲的正中间
DS1302_SCLK=1;
}
for(i=0;i<8;i++)
{
//重复置1,过滤多的一个脉冲 READ的时序只有15个
DS1302_SCLK=1;
DS1302_SCLK=0;
if(DS1302_IO){Data|=(0x01<<i);}
}
DS1302_CE=0;
DS1302_IO=0;
return Data; //将读取时间的BCD码返回
}
BCD码
BCD码(Binary-Coded Decimal),用4位二进制数来表示1位十进制中的0~9这10个数码,是一种二进制的数字编码形式,用二进制编码的十进制代码。
源文件编写
跟着江科大的51可调时钟视频打的
【51单片机入门教程-2020版 程序全程纯手打 从零开始入门】
DS1302.c
#include <REGX52.H>
#define DS1302_WP 0x8E //DS1302的写保护
#define DS1302_SECOND 0x80
#define DS1302_MINUTE 0x82
#define DS1302_HOUR 0x84
#define DS1302_DATE 0x86
#define DS1302_MONTH 0x88
#define DS1302_DAY 0x8A
#define DS1302_YEAR 0x8C
//对三个要用引脚的重定义,方便后续使用
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;
unsigned char DS1302_Time[]={23,4,8,22,26,15,6};
//DS1302的初始化
void DS1302_Init()
{
DS1302_SCLK=0;
DS1302_CE=0;
}
//Command时写入时间的地址,Data是写入时间的数据(BCD)
void DS1302_WriteByte(unsigned char Command,Data) //写入字节,送入字节Command和数据Data
{
unsigned char i;
DS1302_CE=1; //给CE写入高电平
for(i=0;i<8;i++)
{
DS1302_IO=Command&(0x01<<i); //将Command里的第i位赋予IO,依次左移
DS1302_SCLK=1; //根据前8个脉冲循环写入8次高低电平
DS1302_SCLK=0;
}
for(i=0;i<8;i++) //将Data里的第i位赋予IO,依次左移
{
DS1302_IO=Data&(0x01<<i);
DS1302_SCLK=1;
DS1302_SCLK=0;
}
DS1302_CE=0;
}
unsigned char DS1302_ReadByte(unsigned char Command) //读取字节
{
unsigned char i,Data=0x00;
Command|=0x01; //将传入Command的地址第一位置为1,变成READ的地址
DS1302_CE=1;
for(i=0;i<8;i++)
{
DS1302_IO=Command&(0x01<<i);
DS1302_SCLK=0; //这里采用先低后高的置位,使得结束时刚好落在SCLK脉冲的正中间
DS1302_SCLK=1;
}
for(i=0;i<8;i++)
{
//重复置1,过滤多的一个脉冲 READ的时序只有15个
DS1302_SCLK=1;
DS1302_SCLK=0;
if(DS1302_IO){Data|=(0x01<<i);}
}
DS1302_CE=0;
DS1302_IO=0;
return Data; //将读取时间的BCD码返回
}
//DS1302_SetTime 封装了DS1302_WriteByte,将DS1302的年月日等等全部设置
void DS1302_SetTime(void) // 存入十六进制的日期
{
DS1302_WriteByte(DS1302_WP,0x00); //关闭写保护
DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);
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);
DS1302_WriteByte(DS1302_DAY,DS1302_Time[6]/10*16+DS1302_Time[6]%10);
DS1302_WriteByte(DS1302_WP,0x80); //关闭写保护
}
//DS1302_ReadTime是对DS1302_ReadByte的封装,将ReadByte读入的BCD数据转化为十进制的年月日并存入DS1302_Time数组中
void DS1302_ReadTime(void)
{
unsigned char temp;
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;
}
main.c的源码
#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Delay.h"
#include "Key.h"
#include "timer0.h"
unsigned char KeyNum,MODE,TimeSetSelect,TimeSetFlashFlag;
void TimeShow()
{
DS1302_ReadTime();
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);
}
void TimeSet()
{
//判断模式选择按键是否被按下
if(KeyNum==2)
{
TimeSetSelect++;
TimeSetSelect%=6; //越界清0
}
if(KeyNum==3) //加年月日
{
DS1302_Time[TimeSetSelect]++;
if(DS1302_Time[0]>99){DS1302_Time[0]=1;} //年
if(DS1302_Time[1]>12){DS1302_Time[1]=1;} //月
if(DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 || DS1302_Time[1]==8 ||
DS1302_Time[1]==10 || DS1302_Time[1]==12) //大月判断
{
if(DS1302_Time[2]>31){DS1302_Time[2]=1;}
if(DS1302_Time[2]<1){DS1302_Time[2]=31;}
}
else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11) //小月判断
{
if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
if(DS1302_Time[2]<1){DS1302_Time[2]=30;}
}
else if(DS1302_Time[1]==2) //二月比较特殊,需要单独判断
{
if(DS1302_Time[0]%4==0 || DS1302_Time[0]/100!=0) //闰年判断
{
if(DS1302_Time[2]>29){DS1302_Time[2]=1;}
if(DS1302_Time[2]<1){DS1302_Time[2]=29;}
}
else
{
if(DS1302_Time[2]>28){DS1302_Time[2]=1;}
if(DS1302_Time[2]<1){DS1302_Time[2]=28;}
}
}
if(DS1302_Time[3]>24){DS1302_Time[3]=1;}
if(DS1302_Time[4]>60){DS1302_Time[4]=1;}
if(DS1302_Time[5]>60){DS1302_Time[5]=1;}
}
if(KeyNum==4) //减年月日(与加近乎一样)
{
DS1302_Time[TimeSetSelect]--;
if(DS1302_Time[0]<0){DS1302_Time[0]=99;}
if(DS1302_Time[1]<1){DS1302_Time[1]=12;}
if(DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 || DS1302_Time[1]==8 ||
DS1302_Time[1]==10 || DS1302_Time[1]==12)
{
if(DS1302_Time[2]<1){DS1302_Time[2]=31;}
if(DS1302_Time[2]>31){DS1302_Time[2]=1;}
}
else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11)
{
if(DS1302_Time[2]<1){DS1302_Time[2]=30;}
if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
}
else if(DS1302_Time[1]==2)
{
if(DS1302_Time[0]%4==0 || DS1302_Time[0]/100!=0)
{
if(DS1302_Time[2]<1){DS1302_Time[2]=29;}
if(DS1302_Time[2]>29){DS1302_Time[2]=1;}
}
else
{
if(DS1302_Time[2]<1){DS1302_Time[2]=28;}
if(DS1302_Time[2]>28){DS1302_Time[2]=1;}
}
}
if(DS1302_Time[3]<0){DS1302_Time[3]=23;}
if(DS1302_Time[4]<0){DS1302_Time[4]=59;}
if(DS1302_Time[5]<0){DS1302_Time[5]=59;}
}
if(TimeSetSelect==0 && TimeSetFlashFlag==1){LCD_ShowString(1,1," ");} //被TimeSetSelect选择的位闪烁,方便调试
else { LCD_ShowNum(1,1,DS1302_Time[0],2);}
if(TimeSetSelect==1 && TimeSetFlashFlag==1){LCD_ShowString(1,4," ");}
else { LCD_ShowNum(1,4,DS1302_Time[1],2);}
if(TimeSetSelect==2 && TimeSetFlashFlag==1){LCD_ShowString(1,7," ");}
else { LCD_ShowNum(1,7,DS1302_Time[2],2);}
if(TimeSetSelect==3 && TimeSetFlashFlag==1){LCD_ShowString(2,1," ");}
else { LCD_ShowNum(2,1,DS1302_Time[3],2);}
if(TimeSetSelect==4 && TimeSetFlashFlag==1){LCD_ShowString(2,4," ");}
else { LCD_ShowNum(2,4,DS1302_Time[4],2);}
if(TimeSetSelect==5 && TimeSetFlashFlag==1){LCD_ShowString(2,7," ");}
else { LCD_ShowNum(2,7,DS1302_Time[5],2);}
}
void main()
{
LCD_Init();
DS1302_Init();
Timer0_Init();
LCD_ShowString(1,1," - - ");
LCD_ShowString(2,1," / / ");
DS1302_SetTime();
while(1)
{
KeyNum=Key();
if(KeyNum==1)
{
if(MODE==0){MODE=1;}
else if(MODE==1){MODE=0;DS1302_SetTime();}
}
switch(MODE)
{
case 0:TimeShow();break; //进入调试时间模式
case 1:TimeSet();break; //保存调试时间
}
}
}
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TL0 = 0x18;
TH0 = 0xFC;
T0Count++;
if(T0Count>=500)
{
T0Count=0;
TimeSetFlashFlag=!TimeSetFlashFlag; //逻辑取反
}
}
第一次写博客,没写之前很多思路都打不开,这篇也是临摹的别人的思路一点点的推进,如有说明不对的地方,请谅解并帮忙指出,谢谢