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

nestandart
nestandart аватар
Offline
Зарегистрирован: 15.06.2011

Прикупил я , недавно , LCD диплей и решил его протестить (поиграться).
В итоге получились часы.
Решил выложить , вдруг кому пригодиться.
Часы имеют одну кнопку (вторую лень было припаивать).
По хорошему надо четыре кнопки : часы +/- , минуты +/-.
у мя только минуты +.

Вот программа для ***дуино IDE.

#include <LiquidCrystal.h>//библиотека для работы с LCD
 long previousMillis = 0;//переменная для хранения значений таймера
 
 int sek=0;//значение секунд
 int min=0;//значение минут
 int chas=0;//значение часов
 int knopka=0;//значение кнопки

LiquidCrystal lcd(12, 10, 5, 4, 3, 2);//инициализация портов

void setup()
{
 
 lcd.begin(16, 2);//иницифлизация дисплея
 pinMode(8,INPUT);//инициализация портов
 pinMode(9,OUTPUT);//инициализация портов
 pinMode(13,OUTPUT);//инициализация портов
 
}

void loop()
{
   if (millis() - previousMillis >500) 
 {  
   previousMillis = millis();  //запучкаем таймер
   digitalWrite(13, !digitalRead(13));//меняем значение порта каждые 0.5секунд
   
   
   
   
   if(digitalRead(13)==HIGH)//если 13 нога лог1 то...
   {
     sek++;//переменная секунда + 1
     lcd.clear(); //обновление дисплея
     
     
     
     
     //вывод символов на дисплей//
     lcd.setCursor(6, 1);
     lcd.print(":");//выводим символ ":"между часами и минутами
     lcd.setCursor(9, 1);
     lcd.print(":");//выводим символ ":"между  минутами и секундами
   }
   
   if(digitalRead(13)==LOW)
   {
     //через каждые 0.5 секунд меняем символ ":" на "."
     lcd.setCursor(9, 1);
     lcd.print(".");
     lcd.setCursor(6, 1);
     lcd.print(".");
   }
   
   if(sek>59)//если переменная секунда больше 59 ...
   {
     sek=0;//сбрасываем ее на 0
     min++;//пишем +1 в переменную минута
   }
   
   
   if(min>59)//если переменная минута больше 59 ...
   {
     min=0;//сбрасываем ее на 0
     chas++;//пишем +1 в переменную час
   }
   
   
   
   
   if(chas>23)//если переменная час больше 23 ...
   {
     chas=0;//сбрасываем ее на 0
    
   }
   
   
   
   //вывод символов на дисплей//
   
   lcd.setCursor(4, 1);//выводим значение часов в строку - 1
   //столбец -4 
   lcd.print(chas);//количество часов
   
   lcd.setCursor(7, 1);//выводим значение часов в строку - 1
   //столбец -7 
   lcd.print(min);//количество минут
   
   lcd.setCursor(10, 1);//выводим значение часов в строку - 1
   //столбец -10 
   lcd.print(sek);//количество секунд
   
   
 } 
 
 
      //установка времени//
          //кнопка//
 
 
   
   if(digitalRead(8)==HIGH&&knopka==0)//если кнопка нажата 
   // и перемення "knopka" равна 0 , то ...
   {
      min++;//пишем + 1 в переменную минута
      sek=0;//пишем 0 в переменную секунда
      knopka++;//пишем 1 в переменную кнопка
      //это нужно для того что бы с каждым нажатием кнопки
      //происходило только одно действие
      // плюс защита от "дребезга"
      
      
      
      //с каждым нажатием кнопки обновляем значения на дисплее//
      lcd.setCursor(4, 1);
   lcd.print(chas);
   
   lcd.setCursor(7, 1);
   lcd.print(min);
   
   lcd.setCursor(10, 1);
   lcd.print(sek);
   }
   
    if(digitalRead(8)==LOW&&knopka==1)//если кнопка НЕ нажата
   //и переменная knopka равна - 1 ,то ...
   {
      
      knopka=0;//обнуляем переменную "knopka"
   }
   
 
 //выводим надпись "CHASI." в строку - 0 
 lcd.setCursor(5, 0);
 lcd.print("CHASI.");
 
 
}

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

Soso
Offline
Зарегистрирован: 22.03.2011

 Спасибо. Очень кстати.

nestandart
nestandart аватар
Offline
Зарегистрирован: 15.06.2011

Во время тестирования выяснил что они отстают на 10-15 секунд в час.

Разберусь в чем дело - отпишу. 

nestandart
nestandart аватар
Offline
Зарегистрирован: 15.06.2011

Чтобы часы работали точнее нужно работать с микросекундами . 

crabne
Offline
Зарегистрирован: 15.12.2011

Огромное спасибо за код =)

немного переправил под себя, чтобы:

  1. отображение всегда было вида 00:00:00
  2. отсчет в микросекундах
  3. без мигания светодиодом

буду проверять точность при работе с микросекундами:

#include <LiquidCrystal.h>//библиотека для работы с LCD
 
 long prevmicros = 0;//переменная для хранения значений таймера
 int sek=0;//значение секунд
 int minu=0;//значение минут
 int chas=0;//значение часов
 boolean counter=false; // счетчик для полусекунд

LiquidCrystal lcd(53, 51, 49, 47, 45, 43);//инициализация портов 
 
void setup()
{
 lcd.begin(16, 2);//инициализация дисплея
  }

void loop()
{
   if (micros() - prevmicros >500000)
 {    prevmicros = micros();  //принимает значение каждые полсекунды
   counter=!counter;
   if (counter==false)
   {     sek++;              //переменная секунда + 1
     lcd.setCursor(2,0);
     lcd.print(":");         //выводим символ ":"между часами и минутами
     lcd.setCursor(5,0);
     lcd.print(":");         //выводим символ ":"между  минутами и секундами
   }
   else
   {
     lcd.setCursor(2,0);
     lcd.print(" ");    // мигание :
     lcd.setCursor(5,0);
     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,0);//выводим значение часов 
   if (chas>=0 && chas<10) {
     lcd.print("0");
     lcd.print(chas);}//количество часов
     else lcd.print(chas);
     
   lcd.setCursor(3,0);//выводим значение минут
   if (minu>=0 && minu<10) {
     lcd.print("0");
     lcd.print(minu);}//количество минут
     else lcd.print(minu);
     
   lcd.setCursor(6,0);//выводим значение секунд
   if (sek>=0 && sek<10) {
     lcd.print("0");
     lcd.print(sek);}//количество секунд
     else lcd.print(sek);
 }
}

 

nestandart
nestandart аватар
Offline
Зарегистрирован: 15.06.2011

Более менее точный ход получил при вот таком значении микросекунд 499210.

За пять часов сбились не более чем на 10 секунд. Нужно экспериментировать дальше.

step962
Offline
Зарегистрирован: 23.05.2011

nestandart пишет:

Чтобы часы работали точнее нужно работать с микросекундами . 

 

Чтобы часы работали более-менее точно, надо брать ровно полсекунды, а условие

if (millis() - previousMillis >500)

срабатывает при разности = 501, 502 и т.д. То есть отставание 2 миллисекунды за секунду заложено программно - погрешность хода только за счет неправильного условия составляет 0,2% или 7 секунд в час.

Эта строка должна как минимум иметь вид

if (millis() - previousMillis >= 500)

А вообще - для правильного отсчета тиков следует задействовать механизм прерываний от таймера. Что-то вроде

void ISR(TIMER0_OVF_vect) {

// подсчет тиков

}

crabne
Offline
Зарегистрирован: 15.12.2011

 Всем привет =)

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

if (micros<prevmicros) prevmicros=0

в самое начало цикла loop ( в строку 17 в последнем примере) для случая обнуления micros через 50 дней

 

 Подскажите, пожалуйста, что это за функция:

void ISR(TIMER0_OVF_vect) {

// подсчет тиков

}

где можно про нее почитать

crabne
Offline
Зарегистрирован: 15.12.2011

 

Цитата:
Чтобы часы работали более-менее точно, надо брать ровно полсекунды, а условие

if (millis() - previousMillis >500)

срабатывает при разности = 501, 502 и т.д. То есть отставание 2 миллисекунды за секунду заложено программно - погрешность хода только за счет неправильного условия составляет 0,2% или 7 секунд в час.

Эта строка должна как минимум иметь вид

if (millis() - previousMillis >= 500)

 

возможно, что я туплю, но по Вашей же логике получается:

если код: if (millis() - previousMillis >500) то срабатывает приразности = 501, 502 и т.д.

тогда если код: if (millis() - previousMillis >= 500) то срабатывать будет при разности = 500, 501, 502

погрешность ведь никуда не девается, просто событие наступает раньше ...

раз уж нужно точное значение, может тогда строчка должна иметь вид:

if (miсros() - prevmicros == 500001)

?

 

step962
Offline
Зарегистрирован: 23.05.2011

crabne пишет:

 

возможно, что я туплю, но по Вашей же логике получается:

если код: if (millis() - previousMillis >500) то срабатывает приразности = 501, 502 и т.д.

тогда если код: if (millis() - previousMillis >= 500) то срабатывать будет при разности = 500, 501, 502

погрешность ведь никуда не девается, просто событие наступает раньше ...

раз уж нужно точное значение, может тогда строчка должна иметь вид:

if (miсros() - prevmicros == 500001)

?

 

Здесь все зависит от длительности главного цикла (того, что крутится в loop()).

Если длительность его отработки составляет доли миллисекунды, то в исходном варианте отставание будет на миллисекунду и больше в каждой полусекунде работы часов (срабатывание процедуры обработки события "полсекунды" происходит после того, как прошло больше 500 миллисекунд после предыдущего срабатывания - т.е. 501 плюс какая-то дельта, находящаяся в диапазоне от 0 мкс до длительности главного цикла).

В предложенном мной варианте срабатывание процедуры обработки события "полсекунды" происходит через пятьсот миллисекунд плюс та самая упомянутая выше дельта.

Почему >=? Если длительность цикла составляет миллисекунду и больше, то возможна ситуация, когда при предыдущей проверке мы получили время с последнего срабатывания 499, а при последующей проверке - уже 501. Т.е. часы просто остановятся - условие millis() - previousMillis==500 больше никогда не выполнится (точнее, может выполниться через 4294967296 миллисекунд - когда произойдет переполнение результата вычитания типа long int, а это почти 50 суток, т.е. вечность по меркам микроконтроллеров). Соответственно необходимо предусмотреть принудительное завершение "полусекунды" даже если уже образовалась задержка в одну и более миллисекунд.

Ну и теперы вы сами можете сказать, что неправильно в вашем варианте. Он заведомо невыполним, т.к. длительность главного цикла гарантированно больше 1 мкс (=16 команд). Через пару-тройку удачных срабатываний (а может быть и гораздо раньше) вы проскочите-таки удачную проверку и разница miсros() - prevmicros будет увеличиваться до бесконечности (точнее, до вышеупомянутых четырех с копейками миллиардов), а условие будет оставаться ложным

step962
Offline
Зарегистрирован: 23.05.2011

crabne пишет:

  для случая обнуления micros через 50 дней

 

Примерно через час  с четвертью (4294967296/1000000/60/60). 50 дней - это для millis ( 4294967296/1000/60/60/24)

step962
Offline
Зарегистрирован: 23.05.2011

crabne пишет:

 

 Подскажите, пожалуйста, что это за функция:

void ISR(TIMER0_OVF_vect) {

// подсчет тиков

}

где можно про нее почитать

Когда прекомпилятор Arduino IDE встречает функцию ISR, то он генерируент код функции обработки прерывания (Interrupt Service Routine) для того вектора прерывания, который указан в скобках. Их у ATmega328 25, в том числе прерывание по переполнению таймера/счетчика 0 TIMER0_OVF_vect.

Тема достаточно обширная, двумя словами не описать. Прочитать можно в Интернете (ищите по "Arduino ISR"). Ну хотя бы здесь.

Кстати, я, во-первых, допустил очепятку - не

 

void ISR(TIMER0_OVF_vect)

а

ISR(TIMER0_OVF_vect)

- коды обработки прерывания не могут возвращать значение.

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

Значит, пользоваться надо таймерами 1 и 2:

ISR(TIMER1_OVF_vect)

ISR(TIMER2_OVF_vect)

Кстати, в одном скетче может быть несколько функций ISR() -> прекомпилятор без проблем преобразовывает их в разные обработчики прерываний (с разными именами, повешенными на разные вектора).

Вы можете спросить, почему не attachInterrupt()? Потому, что эта функция имеет очень ограниченное применение - с ее помощью можно создавать обработчики лишь для двух первых векторов из 25 доступных INT0 и INT1.
 
 

 

crabne
Offline
Зарегистрирован: 15.12.2011

 step962,

спасибо большое =) очень подробно все разъяснили

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

если не трудно, может быть приведете пример скетча часов, используюя функцию ISR?

или, по крайней мере, подскажите:

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

подойдет ли для этих целей программный таймер или с учетом набегающей погрешности лучше все таки использовать shield

www.ebay.com/itm/Arduino-Real-Time-Clock-RTC-DS1307-Sensor-Shield-module-/200715600779 - покупной

tronixstuff.wordpress.com/2010/05/28/lets-make-an-arduino-real-time-clock-shield/ - самодельный

 

 

Coder
Coder аватар
Offline
Зарегистрирован: 04.03.2012

Как мне кажется, код вывода ведущего нуля можно немножко оптимизировать, а именно:

 

if (chas>=0 && chas<10) {
lcd.print("0");
lcd.print(chas);}//количество часов
else lcd.print(chas);

Здесь ELSE не нужно, т.к. код lcd.print(chas)  выполняется в любом случае. Т.е. все будет работать если написать и вот так:

if (chas>=0 && chas<10) {
lcd.print("0");}//вывод ведущего нуля
lcd.print(chas);

 

Т.е. если часы больше 0 и меньше 10 сначала напечатается 0, а потом часы.  Если часы больше 9 то ноль печататься не будет. ИМХО так.

step962
Offline
Зарегистрирован: 23.05.2011

crabne пишет:

если не трудно, может быть приведете пример скетча часов, используюя функцию ISR?

Вот тут почитайте - на русском языке - о настройке таймера/счетчика 2 для отсчета времени с использованием ISR.

to Coder: дополнительно можно сэкономить на фигурных скобках - для одной инструкции они не нужны, а также с учетом того, что переменная chas может принимать значения в диапазоне 0-23 (т.е. не принимает отрицательные значения), то и условие подсократить:

if (chas<10)
  lcd.print("0"); //количество часов
lcd.print(chas);

 

vworld
vworld аватар
Offline
Зарегистрирован: 26.09.2011

я хотел попробовать скетч из примера DataTime

вот страница примера

в монитор ничего не выводит :(

step962
Offline
Зарегистрирован: 23.05.2011

 А у вас в мониторе какая скорость передачи стоит? По умолчанию (9600) или 19200 выставили?

vworld
vworld аватар
Offline
Зарегистрирован: 26.09.2011

step962 пишет:

 А у вас в мониторе какая скорость передачи стоит? По умолчанию (9600) или 19200 выставили?

и в скетче и в мониторе выставлено 38400...

step962
Offline
Зарегистрирован: 23.05.2011

 В функции loop() перед последней фигурной скобкой поставьте

else Serial.println("DataTime not available");

 Если появится вывод в терминале, значит DataTime не функционирует.

Если не появится  - зацикливание в getPCTime().

vworld
vworld аватар
Offline
Зарегистрирован: 26.09.2011

Если появится вывод в терминале, значит DataTime не функционирует.

Появился вывод :(

ПОчему DataTime может не функционировать? библиотеки все есть...

step962
Offline
Зарегистрирован: 23.05.2011

 К библиотекам, хоть и редко, но прикладываются мануалы. Они читались?

Кстати, раз уж библиотеки в наличии, выложите коды функции DateTime.available().

vworld
vworld аватар
Offline
Зарегистрирован: 26.09.2011

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

15:37:33 Tuesday March 6

2551331048253

 

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

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

//============================================================================
//  Программные часы с 7 сегментным индикатором
//============================================================================

//============================================================================
//  Выводы подключения 7 сегментного индикатора
//============================================================================
#define DIGIT1 7
#define DIGIT2 11
#define DIGIT3 12
#define DIGIT4 A2

#define SEGMENTA 8
#define SEGMENTB 13
#define SEGMENTC A0
#define SEGMENTD A4
#define SEGMENTE A5
#define SEGMENTF 9
#define SEGMENTG A1
#define SEGMENT_DP A3

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

void setup(){
//============================================================================
//  Настраиваем выводы на выход
//============================================================================
  pinMode(DIGIT1, OUTPUT);
  pinMode(DIGIT2, OUTPUT);
  pinMode(DIGIT3, OUTPUT);
  pinMode(DIGIT4, OUTPUT);

  pinMode(SEGMENTA, OUTPUT);
  pinMode(SEGMENTB, OUTPUT);
  pinMode(SEGMENTC, OUTPUT);
  pinMode(SEGMENTD, OUTPUT);
  pinMode(SEGMENTE, OUTPUT);
  pinMode(SEGMENTF, OUTPUT);
  pinMode(SEGMENTG, OUTPUT);
  pinMode(SEGMENT_DP, OUTPUT);
}

void loop(){

  if (micros() - prevmicros >500000){    
    prevmicros = micros();        //принимает значение каждые полсекунды
    counter=!counter;
    if (counter==false)
    {     
      sek++;                      //переменная секунда + 1
      digitalWrite(SEGMENT_DP,0); //включаем точки
    }
    else 
    {
      digitalWrite(SEGMENT_DP,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
    }
  }

//============================================================================
//  разбиваем часы,минуты и секунды на цифры (секунды только на время тестирования)
//============================================================================  
  byte chas_1 = chas/10;  //1 цифра часов
  byte chas_2 = chas%10;  //2 цифра часов
  byte minu_1 = minu/10;  //1 цифра минут
  byte minu_2 = minu%10;  //2 цифра минут
  byte sek_1 = sek/10;    //1 цифра секунд
  byte sek_2 = sek%10;    //2 цифра секунд
  
//============================================================================
//  Выводим время
//============================================================================  
  vivod(minu_1);
  digitalWrite(DIGIT1,1);
  digitalWrite(DIGIT1,0);
  vivod(minu_2);
  digitalWrite(DIGIT2,1);
  digitalWrite(DIGIT2,0);
  vivod(sek_1);
  digitalWrite(DIGIT3,1);
  digitalWrite(DIGIT3,0);
  vivod(sek_2);
  digitalWrite(DIGIT4,1);
  digitalWrite(DIGIT4,0);
 
}
//============================================================================
//  Массив с включаемыми сегментами индикатора соответстуеющие цифрам
//============================================================================  
boolean cifr(byte y, byte x){
  boolean myArray[10][7]={
  {0,0,0,0,0,0,1}, // цифра 0
  {1,0,0,1,1,1,1}, // цифра 1
  {0,0,1,0,0,1,0}, // цифра 2
  {0,0,0,0,1,1,0}, // цифра 3
  {1,0,0,1,1,0,0}, // цифра 4
  {0,1,0,0,1,0,0}, // цифра 5
  {0,1,0,0,0,0,0}, // цифра 6
  {0,0,0,1,1,1,1}, // цифра 7
  {0,0,0,0,0,0,0}, // цифра 8
  {0,0,0,0,1,0,0}, // цифра 9
 };
return myArray[y][x];
}
//============================================================================
//  Управление сегментами индикатор соответствующее числу number
//============================================================================

void vivod(byte number){
  digitalWrite(SEGMENTA,(cifr(number,0)));
  digitalWrite(SEGMENTB,(cifr(number,1)));
  digitalWrite(SEGMENTC,(cifr(number,2)));
  digitalWrite(SEGMENTD,(cifr(number,3)));
  digitalWrite(SEGMENTE,(cifr(number,4)));
  digitalWrite(SEGMENTF,(cifr(number,5)));
  digitalWrite(SEGMENTG,(cifr(number,6)));
}

 

 

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Я бы myArray вынес из функции и вместо boolean сделал бы байтовый массив. Памяти будет есть поменьше.
SEGMENT* можно поместить в массив и при выводе использовать цикл. Также цикл можно будет использовать при инициализации.
Но, в принципе, этого всего можно и не делать.

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

kisoft пишет:
Я бы myArray вынес из функции и вместо boolean сделал бы байтовый массив. Памяти будет есть поменьше. SEGMENT* можно поместить в массив и при выводе использовать цикл. Также цикл можно будет использовать при инициализации. Но, в принципе, этого всего можно и не делать.

можно показать как именно вы бы сделали?

не меняя программу заменил в массиве boolean на byte размер программы не изменился

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

Puhlyaviy
Puhlyaviy аватар
Offline
Зарегистрирован: 22.05.2013

jeka_tm пишет:

 вопрос пока будет управляться шаговый двигатель не повлияет ли это на работу часов?

вы пока на дисплей информацию выводите у вас часы идут? ну так и с двигателем они будут идти если вы не остановите внутренний таймер...

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

Puhlyaviy пишет:

вы пока на дисплей информацию выводите у вас часы идут? ну так и с двигателем они будут идти если вы не остановите внутренний таймер...

да идут. все отлично. хорошо вечермо если время будет проверю что да как

Puhlyaviy
Puhlyaviy аватар
Offline
Зарегистрирован: 22.05.2013

jeka_tm пишет:

Puhlyaviy пишет:

вы пока на дисплей информацию выводите у вас часы идут? ну так и с двигателем они будут идти если вы не остановите внутренний таймер...

да идут. все отлично. хорошо вечермо если время будет проверю что да как

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

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

Puhlyaviy пишет:

jeka_tm пишет:

Puhlyaviy пишет:

вы пока на дисплей информацию выводите у вас часы идут? ну так и с двигателем они будут идти если вы не остановите внутренний таймер...

да идут. все отлично. хорошо вечермо если время будет проверю что да как

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

вот и я о том. что будет на дисплее пока мотором буду крутить. значит мотор нельзя загонять в цикл

knack
knack аватар
Offline
Зарегистрирован: 27.03.2012

step962 пишет:

crabne пишет:

если не трудно, может быть приведете пример скетча часов, используюя функцию ISR?

Вот тут почитайте - на русском языке - о настройке таймера/счетчика 2 для отсчета времени с использованием ISR.

Сделал по тому примеру, отставание 40 минут за ночь :(, правда в скетче еще опрос цифрового датчика температуры, но там задержки все убраны и опрашивается раз в секунду.

step962
Offline
Зарегистрирован: 23.05.2011

knack пишет:

правда в скетче еще 

"Дьявол скрывается в мелочах" (c)

Я два месяца бился со странными спорадическими сбоями в UART-библиотеке. В конце концов обнаружил, что в погоне за эффективностью увеличил размер буфера со 128 до 256 байт. А указатели остались типа char. А в библиотеке несколько проверок типа if(pointer==MAXBUFSIZE)...

Может быть, и у вас при добавлении своих функций случилось нечто подобное?

knack
knack аватар
Offline
Зарегистрирован: 27.03.2012

Причесал немного код, поменял местами условия, операторы...вроде синхронно, понаблюдаю еще.

Не замечал но, ардуинка подвисала, сейчас вроде стабильна.

По настройке времени, не подскажите как переписать вот это: 

    if (keyval==140) {              //настройка часов, даты
      if (y==0 & x==0)hour=hour+10;  
      if (y==0 & x==1)hour++;
      if (y==0 & x==3)minut=minut+10;
      if (y==0 & x==4)minut++;
      if (y==1 & x==0)day=day+10;
      if (y==1 & x==1)day++;
      if (y==1 & x==3)month=month+10;
      if (y==1 & x==4)month++;
      if (y==1 & x==6)year=year+10;
      if (y==1 & x==7)year=year++;
      delay(200);  
      printDisp (sec, minut, hour, day, month, year);  //функция вывода на дисплей
    }
   
   if (keyval==328) {              //настройка часов, даты
      if (y==0 & x==0)hour=hour-10;  
      if (y==0 & x==1)hour--;
      if (y==0 & x==3)minut=minut-10;
      if (y==0 & x==4)minut--;
      if (y==1 & x==0)day=day-10;
      if (y==1 & x==1)day--;
      if (y==1 & x==3)month=month-10;
      if (y==1 & x==4)month--;
      if (y==1 & x==6)year=year-10;
      if (y==1 & x==7)year=year--;
      delay(200); 
     printDisp (sec, minut, hour, day, month, year); //функция вывода на дисплей
    } 

дибеляче выглядит :(

lcd keyboard shield

step962
Offline
Зарегистрирован: 23.05.2011

Для начала - почитайте, чем отличаются операторы & и && (ну, и | и || соответственно), т.е. битовые и логические операторы.

knack
knack аватар
Offline
Зарегистрирован: 27.03.2012

Поправил, хотя до этого работало, чисто интуитивно пишу & а не &&

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

спасибо за ваши размышления о точности хода, мне даже не лень стало зарегистрироваться и рассказать, как на самом деле можно сделать программные часы, которые потом даже можно более-менее точно настроить, в отличие от часов на 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 и добиться максимальной точности, которая возможна без коррекции по температуре итд.

Tmin10
Tmin10 аватар
Offline
Зарегистрирован: 18.04.2013

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

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

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

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

Datak
Offline
Зарегистрирован: 09.10.2014

EvilSpirit, легко согласился, а зря.

"Тики" часов не потеряются, даже если цикл выполняется дольше 255  секунд. Таймер ведь в это время продолжает считать, поэтому все пропущенные тики не пропадут, а добавятся к счётчику секунд позже.

Именно поэтому надо писать "start += 1000000", а не "start = micros()", тут всё правильно.

-------

Update:

Посмотрел внимательней. Всё же, чтобы ничего не терялось, код придётся немного изменить.
Я бы сделал как-нибудь так:















while ((unsigned long)(micros() - start) >= 1000000 + correction)
{
    if (++seconds == 60)
    {
        seconds = 0;

        if (++minutes == 60)
        {
            minutes = 0;

            if (++hours == 24)
            {
                hours = 0;
            }
        }
    }

    start += 1000000 + correction;
}
Tmin10
Tmin10 аватар
Offline
Зарегистрирован: 18.04.2013

Я про то и писал, что инкримент и будет выполняться и после 255 секунд будет 0 секунд, 1,2... Т.е. потеряется 255 секунд.

Datak
Offline
Зарегистрирован: 09.10.2014

256, если уж говорить совсем точно :)

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

Tmin10
Tmin10 аватар
Offline
Зарегистрирован: 18.04.2013

А у меня проблемка при тестировании, при открытии монитора порта, ардуина сбрасывается, это ведь лечится отключением автосброса становкой 120 омного сопротивления между 5 вольтами и пином reset?

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

Datak

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

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

со отсчетом времени всегда беда. у всех программистов. только некоторые еще не знают и не задумываются, а я вот даже статью накатал http://www.gamedev.ru/pages/fadetoblack/articles/timing_discretization

SKub
Offline
Зарегистрирован: 19.04.2015

                                    Еще 1 варриант . Часы без модуля.

/*
 * RTC Control v.01
 * by <http://www.combustory.com> John Vaughters
 * Credit to:
 * Maurice Ribble - http://www.glacialwanderer.com/hobbyrobotics for RTC DS1307 code
 *
 * With this code you can set the date/time, retreive the date/time and use the extra memory of an RTC DS1307 chip.  
 * The program also sets all the extra memory space to 0xff.
 * Serial Communication method with the Arduino that utilizes a leading CHAR for each command described below. 
 * Commands:
 * T(00-59)(00-59)(00-23)(1-7)(01-31)(01-12)(00-99) - T(sec)(min)(hour)(dayOfWeek)(dayOfMonth)(month)(year) - T Sets the date of the RTC DS1307 Chip. 
 * Example to set the time for 02-Feb-09 @ 19:57:11 for the 3 day of the week, use this command - T1157193020209
 * Q(1-2) - (Q1) Memory initialization  (Q2) RTC - Memory Dump
 */

#include "Wire.h"
#define DS1307_I2C_ADDRESS 0x68  // This is the I2C address


// Global Variables

int command = 0;       // This is the command char, in ascii form, sent from the serial port     
int i;
long previousMillis = 0;        // will store last time Temp was updated
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
byte test; 
  
// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
  return ( (val/10*16) + (val%10) );
}

// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return ( (val/16*10) + (val%16) );
}

// 1) Sets the date and time on the ds1307
// 2) Starts the clock
// 3) Sets hour mode to 24 hour clock
// Assumes you're passing in valid numbers, Probably need to put in checks for valid numbers.
 
void setDateDs1307()                
{

   second = (byte) ((Serial.read() - 48) * 10 + (Serial.read() - 48)); // Use of (byte) type casting and ascii math to achieve result.  
   minute = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
   hour  = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
   dayOfWeek = (byte) (Serial.read() - 48);
   dayOfMonth = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
   month = (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
   year= (byte) ((Serial.read() - 48) *10 +  (Serial.read() - 48));
   Wire.beginTransmission(DS1307_I2C_ADDRESS);
   Wire.send(0x00);
   Wire.send(decToBcd(second));    // 0 to bit 7 starts the clock
   Wire.send(decToBcd(minute));
   Wire.send(decToBcd(hour));      // If you want 12 hour am/pm you need to set
                                   // bit 6 (also need to change readDateDs1307)
   Wire.send(decToBcd(dayOfWeek));
   Wire.send(decToBcd(dayOfMonth));
   Wire.send(decToBcd(month));
   Wire.send(decToBcd(year));
   Wire.endTransmission();
}

// Gets the date and time from the ds1307 and prints result
void getDateDs1307()
{
  // Reset the register pointer
  Wire.beginTransmission(DS1307_I2C_ADDRESS);
  Wire.send(0x00);
  Wire.endTransmission();

  Wire.requestFrom(DS1307_I2C_ADDRESS, 7);

  // A few of these need masks because certain bits are control bits
  second     = bcdToDec(Wire.receive() & 0x7f);
  minute     = bcdToDec(Wire.receive());
  hour       = bcdToDec(Wire.receive() & 0x3f);  // Need to change this if 12 hour am/pm
  dayOfWeek  = bcdToDec(Wire.receive());
  dayOfMonth = bcdToDec(Wire.receive());
  month      = bcdToDec(Wire.receive());
  year       = bcdToDec(Wire.receive());
  
  Serial.print(hour, DEC);
  Serial.print(":");
  Serial.print(minute, DEC);
  Serial.print(":");
  Serial.print(second, DEC);
  Serial.print("  ");
  Serial.print(month, DEC);
  Serial.print("/");
  Serial.print(dayOfMonth, DEC);
  Serial.print("/");
  Serial.print(year, DEC);

}


void setup() {
  Wire.begin();
  Serial.begin(57600);
 
}

void loop() {
     if (Serial.available()) {      // Look for char in serial que and process if found
      command = Serial.read();
      if (command == 84) {      //If command = "T" Set Date
       setDateDs1307();
       getDateDs1307();
       Serial.println(" ");
      }
      else if (command == 81) {      //If command = "Q" RTC1307 Memory Functions
        delay(100);     
        if (Serial.available()) {
         command = Serial.read(); 
         if (command == 49) {      //If command = "1" RTC1307 Initialize Memory - All Data will be set to 255 (0xff).  Therefore 255 or 0 will be an invalid value.  
          Wire.beginTransmission(DS1307_I2C_ADDRESS); // 255 will be the init value and 0 will be cosidered an error that occurs when the RTC is in Battery mode.
          Wire.send(0x08); // Set the register pointer to be just past the date/time registers.
         for (i = 1; i <= 27; i++) {
             Wire.send(0xff);
            delay(100);
         }   
         Wire.endTransmission();
         getDateDs1307();
         Serial.println(": RTC1307 Initialized Memory");
         }
         else if (command == 50) {      //If command = "2" RTC1307 Memory Dump
          getDateDs1307();
          Serial.println(": RTC 1307 Dump Begin");
          Wire.beginTransmission(DS1307_I2C_ADDRESS);
          Wire.send(0x00);
          Wire.endTransmission();
          Wire.requestFrom(DS1307_I2C_ADDRESS, 64);
          for (i = 1; i <= 64; i++) {
             test = Wire.receive();
             Serial.print(i);
             Serial.print(":");
             Serial.println(test, DEC);
          }
          Serial.println(" RTC1307 Dump end");
         } 
        }  
       }
      Serial.print("Command: ");
      Serial.println(command);     // Echo command CHAR in ascii that was sent
      }
      
      command = 0;                 // reset command 
      delay(100);
    } 

 

X-Dron
Offline
Зарегистрирован: 24.01.2015

Точно без модуля??? И функции
void getDateDs1307() и void setDateDs1307()
ни в какое устройство с адресом 0x68 по I2C-шине не стучатся?

Snake
Offline
Зарегистрирован: 20.04.2015

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

например запрашиваем и получаем 11:02:41, след. запрос может выдать 02:41:41, потом опять всё хорошо... так же может выдать минусовую температуру

X-Dron
Offline
Зарегистрирован: 24.01.2015

А проблема точно со считыванием, а не выводом на устройство отображения.
Траблы с выводом на LCD были. Из-за неправильного формата вывода - ставые символы не затирались, поэтому после 24-х часов могли быть 20, а к утру и 29. :).
У меня есть готовое универсальное решение для DS3231. Красота его в том, что библиотека сама формирует строку для вывода не экран или в монитор. Но также есть возможность оперировать с целочисленными значениями разрядов времени.

Snake
Offline
Зарегистрирован: 20.04.2015

нет, делал в сериал вывод, та же беда, копнув глубже, понял что дату ему неверную выставлял (год получался 2258), то ли лишние данные забивались после года, привел дату в порядок, баги исчезли :)

bwn
Offline
Зарегистрирован: 25.08.2014

X-Dron пишет:

У меня есть готовое универсальное решение для DS3231. Красота его в том, что библиотека сама формирует строку для вывода не экран или в монитор. Но также есть возможность оперировать с целочисленными значениями разрядов времени.

Це реклама? Или готовы поделиться? )))

 

X-Dron
Offline
Зарегистрирован: 24.01.2015

Дак уже. Легко ищется в поиск "DS3231 X-Dron". 2-я ссылка.
Копирую еще раз с объединением двух постов.
 

для DS3231 предлагаю такой подход и модифицированную библиотеку.
http://forum.amperka.ru/threads/%D0%A7%D0%B0%D1%81%D1%8B-ds-3231.4688/#post-37252
Еще пример использования
http://forum.amperka.ru/threads/rtc1307-%D0%B4%D0%BE%D0%B1%D0%B0%D0%B2%D0%B8%D1%82%D1%8C-%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D1%83-%D0%BA%D0%BD%D0%BE%D0%BF%D0%BA%D0%B0%D0%BC%D0%B8.4742/#post-37869
БОльшая часть кода по моей первой ссылке разжевана здесь
http://wiki.amperka.ru/%D0%B2%D1%80%D0%B5%D0%BC%D1%8F:rtc-%D0%B8%D0%BD%D0%B4%D0%B8%D0%BA%D0%B0%D1%82%D0%BE%D1%80
Я просто адаптировал его под DS3231.
Вторая ссылка - просто пример того, что можно сделать на основе ядра по первой ссылке.

 

 

bwn
Offline
Зарегистрирован: 25.08.2014

X-Dron, пасибки.

sergei7302
Offline
Зарегистрирован: 29.08.2015

nestandart пишет:

Прикупил я , недавно , LCD диплей и решил его протестить (поиграться).
В итоге получились часы.
Решил выложить , вдруг кому пригодиться.
Часы имеют одну кнопку (вторую лень было припаивать).
По хорошему надо четыре кнопки : часы +/- , минуты +/-.
у мя только минуты +.

Вот программа для ***дуино IDE.

#include <LiquidCrystal.h>//библиотека для работы с LCD
 long previousMillis = 0;//переменная для хранения значений таймера
 
 int sek=0;//значение секунд
 int min=0;//значение минут
 int chas=0;//значение часов
 int knopka=0;//значение кнопки

LiquidCrystal lcd(12, 10, 5, 4, 3, 2);//инициализация портов

void setup()
{
 
 lcd.begin(16, 2);//иницифлизация дисплея
 pinMode(8,INPUT);//инициализация портов
 pinMode(9,OUTPUT);//инициализация портов
 pinMode(13,OUTPUT);//инициализация портов
 
}

void loop()
{
   if (millis() - previousMillis >500) 
 {  
   previousMillis = millis();  //запучкаем таймер
   digitalWrite(13, !digitalRead(13));//меняем значение порта каждые 0.5секунд
   
   
   
   
   if(digitalRead(13)==HIGH)//если 13 нога лог1 то...
   {
     sek++;//переменная секунда + 1
     lcd.clear(); //обновление дисплея
     
     
     
     
     //вывод символов на дисплей//
     lcd.setCursor(6, 1);
     lcd.print(":");//выводим символ ":"между часами и минутами
     lcd.setCursor(9, 1);
     lcd.print(":");//выводим символ ":"между  минутами и секундами
   }
   
   if(digitalRead(13)==LOW)
   {
     //через каждые 0.5 секунд меняем символ ":" на "."
     lcd.setCursor(9, 1);
     lcd.print(".");
     lcd.setCursor(6, 1);
     lcd.print(".");
   }
   
   if(sek>59)//если переменная секунда больше 59 ...
   {
     sek=0;//сбрасываем ее на 0
     min++;//пишем +1 в переменную минута
   }
   
   
   if(min>59)//если переменная минута больше 59 ...
   {
     min=0;//сбрасываем ее на 0
     chas++;//пишем +1 в переменную час
   }
   
   
   
   
   if(chas>23)//если переменная час больше 23 ...
   {
     chas=0;//сбрасываем ее на 0
    
   }
   
   
   
   //вывод символов на дисплей//
   
   lcd.setCursor(4, 1);//выводим значение часов в строку - 1
   //столбец -4 
   lcd.print(chas);//количество часов
   
   lcd.setCursor(7, 1);//выводим значение часов в строку - 1
   //столбец -7 
   lcd.print(min);//количество минут
   
   lcd.setCursor(10, 1);//выводим значение часов в строку - 1
   //столбец -10 
   lcd.print(sek);//количество секунд
   
   
 } 
 
 
      //установка времени//
          //кнопка//
 
 
   
   if(digitalRead(8)==HIGH&&knopka==0)//если кнопка нажата 
   // и перемення "knopka" равна 0 , то ...
   {
      min++;//пишем + 1 в переменную минута
      sek=0;//пишем 0 в переменную секунда
      knopka++;//пишем 1 в переменную кнопка
      //это нужно для того что бы с каждым нажатием кнопки
      //происходило только одно действие
      // плюс защита от "дребезга"
      
      
      
      //с каждым нажатием кнопки обновляем значения на дисплее//
      lcd.setCursor(4, 1);
   lcd.print(chas);
   
   lcd.setCursor(7, 1);
   lcd.print(min);
   
   lcd.setCursor(10, 1);
   lcd.print(sek);
   }
   
    if(digitalRead(8)==LOW&&knopka==1)//если кнопка НЕ нажата
   //и переменная knopka равна - 1 ,то ...
   {
      
      knopka=0;//обнуляем переменную "knopka"
   }
   
 
 //выводим надпись "CHASI." в строку - 0 
 lcd.setCursor(5, 0);
 lcd.print("CHASI.");
 
 
}

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

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