Реклама ⓘ
Главная » Arduino
Призовой фонд
на апрель 2024 г.
1. 100 руб.
От пользователей

Похожие статьи:


Реклама ⓘ

Часы на ардуино из дисплея покупателя (VFD)

Как и многие радиолюбители я испытываю весьма теплые чувства к ламповой технике, в особенности - к различным газоразрядным индикаторам. В связи с этим захотелось изготовить что-нибудь с использованием этих самых индикаторов,  что-нибудь красивое и одновременно функциональное и полезное. Было решено сделать часы с дополнительными функциями, такими как отображение температуры и влажности. В сети много материалов посвященных изготовлению часов на газоразрядных индикаторах (ГРИ), их можно найти по запросу nixie clock. Несомненно, они выглядят классно, но мне больше по душе дисплеи на вакуумно-люминесцентных индикаторах (ВЛИ, VFD), с таким дисплеем мы и будем работать. 

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

  • Отображение времени в формате чч:мм:сс
  • Отображение температуры
  • Отображение влажности воздуха
  • Автоматическая регулировка яркости дисплея в зависимости от внешнего освещения
  • Возможность настройки времени (Установка часов, установка минут, сброс секунд)
  • Режим динамического изменения порядка отображения информации для продления срока службы люминофора.

Итак, за основу взят дисплей покупателя Futaba vfd9cb1010 rev 1.2, имеющий 2 стоки по 20 символов в каждой:

Мозгом системы будет плата ардуино нано третьей ревизии, за отсчет времени будет отвечать модуль DS3231, а за измерение температуры и влажности - модуль BME280 (Версия для питания от 5 вольт, со стабилизатором!)

Помимо этого понадобится: фоторезистор (GL5528) с заявленным сопротивлением на свету 8-20 кОм и около мегаома в темноте для отслеживания уровня освещенности и установки яркости дисплея, четыре тактовых кнопки, пару электролитических конденсаторов, резистор на 180кОм, линейный стабилизатор LM7805 (лучше посадить на радиатор) или DC-DC преобразователь, блок питания на напряжение от 10 до 25 вольт (в моем случае 12 вольт, больше 19 вольт, думаю, использовать не стоит), ну и корпус, фурнитура и прочее - по вкусу.

Получает данные этот дисплей согласно документации по интерфейсу RS-232С, поэтому просто подключить его напрямую к ардуино через штатный разъем (RJ-45 в моем случае) не удастся, так как ардуино работает с 5 вольтовой ТТЛ логикой, поэтому нужно либо использовать конвертер уровней, либо подключиться напрямую к процессору, что мы и сделаем. В даташите на процессор ищем ногу RxD, здесь это пятая нога:

К ней и нужно подпаяться (в моем случае эта нога напрямую соединена со второй ногой микроконтроллера AT89C2051, и я подпаялся к ней, т.к. просто удобней, она больше), помимо этого нужно еще два провода, это питание. Землю на плате найти не составит проблем, плюс питания у меня подпаян на разъем выключателя питания (CON2 на фото). 

Далее подключаем это все к ардуино согласно схеме:

Тут все просто: 12 вольт идут на дисплей, и на линейный стабилизатор на 5 вольт (можно заменить понижающим DC-DC преобразователем), от которых уже запитывается ардуино, пару конденсаторов по питанию на 12 и 5 вольтовых линиях соответственно. Далее к контроллеру по шине I2C (пины А4 и А5) подключены модуль часов реального времени DS3231 и датчик BME280 (5 вольтовая версия!), он будет измерять температуру и влажность (еще и давление, но лично мне оно не особо интересно, поэтому его не отображаем). Кнопки подключены к пинам 17(А3), 16 (А2), 15(А1), кнопка 1, 2 и 3 соответственно. Отдельно вынесена кнопка перезагрузки (Reset), так, на всякий случай. Датчик света (фоторезистор) вместе с резистором R2 на 108 кОм образует делитель напряжения, которое подается на 20 пин (А6). Величина резистора R2 подбирается таким образом, чтоб она соответствовала примерно сопротивлению фоторезистора при наиболее частом уровне освещенности, например, мой экземпляр резистора имеет сопротивление около мегаома в темноте и 100-200 ом на ярком свету. В комнате при дневном или электрическом свете его сопротивление около 100-200 кОм, соответственно, это и будет его крейсерский режим, поэтому резистор R2 я взял на 180 кОм. Ну и собственно дисплей: помимо питания (земля дисплея обязательно соединена с землей ардуино!) вывод RxD c процессора подключается к 10 пину ардуино (D10). 

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

Итак, по порядку:

  • Переключатель 1: его назначение осталось загадкой для меня (если кто подскажет,  буду очень рад), эмпирическим путем его рабочее положение определено как включено (вниз)
  • Переключатели 2 - 3: отвечают за протокол передачи данных, нам нужен первый Firich/CD5220 соответственно они включены (в положении вниз)
  • Переключатели 4 - 7: отвечают за используемую таблицу символов, в моем случае роли они особой не играют, т.к. я использовал цифры и латиницу, но на всякий случай включим кириллические символы 4 - 5 выключены (вверх), 6 - 7 включены (вниз)
  • Переключатель 8: отвечает за скорость обмена данными с дисплеем, у меня в она установлена на 19200 БОД, поэтому переключатель 8 выключен (вверх) (можно и 9600, но тогда надо поменять значение в прошивке)

 

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

В самом скетче нужно настроить некоторые параметры, а именно:

#define COR_TEMP 8     // Корректировка температуры (вычитается из показаний датчика)

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

Далее идет установка уровней яркости дисплея, получаемые с датчика освещенности значения лежат в диапазоне от 0 до примерно 230-240, где 0 - полный мрак, 230-240 - под лампой или на солнце. Сам дисплей имеет 4 уровня яркости, следовательно весь диапазон необходимо разбить на 4 части, для этого введено три константы:

#define FIRST_LEVEL 80        // Уровни переключения яркости
#define SECOND_LEVEL 150
#define THIRD_LEVEL  220

Логика работы такая: если значения получаемые с датчика ниже величины FIRST_LEVEL - это первый уровень яркости (самый тусклый), если они находятся между FIRST_LEVEL и SECOND_LEVEL - второй, между SECOND_LEVEL и THIRD_LEVEL - третий, и более THIRD_LEVEL - четвертый соответственно. 

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

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

#define SHIFT 1 

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

Про сборку: плату разводить смысла в данном случае я не вижу, все можно собрать даже навесным монтажом, но я использовал макетную плату 5*7 см, соединения - проводами, при пайке следует аккуратно обращаться с датчиком BME280, следить за тем, чтоб флюс не попал на чувствительный элемент. 

Когда все собрано и прошито, можно переходить к настройке. За настройку времени отвечает функция set_time. Настройка часов происходит с помощью кнопок 1 - 3

  • Кнопка 1: При зажатии (0,5 сек) попадаем в меню настройки, так же с помощью зажатия возвращаемся к отображению времени. Клик по кнопке переключает режимы настройки часов, минут, сброса секунд.
  • Кнопка 2: Клик по кнопке увеличивает настраиваемое значение на единицу (секунды сбрасывает в ноль), при зажатии минуты прибавляются по пять, часы - по два.
  • Кнопка 3: Клик по кнопке уменьшает настраиваемое значение на единицу (секунды сбрасывает в ноль) , при зажатии минуты уменьшаются на пять, часы - на два.

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

Исходники в прикрепленном архиве.

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

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

 


Для тех же, кто хочет понять, как работает скетч, или кому просто интересны подробности, информация далее.

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

void setup() {
  delay(3000); //____________тут надо подождать
  pinMode(10, OUTPUT);
  watch.begin();
  bme.begin(0x76);
  mySerial.begin(19200);
  Serial.begin(9600);
  mySerial.write(0x1B); //
  mySerial.write(0x40); //____инициализация дисплея
  delay(1000); //_____________и тут надо подождать
  CustomFontLoad(); //________загрузка своего шрифта
  delay(2000); //_____________и тут надо подождать
  mySerial.write(0x0b); //____курсор домой
  get_temp (); //_____________получаем температуру и влажность
  show_time (); //____________выводим информацию на дисплей

  for (byte i; i < 10; i++) { //_Набираем массив значений с датчика освещенности для корректной работы сразу после запуска
    Light (analogRead(20)); //___Датчик света подключен к 20 пину (А6)
  }
}

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

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

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

Дальше идут объявления функций используемых в скетче, рассмотрим их подробней. Первая - это функция создания пользовательских символов. Как не трудно заметить, шрифт на часах несколько больше, чем стандартный. Пришел я к этому не сразу, изначально планировал по полной использовать все датчики и выводить всю имеющуюся информацию, прикинул, получалось что-то такое (на экране время, температура, атмосферное давление и влажность воздуха):

Но выглядело это некрасиво и неинформативно. Поэтому было решено сделать большие цифры на несколько знакомест сразу. В сети есть подобные проекты, и большие цифры использовались в самодельных часах-метеостанциях, однако шрифты там были так себе, и при их создании, скорее всего, была цель уменьшить занимаемую память, так как использовалось много типовых элементов в различных цифрах. Поэтому было решено создавать свой шрифт для цифр. Дизайнером я себя не считаю, поэтому мудрить особо не стал, взял стандартное начертание цифр, увеличил в два раза, где-то немного скруглил, где-то подправил - и готово. Для создания шрифта удобно пользоваться таблицами Excel, подгоняем ячейки по ширине и высоте, так, чтобы максимально напоминали знакоместа на экране. В условном форматировании указываем темный цвет ячейки, если там единица, и белый, если ноль. Это позволяет сразу визуализировать то что получается, и единицы и нули удобно копировать, для последующей конвертации в шестнадцатиричные цифры: 

Разберем как именно происходит создание пользовательского символа, на примере значка градуса (°), для начала нарисуем его нолями и единицами в таблице:

Далее следует записать цифры из столбцов начиная с левой верхней, получим пять групп по семь цифр (0110000, 1001000 и т.д.) или пять чисел в двоичной системе исчисления. Теперь их надо конвертировать в шестнадцатеричные числа:

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

void CustomFontLoad() { //Создаем свои символы
  mySerial.write(0x1B);
  mySerial.write(0x25);
  mySerial.write(0x01); // Разрешаем кастомные символы

  mySerial.write(0x1B);
  mySerial.write(0x26);
  mySerial.write(0x01); // Начинаем запись кастомных символов
  mySerial.write(0xB0); // Первый адрес диапазона
  mySerial.write(0xdb); // Последний адрес диапазона

Перед непосредственной записью в память своих символов необходимо выполнить некоторые команды. Первые три строки - это разрешение использования своих символов, далее - команда о начале записи символов, после этого необходимо объявить диапазон адресов, у меня это 43 адреса начальный 0xB0 и конечный 0xDB (четыре на каждую цифру, два на двоеточие и один на символ градуса).

К сожалению, информации по данному дисплею очень мало в сети, и в некоторых местах она противоречива. Например, согласно даташиту, для пользовательских символов доступны адреса от 0x20 до 0xFF итого 223 символа, однако я нашел таблицу (будет в прикрепленных файлах), в которой указаны адреса кириллических символов, пересекающиеся с этим диапазоном. Возможно они используются только при включении кириллицы переключателями, как описано выше, но на всякий случай, чтобы можно было использовать и русские буквы и кастомные символы, был выбран свободный диапазон от 0xB0 до 0xDF, в который можно записать до 47 символов, что тоже неплохо. 

После всего этого идет непосредственно запись символов, выглядит это так (на примере значка градуса):

  mySerial.write(0x05); // Размер символа 5 байт
  mySerial.write(0x30); // Значок градуса °
  mySerial.write(0x48); // \332
  mySerial.write(0x48);
  mySerial.write(0x30);
  mySerial.write((int)0x00);

Обязательно в начале записи каждого символа указываем его размер (5 байт), потом получившиеся шестнадцатиричные цифры. Обратите внимание на последнюю строчку: запись вида mySerial.write(0x00); не скомпилируется в Arduino IDE, не знаю точно, с чем это связано, вроде баг компилятора. Т.е. везде, где возникает необходимость использовать пустой столбец в знакоместе, нужно использовать приведение типа данных, как в примере кода. И так далее для каждого символа. Адрес символа (для значка градуса из примера - 332), по которому он потом будет вызываться, присваивается автоматически, по порядку. Не забываем, что он в восьмеричной системе счисления, следовательно после 267 идет 270 и т.д.

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

mySerial.print("\214\250\340\343 \254\250\340");

Выведет на экран фразу: Миру мир (вместе с пробелом). А строка:

mySerial.print("\332");

Отображает значок градуса: °

Большие цифры хранятся в виде двух массивов строк (String up_half [12] и String down_half [12]), в одном - верхние половины цифр, они отображаются на первой строчке, во втором - соответственно, нижние, в каждой строке по два символа, порядковые номера элементов массивов совпадают с цифрами, части которых в них хранятся. Это упрощает отображение цифр на экране. Например, чтобы отобразить пятерку, нужно на первую строку вывести пятый элемент из первого массива а на вторую так же пятый но из второго.

За отображение времени на экране отвечает функция show_time, в ней сначала получаем текущее время от датчика, потом разбиваем на разряды (десятки, единицы) каждое из чисел (часы, минуты, секунды), и выводим на экран элементы массивов в соответствии с полученными цифрами. Также в первой строке выводим температуру, а во второй - влажность. 

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

Функция set_brightness принимает в качестве аргумента переменную lev, которая может принимать значения от одного до четырех и устанавливает яркость экрана на соответствующий уровень.

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

Ну и функция Light принимает в качестве аргумента значение с датчика света, записывает его в массив, в котором хранятся 10 последних значений, и возвращает их среднее арифметическое. 

Теперь точно все, спасибо за внимание!

 

UPD: Добавил в прошивку (Clock_v1_5) отображение даты, теперь обновление происходит раз в минуту: по четным минутам слева отображается дата, справа часы, по нечетным - слева часы, справа влажность и температура. Также в меню настройки добавлены соответствующие пункты, позволяющие установить дату. 

UPD2: Еще пару обновлений. Часы стоят в комнате, ночью очень хорошо видно время, но с просонок иногда бывает сразу не разберешь и путаешь минуты с секундами или часы с минутами, для устранения этого недостатка я решил сделать секунды маленькими, получилось как то так: Прошивка Clock_v1_5_small_second

 

И еще одно классное обновление от товарища devICEpro, который не перестает развивать тему и продвигать отличные идеи:

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

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

Только для корректного отображения кириллицы необходимо обязательно включить кириллическую таблицу символов (Переключатели 4,5 в положение OFF(подняты) а 6,7 в положение ON (опущены)).

Прикрепленные файлы:

Теги:

Опубликована: Изменена: 01.06.2021 0 3
Я собрал 0 3
x

Оценить статью

  • Техническая грамотность
  • Актуальность материала
  • Изложение материала
  • Полезность устройства
  • Повторяемость устройства
  • Орфография
0

Средний балл статьи: 4.9 Проголосовало: 3 чел.

Комментарии (0) | Я собрал (0) | Подписаться

Статью еще никто не комментировал. Вы можете стать первым.
Добавить комментарий
Имя:
E-mail:
не публикуется
Текст:
Защита от спама:
В чем измеряется электрическое сопротивление?
Файлы:
 
Для выбора нескольких файлов использйте CTRL

Arduino UNO
Arduino UNO
Конструктор: DDS генератор сигналов USB-реле (2 канала)
вверх