Официальный сайт компании Arduino по адресу arduino.cc
Часы и ардуино
- Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии
Вс, 11/09/2011 - 22:04
Прикупил я , недавно , 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."); }
Скину это дело в "архив жизненного опыта" - может пригодится в будущем.
Например для изготовления некой системы , функции которой включаются/выключаются
по часам реального времени.
Навешать кучу кнопок для быстрого програмирования , сделать менюшку ну и т.д.
Спасибо. Очень кстати.
Во время тестирования выяснил что они отстают на 10-15 секунд в час.
Разберусь в чем дело - отпишу.
Чтобы часы работали точнее нужно работать с микросекундами .
Огромное спасибо за код =)
немного переправил под себя, чтобы:
буду проверять точность при работе с микросекундами:
Более менее точный ход получил при вот таком значении микросекунд 499210.
За пять часов сбились не более чем на 10 секунд. Нужно экспериментировать дальше.
Чтобы часы работали точнее нужно работать с микросекундами .
Чтобы часы работали более-менее точно, надо брать ровно полсекунды, а условие
if (millis() - previousMillis >500)
срабатывает при разности = 501, 502 и т.д. То есть отставание 2 миллисекунды за секунду заложено программно - погрешность хода только за счет неправильного условия составляет 0,2% или 7 секунд в час.
Эта строка должна как минимум иметь вид
if (millis() - previousMillis >= 500)
А вообще - для правильного отсчета тиков следует задействовать механизм прерываний от таймера. Что-то вроде
void ISR(TIMER0_OVF_vect) {
// подсчет тиков
}
Всем привет =)
зашел написать, что в предыдущий код еще неплохо бы добавить строку
в самое начало цикла loop ( в строку 17 в последнем примере) для случая обнуления micros через 50 дней
Подскажите, пожалуйста, что это за функция:
где можно про нее почитать
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 (millis() - previousMillis >500) то срабатывает приразности = 501, 502 и т.д.
тогда если код: if (millis() - previousMillis >= 500) то срабатывать будет при разности = 500, 501, 502
погрешность ведь никуда не девается, просто событие наступает раньше ...
раз уж нужно точное значение, может тогда строчка должна иметь вид:
?
Здесь все зависит от длительности главного цикла (того, что крутится в loop()).
Если длительность его отработки составляет доли миллисекунды, то в исходном варианте отставание будет на миллисекунду и больше в каждой полусекунде работы часов (срабатывание процедуры обработки события "полсекунды" происходит после того, как прошло больше 500 миллисекунд после предыдущего срабатывания - т.е. 501 плюс какая-то дельта, находящаяся в диапазоне от 0 мкс до длительности главного цикла).
В предложенном мной варианте срабатывание процедуры обработки события "полсекунды" происходит через пятьсот миллисекунд плюс та самая упомянутая выше дельта.
Почему >=? Если длительность цикла составляет миллисекунду и больше, то возможна ситуация, когда при предыдущей проверке мы получили время с последнего срабатывания 499, а при последующей проверке - уже 501. Т.е. часы просто остановятся - условие millis() - previousMillis==500 больше никогда не выполнится (точнее, может выполниться через 4294967296 миллисекунд - когда произойдет переполнение результата вычитания типа long int, а это почти 50 суток, т.е. вечность по меркам микроконтроллеров). Соответственно необходимо предусмотреть принудительное завершение "полусекунды" даже если уже образовалась задержка в одну и более миллисекунд.
Ну и теперы вы сами можете сказать, что неправильно в вашем варианте. Он заведомо невыполним, т.к. длительность главного цикла гарантированно больше 1 мкс (=16 команд). Через пару-тройку удачных срабатываний (а может быть и гораздо раньше) вы проскочите-таки удачную проверку и разница miсros() - prevmicros будет увеличиваться до бесконечности (точнее, до вышеупомянутых четырех с копейками миллиардов), а условие будет оставаться ложным
для случая обнуления micros через 50 дней
Примерно через час с четвертью (4294967296/1000000/60/60). 50 дней - это для millis ( 4294967296/1000/60/60/24)
Подскажите, пожалуйста, что это за функция:
где можно про нее почитать
Когда прекомпилятор 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.
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/ - самодельный
Как мне кажется, код вывода ведущего нуля можно немножко оптимизировать, а именно:
Здесь ELSE не нужно, т.к. код lcd.print(chas) выполняется в любом случае. Т.е. все будет работать если написать и вот так:
Т.е. если часы больше 0 и меньше 10 сначала напечатается 0, а потом часы. Если часы больше 9 то ноль печататься не будет. ИМХО так.
если не трудно, может быть приведете пример скетча часов, используюя функцию ISR?
Вот тут почитайте - на русском языке - о настройке таймера/счетчика 2 для отсчета времени с использованием ISR.
to Coder: дополнительно можно сэкономить на фигурных скобках - для одной инструкции они не нужны, а также с учетом того, что переменная chas может принимать значения в диапазоне 0-23 (т.е. не принимает отрицательные значения), то и условие подсократить:
я хотел попробовать скетч из примера DataTime
вот страница примера
в монитор ничего не выводит :(
А у вас в мониторе какая скорость передачи стоит? По умолчанию (9600) или 19200 выставили?
А у вас в мониторе какая скорость передачи стоит? По умолчанию (9600) или 19200 выставили?
и в скетче и в мониторе выставлено 38400...
В функции loop() перед последней фигурной скобкой поставьте
Если появится вывод в терминале, значит DataTime не функционирует.
Если не появится - зацикливание в getPCTime().
Если появится вывод в терминале, значит DataTime не функционирует.
Появился вывод :(
ПОчему DataTime может не функционировать? библиотеки все есть...
К библиотекам, хоть и редко, но прикладываются мануалы. Они читались?
Кстати, раз уж библиотеки в наличии, выложите коды функции DateTime.available().
в общем посомтрел библиотеку и сейчас сделал вывод такой для себя...запускаю скетч потом запускаю пример скетча в процессинге и уже в нем вывод идет
15:37:33 Tuesday March 6
2551331048253
использовал ваш скетч за основу для часов на семисегментном дисплее. все получилось работает, но вопрос к знатокам как можн обыло бы оптимизировать программу. я не имею в виду управлять напрямую портами а в плане принципа постоения программы. программирование только осваиваю. led индикатор с общим анодом
Я бы myArray вынес из функции и вместо boolean сделал бы байтовый массив. Памяти будет есть поменьше.
SEGMENT* можно поместить в массив и при выводе использовать цикл. Также цикл можно будет использовать при инициализации.
Но, в принципе, этого всего можно и не делать.
можно показать как именно вы бы сделали?
не меняя программу заменил в массиве boolean на byte размер программы не изменился
и еще. часы допустим работают. как бы вы сделали что то вроде будильника. в определенное время повернуть шаговый двигатель в одну сторону, потом в другую, дальше ожидание до следующего будильника. зажечь светодиод в нужное время не проблема. вопрос пока будет управляться шаговый двигатель не повлияет ли это на работу часов?
вопрос пока будет управляться шаговый двигатель не повлияет ли это на работу часов?
вы пока на дисплей информацию выводите у вас часы идут? ну так и с двигателем они будут идти если вы не остановите внутренний таймер...
вы пока на дисплей информацию выводите у вас часы идут? ну так и с двигателем они будут идти если вы не остановите внутренний таймер...
да идут. все отлично. хорошо вечермо если время будет проверю что да как
вы пока на дисплей информацию выводите у вас часы идут? ну так и с двигателем они будут идти если вы не остановите внутренний таймер...
да идут. все отлично. хорошо вечермо если время будет проверю что да как
аммм в смысле они идти будут внутри, а вот на дисплее они могут и подмерзнуть пока вы там код уйдет двигателем шуршать...
вы пока на дисплей информацию выводите у вас часы идут? ну так и с двигателем они будут идти если вы не остановите внутренний таймер...
да идут. все отлично. хорошо вечермо если время будет проверю что да как
аммм в смысле они идти будут внутри, а вот на дисплее они могут и подмерзнуть пока вы там код уйдет двигателем шуршать...
вот и я о том. что будет на дисплее пока мотором буду крутить. значит мотор нельзя загонять в цикл
если не трудно, может быть приведете пример скетча часов, используюя функцию ISR?
Вот тут почитайте - на русском языке - о настройке таймера/счетчика 2 для отсчета времени с использованием ISR.
Сделал по тому примеру, отставание 40 минут за ночь :(, правда в скетче еще опрос цифрового датчика температуры, но там задержки все убраны и опрашивается раз в секунду.
правда в скетче еще
"Дьявол скрывается в мелочах" (c)
Я два месяца бился со странными спорадическими сбоями в UART-библиотеке. В конце концов обнаружил, что в погоне за эффективностью увеличил размер буфера со 128 до 256 байт. А указатели остались типа char. А в библиотеке несколько проверок типа if(pointer==MAXBUFSIZE)...
Может быть, и у вас при добавлении своих функций случилось нечто подобное?
Причесал немного код, поменял местами условия, операторы...вроде синхронно, понаблюдаю еще.
Не замечал но, ардуинка подвисала, сейчас вроде стабильна.
По настройке времени, не подскажите как переписать вот это:
дибеляче выглядит :(
lcd keyboard shield
Для начала - почитайте, чем отличаются операторы & и && (ну, и | и || соответственно), т.е. битовые и логические операторы.
Поправил, хотя до этого работало, чисто интуитивно пишу & а не &&
спасибо за ваши размышления о точности хода, мне даже не лень стало зарегистрироваться и рассказать, как на самом деле можно сделать программные часы, которые потом даже можно более-менее точно настроить, в отличие от часов на DS1307, которые можно настроить только плясками вокруг кварца.
такие часы будут правильно работать, даже если в программе будут встречаться случайные задержки длительностью до 70 минут.
пока что мне удалось добиться точности хода +- секунда в день. при correction = 0 часы врали очень сильно, десятки секунд в сутки, это уже изза неточности кварца.
если сделать программу синхронизации времени по com порту, возможно удастся подобрать более точное значение correction и добиться максимальной точности, которая возможна без коррекции по температуре итд.
Спасибо за код, сегодня поставлю на ночь потестировать, но думаю всё будет хорошо.
Однако есть один момент, который не даст обработать "случайные задержки длительностью до 70 минут": переменная секунд имеет размерность байт, а это всего 255 секунд, после чего она переполнится, что будет иметь непредсказуемые последствия
да, правильно. в случае типа переменной byte могут быть только случайные задержки длительностью до 255 секунд.
EvilSpirit, легко согласился, а зря.
"Тики" часов не потеряются, даже если цикл выполняется дольше 255 секунд. Таймер ведь в это время продолжает считать, поэтому все пропущенные тики не пропадут, а добавятся к счётчику секунд позже.
Именно поэтому надо писать "start += 1000000", а не "start = micros()", тут всё правильно.
-------
Update:
Посмотрел внимательней. Всё же, чтобы ничего не терялось, код придётся немного изменить.
Я бы сделал как-нибудь так:
Я про то и писал, что инкримент и будет выполняться и после 255 секунд будет 0 секунд, 1,2... Т.е. потеряется 255 секунд.
256, если уж говорить совсем точно :)
Но в последнем кусочке кода всё должно работать. Жалко, проверить не могу, нет сейчас под рукой ардуины. Так что - сгораю от нетерпения, жду результатов тестирования.
А у меня проблемка при тестировании, при открытии монитора порта, ардуина сбрасывается, это ведь лечится отключением автосброса становкой 120 омного сопротивления между 5 вольтами и пином reset?
Datak
тики то не потеряются, а вот минуты потеряются. а вот в твоем случае все действительно будет работать как надо
еще такие жуткие баги есть в реализации управления шагового двигателя, который в стандартных примерах в ардуино ide.
со отсчетом времени всегда беда. у всех программистов. только некоторые еще не знают и не задумываются, а я вот даже статью накатал http://www.gamedev.ru/pages/fadetoblack/articles/timing_discretization
Еще 1 варриант . Часы без модуля.
Точно без модуля??? И функции
void getDateDs1307() и void setDateDs1307()
ни в какое устройство с адресом 0x68 по I2C-шине не стучатся?
а никто не сталкивался с косяком в DS3231, при считывании даты, байты иногда "съежжают" в какую-нибудь сторону...
например запрашиваем и получаем 11:02:41, след. запрос может выдать 02:41:41, потом опять всё хорошо... так же может выдать минусовую температуру
А проблема точно со считыванием, а не выводом на устройство отображения.
Траблы с выводом на LCD были. Из-за неправильного формата вывода - ставые символы не затирались, поэтому после 24-х часов могли быть 20, а к утру и 29. :).
У меня есть готовое универсальное решение для DS3231. Красота его в том, что библиотека сама формирует строку для вывода не экран или в монитор. Но также есть возможность оперировать с целочисленными значениями разрядов времени.
нет, делал в сериал вывод, та же беда, копнув глубже, понял что дату ему неверную выставлял (год получался 2258), то ли лишние данные забивались после года, привел дату в порядок, баги исчезли :)
У меня есть готовое универсальное решение для DS3231. Красота его в том, что библиотека сама формирует строку для вывода не экран или в монитор. Но также есть возможность оперировать с целочисленными значениями разрядов времени.
Це реклама? Или готовы поделиться? )))
Дак уже. Легко ищется в поиск "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.
Вторая ссылка - просто пример того, что можно сделать на основе ядра по первой ссылке.
X-Dron, пасибки.
Прикупил я , недавно , LCD диплей и решил его протестить (поиграться).
В итоге получились часы.
Решил выложить , вдруг кому пригодиться.
Часы имеют одну кнопку (вторую лень было припаивать).
По хорошему надо четыре кнопки : часы +/- , минуты +/-.
у мя только минуты +.
Вот программа для ***дуино IDE.
Скину это дело в "архив жизненного опыта" - может пригодится в будущем.
Например для изготовления некой системы , функции которой включаются/выключаются
по часам реального времени.
Навешать кучу кнопок для быстрого програмирования , сделать менюшку ну и т.д.
А как подключить десплей к Ардуино?????