极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 25466|回复: 14

时钟芯片DS1302可靠起振的方法【转载】

[复制链接]
发表于 2012-10-19 14:28:22 | 显示全部楼层 |阅读模式
简介 在DS1302的实际使用中,采用辅助电容法,可以解决DS1302在应用中由于晶振的负载电容不匹配而引起的停振问题。
关键词 时钟 负载电容匹配
概述
  DS1302是Dallas公司生产的一种实时时钟芯片。它通过串行方式与单片机进行数据传送,能够向单片机提供包括秒、分、时、日、月、年等在内的实时时间信息,并可对月末日期、闰年天数自动进行调整;它还拥有用于主电源和备份电源的双电源引脚,在主电源关闭的情况下,也能保持时钟的连续运行。另外,它还能提供31字节的用于高速数据暂存的RAM。鉴于上述特点,DS1302已在许多单片机系统中得到应用,为系统提供所需的实时时钟信息。
一、 DS1302的主要特性
1. 引脚排列

图1 DS1302引脚排列图
  DS1302的引脚排列如图1所示,各引脚的功能如下:
  X1,X2——32768Hz晶振引脚端;   RST——复位端;   I/O——数据输入/输出端;   SCLK——串行时钟端;   GND——地;   VCC2,VCC1——主电源与后备电源引脚端。
2. 主要功能
  DS1302时钟芯片内主要包括移位寄存器、控制逻辑电路、振荡器、实时时钟电路以及用于高速暂存的31字节RAM。DS1302与单片机系统的数据传送依靠RST,I/O,SCLK三根端线即可完成。其工作过程可概括为:首先系统RST引脚驱动至高电平,然后在作用于SCLK时钟脉冲的作用下,通过I/O引脚向DS1302输入地址/命令字节,随后再在SCLK时钟脉冲的配合下,从I/O引脚写入或读出相应的数据字节。因此,其与单片机之间的数据传送是十分容易实现的。
二、 时钟的产生及存在的问题
  (1) 在实际使用中,我们发现DS1302的工作情况不够稳定,主要表现在实时时间的传送有时会出现误差,有时甚至整个芯片停止工作。我们对DS1302的工作电路进行了分析,其与单片机系统的连接如图2所示。从图中可以看出,DS1302的外部电路十分简单,惟一外接的元件是32768Hz的晶振。通过实验我们发现:当外接晶振电路振荡时,DS1302计时正确;当外接晶振电路停振时,DS1302计时停止。因此,我们认为32768Hz晶振是造成DS1302工作不稳定的主要原因。

图2 DS1302与单片机系统的连接图
  (2) DS1302时钟的产生基于外接的晶体振荡器,振荡器的频率为32768Hz。该晶振通过引脚X1、X2直接连接至DS1302,即DS1302是依靠外部晶振与其内部的电容配合来产生时钟脉冲的。由于DS1302在芯片本身已经集成了6pF的电容,所以,为了获得稳定可靠的时钟,必须选用具有6pF负载电容的晶振。
  然而,许多人在选用晶振时仅仅注意了晶振的额定频率值,而忽视了晶振的负载电容大小,甚至连许多经销商也不能提供所售晶振的负载电容。所以即使在使用中选用了符合32768Hz的晶振,但如果该晶振的负载电容与DS1302提供的6pF不一致时,就会影响晶振的起振或导致振荡频率的偏移,出现上述在应用中的问题。
三、 利用辅助电容实现负载匹配
  (1) 当所选的晶振负载电容不是6pF时,可以采用增加辅助电容的方法提高或降低DS1302振荡器的电容性负载,使之与晶体所需的电容值匹配。如果已知晶体的负载电容为CI,若CI<6pF,则可以增加一个并联电容CS以产生所需的总负载电容CI,即CI=6pF+CS;若CI>6pF,则可以在晶体的一端增加一个串联电容CS,以产生所需的负载电容CI,即1/CI=1/6pF+1/CS,通过计算即可得出应增加的辅助电容大小。辅助电容的接法如图3所示。

图3 CS连接电路图
  (2) 在使用前对晶体的负载电容并不知道的情况下,通过测定晶体振荡频率的方法可以确定该晶体的负载电容。
  对于晶体振荡器来说,其振荡频率与负载电容之间的关系是确定的。以本文讨论的DS1302使用的32768Hz晶振为例:当它工作于所要求的负载电容时,能较准确地产生 32768Hz的频率;当它的负载电容小于6pF时,其振荡频率会正向偏移;当它的负载电容大于6pF时,其振荡频率就会负向偏移。因此,对于未知负载电容的晶体应首先采用实验的方法,在其两端加入辅助电容使晶体起振,然后用频率计测出振荡频率。若测得频率大于32768Hz,说明负载电容偏小;若测得频率小于32768Hz,说明负载电容偏大。对辅助电容逐步调整,最终使振荡频率尽可能接近32768Hz,则此时晶体端所接负载电容的总和就是适合该晶体的负载电容。
结论
  以上方法经我们在实际工作中多次使用,证明确实有效。它放宽了DS1302在使用中对晶振的条件要求,增强了DS1302在工作中的稳定性,对DS1302更广泛地应用具有积极的意义。
参考文献
1 TI最新特低功耗微控制器系列资料. P&S武汉力源电子股份有限公司,2000(3) 2 崔惠柳. 单片机应用技术选编:串行时钟芯片DS1302在汽车智能记录仪中的应用.北京:北京航空航天大学出版社,1999

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复

使用道具 举报

发表于 2013-12-8 22:43:02 | 显示全部楼层
支持下,正在找校准时钟的方法,学习了。
支持人真不容易呀,发表回复验证码输了十几次。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2013-12-9 09:21:32 | 显示全部楼层
黄金情 发表于 2013-12-8 22:43
支持下,正在找校准时钟的方法,学习了。
支持人真不容易呀,发表回复验证码输了十几次。

呵呵,谢谢支持!多在论坛发帖就不用验证码了~~~~
回复 支持 反对

使用道具 举报

发表于 2013-12-9 21:57:01 | 显示全部楼层
这个要好好学习一下啦,我用DS1302芯片加晶振,一直显示不出正常时间啊。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2013-12-10 07:42:59 | 显示全部楼层
philhoo 发表于 2013-12-9 21:57
这个要好好学习一下啦,我用DS1302芯片加晶振,一直显示不出正常时间啊。

我还有几篇DS1302的帖子,都看看一定能搞定
回复 支持 反对

使用道具 举报

发表于 2015-3-19 09:13:47 | 显示全部楼层
世界因你们这些人而精彩啊,谢谢分享
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-3-19 11:21:39 | 显示全部楼层
刘云峰 发表于 2015-3-19 09:13
世界因你们这些人而精彩啊,谢谢分享

谢谢支持~~~~~~~
回复 支持 反对

使用道具 举报

发表于 2015-5-17 10:35:37 | 显示全部楼层
哈哈......我做了个I2CLCD时钟带温度显示、星期显示。{:soso_e100:}晒下代码:
  1. #include <Wire.h>
  2. #include <OneWire.h>
  3. #include <DallasTemperature.h>
  4. #include <LiquidCrystal_I2C.h>

  5. #include <stdio.h>
  6. #include <string.h>
  7. #include <DS1302.h>

  8. int Mode=0;
  9. int h = 0;
  10. int m = 0;
  11. int s = 0;
  12. int d = 12;
  13. int mo = 5;
  14. int y = 15;
  15. uint8_t CE_PIN   = 5;
  16. uint8_t IO_PIN   = 6;
  17. uint8_t SCLK_PIN = 7;
  18. float c = 0.00;
  19. String comdata = "";
  20. int numdata[7] ={0}, j = 0, mark = 0;
  21. char day[10];


  22. byte one[8] = {
  23.   B00000,
  24.   B00000,
  25.   B00000,
  26.   B11111,
  27.   B00000,
  28.   B00000,
  29.   B00000,
  30.   B00000,
  31. };

  32. byte two[8] = {
  33.   B00000,
  34.   B01110,
  35.   B00000,
  36.   B00000,
  37.   B00000,
  38.   B11111,
  39.   B00000,
  40.   B00000,
  41. };

  42. byte three [8] = {
  43.   B00000,
  44.   B01110,
  45.   B00000,
  46.   B01110,
  47.   B00000,
  48.   B11111,
  49.   B00000,
  50.   B00000,
  51. };

  52. byte four[8] = {
  53.   B00000,
  54.   B11111,
  55.   B10001,
  56.   B10101,
  57.   B11101,
  58.   B00001,
  59.   B01110,
  60.   B00000,
  61. };


  62. byte five [8] = {
  63.   B00000,
  64.   B11110,
  65.   B01000,
  66.   B11110,
  67.   B01010,
  68.   B01010,
  69.   B11111,
  70.   B00000,
  71. };

  72. byte six[8] = {
  73.   B00000,
  74.   B00100,
  75.   B00000,
  76.   B11111,
  77.   B00000,
  78.   B01010,
  79.   B10001,
  80.   B00000,
  81. };

  82. byte seven [8] = {
  83.   B11111,
  84.   B10001,
  85.   B10001,
  86.   B11111,
  87.   B10001,
  88.   B10001,
  89.   B11111,
  90.   B00000,
  91. };


  92. LiquidCrystal_I2C lcd(0x27, 16, 2);
  93. DS1302 rtc(CE_PIN, IO_PIN, SCLK_PIN);
  94. #define ONE_WIRE_BUS 2
  95. OneWire oneWire(ONE_WIRE_BUS);
  96. DallasTemperature sensors(&oneWire);



  97. void setup() {
  98.   sensors.begin();
  99.   lcd.init();
  100.   lcd.backlight();
  101.   lcd.createChar(0, one);
  102.   lcd.createChar(1, two);
  103.   lcd.createChar(2, three);
  104.   lcd.createChar(3, four);
  105.   lcd.createChar(4, five);
  106.   lcd.createChar(5, six);
  107.   lcd.createChar(6, seven);
  108.   lcd.home();
  109.   lcd.print("Welcome to use!");
  110.   delay(1000);
  111.   lcd.clear();
  112.   Serial.begin(9600);
  113.   Serial.println("Wait for set time");
  114.   rtc.write_protect(false);
  115.     rtc.halt(false);
  116. }


  117.   

  118. void Display(){
  119.   Time t = rtc.time();
  120.   memset(day, 0, sizeof(day));
  121.     switch (t.day)
  122.     {
  123.     case 1: lcd.setCursor(12,1); lcd.write((byte)0); break;
  124.     case 2: lcd.setCursor(12,1); lcd.write(1); break;
  125.     case 3: lcd.setCursor(12,1); lcd.write(2); break;
  126.     case 4: lcd.setCursor(12,1); lcd.write(3); break;
  127.     case 5: lcd.setCursor(12,1); lcd.write(4); break;
  128.     case 6: lcd.setCursor(12,1); lcd.write(5); break;
  129.     case 7: lcd.setCursor(12,1); lcd.write(6); break;
  130.     }
  131.   lcd.setCursor(0,0);
  132.   if(t.hr < 10)  lcd.print("0");
  133.   lcd.print(t.hr);
  134.   lcd.print(":");
  135.   if(t.min < 10)  lcd.print("0");
  136.   lcd.print(t.min);
  137.   lcd.print(":");
  138.   if(t.sec < 10)  lcd.print("0");
  139.   lcd.print(t.sec);
  140.   lcd.setCursor(0,1);
  141.   lcd.print(t.yr);
  142.   lcd.print("/");
  143.   if(t.mon < 10)  lcd.print("0");
  144.   lcd.print(t.mon);
  145.   lcd.print("/");
  146.   if(t.date < 10)  lcd.print("0");
  147.   lcd.print(t.date);
  148.   lcd.setCursor(9,0);
  149.   lcd.print(c);
  150.   lcd.write((char)223);
  151.   lcd.print("C");
  152.   lcd.print("     ");
  153. }


  154. void loop() {
  155.   sensors.requestTemperatures();
  156.   c = sensors.getTempCByIndex(12);
  157.   Display();
  158.   /* 当串口有数据的时候,将数据拼接到变量comdata */
  159.     while (Serial.available() > 0)
  160.     {
  161.         comdata += char(Serial.read());
  162.         delay(2);
  163.         mark = 1;
  164.     }
  165.     /* 以逗号分隔分解comdata的字符串,分解结果变成转换成数字到numdata[]数组 */
  166.     if(mark == 1)
  167.     {
  168.         Serial.print("You inputed : ");
  169.         Serial.println(comdata);
  170.         for(int i = 0; i < comdata.length() ; i++)
  171.         {
  172.             if(comdata[i] == ',' || comdata[i] == 0x10 || comdata[i] == 0x13)
  173.             {
  174.                 j++;
  175.             }
  176.             else
  177.             {
  178.                 numdata[j] = numdata[j] * 10 + (comdata[i] - '0');
  179.             }
  180.         }
  181.         /* 将转换好的numdata凑成时间格式,写入DS1302 */
  182.         Time t(numdata[0], numdata[1], numdata[2], numdata[3], numdata[4], numdata[5], numdata[6]);
  183.         rtc.time(t);
  184.         mark = 0;j=0;
  185.         /* 清空 comdata 变量,以便等待下一次输入 */
  186.         comdata = String("");
  187.         /* 清空 numdata */
  188.         for(int i = 0; i < 7 ; i++) numdata[i]=0;
  189.     }
  190. }
复制代码
复制代码
注意,设置法有点不一样。如:2015年10月17日10时18分0秒星期天,
输入:2015,10,17,10,18,0,7即可。

用LCD1602不带I2C模块的,代码如下:
  1. #include <OneWire.h>
  2. #include <DallasTemperature.h>
  3. #include <LiquidCrystal.h>

  4. #include <stdio.h>
  5. #include <string.h>
  6. #include <DS1302.h>

  7. int Mode=0;
  8. int h = 0;
  9. int m = 0;
  10. int s = 0;
  11. int d = 12;
  12. int mo = 5;
  13. int y = 15;
  14. uint8_t CE_PIN   = 5;
  15. uint8_t IO_PIN   = 6;
  16. uint8_t SCLK_PIN = 7;
  17. float c = 0.00;
  18. String comdata = "";
  19. int numdata[7] ={0}, j = 0, mark = 0;
  20. char day[10];


  21. byte one[8] = {
  22.   B00000,
  23.   B00000,
  24.   B00000,
  25.   B11111,
  26.   B00000,
  27.   B00000,
  28.   B00000,
  29.   B00000,
  30. };

  31. byte two[8] = {
  32.   B00000,
  33.   B01110,
  34.   B00000,
  35.   B00000,
  36.   B00000,
  37.   B11111,
  38.   B00000,
  39.   B00000,
  40. };

  41. byte three [8] = {
  42.   B00000,
  43.   B01110,
  44.   B00000,
  45.   B01110,
  46.   B00000,
  47.   B11111,
  48.   B00000,
  49.   B00000,
  50. };

  51. byte four[8] = {
  52.   B00000,
  53.   B11111,
  54.   B10001,
  55.   B10101,
  56.   B11101,
  57.   B00001,
  58.   B01110,
  59.   B00000,
  60. };


  61. byte five [8] = {
  62.   B00000,
  63.   B11110,
  64.   B01000,
  65.   B11110,
  66.   B01010,
  67.   B01010,
  68.   B11111,
  69.   B00000,
  70. };

  71. byte six[8] = {
  72.   B00000,
  73.   B00100,
  74.   B00000,
  75.   B11111,
  76.   B00000,
  77.   B01010,
  78.   B10001,
  79.   B00000,
  80. };

  81. byte seven [8] = {
  82.   B11111,
  83.   B10001,
  84.   B10001,
  85.   B11111,
  86.   B10001,
  87.   B10001,
  88.   B11111,
  89.   B00000,
  90. };


  91. LiquidCrystal_I2C lcd(3,4,5,6,7,8);
  92. DS1302 rtc(CE_PIN, IO_PIN, SCLK_PIN);
  93. #define ONE_WIRE_BUS 2
  94. OneWire oneWire(ONE_WIRE_BUS);
  95. DallasTemperature sensors(&oneWire);



  96. void setup() {
  97.   sensors.begin();
  98.   lcd.init();
  99.   lcd.backlight();
  100.   lcd.createChar(0, one);
  101.   lcd.createChar(1, two);
  102.   lcd.createChar(2, three);
  103.   lcd.createChar(3, four);
  104.   lcd.createChar(4, five);
  105.   lcd.createChar(5, six);
  106.   lcd.createChar(6, seven);
  107.   lcd.home();
  108.   lcd.print("Welcome to use!");
  109.   delay(1000);
  110.   lcd.clear();
  111.   Serial.begin(9600);
  112.   Serial.println("Wait for set time");
  113.   rtc.write_protect(false);
  114.     rtc.halt(false);
  115. }


  116.   

  117. void Display(){
  118.   Time t = rtc.time();
  119.   memset(day, 0, sizeof(day));
  120.     switch (t.day)
  121.     {
  122.     case 1: lcd.setCursor(12,1); lcd.write((byte)0); break;
  123.     case 2: lcd.setCursor(12,1); lcd.write(1); break;
  124.     case 3: lcd.setCursor(12,1); lcd.write(2); break;
  125.     case 4: lcd.setCursor(12,1); lcd.write(3); break;
  126.     case 5: lcd.setCursor(12,1); lcd.write(4); break;
  127.     case 6: lcd.setCursor(12,1); lcd.write(5); break;
  128.     case 7: lcd.setCursor(12,1); lcd.write(6); break;
  129.     }
  130.   lcd.setCursor(0,0);
  131.   if(t.hr < 10)  lcd.print("0");
  132.   lcd.print(t.hr);
  133.   lcd.print(":");
  134.   if(t.min < 10)  lcd.print("0");
  135.   lcd.print(t.min);
  136.   lcd.print(":");
  137.   if(t.sec < 10)  lcd.print("0");
  138.   lcd.print(t.sec);
  139.   lcd.setCursor(0,1);
  140.   lcd.print(t.yr);
  141.   lcd.print("/");
  142.   if(t.mon < 10)  lcd.print("0");
  143.   lcd.print(t.mon);
  144.   lcd.print("/");
  145.   if(t.date < 10)  lcd.print("0");
  146.   lcd.print(t.date);
  147.   lcd.setCursor(9,0);
  148.   lcd.print(c);
  149.   lcd.write((char)223);
  150.   lcd.print("C");
  151.   lcd.print("     ");
  152. }


  153. void loop() {
  154.   sensors.requestTemperatures();
  155.   c = sensors.getTempCByIndex(12);
  156.   Display();
  157.   /* 当串口有数据的时候,将数据拼接到变量comdata */
  158.     while (Serial.available() > 0)
  159.     {
  160.         comdata += char(Serial.read());
  161.         delay(2);
  162.         mark = 1;
  163.     }
  164.     /* 以逗号分隔分解comdata的字符串,分解结果变成转换成数字到numdata[]数组 */
  165.     if(mark == 1)
  166.     {
  167.         Serial.print("You inputed : ");
  168.         Serial.println(comdata);
  169.         for(int i = 0; i < comdata.length() ; i++)
  170.         {
  171.             if(comdata[i] == ',' || comdata[i] == 0x10 || comdata[i] == 0x13)
  172.             {
  173.                 j++;
  174.             }
  175.             else
  176.             {
  177.                 numdata[j] = numdata[j] * 10 + (comdata[i] - '0');
  178.             }
  179.         }
  180.         /* 将转换好的numdata凑成时间格式,写入DS1302 */
  181.         Time t(numdata[0], numdata[1], numdata[2], numdata[3], numdata[4], numdata[5], numdata[6]);
  182.         rtc.time(t);
  183.         mark = 0;j=0;
  184.         /* 清空 comdata 变量,以便等待下一次输入 */
  185.         comdata = String("");
  186.         /* 清空 numdata */
  187.         for(int i = 0; i < 7 ; i++) numdata[i]=0;
  188.     }
  189. }
复制代码
注意:三个库

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-5-20 18:10:23 | 显示全部楼层
cccheng 发表于 2015-5-17 10:35
哈哈......我做了个I2CLCD时钟带温度显示、星期显示。晒下代码:复制代码
注意,设置法有点不 ...

嗯嗯,感谢分享,建议你新开一帖show一下你的代码哦,好让更多的人看到学习
回复 支持 反对

使用道具 举报

发表于 2015-5-20 20:26:56 | 显示全部楼层
cccheng 发表于 2015-5-17 10:35
哈哈......我做了个I2CLCD时钟带温度显示、星期显示。晒下代码:复制代码
注意,设置法有点不 ...

怎么显示1302的库有问题
回复 支持 反对

使用道具 举报

发表于 2015-5-22 17:16:51 | 显示全部楼层
连线方法:
CE(DS1302 pin5) -> Arduino D5
IO(DS1302 pin6) -> Arduino D6
SCLK(DS1302 pin7) -> Arduino D7
Vcc2(DS1302 pin1) -> Arduino +5v
GND(DS1302 pin4) -> Arduino GND

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-5-26 13:13:20 | 显示全部楼层
cccheng 发表于 2015-5-22 17:16
连线方法:
CE(DS1302 pin5) -> Arduino D5
IO(DS1302 pin6) -> Arduino D6

show一下你的板子照片啊
回复 支持 反对

使用道具 举报

发表于 2016-5-20 14:41:28 | 显示全部楼层
我的时钟乱调=跳为什么?
回复 支持 反对

使用道具 举报

发表于 2016-5-21 14:11:16 | 显示全部楼层
学习了 。谢谢。
回复 支持 反对

使用道具 举报

发表于 2016-5-22 14:36:00 | 显示全部楼层
学习学习学习学习
回复 支持 反对

使用道具 举报

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

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-5-4 18:10 , Processed in 0.045261 second(s), 26 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

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