Часы на DS3231.Отстают прилично

valera678
Offline
Зарегистрирован: 04.11.2016

счастливые владельцы ds1307 смотрите сюда https://www.youtube.com/watch?v=b8-4o6A7dZs

Тарас Петрович
Offline
Зарегистрирован: 21.11.2015

         Использую DS3231 на самодельной печатной плате около полугода. Нередко работают от батарейки. Относительно телефона, который берёт время из сети, не ушли ни на секунду. Пробовал разные библиотеки, ни одна не понравилась. Те, что мне попадались, выдавали время и дату какими-то строками, с которыми непонятно что делать. Остановился на стандартной библиотеке I2C, отправляя ей коды из даташита. Время выставил раз и читаю, SQW переключается и работает. В дату и будильники не лазал, там сложно, да и мне без надобности. 

         Готовый модуль на ds1307 пробовал, время убегает безбожно. Я и кварц пропаивал, и аккумулятор на батарейку менял....А если в контроллере использовать ежесуточную программную коррекцию (как сделано во многих поделках из интернета), то зачем RTC вообще нужен?

valera678
Offline
Зарегистрирован: 04.11.2016

Вобщем часы работают без замечаний. Добавил в коде еще вывод температуры(ну если есть градусник нужно пользоваться)





#include "CyberLib.h"

#include "TM1637.h"
#define CLK 11   // пин 11
#define DIO 12   // пин 12
TM1637 tm1637(CLK,DIO);
#define ON 1
#define OFF 0

#include <DS3231.h>
#include <Wire.h>
DS3231  rtc(SDA, SCL);
Time t;

#include <IRstD.h> 
IRrecvstd irrecvstd(4); 
decode_resultsstd res_std;

#define MAXMILLIS 4294967295
unsigned long timme;
unsigned long timelapsed = 0;
int chas = 0;
int minuta = 0;
int secunda = 0;
 int temp=0;

///////////// B1 ////////////////////
uint8_t B1_flag = ReadEEPROM_Byte(1);
uint8_t B1chas_vkl = ReadEEPROM_Byte(2);
uint8_t B1minut_vkl = ReadEEPROM_Byte(3);

uint8_t val_shim = ReadEEPROM_Byte(4);

uint8_t flag_disp = 0;
uint8_t flag_zel_led = 0;

int buzzerPin = 5;

int8_t TimeDisp[] = {0x00,0x00,0x00,0x00};
int8_t BudDisp[] = {0x00,0x00,0x00,0x00};

unsigned char ClockPoint = 1;


void setup() 
{
  tm1637.set(val_shim);
  tm1637.init();
  pinMode(buzzerPin, OUTPUT);
  D7_Out;  
  D7_Low;
  D10_Out;

  if(B1_flag) D10_High;
  else D10_Low;
  
  Serial.begin(57600);
  irrecvstd.enableIRInstd();

  rtc.begin();
}


void loop() 
{
   if(irrecvstd.decodestd(&res_std)) 
    {
       if(res_std.valuestd == 16750695) // Прибавление часов
         {
           analogWrite(buzzerPin, 255);
           chas++;
           if(chas > 23) chas = 0;
           rtc.setTime(chas, minuta, secunda);
           disp();
           analogWrite(buzzerPin, 0);
         }

       if(res_std.valuestd == 16738455) // Убавление часов
         {
           analogWrite(buzzerPin, 255);
           chas--;
           if(chas < 1) chas = 0;
           rtc.setTime(chas, minuta, secunda);
           disp();
           analogWrite(buzzerPin, 0);
         } 

       if(res_std.valuestd == 16718055) // Прибавление минут
         {
           analogWrite(buzzerPin, 255);
           minuta++;
           if(minuta > 59) minuta = 0;
           rtc.setTime(chas, minuta, secunda);
           disp();
           analogWrite(buzzerPin, 0);
         }  

       if(res_std.valuestd == 16724175) // Убавление минут
         {
           analogWrite(buzzerPin, 255);
           minuta--;
           if(minuta < 1 || minuta > 60) minuta = 0;
           rtc.setTime(chas, minuta, secunda);
           disp();
           analogWrite(buzzerPin, 0);
         } 

       if(flag_disp && res_std.valuestd == 16761405) // Прибавление часов будильника
         {
           analogWrite(buzzerPin, 255);
           B1chas_vkl++;
           if(B1chas_vkl > 23) B1chas_vkl = 0;
           WriteEEPROM_Byte(2, B1chas_vkl);
           disp();
           analogWrite(buzzerPin, 0);
         } 

       if(flag_disp && res_std.valuestd == 16720605) // Убавление часов будильника
         {
           analogWrite(buzzerPin, 255);
           B1chas_vkl--;
           if(B1chas_vkl < 1 || B1chas_vkl > 24) B1chas_vkl = 0;
           WriteEEPROM_Byte(2, B1chas_vkl);
           disp();
           analogWrite(buzzerPin, 0);
         }  

       if(flag_disp && res_std.valuestd == 16726215) // Прибавление минут будильника
         {
           analogWrite(buzzerPin, 255);
           B1minut_vkl++;
           if(B1minut_vkl > 59) B1minut_vkl = 0;
           WriteEEPROM_Byte(3, B1minut_vkl);
           disp();
           analogWrite(buzzerPin, 0);
         }

       if(flag_disp && res_std.valuestd == 16716015) // Убавление минут будильника
         {
           analogWrite(buzzerPin, 255);
           B1minut_vkl--;
           if(B1minut_vkl < 1 || B1minut_vkl > 60) B1minut_vkl = 0;
           WriteEEPROM_Byte(3, B1minut_vkl);
           disp();
           analogWrite(buzzerPin, 0);
         }  


       if(res_std.valuestd == 16730805) // Вкл/Откл будильник
         {
           analogWrite(buzzerPin, 255);

           B1chas_vkl = ReadEEPROM_Byte(2);
           B1minut_vkl = ReadEEPROM_Byte(3);
           
           if(B1_flag) 
             {
                B1_flag = 0;
                D10_Low;
             }   
                
           else 
             {
               B1_flag = 1;
               D10_High;
             }
               
           disp();
           
           WriteEEPROM_Byte(1, B1_flag);
           analogWrite(buzzerPin, 0);
         }


       if(res_std.valuestd == 16728765) // Вывод будильника на дисплей
         {
           analogWrite(buzzerPin, 255);
           flag_zel_led = 0;
           
           if(flag_disp) 
             {
                flag_disp = 0;
                D7_Low;
             }   
                
           else 
             {
               flag_disp = 1;
               D7_High;
             }
               
           disp();
           analogWrite(buzzerPin, 0);
         }  


       if(res_std.valuestd == 16736925) // Подсветка +
         {
           analogWrite(buzzerPin, 255);
           val_shim++;
           if(val_shim > 7) val_shim = 7;
           WriteEEPROM_Byte(4, val_shim);
           tm1637.set(val_shim);
           disp();
           delay_ms(10);
           analogWrite(buzzerPin, 0);
         }  

       if(res_std.valuestd == 16754775) // Подсветка -
         {
           analogWrite(buzzerPin, 255);
           val_shim--;
           if(val_shim < 1 || val_shim > 254) val_shim = 0;
           WriteEEPROM_Byte(4, val_shim);
           tm1637.set(val_shim);
           disp();
           delay_ms(10);
           analogWrite(buzzerPin, 0);
         }  


       if(res_std.valuestd == 16732845) // +10 минут отсрочка
         {
           analogWrite(buzzerPin, 255);
           B1minut_vkl = B1minut_vkl + 10;
           
           if(B1minut_vkl > 59) 
             {
                B1minut_vkl = 10;
                B1chas_vkl++;
                if(B1chas_vkl>23)  B1chas_vkl=0;
             }

           delay_ms(5);
           analogWrite(buzzerPin, 0);

           flag_disp = 1;
           D7_High;
           disp();
           delay_ms(300);
           flag_disp = 0;
           D7_Low;
         } 
if(res_std.valuestd == 16756815) // смотрим температуру
         {
           analogWrite(buzzerPin, 255);

           temperatura();
           delay_ms(1000);
           analogWrite(buzzerPin, 0);
         }  
       //Serial.println(res_std.valuestd); // Удалить после программирования пульта
       
       irrecvstd.resumestd();
       res_std.valuestd = 0; 
    }


  unsigned long currtime = millis();
  
  if(currtime > timme) timelapsed = (currtime - timme); 
  else timelapsed = (MAXMILLIS - timme + currtime);
  
  if(timelapsed > 999)
    {  
        timme = currtime;
        
        if(flag_disp)
          {
            flag_zel_led++;
            if(flag_zel_led > 59)
              {
                flag_zel_led = 0;
                flag_disp = 0;
                D7_Low;
                analogWrite(buzzerPin, 255);
                delay_ms(5);
                analogWrite(buzzerPin, 0);
              }
          }
        
        disp(); 
     }
temp=rtc.getTemp();

  t = rtc.getTime();
  chas = t.hour,DEC;
  minuta = t.min,DEC;
  secunda = t.sec,DEC;
 
  if(B1_flag)
    {
      if(chas == B1chas_vkl && minuta == B1minut_vkl) 
        {
           buzz();
        }

      if(chas == B1chas_vkl && minuta == B1minut_vkl && secunda == 59) 
        {
           B1minut_vkl = B1minut_vkl + 10;
           if(B1minut_vkl > 59) 
             {
                B1minut_vkl = 10;
                B1chas_vkl++;
                if(B1chas_vkl>23)  B1chas_vkl=0;
             }
        }
    }

} //END LOOP


void buzz() 
{ 
  if(B1_flag)
   { 
     analogWrite(buzzerPin, 30);       
     delay_ms(100); 
     analogWrite(buzzerPin, 50);       
     delay_ms(100);
   }

  analogWrite(buzzerPin, 0);
  delay_ms(100);         
}
void temperatura()
      {
       TimeDisp[0] = temp/ 10;
        TimeDisp[1] = temp % 10;
          TimeDisp[2] = 12;
          TimeDisp[3] = 18;
       tm1637.display(TimeDisp);
     }
     

void disp()
 {
     if(!flag_disp)   
      {
        ClockPoint = (~ClockPoint) & 0x01;
        
        if(ClockPoint)tm1637.point(POINT_ON);
        else tm1637.point(POINT_OFF);

        TimeDisp[0] = chas / 10;
        TimeDisp[1] = chas % 10;
        TimeDisp[2] = minuta / 10;
        TimeDisp[3] = minuta % 10;
        tm1637.display(TimeDisp);
      }

     else
      {
        tm1637.point(POINT_ON);
        BudDisp[0] = B1chas_vkl / 10;
        BudDisp[1] = B1chas_vkl % 10;
        BudDisp[2] = B1minut_vkl / 10;
        BudDisp[3] = B1minut_vkl % 10;
        tm1637.display(BudDisp);
      }
     
 }
  

 

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

valera678 пишет:

Вобщем часы работают без замечаний. Добавил в коде еще вывод температуры(ну если есть градусник нужно пользоваться)

Я же Вам объяснял почему нужно время переключать по SQW а не в loop спрашивать время. Но, дело Ваше. Не нужно точность, а нужна перегрузка контроллера - Вам жить :)

Что касается "<i>есть градусник нужно пользоваться</i>", зачем? Он показывает не температуру воздуха, а температуру внутри чипа и используется для корректировки кварца. Можно его использовать, например, для определения момента, когда надо принудительное охлаждение включать. А показывать его ... нафига?  Цену на овёс смотреть?

valera678
Offline
Зарегистрирован: 04.11.2016

Евгений,Вы здесь много что сказали ,в том числе и полезного. Но я ведь не спорю с Вами.

Благодарю Вас Всех за помощь!

nik182
Offline
Зарегистрирован: 04.05.2015

Переключая время по SQW невозможно обеспечить моргание светодиода раз в секунду. Может вам это не интересно, но без этого цифровые часы выглядят как то не так. Температура чипа, по моим наблюдениям , точно соответствует температуре воздуха. Превышения не наблюдал.

Тарас Петрович
Offline
Зарегистрирован: 21.11.2015

"....Переключая время по SQW невозможно обеспечить моргание светодиода раз в секунду...."

Почему? При частоте в 1 Гц точки пол-секунды горят, пол- не горят, а прерывание по RISE раз в секунду приходит. Точки вообще можно аппаратно запустить, через ключик транзисторный. МОжно в прерывании включать, а по миллису отключать (если программно). Или я вас не понял?

240265
240265 аватар
Offline
Зарегистрирован: 12.08.2015

Тарас Петрович пишет:

 Точки вообще можно аппаратно запустить, через ключик транзисторный.

Ну да и нафига прерываня городить. У меня так  сделано.

Тарас Петрович
Offline
Зарегистрирован: 21.11.2015

По прерыванию можно просить контроллер обновить время, будильник проверять, например.

nik182
Offline
Зарегистрирован: 04.05.2015

При частоте 1 герц одну секунду горит одну секунду не горит, а надо пол секунды. Прерываниями DS3231 этого не обеспечить. От слова никак.

Тарас Петрович
Offline
Зарегистрирован: 21.11.2015

При частоте 1Гц период колебания составит 1 сек. То есть за 1 сек должен пройти полный цикл переключения. А то, про что вы пишите, это-0,5Гц. Или я совсем с ума сошёл, коллеги рассудите?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Тарас Петрович, наш дорогой коллега Nic немножко переотдыхал, со всеми бывает :)

nik182
Offline
Зарегистрирован: 04.05.2015

Почему переотдыхал? Простое размышление. Часы пробуждаются 1 раз в секунду. Значит изменить состояние диода горит - не горит можно раз в секунду. Полный период мигания получается 2 секунды или 0.5 герца. А мигать должно с частотой 1 герц. Т.е 500 милисекунд горит, 500 милисекунд не горит. Если пробуждаться только от часов раз в секунду этого не обеспечить. Нужно что бы ещё через 500 милисекунд процессор проснулся да хоть бы от чего то еще, что бы погасить диод, который будет зажжён при секундном прерывании. Что не так в моих рассуждениях? Как вы обеспечите период 500 мс для управлением морганием? DS3231 не имеет возможности делать прерывание 500 мс.

Тарас Петрович
Offline
Зарегистрирован: 21.11.2015

Ну во-первых можно настроить прерывание на CHANGE, тогда будут приходить каждые 0,5с. Ну и выше предлагался (обсуждался) аппаратный способ управления двоеточием.

nik182
Offline
Зарегистрирован: 04.05.2015

Аппаратный это не наш вариант. Мы микроконтроллер программно мучаем. А вот про: "CHANGE, тогда будут приходить каждые 0,5с" можно по подробнее? Либо я не понял даташит, либо прерывание от секунд нужно сбрасывать в ручную. Т.Е. если вы хотите получить следующее секундное прерывание вы должны в текущей обработке снять флаг A1F. Через 500 милисекунд ничего не происходит. Так как программно? 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

nik182, и всё таки вы переотдыхали :) В #60 "1 герц одну секунду горит одну секунду не горит "  А в №63 уже "А мигать должно с частотой 1 герц. Т.е 500 милисекунд горит, 500 милисекунд не горит" Мне одному кажется что длительность герца  в двух фразах была разной? ;)

И про 1Герц из часов может что-то не пропустил, -но в часах есть два режима дёрганья лапой SQW. Режим генератора, и режим будильника. Соответссно  в первом не требуется снимать флаг.

Тарас Петрович
Offline
Зарегистрирован: 21.11.2015

CHAGE- прерывание по изменению уровня на входе. Реализуется штатными программными средствами ардуино.

 

http://arduino.ru/Reference/AttachInterrupt

nik182
Offline
Зарегистрирован: 04.05.2015

Yes! Действительно в режиме генерации 1 герц будет то что нужно - просыпание каждые 500 мс. У меня проблема в другом. У меня DS3231 пристёгнута к тиньке 25 и ног на прерывание просто нет. Даже кнопки пришлось на ноги управления дисплеем сажать.   

Тарас Петрович
Offline
Зарегистрирован: 21.11.2015

Тогда по таймеру (внутри контроллера) можно прерывание организовать 2 раза за секунду, в каждом светодиод переключать, в каждом втором время обновлять (хотя этот способ и осудили в данной теме из-за несинхронности,но он рабочий).