Часы и ардуино

yul-i-an
yul-i-an аватар
Offline
Зарегистрирован: 10.12.2012

Может кому интересно будет. У меня так работает.

void tik(){//отсчет времени
if (millis()<time){//если переполнился счетчик
 time=0;
  }
  if ((millis()-time)>=60000){//если прошла минута
  timer++;
      if (timer==1440){//если прошли сутки
      timer=0;//сброс часов на ноль
      time=time-4000;//компенсация ухода времени на 4 сек/сутки
    }
    error=(millis()-time-60000);//ошибка (на сколько перескочили millis интервал в 60000
  // time = millis();//без учета погрешности
  time = (millis()-error);//учитываем перескок
 }
}

Я у себя ввел автоматическую корректировку (error). До её ввода часы уходили гдето на 1.5 - 2 минуты в час, после снизилась до 3.5 - 4 секунд в сутки которые я тоже компенсирую в начале суток отнимая от накопленного time 4000 милисикунд.

Время считаю в минутах и перевожу в минуты и часы следующей функцией

void GUI_clock(long t,int x,int y){//вывод времени
  lcd.setCursor(x, y);
  byte h=floor(t/60);
  byte m=t-(h*60);
  if (h<10){//вывод дополнительного нуля (01:05 например)
    lcd.print("0");
  }
  lcd.print(h);
  lcd.print(":");
  if (m<10){
    lcd.print("0");
  }
  lcd.print(m);
  lcd.print(" ");
}

Смысл всей затей в том чтобы в простых и неответственных проектах отказаться от использования часов реального времени.

Сейчас занимаюсь небольшим устройством управляемым со смартфона, так оно время будет по нему синхронизировать.

exez
exez аватар
Offline
Зарегистрирован: 04.04.2015

Соори что влажу в тему, но не хотел плодить клонов.

Написал часы для работы автоматики в теплице. Дата тут не требовалась, так что только учет времени. Вот код:

  if (millis() < PrevMillis) {PrevMillis = 0;}
  deltaT = millis() - PrevMillis;   PrevMillis = millis();
  sek = sek + deltaT;
  while (sek > 1000) 
  { 
  ss = ss + 1; sek = sek - 1000;
  if (ss > 59) {ss = ss - 60; mm++;}
  if (mm > 59) {mm = mm - 60; hh++; if (hh > 23){hh = 0; day++;} }
  }

Попинайте плиз :) так вроде работают норм..

зы: может пригодится кому.

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

а зачем обнулять в 1 строке. неизвестно же в какой момент мк дойдет до условия. ошибка будет накапливаться

exez
exez аватар
Offline
Зарегистрирован: 04.04.2015

Это по идее произойдет 1 раз в 50 дней. Или я ошибаюсь?

bwn
Онлайн
Зарегистрирован: 25.08.2014

На мой взгляд, для проектов длительного использования все таки удобнее RTC+ионистор. Удорожание минимальное, но проблема пропашего питания устранена полностью. Поменяли аккумулятор или произошел сбой сетевого, вводи время заново? ИМХО.

exez
exez аватар
Offline
Зарегистрирован: 04.04.2015

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

Может конденсатор подпаять на питание?? :)

bwn
Онлайн
Зарегистрирован: 25.08.2014

На RTC у меня ионистор 0,25F, а по полной схеме лучше резервного аккумулятора врядли что то найдете.

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

exez пишет:

Это по идее произойдет 1 раз в 50 дней. Или я ошибаюсь?

неа. вот если ты будешь делать как я предлагаю 1 раз за 50 дней. а ты обнуляешь при совпадении условия. но условие может выполнится не через 1 сек, а 1.05 например. и эту ошибку ты накапливаешь постоянно так как обнуляешь время с которым сравниваешь что прошла секунда. отправка на дисплей или еще что то и до условия мк дойдет позже

exez
exez аватар
Offline
Зарегистрирован: 04.04.2015

Не понимаю.. Миллис не идет назад. Только вперед. Соответстевнно условие millis() < PrevMillis выполнится только один раз в 50 дней. Судя по тому, что написано тут http://arduino.ru/Reference/Millis

Каким же образом накапливается ошибка?

jeka_tm
jeka_tm аватар
Offline
Зарегистрирован: 19.05.2013

никак))) забей. это я туплю

exez
exez аватар
Offline
Зарегистрирован: 04.04.2015

))))))))))))))) Все равно спасибо!

zuenko
Offline
Зарегистрирован: 01.01.2016

Здраствуйте. Подключил к ардуино дисплей с сименс а-55 чип PCF8812 , захотелось запустить часы без модуля, нашел эту тему, вот что у меня получолось, не судите строго я новичок.

 

#include "U8glib.h"

U8GLIB_PCF8812 u8g(13, 11, 10, 9, 8);       // SPI Com: SCK = 13, MOSI = 11, CS = 10, A0 = 9, Reset = 8

long prevmicros = 0;//переменная для хранения значений таймера
 int sek=0;//значение секунд
 int minu=0;//значение минут
 int chas=0;//значение часов
 int knopka=0;//значение кнопки
 int knopk=0;//значение кнопки
 boolean counter=false; // счетчик для полусекунд

void draw(void)
{
   u8g.setPrintPos(0,35);//выводим значение часов 
   if (chas<10) 
     u8g.print("0");
     u8g.print(chas);//количество часов
     
   u8g.setPrintPos(35,35);//выводим значение минут
   if (minu<10) 
     u8g.print("0");
     u8g.print(minu);//количество минут
     
   u8g.setPrintPos(70,35);//выводим значение секунд
   if (sek<10) 
     u8g.print("0");
     u8g.print(sek);//количество секунд
     {
      
     if (counter==false)
  {
     u8g.setPrintPos(30,30);
     u8g.print(":");//выводим символ ":"между часами и минутами
     u8g.setPrintPos(65,30);
     u8g.print(":");//выводим символ ":"между  минутами и секундами
    } 
else
   {
     u8g.setPrintPos(30,30);
     u8g.print("");
     u8g.setPrintPos(65,30);
     u8g.print("");
   }
    } 
   }
   
  void setup (void)
  {
  u8g.setFont(u8g_font_unifont);
  u8g.setFont(u8g_font_osb21);
  digitalWrite(7, HIGH);
  digitalWrite(6, HIGH);  
}

void change_font_pos(void) 
{ 
   if (micros() - prevmicros >500000)
 {    prevmicros = micros();  //принимает значение каждые полсекунды
   counter=!counter;
   if (counter==false)
   {     sek++;              //переменная секунда + 1
   }

   if(sek>59)//если переменная секунда больше 59 ...
   {
     sek=0;//сбрасываем ее на 0
     minu++;//пишем +1 в переменную минута
   }

   if(minu>59)//если переменная минута больше 59 ...
   {
     minu=0;//сбрасываем ее на 0
     chas++;//пишем +1 в переменную час
   }
   
   if(chas>23)//если переменная час больше 23 ...
   {
     chas=0;//сбрасываем ее на 0
   }
  }
   
if(digitalRead(7)==HIGH&&knopka==0)//если кнопка нажата
   // и перемення "knopka" равна 0 , то ...
   {
      minu++;//пишем + 1 в переменную минута
      sek=0;//пишем 0 в переменную секунда
      knopka++;//пишем 1 в переменную кнопка
      //это нужно для того что бы с каждым нажатием кнопки
      //происходило только одно действие
      // плюс защита от "дребезга" 
      //с каждым нажатием кнопки обновляем значения на дисплее//
      }
      
    if(digitalRead(7)==LOW&&knopka==1)//если кнопка НЕ нажата
   //и переменная knopka равна - 1 ,то ...
   { 
      knopka=0;//обнуляем переменную "knopka"
   }
    
 if(digitalRead(6)==HIGH&&knopk==0)//если кнопка нажата
   // и перемення "knopk" равна 0 , то ...
   {
      chas++;//пишем + 1 в переменную минута
      knopk++;//пишем 1 в переменную кнопка
      //это нужно для того что бы с каждым нажатием кнопки
      //происходило только одно действие
      // плюс защита от "дребезга"
      //с каждым нажатием кнопки обновляем значения на дисплее//
      }
   
  if(digitalRead(6)==LOW&&knopk==1)//если кнопка НЕ нажата
   //и переменная knopka равна - 1 ,то ...
   { 
      knopk=0;//обнуляем переменную "knopk" 
   }
   } 

void loop(void) {
  change_font_pos();
  u8g.firstPage();  
 do {
    draw();
  } while( u8g.nextPage() );
  // rebuild the picture after some delay
  delay(500);
}

 

AlexLapshyn
Offline
Зарегистрирован: 11.03.2016

Искал простую программу таймера. Во всех найденных вариантах подсчёт времени неправильный - вносится помеха от задержек выполнения команд.

Правильный вариант - просто брать текущие тики от счётчика и переводить их в часы/минуты/секунды, учитывая переполнение счётчика.

Вобщем за час была написана следующая программа:

//часы реального времени на внутреннем таймере

//суточная корректировка
unsigned long cortime=0;

//суточные тики таймера
const unsigned long day=86400000;

byte Second=0;

// include the library code: 
#include <LiquidCrystal.h>

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

void setup() {
 // set up the LCD's number of columns and rows: 
  lcd.begin(16, 2); 
  // Print a message to the LCD. 
  lcd.setCursor(0,0); 
  lcd.print("===== Time ====="); 
  lcd.setCursor(0,1); 
  lcd.print("----------------"); 
}

void loop() {
unsigned long time;
byte Hour;
byte Minute;

//проверяем кнопочки
int x; 
x = analogRead (0); 
  if (x < 60) { 
  cortime=cortime+day/24;
  } 
  else if (x < 200) { 
  cortime=cortime-day/1440;
  } 
  else if (x < 400){ 
  cortime=cortime+day/1440;
  } 
  else if (x < 600){ 
  cortime=cortime-day/24;
  }
  else if (x < 800){ cortime=cortime+1000*long(Second+1);
 }


time=millis();
//проверяем выпадение из диапазона
while(time-cortime>day)cortime+=day;
//считаем текущее время
time=(time-cortime)/1000;
Hour=time/3600;
Minute=(time/60)%60;
Second=time%60;

//печатаем время
lcd.setCursor(4,1);
if(Hour<10)lcd.print("0");
lcd.print(Hour);lcd.print(":");
if(Minute<10)lcd.print("0");
lcd.print(Minute);lcd.print(":");
if(Second<10)lcd.print("0");
lcd.print(Second);

delay(999); }
  

Принцип работы такой.

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

кнопки на шилде: LEFT часы+, RIGHT часы-, UP минуты+, DOWN минуты-, SET сброс секунд в 0.

Дату не делал, это чисто для примера работоспособности.

Сам таймер это строки программы с 50 по 57. Остальное вывод на дисплей и установка времени с клавиатуры. Там реально больше ничего городить ненужно! ;)

Кнопочки и дисплей - китайский LCD Keypad Shield (http://zelectro.cc/LCD_Keypad_shield)

з.ы. При старте если уменьшить часы на 1 будет глюк (перескок на 16 часов). Это связано с начальным состоянием корректирующего значения. В остальном всё нормально.
з.з.ы. Изменение минут в большую/или меньшую сторону автоматически корректирует и часы, при переходе минут через ноль. Такая прикольная фича которая вылезает сама собой, из за особенностей подсчёта времени.
з.з.з.ы. Переменная Second единственная, которая должна быть глобальной. Обнуление секунд происходит на втором такте цикла после нажатия на кнопку SET, поэтому нужно значение секунд с предыдущего такта. Если секунды обнулять не требуется, переменную можно оставить локальной.
AntonM
Offline
Зарегистрирован: 22.10.2015

Через millis() я получил адское отставание от эталонных часов. Сделал через прерывание по таймеру - красота! За 8 ч ушёл на 2сек.

AlexLapshyn
Offline
Зарегистрирован: 11.03.2016

У кварцев любых есть разброс в частоте. В точных часах её подстраивают переменным конденсатором. В цифровых можно подстроить введением поправочных коэффициентов. В примере выше, проще всего ввести суточную поправку. Для этого надо изменить в большую или меньшую сторону статическую переменную "day". Менять как раз на то время отставания/спешки в миллисекундах. Конечно, в 12 часов ночи часы немного будут врать - эти секунды автоматически подкорректируюся раз в 24 часа. Точность часов теоретически можно выставить до 1 мс в сутки. Это 1 секунда в 1000 дней. Такую точность ни с каких внешних часов не получить.

в строке 54 программы надо будет делить не на 1000, а на простую дробь, дающую в результате число: 1000*day/86400000. Рассчёт этой дроби индивидуален. Например при погрешности +8 секунд в день, вычисления будут выглядеть так:  time=(time-cortime)*10/10001

(числитель дроби может быть от 1 до 49, знаменатель любой. Больше 49 нельзя - будет переполнение. Можно, конечно, не заморачиваться с целочисленными вычислениями, а перевести всё в плавующую точку)

Ну для смеха даже можно припаять подстроечник к аналоговому входу и с него брать корректирующий коэффициент раз в сутки ;)

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

sergei7302 пишет:

nestandart пишет:

Скину это дело в "архив жизненного опыта" - может пригодится в будущем.
Например для изготовления некой системы , функции которой включаются/выключаются
по часам реального времени.
Навешать кучу кнопок для быстрого програмирования , сделать менюшку ну и т.д.
 

А как подключить десплей к Ардуино?????

 

 

Я подключил так )))

#include <Wire.h>
 #include <LiquidCrystal_I2C.h>

 LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // наш китайский дисплейчик I2C на 0x3F
 
//*********************************************************
// Здесь новый код часиков

 long prevmicros = 0;     //переменная для хранения значений таймера
   int sek=0;             //значение секунд
   int minu=0;            //значение минут
   int chas=0;            //значение часов
   boolean counter=false; // счетчик для полусекунд
   int knopka=0;          //значение кнопки
   
   byte error, address;
   int nDevices;         //для сканера
 
  void setup()
  {
   lcd.begin(16, 2);      //инициализация дисплея
   lcd.setCursor(0,0);
   lcd.print("www.cherkessk.su");
   lcd.setCursor(11,1);
   lcd.print("UA6EM");  
   Serial.begin(9600);
   Serial.println("\nI2C Scanner");   
   }
 
  void loop()
 
  {
     if (micros() - prevmicros >500000)
   {    prevmicros = micros();  //принимает значение каждые полсекунды
     counter=!counter;
     if (counter==false)
     {     sek++;              //переменная секунда + 1
       lcd.setCursor(2,1);
       lcd.print(":");         //выводим символ ":"между часами и минутами
       lcd.setCursor(5,1);
       lcd.print(":");         //выводим символ ":"между  минутами и секундами
     }
     else
     {
     lcd.setCursor(2,1);
     lcd.print(" ");    // мигание :
     lcd.setCursor(5,1);
     lcd.print(" ");    // мигание :
     }
     if(sek>59)//если переменная секунда больше 59 ...
     {
     sek=0;//сбрасываем ее на 0
     minu++;//пишем +1 в переменную минута
     }
     if(minu>59)//если переменная минута больше 59 ...
     {
     minu=0;//сбрасываем ее на 0
     chas++;//пишем +1 в переменную час
     }
     if(chas>23)//если переменная час больше 23 ...
     {
       chas=0;//сбрасываем ее на 0
     }
      
     lcd.setCursor(0,1);//выводим значение часов
     if (chas>=0 && chas<10) {
     lcd.print("0");
     lcd.print(chas);}//количество часов
     else lcd.print(chas);
       
     lcd.setCursor(3,1);//выводим значение минут
     if (minu>=0 && minu<10) {
     lcd.print("0");
     lcd.print(minu);}//количество минут
     else lcd.print(minu);
       
     lcd.setCursor(6,1);//выводим значение секунд
     if (sek>=0 && sek<10) {
     lcd.print("0");
     lcd.print(sek);}//количество секунд
     else lcd.print(sek);  }

      //установка времени//
           //кнопка//
     if(digitalRead(8)==HIGH&&knopka==0)//если кнопка нажата
     // и перемення "knopka" равна 0 , то ...
     {
        minu++;         //пишем + 1 в переменную минута
        sek=0;          //пишем 0 в переменную секунда
        knopka++;       //пишем 1 в переменную кнопка
                        //это нужно для того что бы с каждым нажатием кнопки
                        //происходило только одно действие
                        // плюс защита от "дребезга"
                        //с каждым нажатием кнопки обновляем значения на дисплее//
                        
     lcd.setCursor(0, 1);
     lcd.print(chas);
     lcd.setCursor(3, 1);
     lcd.print(minu);
     lcd.setCursor(6, 1);
     lcd.print(sek);
     }
      if(digitalRead(8)==LOW&&knopka==1)//если кнопка НЕ нажата
     //и переменная knopka равна - 1 ,то ...
     {
        knopka=0;//обнуляем переменную "knopka"
     }

//***

     }
 
//***END***


 

DNB
Offline
Зарегистрирован: 21.07.2018

Здравствуйте!
Не знал что часы в (моём) компьютере имеют погрешность около 4 секунд в сутки, а  синхронизация времени выполняется раз в 7 дней.
По этой причине не получалось установить точный период отсчёта и коррекцию в микросекундах.
Так что желательно ежечасно синхронизировать эталон. В моём случае более-менее точный сервер для синхронизации - time-b.nist.gov
К стати часы на "Ардуино" получаются точнее чем с модулем реального времени.
Всем удачи.

mexanic38
Offline
Зарегистрирован: 26.02.2018

Скажите пожалуйста , все тут рассуждают о точности часов, на сколько секунд надо корректировать время и как. А вы не думали ,

что все ваши программы используют функцию millis(); и ВАШИ часы встанут через три недели примерно из за переполнения таймера.

И все равно придется делать сброс программы.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

mexanic38 пишет:

Скажите пожалуйста , все тут рассуждают о точности часов, на сколько секунд надо корректировать время и как. А вы не думали ,

что все ваши программы используют функцию millis(); и ВАШИ часы встанут через три недели примерно из за переполнения таймера.

И все равно придется делать сброс программы.

Нет, не думали.

Не встанут.

Не придется (разумеется, если грамотно написано).

EvilSpirit
Offline
Зарегистрирован: 16.05.2014

Берите мой код выше, такие часы работают уже несколько лет, сильно НЕ уходят (раз в несколько месяцев надо подводить), коррекцию я на глаз подбирал для своего случая, если заморочиться - можно подобрать лучше. micros() и millis() надо правильно готовить. Если сделаете ошибку в программе - то работать будет как попало. 

EvilSpirit пишет:

спасибо за ваши размышления о точности хода, мне даже не лень стало зарегистрироваться и рассказать, как на самом деле можно сделать программные часы, которые потом даже можно более-менее точно настроить, в отличие от часов на DS1307, которые можно настроить только плясками вокруг кварца.

byte seconds;
byte minutes;
byte hours;

// задать начало отсчета времени
// !! обязательно unsigned long - чтобы правильно работал переход через переполнение
unsigned long start = micros(); 

// коррекция в микросекундах на секунду. позволяет получить точность +- ~3 сек в месяц. в дополнение можно ввести еще почасовую коррекцию, что даст точность в 60 раз больше.
unsigned long correction = 0;


// seconds
// внимание!! вот так должен выглядеть правильный код хода часов (без использования прерываний)
// здесь учитывается переход счетчика микросекунд через переполнение переменной типа unsigned int 
while ((unsigned long)(micros() - start) >= 1000000 + correction) {
	seconds++;
	// самое важное место:
	// делая именно так, предотвращаем потерю точности, если вдруг условие
	// срабатывает, когда (micros() - start) например, 1000002 (а это может быть)
	// неправильные варианты:
	// start=micros();
	// во-первых, может уже пройти время с предыдущих micros(), которые в условии
	// во-вторых, теряем те самые 2 микросекунды (если 1000002)
	start += 1000000 + correction;
}
		
// minutes
while (seconds >= 60) {
	seconds -= 60;
	minutes++;
}
		
// hours
while (minutes >= 60) {
	minutes -= 60;
	hours++;
}
		
// days
while (hours >= 24) {
	hours -= 24;
}

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

пока что мне удалось добиться точности хода +- секунда в день. при correction = 0 часы врали очень сильно, десятки секунд в сутки, это уже изза неточности кварца.

если сделать программу синхронизации времени по com порту, возможно удастся подобрать более точное значение correction и добиться максимальной точности, которая возможна без коррекции по температуре итд.

b707
Offline
Зарегистрирован: 26.05.2017

mexanic38 пишет:
А вы не думали ,что все ваши программы используют функцию millis(); и ВАШИ часы встанут через три недели примерно из за переполнения таймера.

И все равно придется делать сброс программы.

вы ошибаетесь, причем сразу в нескольких пунктах.

Во-первых, переполнение миллис происходит не через 3 недели, а через 49 дней.

Во-вторых, после переполнения таймер не встает, а спокойно продолжает с нуля. И так бесконечно.

И в третьих, самое главное - простейшее арифметическое действие - вычитание двух значений миллис - позволят добится того, что программа просто не заметит переполнения таймера и будет работать так, как будто таймер никогда не кончается.

bwn
Онлайн
Зарегистрирован: 25.08.2014

mexanic38 пишет:

Скажите пожалуйста , все тут рассуждают о точности часов, на сколько секунд надо корректировать время и как. А вы не думали ,

что все ваши программы используют функцию millis(); и ВАШИ часы встанут через три недели примерно из за переполнения таймера.

И все равно придется делать сброс программы.

Да, я вот тоже не понимаю, циферблат на моих часах заканчивается на цифре 12, но никогда не переполняется. Хм, парадокс однако.
Клапочка, ты где?????

sadman41
Offline
Зарегистрирован: 19.10.2016

bwn пишет:

Да, я вот тоже не понимаю, циферблат на моих часах заканчивается на цифре 12, но никогда не переполняется. Хм, парадокс однако.

Клапочка, ты где?????

У него счетчик регистраций переполнился.

А часы ваши сбрасывают часовые эльфы. Только очень быстро - вы не замечаете.