标题: DS1302掉电不丢失时间单片机程序编写问题 [打印本页]

作者: jianwei2030    时间: 2017-9-2 20:51
标题: DS1302掉电不丢失时间单片机程序编写问题
本帖最后由 jianwei2030 于 2017-9-3 21:12 编辑

自己做了一个万年历时钟,利用STC15W408AS单片机,时钟芯片采用DS1302。当前自己参考网上写了时间读取的程序,在实验板走时正常,但是断电再上电后无法保存时间,程序在首次上电后会初始化一个固定的时间,每次都得自己重新调时。看到网上有种说法是给1302写入标志位,每次系统上电后检测这个标志位,如果能够检测到就不初始化时间,直接读取;如果检测不到标志位就初始化。但是按照这种方法试过后仍然不见效果,请大家帮忙看看我这程序该怎么修改合适?

电路部分:

以下是我的程序:
1302部分:
  1. #include<STC15W.h>
  2. #define uchar unsigned char
  3. #define uint unsigned int
  4. /*  宏定义写DS1302的地址  */
  5. #define write_sec_add            0x80        //秒数据地址    1000 0000
  6. #define write_min_add            0x82        //分数据地址    1000 0010
  7. #define write_hr_add            0x84        //时数据地址    1000 0100
  8. #define write_week_add            0x86        //星期数据地址    1000 0110
  9. #define write_month_add            0x88        //月数据地址    1000 1000
  10. #define write_day_add            0x8a        //日数据地址    1000 1010
  11. #define write_year_add            0x8c        //年数据地址    1000 1100
  12. #define write_control_add        0x8e        //控制数据地址    1000 1110
  13. #define write_charger_add        0x90         //可编程涓流充电地址 1001 0000            
  14. #define write_clkburst_add        0xbe        //时钟脉冲串地址
  15. #define write_RAM0_add            0xc0        //RAM0寄存器写地址     1100 0000
  16. /*  宏定义读DS1302的地址  */
  17. #define read_sec_add            0x81        //秒数据地址    1000 0001
  18. #define read_min_add            0x83        //分数据地址    1000 0011
  19. #define read_hr_add                0x85        //时数据地址    1000 0101
  20. #define read_week_add            0x87        //星期数据地址    1000 0111
  21. #define read_month_add            0x89        //月数据地址    1000 1001
  22. #define read_day_add            0x8b        //日数据地址    1000 1011
  23. #define read_year_add            0x8d        //年数据地址    1000 1101
  24. #define read_control_add        0x8f        //控制数据地址  1000 1111
  25. #define read_charger_add        0x91         //可编程涓流充电地址  1001 0001              
  26. #define read_clkburst_add        0xbe        //时钟脉冲串地址
  27. #define read_RAM0_add            0xc1        //RAM0寄存器读地址      1100 0001

  28. //#define P2M1 = 0x00;    //0000 0000               
  29. //#define P2M0 = 0x04;    //0000 0100
  30. uchar sec,min,hr,week,mouth,day,year,ram_data;

  31. sbit sclk=P2^1;        
  32. sbit io=P2^2;        
  33. sbit ce=P2^3;

  34. void write1302_time(uchar address,uchar dat)  //写入数据给1302
  35. {
  36.     uchar i;
  37.     ce=0;     //禁止读写数据
  38.     sclk=0;   
  39.     for(i=0;i<8;i++)    //写地址
  40.     {
  41.       ce=1;     //允许读写数据
  42.       sclk=0;
  43.       io=address&0x01;  
  44.       address>>=1;
  45.       sclk=1;
  46.     }
  47.     for(i=0;i<8;i++)    //写数据
  48.     {
  49.       ce=1;    //允许读写数据
  50.       sclk=0;
  51.       io=dat&0x01;
  52.       dat>>=1;
  53.       sclk=1;
  54.     }
  55.     ce=0;
  56. }

  57. uchar read1302_time(uchar address) //从1302读取时间数据
  58. {
  59.     uchar i,num;
  60.     ce=0;   //禁止读写数据
  61.     for(i=0;i<8;i++)
  62.     {
  63.       ce=1;
  64.       sclk=0;
  65.       io=address&0x01;
  66.       address>>=1;
  67.       sclk=1;
  68.     }
  69.     for(i=0;i<8;i++)
  70.     {
  71.       if(io==1)
  72.         num=num|0x80;
  73.       else
  74.         num=num|0x00;

  75.       sclk=1;
  76.       num>>=1;
  77.       sclk=0;
  78.     }
  79.     ce=0;
  80.     return num;
  81. }

  82. void init_1302() //初始化函数 设置时间
  83. {
  84.     read_ram_data = read1302_time(read_RAM0_add);
  85.        if(rread_ram_data!=0xf0)
  86.     {
  87.         write1302_time(0x8e,0x00);  //保护取消,可以进行读写操作
  88.         write1302_time(write_sec_add,0x00);//写入初始秒时间 0101 0110
  89.         write1302_time(write_min_add,0x35);//写入初始分时间 0101 0110
  90.         write1302_time(write_hr_add,0x22); //写入初始时时间 0101 0110
  91.         write1302_time(write_week_add,0x06); //写入初始周时间 0101 0110
  92.         write1302_time(write_month_add,0x08); //写入初始月时间 0101 0110
  93.         write1302_time(write_day_add,0x12); //写入初始日时间 0101 0110
  94.         write1302_time(write_year_add,0x17); //写入初始年时间 0101 0110
  95.         write1302_time(0x90,0xa5);    //使能涓流充电,  1010 0101
  96.         write1302_time(write_RAM0_add,0xf0); //往RAM寄存器中写入0xf0数据,因为RAM有掉电丢失特点,所以进入初始化函数的时候首先判断这个寄存器是否还有数据
  97.         write1302_time(0x8e,0x80);   //保护启动,禁止对1302写操作
  98.     }
  99. }
  100. void xianshi_data() //将从1302读出的二进制数据转换为十进制
  101. {
  102.     uchar read_sec,    read_min, read_hr, read_week, read_mouth, read_day, read_year;

  103.     read_sec = read1302_time(read_sec_add);       //从“read1302_time”函数取得返回值num的数据
  104.         sec = ((read_sec&0x70)>>4)*10+(read_sec&0x0f); //十进制“秒”

  105.     read_min = read1302_time(read_min_add);
  106.         min = ((read_min&0x70)>>4)*10+(read_min&0x0f); //十进制“分”

  107.     read_hr = read1302_time(read_hr_add);
  108.         hr = ((read_hr&0x70)>>4)*10+(read_hr&0x0f);     //十进制“时”

  109.     read_week = read1302_time(read_week_add);
  110.         week = ((read_week&0x70)>>4)*10+(read_week&0x0f);  //十进制“周”

  111.     read_day = read1302_time(read_day_add);
  112.         day = ((read_day&0x70)>>4)*10+(read_day&0x0f);    //十进制“日”

  113.     read_mouth = read1302_time(read_month_add);
  114.         mouth = ((read_mouth&0x70)>>4)*10+(read_mouth&0x0f); //十进制“月”

  115.     read_year = read1302_time(read_year_add);
  116.         year = ((read_year&0x70)>>4)*10+(read_year&0x0f);    //十进制“年”
  117. }
复制代码


主函数部分:
  1. #include<STC15W.h>
  2. #include"1302.h"
  3. #include"18B20.h"
  4. #include"display.h"
  5. #include"keyboard.h"
  6. #define uchar unsigned char
  7. #define uint unsigned int

  8. //#define  P2M1_set  0x00    //设置P2口工作模式   0000 0000
  9. //#define  P2M0_set  0x0d //设置P2口工作模式   0000 1110
  10. uchar num;
  11. /*************************延时函数*************************/
  12. void delay(uint a)         // 1ms延时程序(12MHz 10倍于51单片机速度时)
  13. {
  14.     uint i;
  15.     while(--a != 0)
  16.     {
  17.         for(i = 0;i < 600;i++);
  18.     }                           
  19. }
  20. /***************************************初始化IO口状态**************************************/
  21. //void IO_init(void)
  22. //{
  23. //P2M1 = P2M1_set;
  24. //P2M0 = P2M0_set;
  25. //}
  26. /**************************************外部中断INT2初始化函数 **************************************/
  27. void INT2_init()
  28. {      
  29.     INT_CLKO |=    0x10;
  30.     EA = 1;
  31. }

  32. void main()
  33. {   
  34.     INT2_init();
  35. //    IO_init();
  36. //    read_ram();
  37.     init_1302();
  38.     init_18b20();
  39.     while(1)
  40.     {
  41.         display_service();
  42.         display_qiehuan();
  43.     }
  44. }
复制代码

这是网上找到的别人的回答:
https://zhidao.baidu.com/question/345394758.html




作者: cjjcjj1    时间: 2017-9-2 21:31
提示: 作者被禁止或删除 内容自动屏蔽
作者: 黑暗人才    时间: 2017-9-2 23:08
你可以修改一下你的初始化函数,DS1302里面不是有好多空着的寄存器嘛,你可以先检测某一个寄存器是否为0,为零就初始化,初始化完毕后给这个寄存器写1,这样就可以了
作者: jianwei2030    时间: 2017-9-3 20:09
黑暗人才 发表于 2017-9-2 23:08
你可以修改一下你的初始化函数,DS1302里面不是有好多空着的寄存器嘛,你可以先检测某一个寄存器是否为0, ...

网上的说法也是这样做,初始化时在1302的ram中写入一个数值,以后每次上电都检测这个数值,如果检测到就不执行初始化程序,检测不到就执行初始化程序。但是我想知道怎么写这个检测程序。
作者: jianwei2030    时间: 2017-9-3 20:14
cjjcjj1 发表于 2017-9-2 21:31
你好!
1、要想断电时钟继续走时,1302要加后备电池
2、断电后,重启,时钟恢复 17年8月12日

后备电池是有加3V纽扣电池的;删掉初始化函数后第一次上电就什么也不显示了,我希望第一次上电时显示一个初始时间,用户在这个时间的基础上再修改。
作者: 越快乐越堕落    时间: 2017-9-4 22:49
屏蔽掉初始化
作者: mengx    时间: 2017-9-5 10:48
电路没问题,不同公司的1302驱动方法上可能存在不同,我也遇到过同样的问题,换个1302就好了
作者: jianwei2030    时间: 2017-9-7 21:53
mengx 发表于 2017-9-5 10:48
电路没问题,不同公司的1302驱动方法上可能存在不同,我也遇到过同样的问题,换个1302就好了

换过了,前后都是用达拉斯的芯片
作者: jianwei2030    时间: 2017-9-7 21:56
黑暗人才 发表于 2017-9-2 23:08
你可以修改一下你的初始化函数,DS1302里面不是有好多空着的寄存器嘛,你可以先检测某一个寄存器是否为0, ...

网上也是这么说的,我是检测1302内部的31字节的第一位,初始化时给这一位写入一个数据,再上电时读取这一位是不是之前写入的值,但是根据网上的程序修改没有效果。
作者: jianwei2030    时间: 2017-10-8 15:35
我修改了上电检测部分的程序(如下),
思路是:上电后检测RAM中存储的数据十位是否为7,如果是则跳过初始化程序(什么也不执行),如果不是则执行初始化子程序
可是不管我怎么修改,修改后的时间就是无法保存,每次上电后依然是初始化的时间,要怒了。。。

void init_1302() //初始化函数 设置时间
{
                write1302_time(write_control_add,0x00);  //保护取消,可以进行读写操作
                write1302_time(write_sec_add,0x30);//写入初始秒时间 0101 0110
                write1302_time(write_min_add,0x05);//写入初始分时间 0101 0110
                write1302_time(write_hr_add,0x21); //写入初始时时间 0101 0110
                write1302_time(write_week_add,0x07); //写入初始周时间 0101 0110
                write1302_time(write_month_add,0x09); //写入初始月时间 0101 0110
                write1302_time(write_day_add,0x03); //写入初始日时间 0101 0110
                write1302_time(write_year_add,0x17); //写入初始年时间 0101 0110
                write1302_time(write_RAM0_add,0x17); //往RAM寄存器中写入0xf0数据,因为RAM有掉电丢失特点,所以进入初始化函数的时候首先判断这个寄存器是否还有数据
                write1302_time(write_charger_add,0xab);        //使能涓流充电,两个二极管+8k电阻  1010 1011
                write1302_time(write_control_add,0x80);   //保护启动,禁止对1302写操作
}

void ram_flag()
{
        read_ram_data = read1302_time(read_RAM0_add);                
        ram_data = ((read_ram_data&0x70)>>4)*10+(read_ram_data&0x0f);
        if((ram_data%10) == 7)
        {
                write1302_time(write_control_add,0x80);   //保护启动,禁止对1302写操作
        }
        else if((ram_data%10) != 7)
        {
                init_1302();
        }
}

作者: lids    时间: 2017-10-10 15:58
jianwei2030 发表于 2017-9-7 21:53
换过了,前后都是用达拉斯的芯片

换进口的DS1302,同样的程序,进口的芯片可以,有些国产的芯片就是不行
作者: kingyork    时间: 2018-7-2 01:29
DS1302掉电后内部RAM的值不等于3,可以判断后初始化时间,然后标记为3即可。
作者: 家哈哈哈哈    时间: 2018-7-2 09:40
你们也太厉害了吧
作者: 家哈哈哈哈    时间: 2018-7-2 09:41

DS1302掉电后内部RAM的值不等于3,可以判断后初始化时间,然后标记为3即可。
作者: Misol    时间: 2019-3-26 20:44
我还看到过说1302的vcc2连接一个10000uF的电容,就可以断电也能继续计时,说再加一个write1302_time(0x90,0xa6);就可以
这个说法是真的吗?

为什么我的板是连了一个10 0000uF的电容,试了,失败了,还是说一定要加一个3v的备用电池才能做到断电继续计时?

作者: 白幽幽    时间: 2020-3-30 18:50
我以上方法都试了都不行
作者: dzbj    时间: 2020-3-30 20:48
不明白你说的不成都什么不成 也没太看明白你要求什么 1302这东西 掉电就没时间了 必须要重新写入时间

往1302的RAM写校验标记是可以的 完全没问题 我正在用 写的是第一个地址 注意 因为1302是BCD格式写入 如果你写的值超过6位读出来就只有6位 比如写0xaa进去出来是0x2a

如果你的板子没设计后备电池 可以在供电线路上并联一个电池 电池的正极串联一个二极管 和后备电池意思一样

如果是片子断电后再上电 也不一定非要去读1302 写个预处理函数 在主循环的while之前执行 只要是第一次上电 都先从1302读时间到缓冲区 然后再到主循环里显示 当然 这样做的前提是1302不能断电 否则读出来的肯定是错误的时间 读1302的RAM方法只是为了判断是否执行调表
作者: mkkop    时间: 2020-3-30 21:30
软件你已经写好了,而且是可以正常使用的,现在最简单的方法就是用一个IO口来判断是否需要初始化即可,
例如你的P55脚是空的,在初始化前面先判断P55,是0的话初始化,是1就不初始化,你烧录好程序,如果想要初始化就把P55接地再上电就会初始化了,不需要初始化时悬空即可,多简单啊
作者: 余生不负    时间: 2020-3-30 23:51
这个问题啊,时间初始化是程序的问题,要设置一下那个什么命令,不太记得了,我DS1302,都是直接调用程序,改一下IO口就可以用了,你找的程序不对,你这个是程序初始化的时候把时间给初始化了。
作者: 白幽幽    时间: 2020-3-31 09:11
黑暗人才 发表于 2017-9-2 23:08
你可以修改一下你的初始化函数,DS1302里面不是有好多空着的寄存器嘛,你可以先检测某一个寄存器是否为0, ...

我是这样检测的,第一次上电后是我的初始化时间,但是第二次上电后时间从0开始,说明我的备用电源正常,应为向存储器写的内容没有丢失,但是时间为啥不对呢,请问遇到过同类型的问题吗,新手已被逼疯。




欢迎光临 (http://www.51hei.com/bbs/) Powered by Discuz! X3.1