hrefspace

 找回密码
 立即注册
搜索
热搜: PHP PS 程序设计
查看: 989|回复: 0

自学AVR单片机二十二(时钟芯片DS1302)

[复制链接]

460

主题

467

帖子

1421

积分

大司空

Rank: 5Rank: 5

积分
1421
发表于 2024-2-21 02:35:21 | 显示全部楼层 |阅读模式
一、简单原理
        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的实时时间,并发送到串口,在计算机上通过串口助手观察程序执行结果。
  1. #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
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|hrefspace

GMT+8, 2024-5-3 01:23 , Processed in 0.068510 second(s), 22 queries .

Powered by hrefspace X3.4 Licensed

Copyright © 2022, hrefspace.

快速回复 返回顶部 返回列表