Компьютерная грамотность, помощь и ремонт

Часы на матрицах с будильником. Самодельные часы на светодиодных матрицах

Простые часы на светодиодных матрицах. Многие радиолюбители, начинающие и не только любят «изобретать велосипед» - строить СВОИ электронные часы. Не обошла эта участь и меня. Конструкций часов в инете сегодня конечно предостаточно, но вот часов на светодиодных матрицах почему-то среди них единицы. В русскоговорящем интернете я нашел только одну полностью законченную и описанную конструкцию. В тоже время, светодиодные матрицы сейчас очень сильно подешевели, и их стоимость не выше, а то и ниже, чем у семисегментных индикаторов такого же размера. Например примененные мной 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шт, не добавляя больше вообще ни одного компонента, кроме самих кнопок, но захотелось попробовать «выкрутится» всего двумя. Кнопки условно названы «ОК» и «ШАГ». Кнопкой «ШАГ» как правило происходит переход к следующему пункту меню, а кнопкой «ОК» изменение параметров текущего меню. Сигнал сработавшего будильника также выключается кнопками «ОК» или «ШАГ». Нажатие любой кнопки во время сигнала будильника отключает его. Схема управления получилась такой:

Видео как все работает!

Часы + радио+метео+ПДУ

Давно хотелось сделать часы на RGB матрицах. Были найдены и заказаны матрицы GMT2088. Но как говорится аппетит приходит во время еды. Т.к даже просто цветные часы это скучно, то решено было воткнуть в них FM тюнер, эквалайзер, датчики влажности, температуры и давления. Также сделана возможность работы от аккумуляторов на случай пропадания основного сетевого напряжения.

В итоге получились такие часы:




В красных светодиодах можно заметить 4 светящиеся точки, это не у вас в глазах четверится и не глюк фотоаппарата просто перед матрицами вставлены две пленки которые стоят перед ЖК матрицей монитора и свечение диода разбивается на 4 части. Если поиграться расположением пленок относительно друг друга то можно получить много разных отображений. Качество фото хромает но основы видны.

Возможности настройки часов:

1. Цвет отображения принимаемой радиостанции. Для целой и дробного значения станции можно установить свой цвет.

2. Настройка цвета бегущей строки.

3. Изменение скорости бегущей строки.

4. Настройка цвета отображения часов. Можно каждому символу установить свой цвет.

5. Радио диапазон 65мГц до 108мГц. Сохраняет в памяти до 20 станций радио.

6. Звук для каждой станции сохраняется отдельно также как и установки эквалайзера.

7. Будильников 7 . Настраивается время включения, время выключения, громкость звука, дни включения, звук нарастающий или нет и включаемая радиостанция или зуммер.

8. Регулировка НЧ и ВЧ частот.

9. Выбор шрифта отображения часов до 8 шрифтов, можно загружать свои шрифты.

10. Автоматическая или ручная регулировка яркости матрицы.

А) Полностью автоматическая в зависимости от освещенности

Б) Ручная клавишами на ПДУ или самих часах

В) По установленному времени. Устанавливается время включения минимальной и максимальной яркости.

11. Контроль заряда резервной батарейки часов.

12. Управление часами при помощи ПДУ. Сделана возможность обучения ПДУ (форматы NEC, RC5, SAMSUNG)

13. Сделано 6 варианта замены символа при изменении времени. (позже будут добавлены еще варианты)

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

15. Таймер выключения с максимальным интервалом 99 минут. Выключает звук радио.

16. Установка предела яркости минимального и максимального.

17. Принудительный вывод бегущей строки с информацией о температуре в помещении и на улице

Программное обеспечение полностью написано на ассемблере а AVR Studio. Может где -то и не очень грамотно программа написана, т.к занялся ассемблером чуть больше года и то только в свободное время, но главное стабильно работает и быстро.

Сердцем устройства выступает AVR Atmega32 на частоте 16 мГц. Часы сделаны на двух платах. На одной матрицы с обвязкой. На второй все остальное.

Управление матрицами столбцами отдано регистрам STP16CP05 в корпусе TSSOP-24. У этих регистров 16 выходов и нагрузка на каждый выход до 80мА. Управление строками дешифратор 74HC138. На выход дешифратора установлены полевые транзисторы IRF7314. Модули RGB не впаивал в плату, а были установлены в разъемы.

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

FM радио реализовано а микросхеме RDA5807. У этой микросхемы достаточно хорошая чувствительность.

В качестве датчика влажности применен готовый модуль AM2321. В качестве датчика давления готовый модуль BMP180. В этих датчиках также есть датчик температуры.

Датчик BMP180:

Модуль AM2321:


В качестве эквалайзера применена микросхема TEA6330T. Она отвечает за регулировку громкости и настройки НЧ и ВЧ частот.

Усилитель мощности сделан на PAM8403. Маленькая микросхема но выдает достаточную мощность. На выход подключены динамики 8 Ом 2Вт. Можно подключать и динамики на 4 Ом. При 8 Ом выходная мощность примерно 1.6 Вт. При 4 Ом динамике 3 Вт.

В часах предусмотрена автономная работа от аккумулятора(АКБ) . Был установлен АКБ марки L12T1P31 3.7В 3700 мАч.

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

Время работы от АКБ в режиме часов с пониженной яркостью примерно 15 часов. Время работы с максимальной яркостью, шрифт широкий, громкость радио средняя примерно 5 часов.

Реализован контроль напряжения АКБ. Т.К внутренний контролер АКБ отключает ее при напряжении ниже 2.5 В, а при снижении напряжения ниже 3.2 В преобразователь начинает работать не правильно и на его выходе его всего 5В. Часы уходят в бесконечный Reset. Поэтому и был выбран порог напряжения 3.2В что бы не опускать до минимального напряжения для преобразователя. Если напряжение опуститься ниже 3.2В часы будут обесточены.

На плате предусмотрена возможность установки специализированной микросхемы контроля напряжения типа BU48xx. В данной схеме BU4832 контроль 3.2 В.

Контроль заряда аккумулятора собран на мс TP4056. Преобразователь напряжения с 3.7в до 12в собран на LM3488.

Предусмотрена возможность установки как 3-х так и 2-х контактных АКБ. Если не нужен контроль температуры АКБ то часть элементов не устанавливаем (подробно см. схему).Все схемы платы и прошивка в конце статьи.

На элементах R13 R14 собран делитель для контроля напряжения резервной батарейки.

На элементах R1 R10 R11 R12 собран делитель для контроля освещенности в автоматическом режиме.

На элементах Q1 Q2 R19-R22 собран согласователь уровней т.к некоторые мс питаются напряжением 3В, а некоторые 5В.

Для начала схемы основного модуля, модуля RGB матриц и модуля даткика наружной температуры

Несколько фотографий готовой платы RGB матриц. Платы из двухстороннего стеклотекстолита. Некоторые доработки делались в процессе отладки. Поэтому на некоторые фото немного отличаются от плат которые в архиве. В архиве все изменения учтены.

Нижняя сторона платы:


На нижней стороне резисторы на 2.4 кОм просто напаяны на выводы.

Верхняя сторона без установленных матриц:


Вид сбоку:


Вид сверху:


Эффекты смены цифр (на видео не все эффекты)

Управление часами


При первом включении часов управлять ими можно только кнопками. Что бы была возможность управлять ПДУ нужно обучить часы сигналам с ПДУ. Для этого зажимаем любую из кнопок управления и нажимаем кнопку "Сброс". Пробежит бегущая строка "Настройка ПДУ" и увидим отображение первого формата ПДУ - NEC . По умолчанию на матрицах высвечивается первый протокол NEC. Перебор протоколов нажатием любой из кнопок на задней панели часов. Протоколы меняются по кругу NEC - Samsung - RC5.




Как я уже написал выше по умолчанию выставлен протокол NEC. Берем ПДУ и нажимаем на нем любую кнопку. Если выбранный протокол и протокол ПДУ совпали то надпись станет зеленой и далее устройство перейдет в режим обучения ПДУ. Если протоколы не совпали то на матрицах не будет никаких изменений и надпись так и останется голубой. Переставляем на следующий протокол и жмем опять любую кнопку ПДУ.
Определение протокола должно сработать с первого нажатия кнопки ПДУ. Если срабатывает со второго нажатия кнопки ПДУ или более то протоколы не совпадают и надо выбрать другой протокол.
Т.к протоколы NEC и Samsung немного похожи то пульт формата NEC можно обучить в режиме протокола Samsung но потребуется по два раза нажимать одну и туже кнопку. В дальнейшем попробую подкорректировать интервалы замеров импульсов и возможно это уберется.

Протокол RC5 самый простой. В этом режиме можно обучить любой ПДУ но работать он нормально не будет. В этом режиме будет нормально работать только ПДУ протокола RC5 .
Про отличие протоколов ПДУ очень много написано статей и описывать тут все это не буду.

После определения протокола программа перейдет в режим обучения кнопок ПДУ. На матрицах будет высвечиваться какую кнопку нужно нажать. В режиме ожидания цвет символа будет голубой.


Нажимаем кнопку на ПДУ. Если данные приняты удачно то цвет символа станет зеленым:


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


После того как будет нажата последняя запрашиваемая кнопка. Часы перейдут в основной режим работы. Если не будет установлено ни одного датчика ни микросхемы реального времени, то будет постоянно выводится бегущая строка " Ни одного датчика не найдено". Если не устанавливать DS3231 и установить любой из датчиков, то на матрицах будут выводиться только мигающие точки.

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

После настройки пульта мы увидим отображаемое время. Из этого режима можно перейти в разные меню. В программе предусмотрен контроль установленных микросхем TEA6330 и RDA5807. Если какая-то микросхема ни будет установлена то не возможно будет перейти в меню ее настройки и будет введена соответствующая бегущая строка либо "Эквалайзер не найден" либо " Радио не найдено ".

Описание действия кнопок ПДУ:

Кнопка Вкл/Выкл - включение - выключение матрицы

Кнопка стрелка вверх и стрелка вниз - регулировка яркости матрицы или изменения данных в режиме настройки. Яркость этими кнопка регулируется во всех меню.

Кнопка стрелка влево или стрелка вправо - смена шрифта в режиме отображения времени, а в режиме настройки будильника перемещение по подменю будильника

Кнопка "1" - меню "Часы"

Кнопка "2" - меню "Радио"

Кнопка "3" - меню "Будильники"

Кнопка "4" - меню "Эквалайзер"

Кнопка "5" - вывод бегущей строки

А) одно нажатие вывод информации со всех датчиков

Б) два нажатия в течении 3 сек вывод только уличной температуры.

Кнопка "6" - меню "Таймер выключеня"

Кнопка "8" - установка/сброс минимальной и максимальной яркости

Кнопка "9" - сброс секунд и коррекция времени через интернет

Кнопка "0" - автоматическая или ручная регулировка яркости

Кнопка CH+ и CH - перелистывание радио станций в режиме FM

Кнопка звук + и звук - регулировка громкости

Кнопка Вкл/Выкл звука - включение выключение звука

Кнопка "МЕНЮ" - переход в основные настройки в режиме времени, переход в настройки радио в режиме радио и настройки будильников в режиме будильников

Кнопка "ESC" - выход из подменю

Переход в зависимости оттого в каком режиме находится устройство. Если находится в режиме "Время" ,то переход будет в основные настройки. Если в режиме "Радио" , то переход в настройки радио. Если в режиме "Будильники", то в настройки будильника

Кнопка "ОК" - подтверждение изменений в режимах настройки

В часах 8 шрифтов для отображения времени. Переключение между шрифтами кнопками "Влево" или "Вправо" ПДУ или S6 на плате часов.

Пример широкого шрифта:


Пример узкого шрифта:


Добавление своих шрифтов

Шрифты хранятся в EEprom. Максимально можно загрузить 8 шрифтов.

Шрифт создавал при помощи PixelFontEdit-2.7
Сам шрифт и исходник Eprom для Atmel studuo6.2 в архиве в конце страницы.
Шрифты добавляем с метки eeFront1 :
Кол-во шрифтов не более 8, больше не поместятся. Программа сама определит кол-во загруженных шрифтов.
В конце шрифтов обязательно должна быть строка .db "E","N","D" означающая конец шрифтов.
Буквы END могут быть большие или маленькие но латинские.
Компилируем файл EEprom и прошиваем его. Можно сразу в EEprom забить все свои значения в нем подробные коментарии к каждой строке.
Также в шрифте задается какие будут точки разделяющие часы и минуты.Устанавливается только в первом символе каждого шрифта.
В первом символе если установлен бит0 первого байта то будет двойная, если сброшен то одинарная.

Настройки в режиме отображения времени

В этом меню изменения параметров кнопками "Вверх" и "Вниз" подтверждения изменений кнопка "ОК". Выйти из настройки можно в любой момент нажав кнопку "ESC".

Нажимаем кнопку "Меню" пробежит бегущая строка "Основные настройки" далее строка "Цвет радио". Будет мигать целая часть диапазона FM , кнопками "Вверх" или "Вниз" устанавливаем нужный цвет и нажимаем кнопку "ОК". Начнет мигать дробная часть диапазона FM . Также устанавливаем нужный цвет и нажимаем "ОК".



Если будет установлен вариант цвета для бегущей строки как на фото:


То при выводе бегущей строки цвет ее будет каждый раз новый.

Далее пробежит строка " Установка часов". Начнут мигать значение часов. Кнопками "Вверх" или "Вниз" устанавливаем текущий час и нажимаем "ОК". Начнут мигать минуты. Кнопками "Вверх" или "Вниз" устанавливаем текущие минуты и нажимаем "ОК".


Далее пробежит строка "Цвет часов". В этом меню для каждого символа часов можно настроить свой цвет. Выбор цвета кнопками "Вверх" или "Вниз" . Всего 7 вариантов цветов. Восьмой вариант это разноцветный символ с чередующимися цветами. Как только выбрали нужный цвет нажимаем кнопку "ОК". Далее настраиваем цвет для следующих символов и жмем "ОК".


Кнопками "Вверх" или "Вниз" устанавливаем текущую дату и нажимаем "ОК".




Далее пробежит строка "Установка дня недели". Дни недели отображаются в сокращении ВС- воскресение, СБ - суббота, ПТ - пятница и т.д. Кнопками "Вверх" или "Вниз" устанавливаем текущий день недели и нажимаем "ОК". На этом основная настройка завершена.


Далее пробежит бегущая строка с текущем днем недели, датой, годом, давление, температурой и влажностью. Устройство перейдет в режим отображения времени. В этом режиме бегущая строка с выводом информации о температуре, давлении и влажности в помещении будет выводится примерно через 4 минуты. Вывод информации со всех датчиков кнопка "5" ПДУ. Строка о температуре и влажности на улице выводится 1 раз в 15 минут (можно самостоятельно установить нужный интервал, об этом чуть ниже.) Если по каким либо причинам какой-то датчик не будет установлен то информация с этого датчика не будет отображена в бегущей строке. Т.к датчик температуры есть во всех трех датчиках, то данные будут читаться из того датчика который установлен. По умолчанию температура читается с DS3221.

Режим Радио.

Переход в этот режим осуществляется нажатием кнопки "2" ПДУ. Диапазон радио 65мГц - 108мГц.

В этом режиме бегущая строка также пробегает примерно через 4 минуты.

Отображается следующее меню:


Первые четыре цифры это частота принимаемой станции. Буква "М" или "С" принимаемый сигнал моно или стерео. Под буквой красным цветом уровень принимаемого сигнала. В самом низу отображается громкость.Перелистывание настроенных станций кнопками "СН+" и "СН-" , а регулировка громкости кнопками "Гр+" и "Гр-". Значение громкости сохраняется для каждой станции в отдельности. Так же для каждой станции отдельно сохраняются настройки эквалайзера ВЧ и НЧ. Для перехода в режим эквалайзера нажимаем кнопку "4" ПДУ (описание настройки чуть ниже).

Для перехода в режим настройки радио нажимаем кнопку "Меню" ПДУ. Будет выведена бегущая строка " Настройка радио".

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


Изменение частоты станции осуществляется кнопками "СН+" и "СН-" и кнопками "Влево" "Вправо" ПДУ.

В диапазоне 76-108 мГц клавишами "Влево" и "Вправо" изменения диапазона на 1мГц, а клавиши "CH-" и "CH+" ПДУ изменения на 0.1мГц.
В диапазоне 65-76 мГц клавишами "Влево" и "Вправо" изменения диапазона на 0.1мГц, а клавиши "CH-" и "CH+" ПДУ изменения на 0.01мГц.

Регулировка громкости кнопками "Гр+" и "Гр-". После выбора нужной частоты и уровня громкости нажимаем кнопку "ОК". Будет отображена ячейка для записи текущей станции. Если в ячейке уже есть записанная станция то номер ее будет красным цветом.



Если ячейка свободна то она будет зеленым цветом


Выбор нужной ячейки кнопками "СН+" и "СН-". Всего доступно 20 ячеек для записи. После выбора нужной ячейки нажимаем кнопку "ОК". В ячейку будет записана частота станции, и уровень громкости. Устройство вернется в режим настройки. Что бы выйти из режима настройки нажимаем кнопку "ESC" или ничего не нажимаем в течении 15 секунд. Устройство перейдет в режим воспроизведения радио.

Настройка будильников

Переход в режим настройки будильников кнопкой "3" ПДУ. Будет выведена бегущая строка "Будильники". Если ни одна кнопка не будет нажата в течении 15 секунд устройство перейдет в режим отображения времени.

После перехода в меню будильников увидим следующее:


Б1 означает номер будильника. Всего их семь. Прочерки после номера означают что будильник не включен. Кнопками "Влево и "Вправо" изменяем номер будильника. Если будильник выключен будет не возможно просмотреть его настройки. Для включения будильника нажимаем кнопку "ОК". Включится светодиод обозначающий что есть включенные будильники и появится колокольчик.


Теперь если можно будет просмотреть настройки будильника. Меню каждого будильника поделено на четыре части. В первой части настраивается время включения, громкость. Во второй части настраивается время выключения и режим звука линейный или нарастающий. В третьей части настраивается включаемая станция или выбирается зуммер. В четвертой части выбираются дни включения будильника. Нажмем кнопку "Вправо" перед нами будет первая часть.


Здесь отображается время включения будильника. В правом нижнем углу громкость включения. Для изменения настроек нажимаем кнопку "ОК". Начнет мигать часы включения. Кнопками "Влево" и "Вправо" изменяем значение и нажимаем кнопку "ОК". Так настраиваем все четыре параметра.

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

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

Линейная громкость отображается справа в виде прямоугольника:


Нарастающая гро мкость отображается в виде треугольника:


Все действия для настройки такие же как и в первой части.

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

После настройки второй части нажимаем кнопку "Вправо" и попадаем в третью часть настройки будильника.


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


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

После настройки третьей части нажимаем кнопку "Вправо" и попадаем в четветрую часть настройки будильника.

Здесь настраиваем дни включения будильника.


Обозначение дней недели сделано цифрами. 1-понедельник, 2 - вторник, 3 среда и т.д

Нажимаем "ОК" ПДУ начнет мигать первый символ. Кнопками "Влево" или "Вправо" ПДУ включаем день недели или выключаем. Зеленый цвет это задействован день, красный не задействован. Внизу под задействованным днем загорается светодиод. Сделано для того кто собирал часы на обычных светодиодах что бы видеть какой из дне недели задействован.

После настройки нажимаем кнопку "Вправо " на ПДУ и переходим к следующему будильнику или нажимаем "ESC" и выходим в режим отображения времени.

При срабатывании будильника светодиод начнет мигать. Если во время работы будильника нажать кнопку "Выкл" ПДУ светодиод перестанет мигать и будильник не выключится по достижению времени выключения т.е происходит отмена выключения будильника.

Настройка эквалайзера

Переход в меню настройки эквалайзера нажатием кнопки "4" ПДУ. Пробежит бегущая строка "Эквалайзер" .В этом режиме настраиваются НЧ и ВЧ частоты. Для каждого канала радио настройки эквалайзера сохраняются отдельно. Если ни одна из кнопок не будет нажата в течении 15 секунд то будет осуществлен переход в режим отображения времени.

Для настройки нужных частот нажимаем кнопку "Влево" или "Вправо" ПДУ.

Настройка НЧ и ВЧ максимум:



Для изменения фильтра НЧ или ВЧ нажимаем кнопку "ОК" . Начнет мигать полоса слева. Кнопками "Влево" и " Вправо" меняем значение. Для сохранения установленного значения жмем "ОК" ПДУ. В зависимости от величины значения также меняется цвет указатели и цифры обозначающие уровень. Ближе к максимальному значению будут красными. Ближе к минимальному значению синим цветов, а в среднем диапазоне зеленым цветом.

Настройка НЧ минимум:


Настройка НЧ среднее значение:


Установка-снятие порога минимальной яркости

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

Нажимаем кнопку 8 ПДУ попадаем в меню установки пределов яркости.

Первое устанавливает предел минимальной яркости.


Кнопками "Вверх" "Вниз" ПДУ устанавливаем нужную яркость и нажимаем "ОК" ПДУ. Будет длительный сигнал зуммера и записан порог минимальной яркости.


Кнопками "Вверх" "Вниз" ПДУ устанавливаем нужную яркость и нажимаем "ОК" ПДУ.

Будет длительный сигнал зуммера и записан порог минимальной яркости.

В следующем разделе устанавливается время включения минимальной яркости, а далее время включения максимальной яркости.

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

Что бы задействовать режим автоматической регулировки яркости на ПДУ нажимаем "0" переводя в автоматический режим работы. На передней панели часов загорится светодиод.

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

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

Для установки порога с кнопок читаем ниже 8 пункт.

Управление кнопками

На задней панели часов размещены кнопки S4-S9 . В зависимости в каком меню находимся функция кнопок изменяется.

Кнопка S8 переход в разные меню. Для того что бы перейти в нужное меню нажимаем S8 и удерживаем. После необходимого кол-ва сигналов зуммера отпускаем и попадаем в выбранное меню. Кол-во сигналов зуммера будет соответствовать номеру кнопки ПДУ. Если будет 4 сигнала то значит попадем в меню "Эквалайзер" , если 1 сигнал то меню отображения времени и т.д.

1. Функции кнопок в меню часов:

S6 изменение шрифта

S7 сброс секунд и обновление времени через интернет

S9 переход в основные настройки.

При нажатии S9 переходим в меню настройки. В этом меню кнопками S4 и S5 изменяем параметр, кнопка S6 переход к следующему настраиваемому параметру. Выход из меню не возможен придется пройтись по всем настраиваемым параметрам.

После настройки последнего параметра пробежит бегущая строка и устройство вернется в режим отображения времени.

2. Функции кнопок в меню радио:

S4 и S5 регулировка звука "+" и "-"

S6 и S7 переход по настроенным каналам "+" и "-"

S9 переход в режим настройки радио

В меню настроек действие кнопок:

S4 и S5 регулировка звука "+" и "-"

S6 и S7 изменение частоты радиостанции "+" и "-"

S9 переход в меню записи станции в память. Кнопками S6 и S7 меняем номер ячейки памяти, а кнопка S9 подтверждение записи.

Выход из меню настроек- не нажимаем ни какие кнопки и выход будет автоматический через 15-20 сек.

3. Функции кнопок в меню настройки будильников:

S4 и S5 перелистывание будильников "+" и "-"

S6 и S7 не задействованы

S9 Включение и выключение будильника

Если будильник включен нажимаем S4 попадая в основные настройки включенного будильника. Переход по подменю будильника S4 и S5. Для настройки параметра жмем S9 и S4 и S5 изменяем параметр. Следующее нажатие S9 переход к следующему настраиваемому параметру. Устройство выйдет из настроек будильника автоматически если в течении 15 сек. не будет нажата любая из кнопок.

4. Функции кнопок в меню эквалайзера:

S4 и S5 перелистывание настраиваемых параметров.

S6 вход в режим изменения выбранного параметра. В этом режиме S4 и S5 изменяет параметр, а S6 подтверждения изменения.

S7 не задействована.

Выход из режима автоматический через 15 сек если ни одна из кнопок не нажималась.

6. Функции кнопок в меню таймера выключения:

S4 и S5 установка интервала времени выключения с шагом в 5 минут. Интервал 0 минут означает, что таймер выключен

S6 выход в меню отображения времени.

7. Свободно. Возможно будет использовано в дальнейшем.

8. Функции кнопок в меню установки предела яркости:

S4 и S5 изменение яркости.

S6 подтверждение выбранной яркости.

S7 выход из настройки.

9. Свободно. Возможно будет использовано в дальнейшем.

Немного о примененных деталях и их замена и наладке.

Все SMD элементы типоразмера 0805. Стабилизатор U1 LM317ADJ можно заменить на любой с напряжением стабилизации 3 Вольта например ASM1117-30. В этом случаи R18 заменить перемычкой, а R17 не устанавливать. Стабилизатор на 5В U8 LM2576-5 можно заменить на LM2596-5. Светодиоды D6 D7 D11 D12 D13 типоразмера 2835. Резистором R24 устанавливаем громкость работы зуммера. Не забываем выставить ток заряда для применяемого аккумулятора резисторами R44 R45 (см. таблицу на схеме). При большом токе заряда рекомендую на TP4056 установить радиатор. Резисторами R38 и R49 можно ограничить максимальную громкость или же их заменить перемычками. Конденсаторы С28 С29 С42 С43 в обвязке TEA6330T устанавливать которые указаны на схеме. Уход от емкости в ту или иную сторону изменить диапазон регулировки частот. С28 и С43 отвечают за НЧ, а С29 и С42 за ВЧ. Пробовал ставить 6.8nF C29 и С42 ВЧ частоты практически не изменяются. Резисторами R28 R33 R31 можно изменить напряжение на выходе преобразователя. При номиналах указанных на схеме напряжение 11.86В.

Рассчитывается напряжение по формуле:

Uвых=1.26*(1+((R33+R28)/R31))

Диоды Шотки D4 D5 D1 D8 D10 можно заменить любыми на ток 2А. Стабилитрон D3 можно остановить любой на напряжение стабилизации 4.3В 4.7В или 5В.

Монтаж лучше начинать с установки всех стабилизаторов напряжения и проверить их на работоспособность, что бы не было обидно за спаленные контроллеры и датчики. Затем лучше смонтировать преобразователь и проверить его подав на 8 ногу LM3488 напряжение в пределах 3.2-5В. На выходе должно быть около 12В.

Далее монтируем элементы для заряда АКБ и контроля напряжения АКБ и проверяем на работоспособность. Для 2-х проводного АКБ не устанавливаем R39, а R43 заменяем перемычкой. Также поступаем для 3-х проводного если не нужен контроль температуры АКБ. Средний вывод АКБ остается болтаться. Если же контроль температуры нужен, то устанавливаем все элементы, хотя R43 можно и не ставить т.к оно будет подключено параллельно терморезистору АКБ и сильно влиять на работу не будет.

Контролер напряжения аккумулятора собран на TL431 и LM358. На TL431 собран источник образцового напряжения на 2.5 В. Это напряжение поступает на 2 вывод LM358, а на 3 вавод LM358 поступает напряжение от АКБ через делитель R58 R62 как только напряжение на АКБ будет ниже 3.2 на 3 выводе также напряжение станет меньше 2.5 вольт и на 1 выводе будет 0 Q9 закроется. Положительное напряжение через R56 R51 поступит на базу Q8. Q8 Закроется тем самым и закроет транзистор Q7.1 обесточивая всю плату. После отключения запуск часов будет возможен только при подключении внешнего блока питания т.к LM358 питается от внешнего источника питания.

Если есть микросхема BU4832 то TL431 LM358 их обвязку и R56 не устанавливаем. Если же собираем на TL431 и LM358 то не устанавливаем R53 и возможно придется подстроить порог срабатывания делителем R58 R62. При наличии основного питания должен идти заряд АКБ, а на выходе преобразователя не должно быть напряжения. Если напряжение АКБ выше 3.2 вольт то на базе транзистора Q9 должно быть примерно 0.7- 0.8В.

Теперь проверяем работу резервного источника питания. Сначала схему запитываем от основного источника. Затем его выдергиваем из сети и на выходе преобразователя должно появится 12В. Если преобразователь не запустился то смотрим что на базе Q9 . Если там напряжение меньше 0.7В, а АКБ заряжена, то измеряем напряжение на 2 выводе LM358 должно быль 2.5В и на 3 выводе больше 2.5В. Если все нормально, то неисправна LM358 или не подключено к ней питание. Порог отключения можно отрегулировать R62 . При увеличении его номинала порог отключения увеличивается т.е устройство будет полностью обесточено при более глубоком разряде АКБ. При уменьшении номинала порог уменьшается.

Конденсаторы C6 C11 C21 C20 C37 рекомендуется применять с низким ESR.

Если все работает то монтируем процессор прошиваем его и монтируем все остальные элементы.. На плате для удобства прошивки предусмотрены контакты для ISP разъема. Вывод GND для ISP паяем в любом удобном месте.

Индикация светодиодов

Светодиод D11 светится, то значит установлен режим автоматической регулировки яркости. Ручная регулировка не работает. Переключается режим кнопкой "0" ПДУ.

Светодиод D12 светится, то значит есть включенные будильники. Если мигает, то сработал будильник.

Светодиод D13 светится, то значит нужно заменить резервную батарейку часов. Даже если вытащить батарейку время не сбросится т.к за счет заряда конденсаторов некоторые время будет питаться DS3231. Так что минут 5 есть на замену батарейки.

Светодиод D6 светится, то значит идет заряд АКБ

Светодиод D7 светится, то значит АКБ заряжена.

Если оба светодиода D6 и D7 не светятся, то это значит, что часы работают от внутреннего АКБ.

Изготавливаем печатные платы (ПП)

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

Версия платы с WI-FI.



Печатные платы изготавливаю при помощи негативного фоторезиста. Печатаю шаблон на пленке струйным принтером Epson L800 в негативном виде. Очищаю заготовку при помощи порошка "Пемолюкс". На мокрую заготовку наношу пленочный фоторезист. За неимением ламинатора проглаживаю утюгом выставленным на деление 1. Накладываю шаблон и засвечиваю тремя УФ лампами по 20W каждая в течении 1 мин 10 сек. Далее опять под утюг, затем смываю не задубленные участки и травить. Сверлю несколько отверстий в крайних контактных площадках и делаю проколы в этих же местах в шаблоне. Плата и шаблон совмещается при помощи иголок. Травится вторая сторона. Первая заклеивается скотчем.

Сверлим отверстия. Переходные отверстия обычно сверлю диаметром 0.4 мм и запаиваю в них проволочки. Запаиваем элементы.

Фото готовой платы с WI-FI



К монтажу относимся внимательней что бы потом не искать почему не работает. Некоторые выводные элементы нужно запаивать с двух сторон. Выводы этих элементов как переходные отверстия. Под кварц на плату нужно наклеить скотч или изоленту что бы не было контакта с дорожками или перепаять на сторону Atmega32.

Корпус был изготовлен из 4мм мягкого пластика. Режется отлично канцелярским ножом. Т.к светодиоды находятся глубоко то нужны световоды. В качестве световодов использована крышка коробки от автомобильных ламп. Корпус был начерчен в CorelDRAW распечатан на простой бумаге и с помощью двухстороннего скотча закреплен на пластике и вырезан. Прячем все в корпус, прикручиваем антенну:


Датчики торчат сверху:


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

Получилось так:


Вот вроде и все. В планах добавить возможность вывод информации с RDS . Так же сделать еще один вариант под энкодеры.

Написана программа для модуля ESP8266. Также изменена прошивка для Atmega32 в архиве все обновлено. Так же были внесены изменения в схему. Были неправильно подключены сигналы TX RX от Atmega к ESP8266. Все изменения описанные здесь учтены в схеме в архиве.

В схеме сделаны небольшие доработки. Объединены сигналы Reset Atmega и ESP8266. Но для этого нужно согласовать сигналы по напряжению. Добавлен резистор на 9.1 кОм между Reset Atmega и GND. Резистор 10кОм между Reset ESP8266 и шиной 3V удален. Напряжение на выводе Reset должно стать в пределах 3V-3.3V.

Добавлено согласование уровней TX RX. Хотя можно обойтись и без него но сделал так как должно быть по правилам.

Для прошивки модуля ESP8266 необходимо:

1.Качаем(если не установлена) Arduino IDE с офф сайта (https://www.arduino.cc/en/Main/Software ), устанавливаем.

2.Запустить Arduino IDE, далее Файл - Настройки - в поле Additional Boards Manager URLs вставить ссылку на стабильную версию http://arduino.esp8266.com/package_esp8266com_index.json

4.В Boards Manager в поле фильтра введите esp8266 или вручную пролистайте список и кликните на ESP8266 by ESP8266 Community Forum
Кликните Install и дождитесь окончания загрузки (около 130 Мегабайт).
Если загрузка произошла слишком быстро, возможно, что вы уже устанавливали Arduino IDE для ESP8266 и потребуется почистить кэш Boards Manager, иначе у вас останется установленной старая версия. Нужно сначала деинсталлировать старую версию, а потом необходимо удалить файлы кэша. Для Win7 x64 удалите файлы из папки C:UsersПользовательAppDataRoamingArduino15 и повторите все, начиная с п.2
Закройте Boards Manager и в меню Инструменты выберите Плата - Generic ESP8266

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

Для прошивки модуля использовал PL2303. Драйвера на нее ниже в архиве на Win 8.1 работает. Драйвер устанавливаем в ручную.

Можно использовать любой USB-COM переходник.

Модуль ESP 8266 запаиваем на плату. Соединяем модуль с USB-COM переходником соединения сигналов TX RX перекрестное т.е TX ESP8266 на RX USB-COM и RX ESP8266 на TX USB-COM. Устанавливаем перемычку на XP10 и убираем перемычки XP11 и XP12. Нажимаем Reset.

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

В прошивке вписываем имя своей сети WI-FI и пароль к ней.

Нажимаем иконку "Стрелка вправо" начнется прошивка модуля.

После завершения прошивки удаляем перемычку XP10 и устанавливаем назад XP11 и XP12.

Рекомендую проверить открыты у Вас порты 123 на ПК и в роутере.
Проще это сделать на ПК, в настройках времени вкладка Время по интернету - Изменить параметры. В поле вбиваем time.nist.gov и жмем "Обновить сейчас". Если напишет, что время было успешно обновлено то у Вас порты открыты. Если ошибка то открываем порты в роутере.
У меня дома на роутере ASUS RT16 с прошивкой Tomato все работало по умолчанию отлично.
На работе на роутере Zuxel Keenetic Giga II с родной прошивкой пришлось открывать порты, хотя в самом роутере время обновлялось с интернета.

Время обновляется нажатием кнопки "9" ПДУ. Единственное, что надо учитывать при обновлении времени, если не будет доступа к интернету то секунды все ровно обнулятся.
После включения часов или нажатием кнопки Reset для обновления времени через интернет необходимо выждать секунд 30. Это время необходимо модулю для подключения к сети или для возврата ошибки если нет подключения. Если раньше нажимать кнопку "9" ПДУ просто ничего не будет происходить. Во время синхронизации времени матрицы на 1-2 секунды будут погашены.
Если не будет доступа к интернету то выведется строка "Упсс Нет сети" .
Если будет доступ к сети но не будет доступа к серверу точного времени то выведется строка "Нет доступа к серверу времени"

Также появиться новая сеть c именем "Datchik" .Ее название также можете изменить на любое в Arduino и изменить пароль к ней. К этой сети будет подключаться второй модуль с уличными датчиками.

PS. От автоматической синхронизации решил отказаться т.к у DS3231 очень маленький уход по времени и синхронизация нужна не чаще 1 раз в месяц и то уход несколько секунд. Так что проще сделать это в ручную.

Была написана программка теста матриц. Фьюзы как для основной прошивки.
Все контролируется визуально.

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

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

2.Вторая часть теста зажигает только один светодиод и прогоняет его по всем строкам в каждом из трех цветов R G и B . Должен зажигаться только один светодиод. Если будут гореть два светодиода значит где-то КЗ на выводах регистров матриц.

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

Модуль датчика наружной температуры.

Модуль датчика наружной температуры так же сделан на ESP8266 и в качестве датчика применен AM2321. Так же в этом модуле установлен датчик комнатной температуры. Сделано это для того если будут собираться несколько одинаковых часов и тогда данные они могут получать с одного модуля. В наружный модуль встроена USB зарядка для аккумулятора, а также контроль напряжения АКБ. При снижении напряжения АКБ ниже 3.2 вольта загорится светодиод.

Также добавлен визуальный контроль ошибок.

1. Нет датчика AM2321 горит зеленый светодиод

2. Нет датчика BME280 горит красный светодиод

3. Неудачное подключение к WI-FI сети горит синий светодиод



Прошивка делается также как и для модуля в часах. Единственное некоторые строки нужно изменить в программе.

Устанавливаем время опроса датчика температуры. По умолчанию установлено 900 сек = 15 мин
Меняем значения в строке:

ESP.deepSleep(900*1000000,WAKE_RFCAL); // время 900 секунд = 15 минут
Меняем на свое значение. Сохраняем и прошиваем.

У меня модуль питается от аккумулятора по 1000мА.

Многие радиолюбители, начинающие и не только любят «изобретать велосипед» - строить СВОИ электронные часы. Не обошла эта участь и меня. Конструкций часов в инете сегодня конечно предостаточно, но вот часов на светодиодных матрицах почему-то среди них единицы. В русскоговорящем интернете я нашел только одну полностью законченную и описанную конструкцию. В тоже время, светодиодные матрицы сейчас очень сильно подешевели, и их стоимость не выше, а то и ниже, чем у семисегментных индикаторов такого же размера. Например примененные мной 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мм. Так что любители «ЛУТа» смогут без труда изготовить плату самостоятельно.


Часы на светодиодных матрицах (описание и сборка)
Оригинал статьи находится по адресу: http://radiokot.ru/circuit/digital/home/103/
Автор разработки O-LED, комплектация
Схема устройства была немного изменена и стала иметь следующий вид:

Сердцем устройства является микроконтроллер IC1 , в него «зашита» управляющая программа, которая ведет отсчет времени, опрашивает датчики температуры D1, D2, датчик освещенности R35, подает звуковые сигналы на SP1 и выводит информацию на светодиодные матрицы H1-H3.
Для согласования микроконтроллера с матрицами служит микросхема D3. Это регистр с мощным выходом, который выдерживает большие токи, чем МК и позволяет «сэкономить» его выводы.
Микросхема D4 – это ШИМ контроллер. Его задача понизить входное напряжение от +7…+24Вольт до стабильных 5 вольт. Резисторами R32,R33 и задается значение выходного напряжения. Очень толковое описание данной микросхемы можно почитать по этой ссылке (http://mysku.ru/blog/aliexpress/39481.html). В качестве измерителей температуры служат цифровые датчики D1 и D2, один из которых устанавливается на улице, другой остается дома, на плате. Конденсаторы С1-С4, С6, С7, С10 служат для сглаживания помех по питанию. Конденсатор большой емкости С5 (ионистор) необходим для поддержания питания микроконтроллера при пропадании общего напряжения на схеме. Это позволяет не сбрасываться часам при пропадании электричества. Его заряда хватает на 3 - 4 дня непрерывной работы IC1. При этом диод VD1 не позволяет ему разряжаться через другие элементы схемы.
Датчиком освещенности служит фоторезитор R35. Он реагирует на внешнее освещение и «говорит» МК какую необходимо установить яркость свечения светодиодных матриц. При отсутствии R35 яркость свечения в ночное и дневное время можно задать программно.
Кварцевый резонатор XT1 задает тактовую частоту отсчета времени. От его работы зависит точность хода часов. Автором устройства была предусмотрительно сделана программная коррекция точности.
Настройка, установка и управление устройством осуществляется всего двумя кнопками «ОК» и «STEP» (в переводе ШАГ). Сброс устройства осуществляется кнопкой «RESET»(SW3). Кнопкой «STEP» как правило происходит переход к следующему пункту меню, а кнопкой «ОК» изменение параметров текущего меню. Сигнал сработавшего будильника также выключается кнопками «ОК» или «STEP». Нажатие любой кнопки во время сигнала будильника отключает его.
Схема управления получилась такой:



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

Сборка часов.


Итак, набор пришел, начинаем сборку!
Первым делом устанавливаем ШИМ – контроллер на микросхеме D4, а так же элементы его обвязки C1, С8, С9, R32 – R34, L1 и VD2. Следует обратить внимание, что в наборе, в основном, используются резисторы и конденсаторы типоразмера 1206, и только элементы ШИМ-контроллера (перечисленные выше) имеют меньший размер – 0805.

После успешной установки подаем питание на схему больше 7вольт и проверяем, что бы на плюсовом выводе С1 было напряжение примерно 5вольт. Если все в порядке, продолжаем сборку устройства в любом порядке, запаивая сперва самые мелкие детали, потом крупнее и т.д.
Если планируется питать плату от пятивольтового источника тока, схему ШИМ- контроллера можно не собирать, а подавать напряжение непосредственно на С1, соблюдая полярность.
На плате предусмотрено три варианта установки датчика освещения R35. Куда именно его запаять – выбор за вами, это никак не отразится на работе схемы. Тоже самое и с кнопками управления “OK” и “STEP”. Их можно спрятать с обратной стороны платы, а излишки печатки отрезать по белой линии – что бы получить миниатюрный монолитный блок, который легко встроить в какой ни будь небольшой плоский корпус.




После сборки часы заработают сразу. Управляющая программа уже ЗАШИТА в микроконтроллер. Нажав кнопку «RESET» часы высветят версию прошивки (в нашем случае это v.1_09) и через пару секунд начнут показывать время, маяча секундной точкой слева направо. Нужно убрать защитную пленку с матриц и можно пользоваться часами.
Ну и финальный результат должен выглядеть примерно так:


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

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

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


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

Похожие публикации