前言
本篇文章属于STC89C52单片机(以下简称单片机)的学习笔记,来源于B站教学视频。下面是这位up主的视频链接。本文为个人学习笔记,只能做参考,细节方面建议观看视频,肯定受益匪浅。
一、DS1302介绍
单片机定时器计时较DS1302的缺点:
1.精度不高
2.占用单片机CPU时间
3.不能掉电继续运行
二、引脚定义和应用电路
注意:VCC2才是主电源,VCC1是备用电池
由于该开发板没有接VCC1备用电池,不具备掉电时间继续前进的能力
VCC2、VCC1、GND、X1、X2提供技术脉冲,芯片可以工作了
CE、IO、SCLK是通信端,用来设置时间,读取时间,通过这三个端口,把芯片内部的时间读出来
,也可以写入时间,其操作方式与前面讲的74HC595相似,通过一定的通信协议来操控
三、内部结构框图
CE使能起到一个开关的作用,当CE接高电平时,下面两个端口的操作才是有用的 ,如果是低电平,就无效
四、寄存器定义
WP是writing protection 写入保护,当它为高电平时,无法写入,但可以读取
该图是命令字,用来操作在哪 读出(写入) 什么
字母上有横线代表低电平有效
7位默认为1,6位来选择操作RAM寄存器还是CK时钟,5-1位来选择操作的地址,0位来选择是读出还是写入
五、时序定义
SCLK时钟的上升沿写入数据,时钟的下降沿读出数据
写入数据: 先将命令字写入,第一步先将CE端置1,第二步I/O写入数据,先写最低位,最低位给0即是写入,第三步给时钟SCLK一个上升沿,即给1,I/O的数据就会进去,第四步将SCLK时钟一个下降沿,给回0,第五步,I/O口写入次低位,第六步给时钟SCLK1。按此步骤重复下去,命令字写完后,紧接着就可以用相似的操作写入要写的数据,同样是低位先写。
全部操作完后,再把CE置0,整个时序就完成了
读出数据: 前面操作与写入数据相同,先把命令字写入。写完命令字后,单片机不再操作I/O,只需给SCLK下降沿来读取数据即可。最后再把CE置0。
六、实例一(DS1302时钟)
注意:
1.时钟的上升沿和下降沿之间是否需要延时,要看手册的时间符不符合
2.Keil C51函数中的局部变量必须要在函数体的开头定义,否则会出现未定义的error
BCD码
即前4位表示十位上的数,后4位表示个位上的数
注意的是:写操作时候,要将十进制转换为BCD码,然后写入到DS1302中
读操作时候,将读取的BCD码转换为十进制数字
代码实例:
DS1302.h
#ifndef __DS1302_H__
#define __DS1302_H__
extern unsigned char DS1302_Time[];
void DS1302_Init(void);
void DS1302_WriteByte(unsigned char Command,Data);
unsigned char DS1302_ReadByte(unsigned char Command);
void DS1302_SetTime(void);
void DS1302_ReadTime(void);
#endif
DS1302.c
#include <REGX52.H>
//命名寄存器方便使用
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;
//宏定义时间的地址
#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
#define DS1302_WP 0x8E
//存储时间的数组:年月日时分秒星期
unsigned char DS1302_Time[]={24,6,17,16,59,55,1};
//DS1302时钟初始化
void DS1302_Init(void)
{
DS1302_CE=0;
DS1302_SCLK=0;
}
//DS1302时钟写入一个字节
void DS1302_WriteByte(unsigned char Command,Data)
{
//Keil C51函数中的局部变量必须要在函数体的开头定义,否则会出现未定义的error
unsigned char i;
//使能置1
DS1302_CE=1;
//写入命令字
for (i=0;i<8;i++)
{
DS1302_IO=Command&(0x01<<i);
//时钟的上升沿和下降沿之间是否需要延时,要看手册的时间符不符合
DS1302_SCLK=1;
DS1302_SCLK=0;
}
//写入数据
for (i=0;i<8;i++)
{
DS1302_IO=Data&(0x01<<i);
DS1302_SCLK=1;
DS1302_SCLK=0;
}
//数据传输结束,使能置0
DS1302_CE=0;
}
//DS1302时钟读出一个字节
unsigned char DS1302_ReadByte(unsigned char Command)
{
unsigned char i,Data=0x00;
//读出时最低位为1
Command|=0x01;
DS1302_CE=1;
for (i=0;i<8;i++)
{
DS1302_IO=Command&(0x01<<i);
DS1302_SCLK=0;
DS1302_SCLK=1;
}
for (i=0;i<8;i++)
{
DS1302_SCLK=1;
DS1302_SCLK=0;
if(DS1302_IO) Data|=(0x01<<i);
}
DS1302_CE=0;
//读完后IO口要置0,不然会出错
DS1302_IO=0;
return Data;
}
//设置时间
void DS1302_SetTime(void)
{
//将数组存储的日期写入DS1302时钟,写入要由十进制转为BCD码
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);//打开写入保护
}
//读取时间
void DS1302_ReadTime(void)
{
//将读出的时间放进数组中,读出要由BCD码转为十进制
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"
void main()
{
LCD_Init(); //LCD液晶屏初始化
DS1302_Init(); //DS1302时钟初始化
LCD_ShowString(1,1," - - ");
LCD_ShowString(2,1," : : ");
DS1302_SetTime(); //设置时间
while(1)
{
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);
}
}
七、实例二(DS1302时钟)
主要是在main.c主函数中写逻辑
#include <REGX52.H>
#include "LCD1602.h"
#include "DS1302.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()
{
//按键2选择调节的时间位
if (KeyNum==2)
{
TimeSetSelect++;
TimeSetSelect%=6;
}
//按键3控制时间位的加,其中要有越界判断
if (KeyNum==3)
{
DS1302_Time[TimeSetSelect]++;
if (DS1302_Time[0]>99) {DS1302_Time[0]=0;}
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;}
}
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;}
}
else if (DS1302_Time[1]==2)
{
if (DS1302_Time[0]%4==0)
{
if (DS1302_Time[2]>29) {DS1302_Time[2]=1;}
}
else
{
if (DS1302_Time[2]>28) {DS1302_Time[2]=1;}
}
}
if (DS1302_Time[3]>23) {DS1302_Time[3]=0;}
if (DS1302_Time[4]>59) {DS1302_Time[4]=0;}
if (DS1302_Time[5]>59) {DS1302_Time[5]=0;}
}
//按键4控制时间位的减,其中也要有越界判断
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)
{
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;}
}
//设置时间位1s为周期闪烁
if (TimeSetSelect==0 && TimeSetFlashFlag==1) {LCD_ShowString(1,1," ");}
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();
Timer0Init();
LCD_ShowString(1,1," - - ");
LCD_ShowString(2,1," : : ");
DS1302_SetTime();
while(1)
{
//获取按键键码
KeyNum=Key();
//按键1切换模式
if(KeyNum==1)
{
if(MODE==0){MODE=1;TimeSetSelect=0;}
else if(MODE==1){MODE=0;DS1302_SetTime();}
}
//根据变量MODE的值来切换模式,进入到不同的函数中
switch(MODE)
{
case 0:TimeShow();break;
case 1:TimeSet();break;
}
}
}
//定时器中断系统
void Timer0_Rountine() interrupt 1
{
static unsigned int T0Count;
TH0=0xFC;
TL0=0x66;
T0Count++;
if (T0Count==500)
{
T0Count=0;
TimeSetFlashFlag=!TimeSetFlashFlag;
}
}