Современная мебель

Простые многофункциональные часы на трех светодиодных матрицах. Делаем электронные часы на ардуино своими руками Радиокот часы на светодиодных матрицах

Многие радиолюбители, начинающие и не только любят «изобретать велосипед» — строить СВОИ электронные часы. Не обошла эта участь и меня. Конструкций часов в инете сегодня конечно предостаточно, но вот часов на светодиодных матрицах почему-то среди них единицы. В русскоговорящем интернете я нашел только одну полностью законченную конструкцию. В тоже время, светодиодные матрицы сейчас очень сильно подешевели, и их стоимость не выше, а то и ниже, чем у семисегментных индикаторов такого же размера. Например примененные мной GNM23881AD при размере 60х60мм были куплены за 1,5уе (3 индикатора обошлись в 4,5уе) за эти деньги вы вряд ли купите четыре семисегментника таких-же размеров). А вот информации, разместить на матричном индикаторе, можно намного больше. Кроме цифр на них можно отображать буквы, знаки, а с помощью бегущей строки еще и текст. Исходя из этого, появилось желание построить часы на светодиодных матрицах, но чтоб схема при этом получилась не сложнее чем на семисегментниках. Также хотелось чтоб она была достаточно функциональна и не похожа на другие. Так родилась следующая схема.

Функционал у часов такой:

  • Отсчет времени, календарь, день недели. (високосный год учитывается, переход на летнее/зимнее время не осуществляется).
  • Сохранение хода часов при пропадании внешнего питания (потребление составляет 15мка).
  • Коррекция хода + — 59,9сек\сутки, с шагом 0,1сек.
  • 9 будильников. 3 из которых «одноразовые», и 6 «постоянных», индивидуально настраиваемых по дням недели.
  • Индивидуально настраиваемая длительность звукового сигнала каждого будильника (1-15мин).
  • Звуковое подтверждение нажатия кнопок (возможно отключить).
  • Ежечасный звуковой сигнал (возможно отключить). С 00-00 до 08-00 сигнал не подаётся.
  • 1 или 2 датчика температуры (Улица и дом).
  • Настраиваемая бегущая строка, посредством которой выводится вся информация (кроме времени)
  • Значение коррекции хода, и настройки «бегущей строки» — сохраняются даже при пропадании резервного питания.

«Сердцем» часов выбрана AtMega16A, из-за её доступности, дешевизны и «ногастости». Схему хотелось максимально упростить, поэтому все что можно, было возложено на контроллер. В результате удалось обойтись всего двумя микросхемами, контроллером и регистром с мощными выходами TPIC6B595. Если кому то недоступен TPIC6B595, то можно его заменить на 74НС595 + ULN2803. Оба варианта были опробованы. Так же можно попробовать применить TPIC6С595, она немного слабовата, и слегка грелась, но в целом работала стабильно. Отсчет времени производится с помощью асинхронного тайме – Т2. Ход часов продолжается и при пропадании питания. В это время бОльшая часть схемы обесточена, и только контроллер получает питание от батарейки, аккумулятора, или от ионистора. Мне было интересно «по играться» с ионистором, поэтому применил его. Ток потребления часами в дежурном режиме составляет 15мка. При питании от ионистора на 1Ф, часы «продержались» четверо суток. Этого вполне достаточно для поддержания хода во время перебоев питания. Если применить батарейку СR2032, то теоретически, по расчетам заряда должно хватить на 1,5года. Наличие сетевого напряжения контроллер «слушает» через вывод РВ.3. Напряжение питания, через делитель R2-R3 подается на вывод РВ.3, и в нормальном состоянии равно примерно 1,5в. Если внешнее напряжение упадет ниже 4,1 вольта, то напряжение на выводе РВ.3 станет меньше 1,23вольта, при этом сгенерируется прерывание от компаратора, и в обработчике этого прерывания выключаются все «лишние» узлы контроллера и сам контроллер усыпляется. В этом режиме продолжает работать только отсчитывающий время таймер Т2. При появлении внешнего питания, напряжение на РВ.3 сново подымится выше 1,23в, контроллер «увидев» это, переведет все узлы в рабочее состояние. Если вместо ионистора, будет использоваться батарейка СR2032, то её нужно подключить через диод(предпочтительно диод шоттки). Анод диода подключается к + батарейки, а катод к катоду VD1.

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

9 будильников разделены на 3 одноразовых и 6 многоразовых. При включении будильников 1-3, они срабатывают только один раз. Для того чтоб они сработали еще раз, их нужно повторно включать вручную. А будильники 4-9 многоразовые, т.е. они будут срабатывать ежедневно, в установленное время. Кроме того эти будильники можно настроить на сработку только в определенные дни недели. Это удобно, например если не хотите чтоб будильник разбудил Вас в выходные. Или например Вам нужно просыпаться в будние дни в 7-00, а в четверг в 8-00, а на выходных будильник не нужен. Тогда настраиваем один многоразовый на 7-00 в понедельник-среду и пятницу, а второй на 8-00 в четверг….. Кроме того все будильники имеют настройку длительности сигнала, и если Вам, для того чтоб проснуться, мало сигнала в течении 1 минуты, то можно увеличить его на время от 1 до 15мин.

Коррекция хода производится один раз в сутки, в 00-00. Если часы спешат к примеру на 5 сек в сутки, то в 00-00-00 время установится в 23-59-55, если же часы отстают на 5 сек, то в 00-00-00 время установится в 00-00-05. Шаг коррекции – 0,1 сек. Максимальная коррекция – 59,9 сек/сутки. С исправным кварцем больше вряд ли понадобиться. Коррекция осуществляется и в дежурном режиме при питании от батареи.

Светодиодные матрицы можно использовать любые 8*8 светодиодов с общим катодом. Как уже было указано, я применил GNM23881AD. В принципе можно «набрать» матрицу и из отдельных светодиодов. Микроконтроллер AtMega16a можно заменить на «старый» AtMega16 с буквой L. При этом, теоретически должен немного увеличится ток потребления от батарейки. Наверное будет работать и просто AtMega16, но могут возникнуть проблемы при работе от 3х вольтовой батарейки. Диод D1 — желательно любой диод шоттки. С обычным выпрямительным тоже работает, но чтоб обезопасить себя от различных глюков, связанных с тем что часть схемы питается напряжением «до диода», а часть «после диода» лучше поискать шоттки. Транзистор VT1 – любой n-p-n.

Управление часами осуществляется двумя кнопками. Их количество можно было довести до 8шт, не добавляя больше вообще ни одного компонента, кроме самих кнопок, но захотелось попробовать «выкрутится» всего двумя. Кнопки условно названы «ОК» и «ШАГ». Кнопкой «ШАГ» как правило происходит переход к следующему пункту меню, а кнопкой «ОК» изменение параметров текущего меню. Сигнал сработавшего будильника также выключается кнопками «ОК» или «ШАГ». Нажатие любой кнопки во время сигнала будильника отключает его. Схема управления получилась такой:

Конструктивно часы выполнены на одной ПП. Размер ПП соответствует размеру индикаторов. Минимальная ширина дорог ПП – 0,4мм, расстояние между – 0,4мм. Так что любители «ЛУТа» смогут без труда изготовить плату самостоятельно.

Все элементы — в SMD исполнении, и расположены с одной стороны платы. А индикаторы с другой. Получается миниатюрный монолитный блок, который легко встроить в какой ни будь небольшой плоский корпус.

Корпус спаян из стеклотекстолита, прошпаклеван и покрашен в цвет «спелая вишня». Стекло передней панели – обычное тонированное стекло.

Финальный результат.

Добавлена регулировка яркости. Схема не меняется. Регулировка яркости производится либо по времени, либо в зависимости от освещенности. Тип регулировки выбирается автоматически. Если подключен фоторезистор, то яркость индикаторов меняется в зависимости от освещенности, а если фоторезистор не подключен, то вручную устанавливаем время когда снижать яркость экрана, и когда восстанавливать обратно.

Настройка по времени производится так. В меню «НАСТРОЙКИ» после переопределения датчиков температуры (если датчик один, или вовсе нет, то после настройки скорости строки) мы попадаем в меню где устанавливаем время снижения яркости, и время восстановления, а также желаемый уровень снижения яркости.

Если фото датчик (фоторезистор) подключен, то после переопределения датчиков температуры мы попадем в меню «калибровки фоторезистора» В это время на экран выводится результат измерения напряжения на фоторезисторе. Нужно осветить датчик чем нибудь очень ярким и нажать кнопку «ОК». Программа запомнит это значение. Это делается для того чтоб отсечь «мертвую зону».

Фоторезистор подключается к выводу РА.7 Подойдет любой фоторезистор у которого световое и темновое сопротивление изменяется в 50 и более раз(у современных фоторезисторов как правило 500-2000). Резистор R4 должен быть примерно в 10 раз больше чем световое сопротивление фоторезистора. Если у вашего фоторезистора световое сопротивление меньше 0,5ком, нужно последовательно с ним включить обычный резистор, чтоб общее сопротивление стало 0,5ком.

СХЕМА ПОДКЛЮЧЕНИЯ ФОТОРЕЗИСТОРА.

Убрал глюки в работе датчиков ds18b20. периодически один, а то и два датчика «пропадали» (было не у всех)

На тему самодельных часов и обещал продолжения, так вот извольте. Часы на самодельных светодиодных матрицах.

Эти светодиоды были заказаны ещё ДО того как я решил делать часы на матрицах
Я заказал три разных цвета светодиодов у одного продавца: , и
Делалось это для того чтоб попробовать изготовить сегментные часы, из расчёта по три светодиода на сегмент 7*4*3=84+2=86 штук
Вот приблизительно по такому принципу:

Хотелось попробовать разный цвет и обязательно яркие (а как по другому то?)
По этому партии по 100 штук меня вполне устраивали, тем более цена вполне приемлемая, а так-же, заказал ещё в этом же магазине - сделать разноцветными цифры, чтоб можно было сравнить цвета на одном дисплее - какой будет красивее, ну и так - поприкалываться… :)

Товар заказан и оплачен был 20 августа 2016 , а прибыл 15 сентября 2016 , по нашим меркам достаточно оперативно. НО! Светодиоды красного цвета - не пришли! По окончанию срока защиты покупателя был открыт спор и продавец вернул деньги. Было просто жаль потраченного времени…

Товар пришёл в стандартном жёлтопупырчатом почтовом конверте, сами светодиоды были упакованы в отдельный целлофановый пакет, количество штук в синие, зелёные - было чуть больше 100 (точно сейчас уже не помню, но помню что было на 4-5 штук больше)
Все рабочие (т.е. светятся)
В тех, что разноцветные - было упаковано в отдельные пакетики, на которых было маркером помечен цвет R G B W Y - цвет везде соответствовал, и так-же по 2-3 лишних - создавалось впечатление, что так наверное у всех, но позже убедился, что не всегда так - бывает и наоборот…

Если бы не косяк с красным цветом, продавцу можно было-бы поставить жирную пятёрку с плюсом за щедрость.

С другой стороны, деньги возвращены - проблема просто в потерянном времени и обманутых ожиданиях
А ещё он прислал письмо, где он писал, что мол возвращает мне СВОИ деньги, и если товар придёт - просил вернуть ему эту сумму
Дословно:

Hi, my friend, I am now a refund, when you receive a package, please return my money,Please give me a five-star high praise.
На что ему пришлось накатать письмо, что это не ЕГО деньги, а просто он возвращает мне в результате спора МОИ деньги, т.к. я его товара так и не получил и при этом потерял много времени ожидая его товар

Кстати, когда пришли все остальные заказы от него, спустя 10 дней, я написал ему, что мол - переживаю, т.к. это странно - все пришли, а один из заказов не пришёл
На что он без всяких «хелоу френд» - сухо ответил «60 дней ещё не прошло»
А тут вдруг и «другом» стал, и вроде как ЕГО деньги поимел…

Но в общем-то - продавец свои обязанности выполняет, на письма реагирует, от отправленного товара - эмоции только положительные, деньги за не отправленное вернул - всё согласно ПРАВИЛ

Ну а теперь про то ради чего это приобреталось

Существует множество способов собрать электронные часы своими руками: схемы широко представлены в литературе и сети Интернет. Большинство современных реализаций построено на основе микроконтроллеров. Выполнение таких проектов зачастую требует обширных практических навыков и теоретических знаний в области электроники: умения пользоваться специализированным программным обеспечением, создавать в домашних условиях печатные платы методом травления в хлорном железе, хорошо паять. Также необходимо иметь множество инструментов и расходных материалов.

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

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

Шаг 1. Список компонентов

Чтобы собрать простые часы на светодиодных матрицах вам потребуется всего несколько дешёвых компонентов:

  • платформа Arduino. Подойдут самые простые модели - или Micro;
  • контактная макетная плата;
  • соединительные провода для макетной платы;
  • модуль часов реального времени Adafruit DS3231;
  • светодиодный матричный модуль 32x8 MAX7219;
  • две кнопки.

Также понадобится персональный компьютер и USB-mini-USB кабель для загрузки программы управления в память . Вот и всё - паяльник, щипцы для снятия изоляции, монтажные ножи и прочие профессиональные инструменты не нужны: все операции выполняются руками. Разве что в некоторых случаях удобнее использовать пинцет, но можно обойтись и без него.


Шаг 2. Сборка электронной схемы

Схема электронных часов с индикацией на светодиодах с применением Arduino даже для неопытных радиолюбителей покажется довольно простой. Для сборки требуется всего несколько проводников. Таблица подключений:

Модуль Arduino → светодиодная матрица 32x8 MAX7219

Модуль Arduino → часы реального времени Adafruit DS3231

Модуль Arduino → кнопки

D2 - кнопка 1

D3 - кнопка 2

Второй вывод кнопок соединяется с землёй GND.

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


Два ряда (1 и 4) с обеих сторон замкнуты горизонтально - обычно они используются как линия питания +5V и земля GND. Все внутренние контакты (2 и 3) замкнуты вертикально. При этом монтажная плата как вертикально, так и горизонтально разделена на две независимые друг от друга симметричные части. Это позволяет, например, собрать два разных устройства на одной плате.

Схема электронных часов с индикацией на светодиодах, а также расположение элементов на монтажной плате представлена на иллюстрации:

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


Шаг 3. Прошивка Arduino

После того как сборка и проверка схемы завершена, можно приступать к загрузке управляющей программы (или «прошивки») в память Arduino.


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

//include libraries: #include "LedControl.h" #include // Font library #include // DS1307 clock #include "RTClib.h" // DS1307 clock #include // Button library by Alexander Brevig // Setup LED Matrix // pin 12 is connected to the DataIn on the display // pin 11 is connected to the CLK on the display // pin 10 is connected to LOAD on the display LedControl lc = LedControl(6, 5, 4, 4); //sets the 3 pins as 12, 11 & 10 and then sets 4 displays (max is 8 displays) //global variables byte intensity = 7; // Default intensity/brightness (0-15) byte clock_mode = 0; // Default clock mode. Default = 0 (basic_mode) bool random_mode = 0; // Define random mode - changes the display type every few hours. Default = 0 (off) byte old_mode = clock_mode; // Stores the previous clock mode, so if we go to date or whatever, we know what mode to go back to after. bool ampm = 0; // Define 12 or 24 hour time. 0 = 24 hour. 1 = 12 hour byte change_mode_time = 0; // Holds hour when clock mode will next change if in random mode. unsigned long delaytime = 500; // We always wait a bit between updates of the display int rtc; // Holds real time clock output char days = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; //day array - used in slide, basic_mode and jumble modes (The DS1307 outputs 1-7 values for day of week) char daysfull = { "Sunday", "Monday", "Tuesday", "Wed", "Thursday", "Friday", "Saturday" }; char suffix = { "st", "nd", "rd", "th" }; //date suffix array, used in slide, basic_mode and jumble modes. e,g, 1st 2nd ... //define constants #define NUM_DISPLAY_MODES 3 // Number display modes (conting zero as the first mode) #define NUM_SETTINGS_MODES 4 // Number settings modes = 6 (conting zero as the first mode) #define SLIDE_DELAY 20 // The time in milliseconds for the slide effect per character in slide mode. Make this higher for a slower effect #define cls clear_display // Clear display RTC_DS1307 ds1307; // Create RTC object Button buttonA = Button(2, BUTTON_PULLUP); // Setup button A (using button library) Button buttonB = Button(3, BUTTON_PULLUP); // Setup button B (using button library) void setup() { digitalWrite(2, HIGH); // turn on pullup resistor for button on pin 2 digitalWrite(3, HIGH); // turn on pullup resistor for button on pin 3 digitalWrite(4, HIGH); // turn on pullup resistor for button on pin 4 Serial.begin(9600); //start serial //initialize the 4 matrix panels //we have already set the number of devices when we created the LedControl int devices = lc.getDeviceCount(); //we have to init all devices in a loop for (int address = 0; address < devices; address++) { /*The MAX72XX is in power-saving mode on startup*/ lc.shutdown(3-address, false); /* Set the brightness to a medium values */ lc.setIntensity(3-address, intensity); /* and clear the display */ lc.clearDisplay(3-address); } //Setup DS1307 RTC #ifdef AVR Wire.begin(); #else Wire1.begin(); // Shield I2C pins connect to alt I2C bus on Arduino #endif ds1307.begin(); //start RTC Clock if (! ds1307.isrunning()) { Serial.println("RTC is NOT running!"); ds1307.adjust(DateTime(__DATE__, __TIME__)); // sets the RTC to the date & time this sketch was compiled } //Show software version & hello message printver(); //enable red led digitalWrite(13, HIGH); } void loop() { //run the clock with whatever mode is set by clock_mode - the default is set at top of code. switch (clock_mode){ case 0: basic_mode(); break; case 1: small_mode(); break; case 2: slide(); break; case 3: word_clock(); break; case 4: setup_menu(); break; } } //plot a point on the display void plot (byte x, byte y, byte val) { //select which matrix depending on the x coord byte address; if (x >= 0 && x <= 7) { address = 3; } if (x >= 8 && x <= 15) { address = 2; x = x - 8; } if (x >= 16 && x <= 23) { address = 1; x = x - 16; } if (x >= 24 && x <= 31) { address = 0; x = x - 24; } if (val == 1) { lc.setLed(address, y, x, true); } else { lc.setLed(address, y, x, false); } } //clear screen void clear_display() { for (byte address = 0; address < 4; address++) { lc.clearDisplay(address); } } //fade screen down void fade_down() { //fade from global intensity to 1 for (byte i = intensity; i > 0; i--) { for (byte address = 0; address < 4; address++) { lc.setIntensity(address, i); } delay(30); //change this to change fade down speed } clear_display(); //clear display completely (off) //reset intentsity to global val for (byte address = 0; address < 4; address++) { lc.setIntensity(address, intensity); } } //power up led test & display software version number void printver() { byte i = 0; char ver_a = "MADE"; char ver_b = "IN"; char ver_c = "RUSSIA"; //test all leds. for (byte x = 0; x <= 32; x++) { for (byte y = 0; y <= 7; y++) { plot(x, y, 1); } } delay(300); fade_down(); while (ver_a[i]) { puttinychar((i * 4), 1, ver_a[i]); delay(35); i++; } delay(500); fade_down(); i = 0; while (ver_b[i]) { puttinychar((i * 4), 1, ver_b[i]); delay(35); i++; } delay(500); fade_down(); i = 0; while (ver_c[i]) { puttinychar((i * 4), 1, ver_c[i]); delay(35); i++; } delay(500); fade_down(); } // puttinychar // Copy a 3x5 character glyph from the myfont data structure to display memory, with its upper left at the given coordinate // This is unoptimized and simply uses plot() to draw each dot. void puttinychar(byte x, byte y, char c) { byte dots; if (c >= "A" && c <= "Z" || (c >= "a" && c <= "z")) { c &= 0x1F; // A-Z maps to 1-26 } else if (c >= "0" && c <= "9") { c = (c - "0") + 32; } else if (c == " ") { c = 0; // space } else if (c == ".") { c = 27; // full stop } else if (c == ":") { c = 28; // colon } else if (c == "\"") { c = 29; // single quote mark } else if (c == "!") { c = 30; // single quote mark } else if (c == "?") { c = 31; // single quote mark } for (byte col = 0; col < 3; col++) { dots = pgm_read_byte_near(&mytinyfont[c]); for (char row = 0; row < 5; row++) { if (dots & (16 >> row)) plot(x + col, y + row, 1); else plot(x + col, y + row, 0); } } } void putnormalchar(byte x, byte y, char c) { byte dots; // if (c >= "A" && c <= "Z" || (c >= "a" && c <= "z")) { // c &= 0x1F; // A-Z maps to 1-26 // } if (c >= "A" && c <= "Z") { c &= 0x1F; // A-Z maps to 1-26 } else if (c >= "a" && c <= "z") { c = (c - "a") + 41; // A-Z maps to 41-67 } else if (c >= "0" && c <= "9") { c = (c - "0") + 31; } else if (c == " ") { c = 0; // space } else if (c == ".") { c = 27; // full stop } else if (c == "\"") { c = 28; // single quote mark } else if (c == ":") { c = 29; // clock_mode selector arrow } else if (c == ">") { c = 30; // clock_mode selector arrow } else if (c >= -80 && c <= -67) { c *= -1; } for (char col = 0; col < 5; col++) { dots = pgm_read_byte_near(&myfont[c]); for (char row = 0; row < 7; row++) { //check coords are on screen before trying to plot //if ((x >= 0) && (x <= 31) && (y >= 0) && (y <= 7)){ if (dots & (64 >> row)) { // only 7 rows. plot(x + col, y + row, 1); } else { plot(x + col, y + row, 0); } //} } } } //small_mode //show the time in small 3x5 characters with seconds display void small_mode() { char textchar; // the 16 characters on the display byte mins = 100; //mins byte secs = rtc; //seconds byte old_secs = secs; //holds old seconds value - from last time seconds were updated o display - used to check if seconds have changed cls(); //run clock main loop as long as run_mode returns true while (run_mode()) { get_time(); //check for button press if (buttonA.uniquePress()) { switch_mode(); return; } if (buttonB.uniquePress()) { display_date(); return; } //if secs changed then update them on the display secs = rtc; if (secs != old_secs) { //secs char buffer; itoa(secs, buffer, 10); //fix - as otherwise if num has leading zero, e.g. "03" secs, itoa coverts this to chars with space "3 ". if (secs < 10) { buffer = buffer; buffer = "0"; } puttinychar(20, 1, ":"); //seconds colon puttinychar(24, 1, buffer); //seconds puttinychar(28, 1, buffer); //seconds old_secs = secs; } //if minute changes change time if (mins != rtc) { //reset these for comparison next time mins = rtc; byte hours = rtc; if (hours > < 1) { hours = hours + ampm * 12; } //byte dow = rtc; // the DS1307 outputs 0 - 6 where 0 = Sunday0 - 6 where 0 = Sunday. //byte date = rtc; //set characters char buffer; itoa(hours, buffer, 10); //fix - as otherwise if num has leading zero, e.g. "03" hours, itoa coverts this to chars with space "3 ". if (hours < 10) { buffer = buffer; //if we are in 12 hour mode blank the leading zero. if (ampm) { buffer = " "; } else { buffer = "0"; } } //set hours chars textchar = buffer; textchar = buffer; textchar = ":"; itoa (mins, buffer, 10); if (mins < 10) { buffer = buffer; buffer = "0"; } //set mins characters textchar = buffer; textchar = buffer; //do seconds textchar = ":"; buffer; secs = rtc; itoa(secs, buffer, 10); //fix - as otherwise if num has leading zero, e.g. "03" secs, itoa coverts this to chars with space "3 ". if (secs < 10) { buffer = buffer; buffer = "0"; } //set seconds textchar = buffer; textchar = buffer; byte x = 0; byte y = 0; //print each char for (byte x = 0; x < 6 ; x++) { puttinychar(x * 4, 1, textchar[x]); } } delay(50); } fade_down(); } // basic_mode() // show the time in 5x7 characters void basic_mode() { cls(); char buffer; //for int to char conversion to turn rtc values into chars we can print on screen byte offset = 0; //used to offset the x postition of the digits and centre the display when we are in 12 hour mode and the clock shows only 3 digits. e.g. 3:21 byte x, y; //used to draw a clear box over the left hand "1" of the display when we roll from 12:59 -> 1:00am in 12 hour mode. //do 12/24 hour conversion if ampm set to 1 byte hours = rtc; if (hours > 12) { hours = hours - ampm * 12; } if (hours < 1) { hours = hours + ampm * 12; } //do offset conversion if (ampm && hours < 10) { offset = 2; } //set the next minute we show the date at //set_next_date(); // initially set mins to value 100 - so it wll never equal rtc on the first loop of the clock, meaning we draw the clock display when we enter the function byte secs = 100; byte mins = 100; int count = 0; //run clock main loop as long as run_mode returns true while (run_mode()) { //get the time from the clock chip get_time(); //check for button press if (buttonA.uniquePress()) { switch_mode(); return; } if (buttonB.uniquePress()) { display_date(); return; } //check whether it"s time to automatically display the date //check_show_date(); //draw the flashing: as on if the secs have changed. if (secs != rtc) { //update secs with new value secs = rtc; //draw: plot (15 - offset, 2, 1); //top point plot (15 - offset, 5, 1); //bottom point count = 400; } //if count has run out, turn off the: if (count == 0) { plot (15 - offset, 2, 0); //top point plot (15 - offset, 5, 0); //bottom point } else { count--; } //re draw the display if button pressed or if mins != rtc i.e. if the time has changed from what we had stored in mins, (also trigggered on first entering function when mins is 100) if (mins != rtc) { //update mins and hours with the new values mins = rtc; hours = rtc; //adjust hours of ampm set to 12 hour mode if (hours > 12) { hours = hours - ampm * 12; } if (hours < 1) { hours = hours + ampm * 12; } itoa(hours, buffer, 10); //if hours < 10 the num e.g. "3" hours, itoa coverts this to chars with space "3 " which we dont want if (hours < 10) { buffer = buffer; buffer = "0"; } //print hours //if we in 12 hour mode and hours < 10, then don"t print the leading zero, and set the offset so we centre the display with 3 digits. if (ampm && hours < 10) { offset = 2; //if the time is 1:00am clear the entire display as the offset changes at this time and we need to blank out the old 12:59 if ((hours == 1 && mins == 0)) { cls(); } } else { //else no offset and print hours tens digit offset = 0; //if the time is 10:00am clear the entire display as the offset changes at this time and we need to blank out the old 9:59 if (hours == 10 && mins == 0) { cls(); } putnormalchar(1, 0, buffer); } //print hours ones digit putnormalchar(7 - offset, 0, buffer); //print mins //add leading zero if mins < 10 itoa (mins, buffer, 10); if (mins < 10) { buffer = buffer; buffer = "0"; } //print mins tens and ones digits putnormalchar(19 - offset, 0, buffer); putnormalchar(25 - offset, 0, buffer); } } fade_down(); } //like basic_mode but with slide effect void slide() { byte digits_old = {99, 99, 99, 99}; //old values we store time in. Set to somthing that will never match the time initially so all digits get drawn wnen the mode starts byte digits_new; //new digits time will slide to reveal byte digits_x_pos = {25, 19, 7, 1}; //x pos for which to draw each digit at char old_char; //used when we use itoa to transpose the current digit (type byte) into a char to pass to the animation function char new_char; //used when we use itoa to transpose the new digit (type byte) into a char to pass to the animation function //old_chars - stores the 5 day and date suffix chars on the display. e.g. "mon" and "st". We feed these into the slide animation as the current char when these chars are updated. //We sent them as A initially, which are used when the clocl enters the mode and no last chars are stored. //char old_chars = "AAAAA"; //plot the clock colon on the display cls(); putnormalchar(13, 0, ":"); byte old_secs = rtc; //store seconds in old_secs. We compare secs and old secs. WHen they are different we redraw the display //run clock main loop as long as run_mode returns true while (run_mode()) { get_time(); //check for button press if (buttonA.uniquePress()) { switch_mode(); return; } if (buttonB.uniquePress()) { display_date(); return; } //if secs have changed then update the display if (rtc != old_secs) { old_secs = rtc; //do 12/24 hour conversion if ampm set to 1 byte hours = rtc; if (hours > 12) { hours = hours - ampm * 12; } if (hours < 1) { hours = hours + ampm * 12; } //split all date and time into individual digits - stick in digits_new array //rtc = secs //array pos and digit stored //digits_new = (rtc%10); //0 - secs ones //digits_new = ((rtc/10)%10); //1 - secs tens //rtc = mins digits_new = (rtc % 10); //2 - mins ones digits_new = ((rtc / 10) % 10); //3 - mins tens //rtc = hours digits_new = (hours % 10); //4 - hour ones digits_new = ((hours / 10) % 10); //5 - hour tens //rtc = date //digits_new = (rtc%10); //6 - date ones //digits_new = ((rtc/10)%10); //7 - date tens //draw initial screen of all chars. After this we just draw the changes. //compare digits 0 to 3 (mins and hours) for (byte i = 0; i <= 3; i++) { //see if digit has changed... if (digits_old[i] != digits_new[i]) { //run 9 step animation sequence for each in turn for (byte seq = 0; seq <= 8 ; seq++) { //convert digit to string itoa(digits_old[i], old_char, 10); itoa(digits_new[i], new_char, 10); //if set to 12 hour mode and we"re on digit 2 (hours tens mode) then check to see if this is a zero. If it is, blank it instead so we get 2.00pm not 02.00pm if (ampm && i == 3) { if (digits_new == 0) { new_char = " "; } if (digits_old == 0) { old_char = " "; } } //draw the animation frame for each digit slideanim(digits_x_pos[i], 0, seq, old_char, new_char); delay(SLIDE_DELAY); } } } /* //compare date digit 6 (ones) and (7) tens - if either of these change we need to update the date line. We compare date tens as say from Jan 31 -> Feb 01 then ones digit doesn"t change if ((digits_old != digits_new) || (digits_old != digits_new)) { //change the day shown. Loop below goes through each of the 3 chars in turn e.g. "MON" for (byte day_char = 0; day_char <=2 ; day_char++){ //run the anim sequence for each char for (byte seq = 0; seq <=8 ; seq++){ //the day (0 - 6) Read this number into the days char array. the seconds number in the array 0-2 gets the 3 chars of the day name, e.g. m o n slideanim(6*day_char,8,seq,old_chars,days); //6 x day_char gives us the x pos for the char delay(SLIDE_DELAY); } //save the old day chars into the old_chars array at array pos 0-2. We use this next time we change the day and feed it to the animation as the current char. The updated char is fed in as the new char. old_chars = days; } //change the date tens digit (if needed) and ones digit. (the date ones digit wil alwaus change, but putting this in the "if" loop makes it a bit neater code wise.) for (byte i = 7; i >= 6; i--){ if (digits_old[i] != digits_new[i]) { for (byte seq = 0; seq <=8 ; seq++){ itoa(digits_old[i],old_char,10); itoa(digits_new[i],new_char,10); slideanim(digits_x_pos[i],8,seq,old_char,new_char); delay(SLIDE_DELAY); } } } //print the day suffix "nd" "rd" "th" etc. First work out date 2 letter suffix - eg st, nd, rd, th byte s = 3; //the pos to read our suffix array from. byte date = rtc; if(date == 1 || date == 21 || date == 31) { s = 0; } else if (date == 2 || date == 22) { s = 1; } else if (date == 3 || date == 23) { s = 2; } for (byte suffix_char = 0; suffix_char <=1 ; suffix_char++){ for (byte seq = 0; seq <=8 ; seq++){ slideanim((suffix_char*6)+36,8,seq,old_chars,suffix[s]); // we pass in the old_char array char as the current char and the suffix array as the new char delay(SLIDE_DELAY); } //save the suffic char in the old chars array at array pos 3 and 5. We use these chars next time we change the suffix and feed it to the animation as the current char. The updated char is fed in as the new char. old_chars = suffix[s]; } }//end do date line */ //save digita array tol old for comparison next loop for (byte i = 0; i <= 3; i++) { digits_old[i] = digits_new[i]; } }//secs/oldsecs }//while loop fade_down(); } //called by slide //this draws the animation of one char sliding on and the other sliding off. There are 8 steps in the animation, we call the function to draw one of the steps from 0-7 //inputs are are char x and y, animation frame sequence (0-7) and the current and new chars being drawn. void slideanim(byte x, byte y, byte sequence, char current_c, char new_c) { // To slide one char off and another on we need 9 steps or frames in sequence... // seq# 0123456 <-rows of the display // | ||||||| // seq0 0123456 START - all rows of the display 0-6 show the current characters rows 0-6 // seq1 012345 current char moves down one row on the display. We only see it"s rows 0-5. There are at display positions 1-6 There is a blank row inserted at the top // seq2 6 01234 current char moves down 2 rows. we now only see rows 0-4 at display rows 2-6 on the display. Row 1 of the display is blank. Row 0 shows row 6 of the new char // seq3 56 0123 // seq4 456 012 half old / half new char // seq5 3456 01 // seq6 23456 0 // seq7 123456 // seq8 0123456 END - all rows show the new char //from above we can see... //currentchar runs 0-6 then 0-5 then 0-4 all the way to 0. starting Y position increases by 1 row each time. //new char runs 6 then 5-6 then 4-6 then 3-6. starting Y position increases by 1 row each time. //if sequence number is below 7, we need to draw the current char if (sequence < 7) { byte dots; // if (current_c >= "A" && || (current_c >= "a" && current_c <= "z")) { // current_c &= 0x1F; // A-Z maps to 1-26 // } if (current_c >= "A" && current_c <= "Z") { current_c &= 0x1F; // A-Z maps to 1-26 } else if (current_c >= "a" && current_c <= "z") { current_c = (current_c - "a") + 41; // A-Z maps to 41-67 } else if (current_c >= "0" && current_c <= "9") { current_c = (current_c - "0") + 31; } else if (current_c == " ") { current_c = 0; // space } else if (current_c == ".") { current_c = 27; // full stop } else if (current_c == "\"") { current_c = 28; // single quote mark } else if (current_c == ":") { current_c = 29; //colon } else if (current_c == ">") { current_c = 30; // clock_mode selector arrow } byte curr_char_row_max = 7 - sequence; //the maximum number of rows to draw is 6 - sequence number byte start_y = sequence; //y position to start at - is same as sequence number. We inc this each loop //plot each row up to row maximum (calculated from sequence number) for (byte curr_char_row = 0; curr_char_row <= curr_char_row_max; curr_char_row++) { for (byte col = 0; col < 5; col++) { dots = pgm_read_byte_near(&myfont); if (dots & (64 >> curr_char_row)) plot(x + col, y + start_y, 1); //plot led on else plot(x + col, y + start_y, 0); //else plot led off } start_y++;//add one to y so we draw next row one down } } //draw a blank line between the characters if sequence is between 1 and 7. If we don"t do this we get the remnants of the current chars last position left on the display if (sequence >= 1 && sequence <= 8) { for (byte col = 0; col < 5; col++) { plot(x + col, y + (sequence - 1), 0); //the y position to draw the line is equivalent to the sequence number - 1 } } //if sequence is above 2, we also need to start drawing the new char if (sequence >= 2) { //work out char byte dots; //if (new_c >= "A" && new_c <= "Z" || (new_c >= "a" && new_c <= "z")) { // new_c &= 0x1F; // A-Z maps to 1-26 //} if (new_c >= "A" && new_c <= "Z") { new_c &= 0x1F; // A-Z maps to 1-26 } else if (new_c >= "a" && new_c <= "z") { new_c = (new_c - "a") + 41; // A-Z maps to 41-67 } else if (new_c >= "0" && new_c <= "9") { new_c = (new_c - "0") + 31; } else if (new_c == " ") { new_c = 0; // space } else if (new_c == ".") { new_c = 27; // full stop } else if (new_c == "\"") { new_c = 28; // single quote mark } else if (new_c == ":") { new_c = 29; // clock_mode selector arrow } else if (new_c == ">") { new_c = 30; // clock_mode selector arrow } byte newcharrowmin = 6 - (sequence - 2); //minimumm row num to draw for new char - this generates an output of 6 to 0 when fed sequence numbers 2-8. This is the minimum row to draw for the new char byte start_y = 0; //y position to start at - is same as sequence number. we inc it each row //plot each row up from row minimum (calculated by sequence number) up to 6 for (byte newcharrow = newcharrowmin; newcharrow <= 6; newcharrow++) { for (byte col = 0; col < 5; col++) { dots = pgm_read_byte_near(&myfont); if (dots & (64 >> newcharrow)) plot(x + col, y + start_y, 1); //plot led on else plot(x + col, y + start_y, 0); //else plot led off } start_y++;//add one to y so we draw next row one down } } } //print a clock using words rather than numbers void word_clock() { cls(); char numbers = { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen" }; char numberstens = { "ten", "twenty", "thirty", "forty", "fifty" }; //potentially 3 lines to display char str_a; char str_b; char str_c; //byte hours_y, mins_y; //hours and mins and positions for hours and mins lines byte hours = rtc; if (hours > 12) { hours = hours - ampm * 12; } if (hours < 1) { hours = hours + ampm * 12; } get_time(); //get the time from the clock chip byte old_mins = 100; //store mins in old_mins. We compare mins and old mins & when they are different we redraw the display. Set this to 100 initially so display is drawn when mode starts. byte mins; //run clock main loop as long as run_mode returns true while (run_mode()) { //check for button press if (buttonA.uniquePress()) { switch_mode(); return; } if (buttonB.uniquePress()) { display_date(); } get_time(); //get the time from the clock chip mins = rtc; //get mins //if mins is different from old_mins - redraw display if (mins != old_mins) { //update old_mins with current mins value old_mins = mins; //reset these for comparison next time mins = rtc; hours = rtc; //make hours into 12 hour format if (hours > 12) { hours = hours - 12; } if (hours == 0) { hours = 12; } //split mins value up into two separate digits int minsdigit = rtc % 10; byte minsdigitten = (rtc / 10) % 10; //if mins <= 10 , then top line has to read "minsdigti past" and bottom line reads hours if (mins < 10) { strcpy (str_a, numbers); strcpy (str_b, "PAST"); strcpy (str_c, numbers); } //if mins = 10, cant use minsdigit as above, so soecial case to print 10 past /n hour. if (mins == 10) { strcpy (str_a, numbers); strcpy (str_b, " PAST"); strcpy (str_c, numbers); } //if time is not on the hour - i.e. both mins digits are not zero, //then make first line read "hours" and 2 & 3rd lines read "minstens" "mins" e.g. "three /n twenty /n one" else if (minsdigitten != 0 && minsdigit != 0) { strcpy (str_a, numbers); //if mins is in the teens, use teens from the numbers array for the 2nd line, e.g. "fifteen" //if (mins >= 11 && mins <= 19) { if (mins <= 19) { strcpy (str_b, numbers); } else { strcpy (str_b, numberstens); strcpy (str_c, numbers); } } // if mins digit is zero, don"t print it. read read "hours" "minstens" e.g. "three /n twenty" else if (minsdigitten != 0 && minsdigit == 0) { strcpy (str_a, numbers); strcpy (str_b, numberstens); strcpy (str_c, ""); } //if both mins are zero, i.e. it is on the hour, the top line reads "hours" and bottom line reads "o"clock" else if (minsdigitten == 0 && minsdigit == 0) { strcpy (str_a, numbers); strcpy (str_b, "O"CLOCK"); strcpy (str_c, ""); } }//end worknig out time //run in a loop //print line a "twelve" byte len = 0; while (str_a) { len++; }; //get length of message byte offset_top = (31 - ((len - 1) * 4)) / 2; // //plot hours line byte i = 0; while (str_a[i]) { puttinychar((i * 4) + offset_top, 1, str_a[i]); i++; } //hold display but check for button presses int counter = 1000; while (counter > 0){ //check for button press if (buttonA.uniquePress()) { switch_mode(); return; } if (buttonB.uniquePress()) { display_date(); } delay(1); counter--; } fade_down(); //print line b len = 0; while (str_b) { len++; }; //get length of message offset_top = (31 - ((len - 1) * 4)) / 2; i = 0; while (str_b[i]) { puttinychar((i * 4) + offset_top, 1, str_b[i]); i++; } //hold display but check for button presses counter = 1000; while (counter > 0){ if (buttonA.uniquePress()) { switch_mode(); return; } if (buttonB.uniquePress()) { display_date(); } delay(1); counter--; } fade_down(); //print line c if there. len = 0; while (str_c) { len++; }; //get length of message offset_top = (31 - ((len - 1) * 4)) / 2; i = 0; while (str_c[i]) { puttinychar((i * 4) + offset_top, 1, str_c[i]); i++; } counter = 1000; while (counter > 0){ //check for button press if (buttonA.uniquePress()) { switch_mode(); return; } if (buttonB.uniquePress()) { display_date(); } delay(1); counter--; } fade_down(); //hold display blank but check for button presses before starting again. counter = 1000; while (counter > 0){ //check for button press if (buttonA.uniquePress()) { switch_mode(); return; } if (buttonB.uniquePress()) { display_date(); } delay(1); counter--; } } fade_down(); } /// scroll message - not used at present - too slow. void scroll() { char message = {"Hello There "}; cls(); byte p = 6; //current pos in string byte chara = {0, 1, 2, 3, 4, 5}; //chars from string int x = {0, 6, 12, 18, 24, 30}; //xpos for each char byte y = 0; //y pos // clear_buffer(); while (message[p] != "\0") { //draw all 6 chars for (byte c = 0; c < 6; c++) { putnormalchar(x[c],y,message[ chara[c] ]); //draw a line of pixels turned off after each char,otherwise the gaps between the chars have pixels left in them from the previous char for (byte yy = 0 ; yy < 8; yy ++) { plot(x[c] + 5, yy, 0); } //take one off each chars position x[c] = x[c] - 1; } //reset a char if it"s gone off screen for (byte i = 0; i <= 5; i++) { if (x[i] < -5) { x[i] = 31; chara[i] = p; p++; } } } } //display_date - print the day of week, date and month with a flashing cursor effect void display_date() { cls(); //read the date from the DS1307 byte dow = rtc; // day of week 0 = Sunday byte date = rtc; byte month = rtc - 1; //array of month names to print on the display. Some are shortened as we only have 8 characters across to play with char monthnames = { "January", "February", "March", "April", "May", "June", "July", "August", "Sept", "October", "November", "December" }; //print the day name //get length of text in pixels, that way we can centre it on the display by divindin the remaining pixels b2 and using that as an offset byte len = 0; while(daysfull) { len++; }; byte offset = (31 - ((len-1)*4)) / 2; //our offset to centre up the text //print the name int i = 0; while(daysfull[i]) { puttinychar((i*4) + offset , 1, daysfull[i]); i++; } delay(1000); fade_down(); cls(); // print date numerals char buffer; itoa(date,buffer,10); offset = 10; //offset to centre text if 3 chars - e.g. 3rd // first work out date 2 letter suffix - eg st, nd, rd, th etc // char suffix={"st", "nd", "rd", "th" }; is defined at top of code byte s = 3; if(date == 1 || date == 21 || date == 31) { s = 0; } else if (date == 2 || date == 22) { s = 1; } else if (date == 3 || date == 23) { s = 2; } //print the 1st date number puttinychar(0+offset, 1, buffer); //if date is under 10 - then we only have 1 digit so set positions of sufix etc one character nearer byte suffixposx = 4; //if date over 9 then print second number and set xpos of suffix to be 1 char further away if (date > 9){ suffixposx = 8; puttinychar(4+offset, 1, buffer); offset = 8; //offset to centre text if 4 chars } //print the 2 suffix characters puttinychar(suffixposx+offset, 1, suffix[s]); puttinychar(suffixposx+4+offset, 1, suffix[s]); delay(1000); fade_down(); //print the month name //get length of text in pixels, that way we can centre it on the display by divindin the remaining pixels b2 and using that as an offset len = 0; while(monthnames) { len++; }; offset = (31 - ((len-1)*4)) / 2; //our offset to centre up the text i = 0; while(monthnames[i]) { puttinychar((i*4) +offset, 1, monthnames[i]); i++; } delay(1000); fade_down(); } //dislpay menu to change the clock mode void switch_mode() { //remember mode we are in. We use this value if we go into settings mode, so we can change back from settings mode (6) to whatever mode we were in. old_mode = clock_mode; char* modes = { "Basic", "Small", "Slide", "Words", "Setup" }; byte next_clock_mode; byte firstrun = 1; //loop waiting for button (timeout after 35 loops to return to mode X) for (int count = 0; count < 35 ; count++) { //if user hits button, change the clock_mode if (buttonA.uniquePress() || firstrun == 1) { count = 0; cls(); if (firstrun == 0) { clock_mode++; } if (clock_mode > NUM_DISPLAY_MODES + 1) { clock_mode = 0; } //print arrown and current clock_mode name on line one and print next clock_mode name on line two char str_top; //strcpy (str_top, "-"); strcpy (str_top, modes); next_clock_mode = clock_mode + 1; if (next_clock_mode > NUM_DISPLAY_MODES + 1) { next_clock_mode = 0; } byte i = 0; while (str_top[i]) { putnormalchar(i * 6, 0, str_top[i]); i++; } firstrun = 0; } delay(50); } } //run clock main loop as long as run_mode returns true byte run_mode() { //if random mode is on... check the hour when we change mode. if (random_mode) { //if hour value in change mode time = hours. then reurn false = i.e. exit mode. if (change_mode_time == rtc) { //set the next random clock mode and time to change it set_next_random(); //exit the current mode. return 0; } } //else return 1 - keep running in this mode return 1; } //set the next hour the clock will change mode when random mode is on void set_next_random() { //set the next hour the clock mode will change - current time plus 1 - 4 hours get_time(); change_mode_time = rtc + random (1, 5); //if change_mode_time now happens to be over 23, then set it to between 1 and 3am if (change_mode_time > 23) { change_mode_time = random (1, 4); } //set the new clock mode clock_mode = random(0, NUM_DISPLAY_MODES + 1); //pick new random clock mode } //dislpay menu to change the clock settings void setup_menu() { char* set_modes = { "Rndom", "24 Hr","Set", "Brght", "Exit"}; if (ampm == 0) { set_modes = ("12 Hr"); } byte setting_mode = 0; byte next_setting_mode; byte firstrun = 1; //loop waiting for button (timeout after 35 loops to return to mode X) for(int count=0; count < 35 ; count++) { //if user hits button, change the clock_mode if(buttonA.uniquePress() || firstrun == 1){ count = 0; cls(); if (firstrun == 0) { setting_mode++; } if (setting_mode > NUM_SETTINGS_MODES) { setting_mode = 0; } //print arrown and current clock_mode name on line one and print next clock_mode name on line two char str_top; strcpy (str_top, set_modes); next_setting_mode = setting_mode + 1; if (next_setting_mode > NUM_SETTINGS_MODES) { next_setting_mode = 0; } byte i = 0; while(str_top[i]) { putnormalchar(i*6, 0, str_top[i]); i++; } firstrun = 0; } delay(50); } //pick the mode switch(setting_mode){ case 0: set_random(); break; case 1: set_ampm(); break; case 2: set_time(); break; case 3: set_intensity(); break; case 4: //exit menu break; } //change the clock from mode 6 (settings) back to the one it was in before clock_mode=old_mode; } //toggle random mode - pick a different clock mode every few hours void set_random(){ cls(); char text_a = "Off"; char text_b = "On"; byte i = 0; //if random mode is on, turn it off if (random_mode){ //turn random mode off random_mode = 0; //print a message on the display while(text_a[i]) { putnormalchar((i*6), 0, text_a[i]); i++; } } else { //turn randome mode on. random_mode = 1; //set hour mode will change set_next_random(); //print a message on the display while(text_b[i]) { putnormalchar((i*6), 0, text_b[i]); i++; } } delay(1500); //leave the message up for a second or so } //set 12 or 24 hour clock void set_ampm() { // AM/PM or 24 hour clock mode - flip the bit (makes 0 into 1, or 1 into 0 for ampm mode) ampm = (ampm ^ 1); cls(); } //change screen intensityintensity void set_intensity() { cls(); byte i = 0; char text = "Bright"; while(text[i]) { puttinychar((i*4)+4, 0, text[i]); i++; } //wait for button input while (!buttonA.uniquePress()) { levelbar (0,6,(intensity*2)+2,2); //display the intensity level as a bar while (buttonB.isPressed()) { if(intensity == 15) { intensity = 0; cls (); } else { intensity++; } //print the new value i = 0; while(text[i]) { puttinychar((i*4)+4, 0, text[i]); i++; } //display the intensity level as a bar levelbar (0,6,(intensity*2)+2,2); //change the brightness setting on the displays for (byte address = 0; address < 4; address++) { lc.setIntensity(address, intensity); } delay(150); } } } // display a horizontal bar on the screen at offset xposr by ypos with height and width of xbar, ybar void levelbar (byte xpos, byte ypos, byte xbar, byte ybar) { for (byte x = 0; x < xbar; x++) { for (byte y = 0; y <= ybar; y++) { plot(x+xpos, y+ypos, 1); } } } //set time and date routine void set_time() { cls(); //fill settings with current clock values read from clock get_time(); byte set_min = rtc; byte set_hr = rtc; byte set_date = rtc; byte set_mnth = rtc; int set_yr = rtc; //Set function - we pass in: which "set" message to show at top, current value, reset value, and rollover limit. set_date = set_value(2, set_date, 1, 31); set_mnth = set_value(3, set_mnth, 1, 12); set_yr = set_value(4, set_yr, 2013, 2099); set_hr = set_value(1, set_hr, 0, 23); set_min = set_value(0, set_min, 0, 59); ds1307.adjust(DateTime(set_yr, set_mnth, set_date, set_hr, set_min)); cls(); } //used to set min, hr, date, month, year values. pass //message = which "set" message to print, //current value = current value of property we are setting //reset_value = what to reset value to if to rolls over. E.g. mins roll from 60 to 0, months from 12 to 1 //rollover limit = when value rolls over int set_value(byte message, int current_value, int reset_value, int rollover_limit){ cls(); char messages = { "Set Mins", "Set Hour", "Set Day", "Set Mnth", "Set Year"}; //Print "set xyz" top line byte i = 0; while(messages[i]) { puttinychar(i*4 , 1, messages[i]); i++; } delay(2000); cls(); //print digits bottom line char buffer = " "; itoa(current_value,buffer,10); puttinychar(0 , 1, buffer); puttinychar(4 , 1, buffer); puttinychar(8 , 1, buffer); puttinychar(12, 1, buffer); delay(300); //wait for button input while (!buttonA.uniquePress()) { while (buttonB.isPressed()){ if(current_value < rollover_limit) { current_value++; } else { current_value = reset_value; } //print the new value itoa(current_value, buffer ,10); puttinychar(0 , 1, buffer); puttinychar(4 , 1, buffer); puttinychar(8 , 1, buffer); puttinychar(12, 1, buffer); delay(150); } } return current_value; } void get_time() { //get time DateTime now = ds1307.now(); //save time to array rtc = now.year(); rtc = now.month(); rtc = now.day(); rtc = now.dayOfWeek(); //returns 0-6 where 0 = Sunday rtc = now.hour(); rtc = now.minute(); rtc = now.second(); //flash arduino led on pin 13 every second //if ((rtc % 2) == 0) { // digitalWrite(13, HIGH); //} //else { // digitalWrite(13, LOW); //} //print the time to the serial port - useful for debuging RTC issues /* Serial.print(rtc); Serial.print(":"); Serial.print(rtc); Serial.print(":"); Serial.println(rtc); */ }

Теперь для завершения работы над устройством потребуется выполнить лишь ряд простых операций:


Компиляция программного кода и дальнейшая загрузка в память микроконтроллера займёт некоторое время, обычно не более одной минуты. Об успешном завершении операции будет сообщено в консоли Arduino IDE. После чего остаётся лишь перезагрузить Arduino с помощью кнопки Reset на устройстве - простые часы на светодиодных матрицах готовы!

Готовые часы на Arduino

Настройка часов осуществляется с помощью двух кнопок. Устройство поддерживает 12- и 24-часовой формат вывода времени, показ даты и дня недели, отображение времени с секундами и без. Также имеется возможность менять яркость свечения светодиодов.


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

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

Представленные здесь электронные часы, кроме того, позволяют измерять температуру в помещении. Есть четыре независимых будильника, а так же индикатор календарных событий, который сигнализирует о наступлении таких событий, как годовщина, день рождения и т.д.. Позволяют изменять яркость в ручном и автоматическом режиме, что резко улучшает комфортность восприятия в темное время суток, и наконец, управляются любым пультом от БРА либо локальными кнопками.

Основные характеристики:

  • Напряжение питания (В) ………… .…………………... 5
  • Ток потребления (мА) …………………………………. 100 - 200
  • Общий размер матрицы (пикселей) …………………… 8 х 40
  • Пределы измеряемой температуры (градусов) …….…. -40 - +99.5
  • Точность измерения температуры (градусов) ……….. 0.5
  • Пределы суточной коррекции хода (секунд) …………. -9.9 - +9.9
  • Число градаций изменения яркости …………………… 31 + режим «авто»
  • Количество будильников ………………………………. 4
  • Объем текста каждого будильника (символов) ……….. 11
  • Количество индикаторов событий …………………….. 14
  • Объем текста для каждого события (символов) ....……. 15
  • Управление режимами …………………………………. Кнопками/любым пультом

Принципиальная схема часов условно разделена на индикаторную и «контроллерную» части, представленные на рисунках ниже. Нумерация элементов единая и далее, при описании, будем рассматривать схему как одно целое.

Схема индикации

Схема управления

Собственно управление регистрами осуществляется посредством трех портов микроконтроллера (МК) DD7, в прерываниях от таймера TMR0 интервалом 2 мс, что обеспечивает общую частоту смены информации 63 Гц. Так же с МК на 9 вывод DD6 (включение-выключение выходов) поступает сигнал ШИМ для изменения яркости индикаторов. МК DD7 выбран типа PIC16F88, который отличается от популярного PIC16F628A наличием АЦП и вдвое емкой памятью программ. МК работает от внутреннего генератора на частоте 8 МГц (еще одно отличие от 628A).

В устройстве применена микросхема часов реального времени DS1307, которая обеспечивает счет и хранение секунд, минут, часов, дня недели, даты (включая год от 0 до 99). При начальной инициализации DS1307 настраивается в режим, при котором на выводе 7 присутствует меандр с частотой 1 Гц, который подается на вход RB0 МК. По этому входу включено прерывание, где один раз в секунду устанавливается специальный флаг. В основной программе посредством этого флага один раз в секунду выполняются основные действия: считывание данных с DS1307, индикация времени, проверка на совпадение будильников и индикаторов событий, установка яркости в автоматическом режиме. Дополнительная батарея BAT1 позволяет сохранять данные в отсутствии напряжения питания устройства практически неограниченное время. МК управляет DS1307 по шине I2C портами RB1, RB2. Так же по I2C управляется и датчик температуры – микросхема DS1621.

Устройство управляется с помощью кнопок S1 и S2, подключенных соответственно к портам МК RA4, RA2. Функции кнопки S1 могут выполняться с помощью любого пульта от БРА с помощью схемы на приемнике DA1 типа TSOP48 и одновибратора на D-триггере DD8.1 и элементах C8, R47 и D1. Длительность сигнала одновибратора выбрана 0.2 с, что обеспечивает преобразование пачек коротких импульсов с выхода DA1 в импульсы этой длительности при коротком нажатии и в непрерывный сигнал при удержании нажатой любой кнопки пульта. Сигнал с инверсного выхода триггера через диод D2 дублирует кнопку S1. С помощью элементов R48, HL6 организована индикация этого факта..

На втором триггере микросхемы DD8.2 и пьезокерамическом излучателе LS1 собрана схема звуковой сигнализации при срабатывании будильника либо индикатора события. На счетный вход триггера поступает сигнал ШИМ с выхода МК частотой 8 кГц. При сигнале логического 1 на объединенных входах R и S триггера на обоих выходах присутствует лог.1 и звук отсутствует. При наступлении события индикации МК с частотой 1 Гц меняет уровень на этих входах и на выходах триггера с этой периодичностью возникают противофазные импульсы с частотой 4000 Гц. Так как эта частота – резонансная для данного излучателя, возникает громкий прерывистый сигнал. Программно приняты меры для сохранения импульсом ШИМ и при крайних значениях яркости.

На элементах FR1, R45 и C6 собран датчик освещенности. МК с помощью встроенного АЦП считывает зависящее от освещения напряжение с этого делителя и выставляет соответствующий коэффициент заполнения ШИМ сигнала.

Питается устройство напряжением +5В, поступающим на разъем J1 и далее через фильтрующие цепи R41C3C4, R42C5, C7R46 на элементы схемы. Цепь R49 D3 защищает устройство от случайного подключения чрезмерного либо обратного напряжения. Удобно применить в качестве ИП зарядное устройство для смартфонов на 5 Вольт.

Устройство собрано на печатной плате из стеклотекстолита с односторонней металлизацией размерами 60 мм на 170 мм

DD1-DD5 применены в корпусе SO16, DD6 – в корпусе SOIC20. DD8 использована в корпусе DIP. Ее можно заменить отечественной К561ТМ2. DD9, DD10 – в корпусах SO8. Почти все SMD резисторы и конденсаторы – типоразмера 0805, C1,C7,C3,C5 – типоразмера 1206. Кварцевый резонатор лучше выпаять со старой материнской платы – они, как правило, стабильнее. Фоторезистор FR1, при замене на другой, должен быть с темновым сопротивлением примерно 1 – 2 МОм. Батарея BAT1 типа CR2032 установлена на стандартном держателе. Ее можно заменить любой, подходящей по размеру, напряжением 3В. При замене пьезоизлучателя на другой очень желательно, чтобы резонансная частота была близка к 4000 Гц. Фотоприемник можно выпаять со старого телевизора. Светодиод – любой яркий, красного свечения. Диоды можно заменить на КД521, КД522. Стабилитрон – напряжением 5.6В и желательно мощностью не менее 1 Вт. Так как примененные матричные индикаторы оказались с общим анодом, пришлось при разработке платы установить их с поворотом на 90 градусов и использовать строки как колонки и наоборот. При замене на индикаторы с общим катодом, например 1088AS, придется полностью изменить схему подключения. Кнопки – любые тактовые, угловые. При закрытии индикаторов спереди цветным оргстеклом наблюдались ошибочные срабатывания DA 1 отраженным яркостным сигналом от индикаторов. Этот эффект легко устраняется вклеиванием экрана из черной бумаги между индикатором и фотоприемником.

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

Внешний вид собранной платы показан на фотографиях.

Причина не очень красиво припаянных токоограничительных резисторов в том, что после изготовления платы выяснилось, что типономинал оных не 0805, а 1206. Пришлось как-то припаять...

Программа управления контроллером написана на языке Си и оттранслирована в среде MikroC for PIC. Слово конфигурации содержится в прошивке и заносится автоматически. Авторская ориентация часов – матрицы сверху, что минимизирует возможное воздействие от деталей схемы на температуру микросхем DD9, DD10. Однако, если кому-то понравится расположение часов матрицами вниз, следует перед трансляцией программы изменить в файле “watch.c” значение параметра в самой первой строке с 1 на 0 согласно комментарию. Прошивка прилагается для обоих случаев. Так же возможно изменить шрифт цифр на тонкий (по умолчанию – жирный), аналогичный буквам (это, кстати, несколько снизит потребляемый ток в исходном режиме). Для этого в первой строке в файле “font.c” надо изменить значение параметра “font_2” с 1 на 0.

Теперь перейдем к описанию режимов часов и работы с ними.

Сразу при включении, часы переходят в основной режим – индикации времени. При этом, цифра часов и минут высотой 8 пикселей, а секунд – 7 (в целом каждая цифра или буква изображается в поле 5х8 пикселей, цифры секунд – 5х7). При нажатии на кнопку S1 или любой кнопки пульта (эти действия эквивалентны и далее будем иметь в виду, что когда речь идет о нажатии кнопки S1, то же происходит и по сигналу с пульта) с помощью бегущей строки отображается текущая полная дата и день недели. Если больше не предпринимать никаких действий, спустя 16 сек. устройство возвращается в исходное состояние – индикации времени. Если же нажать кнопку S1 еще раз во время индикации даты, то начинается индикация температуры тоже посредством бегущей строки в течении 16 сек. (повторное нажатие кнопки – возврат в исходное состояние сразу).

При непрерывном нажатии на кнопку S1 в течении 9 секунд, часы переходят в режим «МЕНЮ», о чем говорит соответствующая надпись. Столь длительное нажатие для этого выбрано в целях уменьшения вероятности случайного попадания в меню при обычном пользовании пультом (собственно для управления бытовой техникой).

Далее следует небольшое отступление - поясним работу с кнопками в целом. Управление часами сделано так, что все действия с ними можно произвести с помощью одной лишь кнопки S1 (или пультом). Ведь часы могут висеть высоко на стене, и доступ будет только к кнопке S1 (через пульт). Итак, перебор пунктов меню, так же как перебор из списка будильников либо событий производится короткими (до 3 сек.) нажатиями S1, а переход в выбранный пункт – длинное (более 3 сек.) нажатие. Без воздействия в течении 15 – 30 сек. (в зависимости от конкретного режима) – возврат в исходное состояние. Там же, где происходит изменение значения какого-то параметра, короткое нажатие увеличивает значение не 1, а удерживание более 2 сек. - значение увеличивается автоматически примерно 3 раза в секунду. Здесь отсутствие воздействия в течении некоторого настраиваемого (от 5 до 8 сек.) времени приводит к переходу к следующему параметру с сохранением текущего параметра. Вот здесь и можно использовать кнопку S2 – для сохранения параметра с переходом к следующему без ожидания, сразу. Так, держа часы в руках можно значительно ускорить подобные манипуляции.

Вернемся к пункту «МЕНЮ». Здесь пять пунктов: УСТАНОВКА, КОРРЕКЦИЯ, БУДИЛЬНИК, СОБЫТИЯ, НАСТРОЙКИ. Расскажем о каждом по порядку.

УСТАНОВКА. Здесь производится установка текущих даты и времени. Изменяемый параметр вначале выделяется инверсией, которая исчезает с началом изменений. Значения меняются только в большую сторону, с переходом с максимального значения (при ее достижении) к минимальному и далее по кругу. В самом конце, когда дата и часы с минутами выставлены, в месте секунд будут инверсные нули. Здесь надо в момент обнуления эталонных секунд нажать S1 и часы перейдут в исходное состояние с измененными датой и временем.

КОРРЕКЦИЯ. С течением времени, неминуемо возникает ошибка в ходе часов. Для ее коррекции и служит данный пункт. Предварительно надо замерить «уход» часов в течении 10 суток. Предположим, часы спешат на 58 секунд. Входим затем в этот режим, о чем говорит строка «уход за 10 суток». При нажатии S1 строка сменяется статичной надписью «ХХ сек», где ХХ – текущее значение коррекции, предположим 31. Так как часы спешат, отнимаем от данного значения наши 58 сек. (если отстают – прибавляем). Набираем полученные (-27) сек. После выхода это значение сохранится в EEPROM. Интервал вводимых значений – от -99 до 99, знак меняется при переходе через 0. Коррекция происходит каждый день в 00:00:19, для чего целая от одной десятой значения коррекции (в нашем случае (-2)) добавляется к текущему времени, а остаток (у нас (-7)) сохраняется в EEPROM и в следующий раз прибавляется к коэффициенту до обработки. То есть через день от времени отнимется 3 секунды и сохранится (-4) (-27-7 = -34).

БУДИЛЬНИК. При входе в этот пункт на экране бежит стока, состоящая из: номер (1-4), признак включенности (“+” или “-“), время срабатывания, название будильника (текст из 11 знаков). По умолчанию, например, во втором будильнике, будут данные: «2. – 12:00 БУДИЛЬНИК-2». Короткими нажатиями можно переходить из одного будильника в другой, долгим нажатием переходим в текущий будильник, где можно изменять данные, включать – отключать выбирать активность для каждого дня недели, установить время срабатывания, изменять текст название путем перебора знаков каждой позиции. Вся информация о четырех будильниках хранится в свободной памяти микросхемы DS1307 (56 байт) . При совпадении текущего времени с каким-нибудь включенным и активным в этот день недели будильником выводится строка с данными об этом будильнике, с прерывистым звуковым сигналом, в течении одной минуты. Сигнал будильника можно «досрочно» прервать нажатием кнопки S1.

СОБЫТИЯ. При входе в этот пункт, на индикаторе при коротких нажатиях кнопки, попеременно пробегают активные события плюс пункт «ДОБАВИТЬ НОВОЕ». По умолчанию активных событий нет, поэтому, будет только приглашение о добавлении нового события. Формат активного события: «<дата> <текст из 16 знаков>». Когда все 14 событий активны, пункт «ДОБАВИТЬ НОВОЕ» не выводится и новое событие можно добавить только вместо какого либо из существующих. Длительным нажатием переходим в режим редактирования события. Здесь можно включить-отключить, изменить дату, а так же написать свой текст, соответствующий данному событию. Все изменения сохраняются в энергонезависимой памяти МК (EEPROM).

На совпадение с датами событий текущая дата проверяется каждый день в 8:00, и при совпадении выводится строка из текущей даты и текста события с прерывистым звуковым сигналом в течении одной минуты. Такой сигнал будет подаваться каждый час до 19:00, при условии, что он не был прерван кнопкой. Прерванный кнопкой сигнал уже не повторяется.

НАСТРОЙКИ. При входе в этот режим, на экран выводится сообщение типа «ЯРК.ХХ», где «ХХ» - текущая яркость. Нажатиями кнопки S1 это значение можно изменять в пределах от 1 до 31 либо выставить значение «АВ», что означает «автоматически». При изменении значения яркости, сразу наглядно меняется реальная яркость индикатора. При сохранении автоматического режима, яркость индикации часов впоследствии изменяется без вмешательства, в зависимости от уровня освещенности в помещении. В этом случае, попадаем в пункт установки минимальной яркости для авто-режима (выводится «МИН.ХХ»). Это значение лучше выставлять в условиях минимально ожидаемой освещенности. Если в предыдущем пункте было выставлено конкретное числовое значение яркости, данный пункт пропускается.

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

Все настройки сохраняются в EEPROM памяти МК и не теряются при пропадании питания.

В приложении, кроме исходного кода и прошивок имеются Proteus-модель и плата в формате Lay6.

Список радиоэлементов

Обозначение Тип Номинал Количество Примечание Магазин Мой блокнот
DD7 МК PIC 8-бит

PIC16F88

1 В блокнот
DD1-DD5 Сдвиговый регистр 74HC595 5 SOP-16 В блокнот
DD8 Сдвоенный D-триггер CD4013 1 K561TM2 В блокнот
DD9 Часы реального времени (RTC)

DS1307

1 В блокнот
DD10 Датчик температуры

DS1821

1 В блокнот
DA1 Фотоприемник TSOP48 1 В блокнот
DD6 Мощный сдвиговый регистр TPIC6B595 1 В блокнот
LS1 Пьезоизлучатель XCFT-14A 1 В блокнот
HL1-HL5 Светодиодная матрица 1088BS 5 В блокнот
HL6 Светодиод ARL-3514 1 red В блокнот
X1 Кварцевый резонатор 32768 Гц 1 В блокнот
BAT1 Литиевая батарея CR2032 1 3 V В блокнот
D1-D2 Диод 1N4748 2 В блокнот
D3 Стабилитрон

1N4734A

1 В блокнот
S1, S2 Кнопка тактовая угловая 2 В блокнот
FR1 Фоторезистор NCL-4952 1 В блокнот
R1-R40 Резистор

220 Ом

40 0805

Многие радиолюбители, начинающие и не только любят «изобретать велосипед» - строить СВОИ электронные часы. Не обошла эта участь и меня. Конструкций часов в инете сегодня конечно предостаточно, но вот часов на светодиодных матрицах почему-то среди них единицы. В русскоговорящем интернете я нашел только одну полностью законченную и описанную конструкцию. В тоже время, светодиодные матрицы сейчас очень сильно подешевели, и их стоимость не выше, а то и ниже, чем у семисегментных индикаторов такого же размера. Например примененные мной GNM23881AD при размере 60х60мм были куплены за 1,5уе (3 индикатора обошлись в 4,5уе), за эти деньги врядли можно купить четыре семисегментника таких-же размеров. А вот информации, разместить на матричном индикаторе, можно намного больше. Кроме цифр на них можно отображать любые буквы, знаки, а с помощью бегущей строки еще и текст. Исходя из этого, появилось желание построить часы на светодиодных матрицах, но чтоб схема при этом получилась не сложнее чем на семисегментниках. Также хотелось чтоб она была достаточно функциональная и не похожая на другие. Так родилась следующая схема.

Функционал у часов такой:

Отсчет времени, календарь, день недели. (високосный год учитывается, переход на летнее/зимнее время не осуществляется).

Сохранение хода часов при пропадании внешнего питания (потребление составляет 15мка).

Коррекция хода + - 59,9сек\сутки, с шагом 0,1сек.

9 будильников. 3 из которых «одноразовые», и 6 «постоянных», индивидуально настраиваемых по дням недели.

Индивидуально настраиваемая длительность звукового сигнала каждого будильника (1-15мин).

Звуковое подтверждение нажатия кнопок (возможно отключить).

Ежечасный звуковой сигнал (возможно отключить). С 00-00 до 08-00 сигнал не подаётся.

1 или 2 датчика температуры (Улица и дом).

Настраиваемая бегущая строка, посредством которой выводится вся информация (кроме времени)

Значение коррекции хода, и настройки «бегущей строки» - сохраняются даже при пропадании резервного питания.

«Сердцем» часов выбрана AtMega16A, из-за её доступности, дешевизны и «ногастости». Схему хотелось максимально упростить, поэтому все что можно, было возложено на контроллер. В результате удалось обойтись всего двумя микросхемами, контроллером и регистром TPIC6B595. Если кому то недоступен TPIC6B595, то можно его заменить на 74НС595 + ULN2803. Оба варианта были опробованы. Так же можно попробовать применить TPIC6С595, она немного слабовата, и слегка грелась, но в целом работала стабильно. Отсчет времени производится с помощью асинхронного тайме - Т2. Ход часов сохраняется и при пропадании питания. В это время бОльшая часть схемы обесточивается, а контроллер питается от батарейки, аккумулятора, или от ионистора. Мне было интересно «по играться» с ионистором, поэтому применил его. Ток потребления часами в дежурном режиме составляет 15мка. При питании от ионистора на 1Ф, часы «продержались» четверо суток. Этого вполне достаточно для поддержания хода во время перебоев питания. Если применить батарейку СR2032, то теоретически, по расчетам заряда должно хватить на 1,5года. Наличие сетевого напряжения контроллер «слушает» через вывод РВ.3 Этот вывод является инвертирующем входом компаратора. Напряжение питания, через делитель R2-R3 подается на вывод РВ.3, и в нормальном состоянии равно примерно 1,5в. Если внешнее напряжение упадет ниже 4,1 вольта, то напряжение на выводе РВ.3 станет меньше 1,23вольта, при этом сгенерируется прерывание от компаратора, и в обработчике этого прерывания выключаются все «лишние» узлы контроллера и сам контроллер усыпляется. В этом режиме продолжает работать только отсчитывающий время таймер Т2. При появлении внешнего питания, напряжение на РВ.3 снова подымится выше 1,23в, контроллер «увидев» это, переведет все узлы в рабочее состояние. Если вместо ионистора, будет использоваться батарейка СR2032, то её нужно подключить через диод(предпочтительно диод шоттки). Анод диода подключается к + батарейки, а катод к катоду VD1.

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

9 будильников разделены на 3 одноразовых и 6 многоразовых. При включении будильников 1-3, они срабатывают только один раз. Для того чтоб они сработали еще раз, их нужно повторно включать вручную. А будильники 4-9 многоразовые, т.е. они будут срабатывать ежедневно, в установленное время. Кроме того эти будильники можно настроить на сработку только в определенные дни недели. Это удобно, например если не хотите чтоб будильник разбудил Вас в выходные. Или например Вам нужно просыпаться в будние дни в 7-00, а в четверг в 8-00, а на выходных будильник не нужен. Тогда настраиваем один многоразовый на 7-00 в понедельник-среду и пятницу, а второй на 8-00 в четверг….. Кроме того все будильники имеют настройку длительности сигнала, и если Вам, для того чтоб проснуться, мало сигнала в течении 1 минуты, то можно увеличить его на время от 1 до 15мин.

Коррекция хода производится один раз в сутки, в 00-00. Если часы спешат к примеру на 5 сек в сутки, то в 00-00-00 время установится в 23-59-55, если же часы отстают, то в 00-00-00 время установится в 00-00-05. Шаг коррекции - 0,1 сек. Максимальная коррекция - 59,9 сек/сутки. С исправным кварцем больше вряд ли понадобиться. Коррекция осуществляется и в дежурном режиме при питании от батареи.

Светодиодные матрицы можно использовать любые 8*8 светодиодов с общим катодом. Как уже было указано, я применил GNM23881AD. В принципе можно «набрать» матрицу и из отдельных светодиодов. Микроконтроллер AtMega16a можно заменить на «старый» AtMega16 с буквой L. При этом, теоретически должен немного увеличится ток потребления от батарейки. Наверное будет работать и просто AtMega16, но могут возникнуть проблемы при работе от батарейки. Диод D1 - желательно любой диод шоттки. С обычным выпрямительным тоже работает, но чтоб обезопасить себя от различных глюков, связанных с тем что часть схемы питается напряжением «до диода», а часть «после диода» лучше поискать шоттки. Транзистор VT1 - любой n-p-n.

Управление часами осуществляется двумя кнопками. Их количество можно было довести до 8шт, не добавляя больше вообще ни одного компонента, кроме самих кнопок, но захотелось попробовать «выкрутится» всего двумя. Кнопки условно названы «ОК» и «ШАГ». Кнопкой «ШАГ» как правило происходит переход к следующему пункту меню, а кнопкой «ОК» изменение параметров текущего меню. Сигнал сработавшего будильника также выключается кнопками «ОК» или «ШАГ». Нажатие любой кнопки во время сигнала будильника отключает его. Схема управления получилась такой:

Конструктивно часы выполнены на одной ПП. Размер ПП соответствует размеру индикаторов. Минимальная ширина дорог ПП - 0,4мм, расстояние между - 0,4мм. Так что любители «ЛУТа» смогут без труда изготовить плату самостоятельно.