|
一、简单原理
DS1302是一种高性能、低功耗、带RAM的实时时钟芯片,它可以对年、月、日、星期、天、时、分、秒自动计时,并且具有闰年补偿功能。
DS1302具有宽电压工作范围:2.7-5.5v。
DS1302采用3线串行接口与单片机进行同步通信,并可采用突发方式一次传送多字节的时钟信号或RAM数据。
DS1302内部有一个31字节的用于临时存放数据的RAM寄存器。
DS1302还具备主电源/后备电源供电的双电源引脚,可以自动切换电源,并且提供了对后备电源进行涓细电流充电的能力。
DS1302的引脚说明如下:
Vcc1为后备电源,VCC2为主电源。在主电源关闭的情况下,也能保持时钟的连续运行。DS1302由Vcc1或Vcc2两者中的较大者供电。当Vcc2大于Vcc1+0.2V时,Vcc2给DS1302供电。当Vcc2小于Vcc1时,DS1302由Vcc1供电。X1和X2是振荡源,外接32.768kHz晶振。RST是复位/片选线,通过把RST输入驱动置高电平来启动所有的数据传送。RST输入有两种功能:首先,RST接通控制逻辑,允许地址/命令序列送入移位寄存器;其次,RST提供终止单字节或多字节数据的传送手段。当RST为高电平时,所有的数据传送被初始化,允许对DS1302进行操作。如果在传送过程中RST置为低电平,则会终止此次数据传送,I/O引脚变为高阻态。上电运行时,在Vcc≥2.5V之前,RST必须保持低电平。只有在SCLK为低电平时,才能将RST置为高电平。I/O为串行数据输入输出端(双向),SCLK始终是输入端。
DS1302的控制字节
DS1302的控制字如下图所示。控制字节的最高有效位(位7)必须是逻辑1,如果它为0,则不能把数据写入DS1302中,位6如果为0,则表示存取日历时钟数据,为1表示存取RAM数据;位5至位1指示操作单元的地址;最低有效位(位0)如为0表示要进行写操作,为1表示进行读操作,控制字节总是从最低位开始输出。
数据输入输出(I/O)
在控制指令字输入后的下一个SCLK时钟的上升沿时,数据被写入DS1302,数据输入从低位即位0开始。同样,在紧跟8位的控制指令字后的下一个SCLK脉冲的下降沿读出DS1302的数据,读出数据时从低位0位到高位7。
DS1302的寄存器
DS1302有12个寄存器,其中有7个寄存器与日历、时钟相关,存放的数据位为BCD码形式,其日历、时间寄存器及其控制字请查阅DS1302的数据手册。
二、电路实现
如下是本实例中DS1302的电路连接图。由于只是学习DS1302的基本功能,所以本电路没有使用后备电源。
三、 程序设计
下面是DS1302的完整程序,程序实现将一个初始化时间写入DS1302,然后读出DS1302的实时时间,并发送到串口,在计算机上通过串口助手观察程序执行结果。- #include<[url=http://www.mcuzx.net]AVR[/url]/io.h> #include <util/delay.h>#include <avr/interrupt.h> //中断函数头文件//常量声明#define BAUD 9600#define TURE 1#define FALSE 0//时钟/日历寄存器#define RD 0x01 //读#define WR 0x00 //写#define SECOND 0x80 //秒#define MINUTE 0x82 //分#define HOUR 0x84 //时#define DAY 0x86 //日#define MONTH 0x88 //月#define WEEK 0x8A //星期 DATE#define YEAR 0x8C //年#define WR_PROTECT 0x8E //控制(写保护)#define CHARGE 0x90 //涓流充电#define BURST 0xBE //时钟多字节//配置位#define CLK_HALT 0x80 //停止时钟控制位 SECOND bit7#define CLK_START 0x00 //启动时钟#define M12_24 0x80 //12/24小时值选择位 HOUR bit7 #define PROTECT 0x80 //写保护控制位 CONTROL bit7 #define UPROTECT 0x00 //写保护控制位 CONTROL bit7 //涓流充电控制常量#define TC_D1R2 0xA5 //充电时选择一个二极管和2K电阻 #define TC_D2R8 0xAB //充电时选择二个二极管和8K电阻 #define TC_DISABLED 0x00 //禁止充电功能//RAM 命令#define RAMBASE 0xC0 //RAM起始位为0XCO,RAM范围0-31//全局变量声明unsigned char Get_Time[7] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00};//函数声明void Delayus(unsigned int lus); //us延时函数void Delayms(unsigned int lms); //ms延时函数void Port_Init(void); //端口初始化配置void Usart_Init(void); //USART寄存器设置void Usart_PutChar(unsigned char cTXData); //字节发送函数void Usart_PutString(unsigned char *pcString); //字符串发送函数unsigned char DS1302_ReadByte(void); //从DS1302读一个字节数据void DS1302_WriteByte(unsigned char dat); //向DS1302写一个字节数据unsigned char DS1302_ReadData(unsigned addr); //从DS1302的指定地址读一个字节数据void DS1302_WriteData(unsigned char addr,unsigned data); //向DS1302的指定地址写一个字节数据void DS1302_SetTime(unsigned char *time); //对DS1302设置时间void DS1302_GetTime(void); //从DS1302读取时间unsigned char DS1302_Check(void); //DS1302是否工作检测void DS1302_Init(void); //DS1302初始化int main(void) {unsigned char Set_Time[7] = {0x59,0x59,0x10,0x20,0x01,0x02,0x09}; //设置秒,分,时,日,月,星期,年unsigned char i;Port_Init();Usart_Init();Usart_PutString("DS1302实时时钟试验");Usart_PutString("设置当前时间为:");Usart_PutChar(0x0D);Usart_PutChar(0x0A); //发送回车换行Usart_PutChar('2');Usart_PutChar('0');Usart_PutChar(Set_Time[6] / 16 + 0x30);Usart_PutChar(Set_Time[6] % 16 + 0x30);Usart_PutString("年");Usart_PutChar(' ');Usart_PutChar(Set_Time[4] / 16 + 0x30);Usart_PutChar(Set_Time[4] % 16 + 0x30);Usart_PutString("月");Usart_PutChar(' ');Usart_PutChar(Set_Time[3] / 16 + 0x30);Usart_PutChar(Set_Time[3] % 16 + 0x30);Usart_PutString("日");Usart_PutChar(' ');Usart_PutChar(Set_Time[2] / 16 + 0x30);Usart_PutChar(Set_Time[2] % 16 + 0x30);Usart_PutString("时");Usart_PutChar(' ');Usart_PutChar(Set_Time[1] / 16 + 0x30);Usart_PutChar(Set_Time[1] % 16 + 0x30);Usart_PutString("分");Usart_PutChar(' ');Usart_PutChar(Set_Time[0] / 16 + 0x30);Usart_PutChar(Set_Time[0] % 16 + 0x30);Usart_PutString("秒");Usart_PutChar(' ');Usart_PutString("星期");Usart_PutChar(' ');//Usart_PutChar(Set_Time[5] / 16 + 0x30);Usart_PutChar(Set_Time[5] % 16 + 0x30);Usart_PutChar(0x0D);Usart_PutChar(0x0A); //发送回车换行 DS1302_SetTime(Set_Time);sei(); //使能全局中断 while(1){ DS1302_GetTime(); Usart_PutString("现在时间"); Usart_PutChar(0x0D); Usart_PutChar(0x0A); //发送回车换行 Usart_PutChar('2'); Usart_PutChar('0'); Usart_PutChar(Get_Time[6] / 16 + 0x30); Usart_PutChar(Get_Time[6] % 16 + 0x30); Usart_PutString("年"); Usart_PutChar(' '); Usart_PutChar(Get_Time[4] / 16 + 0x30); Usart_PutChar(Get_Time[4] % 16 + 0x30); Usart_PutString("月"); Usart_PutChar(' '); Usart_PutChar(Get_Time[3] / 16 + 0x30); Usart_PutChar(Get_Time[3] % 16 + 0x30); Usart_PutString("日"); Usart_PutChar(' '); Usart_PutChar(Get_Time[2] / 16 + 0x30); Usart_PutChar(Get_Time[2] % 16 + 0x30); Usart_PutString("时"); Usart_PutChar(' '); Usart_PutChar(Get_Time[1] / 16 + 0x30); Usart_PutChar(Get_Time[1] % 16 + 0x30); Usart_PutString("分"); Usart_PutChar(' '); Usart_PutChar(Get_Time[0] / 16 + 0x30); Usart_PutChar(Get_Time[0] % 16 + 0x30); Usart_PutString("秒"); Usart_PutChar(' '); Usart_PutString("星期"); Usart_PutChar(' '); //Usart_PutChar(Set_Time[5] / 16 + 0x30); Usart_PutChar(Get_Time[5] % 16 + 0x30); Usart_PutChar(0x0D); Usart_PutChar(0x0A); //发送回车换行for(i = 0;i < 4;i++){ Delayms(1000);}}}//端口状态初始化设置函数void Port_Init(){PORTD = 0X00; //USART的发送接收端口分别为PD0和PD1DDRD |= (1 << PD3); //PD0为接收端口,置为输入口;PD1为发送端口,置为输出口DDRE |= (1 << PE2) | (1 << PE3) | (1 << PE6); //DS1302的SCLK和I/O引脚设为输出//DDRE |= (1 << PE6); //DS1302的RST引脚设为输出}//USART寄存器配置函数void Usart_Init(){UCSR1A = 0X00; UCSR1C |= (1 << UCSZ11) | (1 << UCSZ10); //异步,数据格式8,N,1 UBRR1L = (F_CPU / BAUD / 16 - 1) % 256; //波特率设置UBRR1H = (F_CPU / BAUD / 16 - 1) / 256; UCSR1B |= (1 << RXCIE1) | (1 << RXEN1) | (1 << TXEN1); //发送使能}//字节发送函数void Usart_PutChar(unsigned char cTXData){while( !(UCSR1A & (1 << UDRE1)) ); //只有数据寄存器为空时才能发送数据UDR1 = cTXData; //发送数据送USART I/O数据寄存器-UDR}//接收中断函数ISR(USART1_RX_vect){unsigned char Rev;Rev = UDR1; //从USART I/O数据寄存器-UDR中读出数据Usart_PutChar(Rev); //将接收到的数据发送}void Usart_PutString(unsigned char *pcString){while (*pcString){ Usart_PutChar(*pcString++);} }//从DS1302读一个字节数据unsigned char DS1302_ReadByte(void) {unsigned char i,dat = 0; //dat存放读出的数据,初始化为0PORTE &= ~(1 << PE3); //DS1302的I/O口上拉不使能,DDRE &= ~(1 << PE3); //DS1302的I/O口设置为输入口,准备读数据for(i = 0;i < 8;i++) //读8位,低位在前,右移{ dat >>= 1; //读出的数据右移一位 PORTE |= (1 << PE2); //DS1302的SCLK端口拉高 Delayus(10); // PORTE &= ~(1 << PE2); //DS1302的SCLK端口拉低,产生下降沿, Delayus(10); if(PINE & (1 << PE3)) //读数据端口状态 { dat |= 0x80; //如果数据端口位高,相应数据位置1 } }DDRE |= (1 << PE3); //最后将数据端口设置为输出 return dat; //返回读出的数据}//向DS1302写一个字节数据void DS1302_WriteByte(unsigned char dat) {unsigned char i;for(i = 0;i < 8;i++) //写8位,低位在前{ PORTE &= ~(1 << PE2); //DS1302的SCLK置低 if(dat & 0x01) //写数据位 { PORTE |= (1 << PE3); //如果该位为1,则I/O口置高 } else { PORTE &= ~(1 << PE3); //如果该位为0,则I/O口置低 } Delayus(10); // PORTE |= (1 << PE2); //DS1302的SCLK置高,产生上升沿 dat >>= 1; //数据右移1位} }//从DS1302的指定地址读一个字节数据unsigned char DS1302_ReadData(unsigned addr) {unsigned char data;PORTE &= ~(1 << PE6); //拉低片选端PORTE &= ~(1 << PE2);//拉低时钟端Delayus(10);PORTE |= (1 << PE6);//拉高片选端Delayus(10);DS1302_WriteByte(addr);//写入操作命令(地址)Delayus(10);data = DS1302_ReadByte();//读出数据Delayus(10);PORTE &= ~(1 << PE2); //拉低时钟端 PORTE &= ~(1 << PE6); //拉低片选端 return data; }//向DS1302的指定地址写一个字节数据void DS1302_WriteData(unsigned char addr,unsigned data) {PORTE &= ~(1 << PE6); //拉低片选端PORTE &= ~(1 << PE2);//拉低时钟端Delayus(10);PORTE |= (1 << PE6);//拉高片选端Delayus(10);DS1302_WriteByte(addr);//写入操作命令(地址)Delayus(10);PORTE &= ~(1 << PE2);//拉低时钟端Delayus(10);DS1302_WriteByte(data);//写入数据PORTE &= ~(1 << PE2); //拉低时钟端Delayus(10);PORTE &= ~(1 << PE6); //拉低片选端 }//对DS1302设置时间 void DS1302_SetTime(unsigned char *time) {unsigned char i;unsigned char addr = 0x80;//写入地址从秒寄存器开始DS1302_WriteData(WR_PROTECT | WR,UPROTECT);//控制命令,WP位为0,允许写操作Delayms(5);for(i = 0;i < 7;i++){ DS1302_WriteData(addr | WR,time);// 秒 分 时 日 月 星期 年 addr += 2; Delayms(1);}DS1302_WriteData(WR_PROTECT | WR,PROTECT);//控制命令,WP位为1,不允许写操作 }//从DS1302读取时间void DS1302_GetTime(void) {unsigned char i;/*//unsigned char addr;PORTA &= ~(1 << PA1);for(i = 0;i < 7;i++){ tim = DS1302_ReadData(addr | RD); addr += 2;}*/////////////////////PORTE &= ~(1 << PE6);Delayus(10);PORTE |= (1 << PE6);Delayus(10);DS1302_WriteByte(0xbf);for(i = 0;i < 8;i++){ Get_Time = DS1302_ReadByte();}PORTE &= ~(1 << PE6);///////////////////////////////////PORTE &= ~(1 << PE2); }//DS1302是否工作检测unsigned char DS1302_Check(void) {DS1302_WriteData(WR_PROTECT | WR,UPROTECT);DS1302_WriteData(RAMBASE | WR,0x31);if(DS1302_ReadData(RAMBASE | WR) == 0x31){ return TURE;}else{ return FALSE;} }//DS1302初始化void DS1302_Init(void) {DS1302_WriteData(WR_PROTECT | WR,UPROTECT); //写入写允许命令 DS1302_WriteData(SECOND | WR,CLK_START); //启动振荡器,DS1302开始工作DS1302_WriteData( WR_PROTECT | WR,PROTECT); //控制命令,WP位为1,不允许写操作 }//us级别的延时函数void Delayus(unsigned int lus){while(lus--){ _delay_loop_2(4); //_delay_loop_2(1)是延时4个时钟周期,参数为3则延时12 //个时钟周期,本实验用12M晶体,则12个时钟周期为12/12=1us }}//ms级别的延时函数void Delayms(unsigned int lms){while(lms--){ Delayus(1000); //延时1ms }}
复制代码 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
|