[单片机类] 【蓝桥杯单片机12】实时时钟DS1302的基本操作

[复制链接]
 楼主| ohy3686 发表于 2018-2-10 12:57 | 显示全部楼层 |阅读模式
本帖最后由 ohy3686 于 2018-2-10 13:28 编辑

【蓝桥杯单片机12】实时时钟DS1302的基本操作

广东职业技术学院  欧浩源

    实时时钟DS1302几乎是蓝桥杯“单片机设计与开发”每年必考的内容,虽然在竞赛现场有提供一个底层读写寄存器的库文件,但是作为备赛阶段,你应该搞清楚底层读写时序的代码实现。你会使用库文件开发,不一定会自己写底层;你会自己写驱动,就一定会使用库文件开发。你使用库文件开发的过程中碰到问题,或者需要调整时序的时候,如果没有过硬的功夫,那只能懵逼了。

1、什么是DS1302?
    DS1302是美国DALLAS公司推出的高性能、低功耗的实时时钟,附加31字节的静态RAM,采用SP三线接口与MCU进行同步通信,并可采用突发方式一次传送多个字节的时钟参数和RAM数据。实时时钟可提供秒、分、时、日、星期、月和年,一个月小于31天时可以自动调整,并具有润年补偿功能。
    简单来说,DS1302可以理解为一个电子手表,里面带有一个31字节的内存。当然,基本的使用方法和我们平时使用电子手表差不多,你可以设定时间,也可以读取时间,只不过这些工作是通过SPI接口有MCU去完成而已。
    在DS1302中有两块存储器:日历时钟寄存器和今天RAM存储器。前者用于记录实时时间,后者用于记录其他数据。对于基本计时应用,重点关注的是日历时钟寄存器。设定时间参数就是往这些寄存器写入内容,读取实时时间也是从这些寄存器读出数据。

2、日历时钟寄存器
    DS1302有关日历和时钟的寄存器有12个,我们最常用的有7个。
    51单片机-DS1302-01.PNG    
   什么是BCD码?
   就是用十六进制来表示十进制。什么意思?怎么理解?
   例如,十六进制数0x13的值为整数19,但BCD码表示的是整数13

3、控制字的格式
    DS1302将地址和读写控制放到一个字节里面,形成一个控制字,格式如下:
    51单片机-DS1302-00.PNG
    通过上面的控制字格式,大家就可以明白为什么DS1302读寄存器和写寄存器的地址是不一样的了,因为这个地址包含了读写控制位。为了方便程序设计,我们把读寄存器地址、写寄存器地址和日历时钟寄存器方面用三个数组定义。
    51单片机-DS1302-06.PNG

4、接口时序的实现
    DS1302的基本操作实际上非常简单,只有两个操作:其一是设定时间参数,其二是读取实时时间。不管是那个操作,MCU都要通过SPI接口进行数据交互,而SPI接口有其规定的时序,这个必须参考数据手册。
    控制字总是从最低位开始输出。在控制字指令输入后的下一个SCLK时钟信号的上升沿,数据被写入DS1302,数据的输入从最低位开始;在控制字指令输入后的下一个SCLK时钟信号的下降沿,数据从DS1302读出,数据的读出也是从最低位到最高位。
    <1> 单字节写的时序
51单片机-DS1302-05.PNG
    底层驱动代码实现可参考如下:
  1. void DS1302_WriteByte(unsigned char addr, unsigned char dat)
  2. {
  3.         unsigned char n;
  4.         RST = 0;
  5.         _nop_();
  6.         SCLK = 0;
  7.         _nop_();
  8.         RST = 1;
  9.         _nop_();        

  10.         for (n=0; n<8; n++)         //发送要写入数据的内存地址
  11.         {
  12.                 DSIO = addr & 0x01;
  13.                 addr >>= 1;
  14.                 SCLK = 1;
  15.                 _nop_();
  16.                 SCLK = 0;
  17.                 _nop_();
  18.         }
  19.         for (n=0; n<8; n++)         //将指定内容写入该地址的内存
  20.         {
  21.                 DSIO = dat & 0x01;
  22.                 dat >>= 1;
  23.                 SCLK = 1;
  24.                 _nop_();
  25.                 SCLK = 0;
  26.                 _nop_();
  27.         }                 
  28.         RST = 0;
  29.         _nop_();
  30. }
   <2> 单字节读的时序
51单片机-DS1302-04.PNG
   底层驱动代码实现可参考如下:
  1. unsigned char DS1302_ReadByte(unsigned char addr)
  2. {
  3.         unsigned char n,dat,tmp;
  4.         RST = 0;
  5.         _nop_();
  6.         SCLK = 0;
  7.         _nop_();
  8.         RST = 1;
  9.         _nop_();

  10.         for(n=0; n<8; n++)         //发送要读出数据的内存地址
  11.         {
  12.                 DSIO = addr & 0x01;
  13.                 addr >>= 1;
  14.                 SCLK = 1;
  15.                 _nop_();
  16.                 SCLK = 0;
  17.                 _nop_();
  18.         }
  19.         
  20.         for(n=0; n<8; n++)         //读出该地址内存的数据
  21.         {
  22.                 tmp = DSIO;
  23.                 dat = (dat>>1) | (tmp<<7);
  24.                 SCLK = 1;
  25.                 _nop_();
  26.                 SCLK = 0;
  27.                 _nop_();
  28.         }

  29.         RST = 0;
  30.         _nop_();
  31.         SCLK = 1;
  32.         _nop_();
  33.         DSIO = 0;
  34.         _nop_();
  35.         DSIO = 1;
  36.         _nop_();
  37.         return dat;        
  38. }
   有了上面两个底层的SPI接口数据读写代码,那么DS1302的基本操作就很容易实现了。
    51单片机-DS1302-07.PNG

5、单元实训题目
11-DS1302的基本操作.PNG

6、实现源码参考
  1. #include "reg52.h"  
  2. #include "intrins.h"

  3. sbit HC138_A = P2^5;        
  4. sbit HC138_B = P2^6;        
  5. sbit HC138_C = P2^7;        

  6. sbit SCLK = P1^7;
  7. sbit RST =         P1^3;
  8. sbit DSIO = P2^3;
  9. unsigned char code READ_RTC_ADDR[7] = {0x81, 0x83, 0x85, 0x87, 0x89, 0x8b, 0x8d};
  10. unsigned char code WRITE_RTC_ADDR[7] = {0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c};
  11. unsigned char TIME[7] = {0x30, 0x50, 0x23, 0x17, 0x02, 0x06, 0x18};

  12. unsigned char code SMG_NoDot[18] =
  13.     {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
  14.      0x80,0x90,0x88,0x80,0xc6,0xc0,0x86,0x8e,
  15.      0xbf,0x7f};

  16. void DelaySMG(unsigned int time)
  17. {
  18.         while(time--);
  19. }

  20. void Init74HC138(unsigned char n)
  21. {
  22.         switch(n)
  23.         {
  24.                 case 4:
  25.                         HC138_A = 0;
  26.                         HC138_B = 0;
  27.                         HC138_C = 1;
  28.                         break;
  29.                 case 5:
  30.                         HC138_A = 1;
  31.                         HC138_B = 0;
  32.                         HC138_C = 1;
  33.                         break;
  34.                 case 6:
  35.                         HC138_A = 0;
  36.                         HC138_B = 1;
  37.                         HC138_C = 1;
  38.                         break;
  39.                 case 7:
  40.                         HC138_A = 1;
  41.                         HC138_B = 1;
  42.                         HC138_C = 1;
  43.                         break;
  44.                 case 8:
  45.                         HC138_A = 0;
  46.                         HC138_B = 0;
  47.                         HC138_C = 0;
  48.                         break;
  49.         }
  50. }

  51. void DispaySMG_Bit(unsigned char value, unsigned char pos)
  52. {
  53.         Init74HC138(6);
  54.         P0 = (0x01 << pos);
  55.         Init74HC138(7);
  56.         P0 = value;
  57. }

  58. void DS1302_WriteByte(unsigned char addr, unsigned char dat)
  59. {
  60.         unsigned char n;
  61.         RST = 0;
  62.         _nop_();
  63.         SCLK = 0;
  64.         _nop_();
  65.         RST = 1;
  66.         _nop_();        

  67.         for (n=0; n<8; n++)
  68.         {
  69.                 DSIO = addr & 0x01;
  70.                 addr >>= 1;
  71.                 SCLK = 1;
  72.                 _nop_();
  73.                 SCLK = 0;
  74.                 _nop_();
  75.         }
  76.         for (n=0; n<8; n++)
  77.         {
  78.                 DSIO = dat & 0x01;
  79.                 dat >>= 1;
  80.                 SCLK = 1;
  81.                 _nop_();
  82.                 SCLK = 0;
  83.                 _nop_();
  84.         }                 
  85.         RST = 0;
  86.         _nop_();
  87. }

  88. unsigned char DS1302_ReadByte(unsigned char addr)
  89. {
  90.         unsigned char n,dat,tmp;
  91.         RST = 0;
  92.         _nop_();
  93.         SCLK = 0;
  94.         _nop_();
  95.         RST = 1;
  96.         _nop_();

  97.         for(n=0; n<8; n++)
  98.         {
  99.                 DSIO = addr & 0x01;
  100.                 addr >>= 1;
  101.                 SCLK = 1;
  102.                 _nop_();
  103.                 SCLK = 0;
  104.                 _nop_();
  105.         }
  106.         
  107.         for(n=0; n<8; n++)
  108.         {
  109.                 tmp = DSIO;
  110.                 dat = (dat>>1) | (tmp<<7);
  111.                 SCLK = 1;
  112.                 _nop_();
  113.                 SCLK = 0;
  114.                 _nop_();
  115.         }

  116.         RST = 0;
  117.         _nop_();
  118.         SCLK = 1;
  119.         _nop_();
  120.         DSIO = 0;
  121.         _nop_();
  122.         DSIO = 1;
  123.         _nop_();
  124.         return dat;        
  125. }

  126. void DS1302_Config()
  127. {
  128.         unsigned char n;
  129.         DS1302_WriteByte(0x8E,0x00);
  130.         for (n=0; n<7; n++)
  131.         {
  132.                 DS1302_WriteByte(WRITE_RTC_ADDR[n],TIME[n]);        
  133.         }
  134.         DS1302_WriteByte(0x8E,0x80);
  135. }

  136. void DS1302_ReadTime()
  137. {
  138.         unsigned char n;
  139.         for (n=0; n<7; n++)
  140.         {
  141.                 TIME[n] = DS1302_ReadByte(READ_RTC_ADDR[n]);
  142.         }               
  143. }

  144. void XMF_ShowRealTime()
  145. {

  146.         DispaySMG_Bit(SMG_NoDot[TIME[2]/16],0);        
  147.         DelaySMG(500);
  148.         DispaySMG_Bit(0xff,0);                        
  149.         DispaySMG_Bit(SMG_NoDot[TIME[2]&0x0f],1);
  150.         DelaySMG(500);
  151.         DispaySMG_Bit(0xff,1);
  152.         DispaySMG_Bit(SMG_NoDot[16],2);
  153.         DelaySMG(500);
  154.         DispaySMG_Bit(0xff,2);

  155.         DispaySMG_Bit(SMG_NoDot[TIME[1]/16],3);
  156.         DelaySMG(500);
  157.         DispaySMG_Bit(0xff,3);
  158.         DispaySMG_Bit(SMG_NoDot[TIME[1]&0x0f],4);
  159.         DelaySMG(500);
  160.         DispaySMG_Bit(0xff,4);
  161.         DispaySMG_Bit(SMG_NoDot[16],5);
  162.         DelaySMG(500);
  163.         DispaySMG_Bit(0xff,5);

  164.         DispaySMG_Bit(SMG_NoDot[TIME[0]/16],6);
  165.         DelaySMG(500);
  166.         DispaySMG_Bit(0xff,6);
  167.         DispaySMG_Bit(SMG_NoDot[TIME[0]&0x0f],7);
  168.         DelaySMG(500);
  169.         DispaySMG_Bit(0xff,7);
  170. }

  171. main()
  172. {
  173.         DS1302_Config();
  174.         while(1)
  175.         {
  176.                 DS1302_ReadTime();
  177.                 XMF_ShowRealTime();
  178.         }
  179. }



 楼主| ohy3686 发表于 2018-2-11 14:12 | 显示全部楼层
一路向北lm 发表于 2018-2-12 18:52 | 显示全部楼层
几乎每年都要考时钟DS1302
CWKQL 发表于 2018-2-20 22:51 | 显示全部楼层
学习的好资料,老师辛苦了,新年快乐~~~~~~
h472438470 发表于 2020-3-30 22:09 | 显示全部楼层
找了好多个单片机的资料就老师这个最俗   
tzj31388 发表于 2024-3-2 20:07 | 显示全部楼层
嗯哈哈11 发表于 2024-3-17 14:46 | 显示全部楼层
老师,你好!
为什么DS1302时钟显示中间两个数码管一直跳FF?
路人七 发表于 2024-3-22 20:17 | 显示全部楼层
嗯哈哈11 发表于 2024-3-17 14:46
老师,你好!
为什么DS1302时钟显示中间两个数码管一直跳FF?

解决了吗兄弟?我这不光中间两个跳FF,前面两个也跳
2663872054 发表于 2024-11-3 20:59 | 显示全部楼层
嗯哈哈11 发表于 2024-3-17 14:46
老师,你好!
为什么DS1302时钟显示中间两个数码管一直跳FF?

我也是,解决了吗,哥们

评论

俺也一样,有什么办法吗  发表于 2024-12-29 10:39
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:广东职业技术学院 欧浩源(ohy3686@foxmail.com)    [Zigbee技术&物联网应用] 群:168225322    [技术交流微信]:gzyohy (暗号:宝塔镇河妖)

322

主题

899

帖子

3548

粉丝
快速回复 在线客服 返回列表 返回顶部