蓝桥杯----DS1302实时时钟
(六)、DS1302实时时钟
1、原理(图 二十六)
DS1302通过三线串行接口与单片机进行通信。微控制器可以通过设置RST引脚为高电平来使能DS1302,并通过SCK引脚提供串行时钟信号,然后通过I/O引脚进行数据的读写操作。在开头用sbit来定义三个引脚,方便后续操作。(sbit SDA = P2^3;sbit SCK = P1^7;sbit RST = P1^3;)操作RST = 0实际含义就是P1^3 = 0。DS1302 的时钟和日历数据采用二进制编码的十进制(BCD码,即字节的高四位是数据的十位,字节的低四位是数据的个位)格式存储,这样方便读取和显示。所以在发送数据前我们可以将十进制转为BCD码形式发送。读取时将BCD码形式转换为十进制数据。
蓝桥杯官方会给出底层通信函数,包括在指定地址下写入一个数据,在指定地址下读取一个数据。通过这两个函数就可以实现简单的通信了。至于具体的时序图我们可以不深入研究。 在DS1302中时分秒、年月日、星期都有固定的写入与读取的地址。见图 二十七注释。
在写入之前必须解除写保护,也就是将0x8E对应寄存器下WP位数据置0,在写入完成后再重新开启写保护。
定义一个unsigned char 类型数据Time[3]存放十进制的数据,再写入时要将十进制转换为BCD码,公式:Time[0]/10*16+Time[0]%10;读取时BCD码形式转换为十进制数据,公式:Data/16*10+Data%16。
在今年蓝桥杯国赛中出现了从来没考过的读取负温度,打破了常规,目前DS1302只考过24小时制度--时分秒写入与读取(十分简单)。在后期的文案中会给大家普及,包括可能突破常规的DS1302年月日写入与读取、12小时制度小时…
图 二十六 DS1302原理图
图 二十七 时分秒、年月日、星期地址
注释:
第一列READ—read读取、第二列WRITE-write写入、BIT就是二进制数据下对应的位,Seconds秒、Minutes分钟、Hour小时、Date日,Month月、Day星期、Year年、WP是写保护位(该位置为0关闭写保护,允许写入,为1禁止写入)
标为10Year或者10Seconds类似表示该位置表示十位,如写入0x56数据(BCD码)在秒寄存器中就表示十位为5,个位为6的秒数据,就是56s,如果此时写入的表示BCD码形式,而是十进制56,数据就会出现错误。
小时默认都为24小时制度,不用管。写入0x23就是23小时,默认Bi7写入0,Bit5、Bit4共同决定十位,Bit0-3就是决定个位的数据。
2、代码解读
写入:
第一步,关闭写保护也就是让WP位置置0,Write_Ds1302_Byte(地址,数据),所以在0x8e位置写0x00就可以解除写保护。
接下来我们需要将秒位置停止,当在23:59:59时,此时如果继续让秒指针转动,而分与小时还没写入就会出现一些问题。所以在0x80(秒对应写入地址如图 二十七)写入数据0x80停止秒。
然后开始写入时分秒数据,可以在主函数中定义unsigned char Time[3]={20,56,45}数组,在Ds1302.c中声明外部可调用数组extern unsigned char Time[3],秒写入地址0x80,分0x82,小时0x84,分别在对应地址下可写入对应数据,将十进制数据转换成BCD码形式。公式:Time[0]/10*16+Time[0]%10,Time[0]为即将写入的十进制小时数据。
最后再打开写保护,将Bit7位置WP位置置1,即在0x8e位置下写0x80就可以打开写保护。
读取:
依次读取0x85(小时读取地址),0x83(分钟读取地址),0x81(秒读取地址),并且将数据转换为十进制即可。公式:Data / 16 * 10 + Data % 16,Data为BCD码形式数据(读取原始数据)
图 二十八 DS1302时分秒写入代码展示
图 二十九 DS1302时分秒读取代码展示
3、提供参考代码,希望对读者有帮助
#include <STC15F2K60S2.H>
#include <INTRINS.h>sbit SDA = P2^3;
sbit SCK = P1^7;
sbit RST = P1^3;/*--------------------------------------官方提供------------------------------------------*/
extern unsigned char Time[3];
/* # DS1302代码片段说明1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题中对单片机时钟频率的要求,进行代码调试和修改。
*/ //
void Write_Ds1302(unsigned char temp)
{unsigned char i;for (i=0;i<8;i++) { SCK = 0;SDA = temp&0x01;temp>>=1; SCK=1;}
} //
void Write_Ds1302_Byte( unsigned char address,unsigned char dat )
{RST=0; _nop_();SCK=0; _nop_();RST=1; _nop_(); Write_Ds1302(address); Write_Ds1302(dat); RST=0;
}//
unsigned char Read_Ds1302_Byte ( unsigned char address )
{unsigned char i,temp=0x00;RST=0; _nop_();SCK=0; _nop_();RST=1; _nop_();Write_Ds1302(address);for (i=0;i<8;i++) { SCK=0;temp>>=1; if(SDA)temp|=0x80; SCK=1;} RST=0; _nop_();SCK=0; _nop_();SCK=1; _nop_();SDA=0; _nop_();SDA=1; _nop_();return (temp);
}/*--------------------------------------官方提供------------------------------------------*///DS1302写入时分秒数据函数
void DS1302_WriteTime()
{unsigned char i;Write_Ds1302_Byte(0x8e,0x00); //关闭写保护(WP位置置0)Write_Ds1302_Byte(0x80,0x80); //停止秒时钟,防止写入时产生错误Write_Ds1302_Byte(0x84,Time[0]/10*16+Time[0]%10); //小时Write_Ds1302_Byte(0x82,Time[1]/10*16+Time[1]%10); //分Write_Ds1302_Byte(0x80,Time[2]/10*16+Time[2]%10); //秒/*同义表达for(i = 0;i<3;i++)Write_Ds1302_Byte(0x84-i*2,Time[i]/10*16+Time[i]%10);*/Write_Ds1302_Byte(0x8e,0x80); //打开写保护
}//DS1302读取时分秒数据函数
void DS1302_ReadTime()
{unsigned char i;unsigned char temp;EA = 0; //保护时序,关闭中断/*同义表达for(i = 0;i<3;i++){temp = Read_Ds1302_Byte(0x85-i*2);Time[i] = temp/16*10+temp%16;}*/temp = Read_Ds1302_Byte(0x85); //读取时数据Time[0] = temp/16*10+temp%16; //BCD码转十进制temp = Read_Ds1302_Byte(0x83); //读取分数据Time[1] = temp/16*10+temp%16;temp = Read_Ds1302_Byte(0x81); //读取秒数据Time[2] = temp/16*10+temp%16;EA = 1; //重新打开中断
}