DS1302学习
为什么使用外部计时芯片?精度更高且不怕断电
根本原因是单片机内部计时具有如下两大缺点
- 电子时钟精度不高,原因如下:1. 单片机电子时钟的计时脉冲基准,是由外部晶振的频率经过12分频后提供的,采用内部的定时,计数器来实现计时功能。所以,外接晶振频率的精确度直接影响电子钟计时的准确性。 2. 单片机电子时钟利用内部定时,计数器溢出产生中断(12MHz晶振一般为50ms)再乘以相应的倍率,来实现秒、分、时的转换。大家都知道,从定时,计数器产生中断请求到响应中断,需要3_8个机器周期。定时中断子程序中的数据人栈和重装定时,计数器的初值还需要占用数个机器周期。此外。从中断人口转到中断子程序也要占用一定的机器周期
- 断电后计时功能中断,不够稳定
因此使用外部计时芯片,更精确,而且使用了备用电池,不怕断电
DS1302的引脚
所有的引脚可以分为三个部分
- 电源部分:VCC1,VCC2和GND,VCC1和VCC2原则上只接一个就可以工作,一般VCC1接电池
- 晶振部分:X1和X2,给DS1302提供一个外部时钟信号
- 信号传输部分:SCK(serial clock,串行时钟)和IO(input output,输入输出),IO也叫SDA(serial data,串行数据)
数据传输:先写入地址,再读/写数据
DS1302中有很多位置(寄存器)放置数据。信号传输过程须先说明操作的地址,再进行读写数据操作。
读和写操作会放在不同的地方(寄存器),防止相互干扰。读写操作各对应8个寄存器,共16个寄存器,它们分别是
需注意,读取寄存器地址末尾都是奇数(1、3、5、7、9、B、D),对应地址末位是1;而写入寄存器地址的末尾都是偶数(2,4,6,8,A,C,E),对应地址末位是0。实际上,DS1302就是据此分辨是读还是写:1是读,0是写
读写操作具体实现:如何写一个字节
DS1302采用SPI通信,是最常用的串行通信方式,DS1302中有三条线,SCK,IO和 RST
- SCK是时钟信号,遵循“升写降读”的规则:
- SCK上升沿时,单片机向DS1302写入数据,DS1302读取数据
- SCK下降沿时,单片机从DS1302读取数据,DS1302放置数据到IO上
- IO(也叫SDA):具体的数据信号。显然,这个位的信号要在SCK 上升沿(针对写操作)或下降沿(针对读操作)产生之前准备好。
- RST(也叫CE,chansfer enable,传输使能):信号传输开关信号,1打开,0关闭。关闭时不会传输信号,可防止误操作。
比如写一个字节的程序如下
void Write_Ds1302(unsigned char temp) { unsigned char i; for (i=0;i<8;i++) { SCK=0;//先把SCK置为低电平 SDA=temp&0x01; //待写入数据temp和0x01求与操作,取最后一位放入SDA temp>>=1; //temp右移一位,让倒数第二位变为最后一位,为下一次传输做准备 SCK=1;//SCK从低到高,形成上升沿,此时SDA数据写入DS1302 } }
完整写入过程:写入两个字节(地址和数据)
前面提到,与写入相关的寄存器有8个。因此,我们在写入数据之前,必须要告诉DS1302数据写到哪里,也就是写入地址。
一个完整的写入过程如下:
void Write_Ds1302_Byte( unsigned char address,unsigned char dat ) { //不管是数据的读操作还是写操作,都要首先写入地址。 //而根据"升写降读”的原则,需要SCL产生一个上升沿,也就是从0到1 //如果SCL现在是1,直接设为0,那会首先产生一个下降延,可能会让DS1302误以为要进行读操作,它会放一个数据到IO口上,这样容易出错。 //为了避免这个问题,先关闭数据传输,再把SCL设为0,再打开数据传输 RST=0; _nop_(); //RST置为0,禁止数据传输 SCK=0; _nop_(); // SCK设为0 RST=1; _nop_(); //打开数据传输 //先写入地址,再写入数据 Write_Ds1302(address); Write_Ds1302(dat); //最后再次关闭数据传输 RST=0; }
完整读取过程:写入一个字节(地址),读取一个字节(数据)
一个完整的读取过程如下:
unsigned char Read_Ds1302_Byte ( unsigned char address ) { unsigned char i,temp=0x00; //同样先在关闭数据传输的情况下拉低SCK RST=0; _nop_(); SCK=0; _nop_(); RST=1; _nop_(); //写入地址 Write_Ds1302(address); //需要注意的是,上述过程完成后SCK已经是高电平了 for (i=0;i<8;i++) { SCK=0; //SCK回到低电平,产生下降沿,DS1302把数据末位放到IO(SDA)口上 temp>>=1;//右移1位,最高位补0, if(SDA) //如果SDA读到的数据是1的时候 temp|=0x80; //那就把最高位修正为1 SCK=1; //SCK变为高电平 } //关闭数据传输 RST=0; _nop_(); //这几行代码还没搞清楚。。。。 SCK=0; _nop_(); SCK=1; _nop_(); SDA=0; _nop_(); SDA=1; _nop_(); return (temp); }
蓝桥杯给出资料包中的DS1302相关的完整代码
#ifndef __DS1302_H #define __DS1302_H void Write_Ds1302(unsigned char temp); void Write_Ds1302_Byte( unsigned char address,unsigned char dat ); unsigned char Read_Ds1302_Byte( unsigned char address ); #endif
/* 程序说明: DS1302驱动程序 软件环境: Keil uVision 4.10 硬件环境: CT107单片机综合实训平台 8051,12MHz 日 期: 2011-8-9 */ #include <reg52.h> #include <intrins.h> sbit SCK=P1^7; sbit SDA=P2^3; sbit RST = P1^3; // DS1302复位 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); }
蓝桥杯主函数的代码(可直接用于仿真)
#include <DS1302.H> void delay() { char i; for(i=0;i<100;i++); } //显示程序,在数码管的add位置显示数字dat void display(unsigned char add,unsigned char dat) { unsigned char a[11]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf}; P2 = (P2 & 0x1f) | 0xc0; //前三位变为110,对应6,打开Y6C对应锁存器 P0 = 0x01 << add; //输出位选信号 P2 = (P2 & 0x1f) | 0xe0;//前三位变为111,打开Y7C对应锁存器 P0 = a[dat]; //输出数字信号 delay(); P0 = 0xff; } void main() { unsigned char i; unsigned char tens, ones;//定义存储十位和各位的变量 //设定初始时间,比如下面的例子为,0秒,47分,17时,30日,4月,星期6,22年(年也只有2位) unsigned char timer[7] = {0x00,0x47,0x17,0x30,0x04,0x06,0x22}; //----------定义DS1302寄存器地址,这是固定的,不可更改-------- unsigned char code Write_Add[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c}; unsigned char code Read_Add[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d}; //---------关闭蜂鸣器,电机和LED灯------------------ P2 = (P2 & 0x1f) | 0xa0;//前三位变为101,Y5C对应锁存器打开 P0 = 0;//ULN2003芯片输入0,输出高阻态(可以理解为断路) P2 = (P2 & 0x1f) |0x80;//前三位变为100,Y4C对应锁存器打开 P0 = 0xff;//L1-L7变为高电平,所有灯关闭 //---------向DS1302写入初始时间------------------ Write_Ds1302(0x8e,0x00);//关闭写保护,允许写入 for(i=0;i<7;i++) Write_Ds1302(Write_Add[i],timer[i]);//写入初始时间信息 Write_Ds1302(0x8e,0x80);//打开写保护,不再允许写入 //---------不停的从DS1302芯片中读取数据,并显示在7段数码管上-------- while(1) { //读取时间信息 for(i=0;i<7;i++) timer[i] = Read_Ds1302(Read_Add[i]); //显示时间信息 for(i=0;i<3;i++) { ones = timer[2-i]%16; //计算低四位获取个位 tens = timer[2-i]/16;//计算高四位获取十位 display(3*i,tens); //显示十位 display(3*i+1,ones);//显示各位 display(3*i+2,10); //显示- } } }
仿真的结果如下所示
============================找指导老师布置任务吧==============================