Как я разрабатывал настольные часы
Доброго дня всем!
Когда-то давным давно я прочитал на Хабре статью про восстановление настенных советских электронных часов. Я вспомнил про нее, когда при уборке нашлись останки двух настольных часов Электроника-4.13. Передние пластиковые крышки были обшарпаны, индикаторы полностью севшие, а работоспособность плат вызывала вопросы — некоторые сегменты не горели у всех восьмерок. Как они выглядели в живом виде можно посмотреть на площадках объявлений.
В моем же случае ценность имели только корпуса.
Итак, вдохновившись той статьей, я решил сделать из них настольные часы. Это не реставрация, а попытка сделать часы с аналогичным функционалом и новыми фичами на современной электронной базе.
Дисклаймер: сейчас схема представляет из себя зоопарк из чипов китайских производителей разного уровня и европейских — проект длился не один год и претерпел много переделок, и схема адаптировалась под то, что было под рукой. В следующей версии схема будет оптимизирована под конкретный магазин деталей, скорее всего https://www.lcsc.com/.
Поехали.
Выбор экрана
Поочередно у меня появились следующие идеи:
Вариант 1. Расположить круглые светодиоды на плате так, чтобы они образовывали восьмерки наподобие оригинальной индикации, и использовать что-нибудь типа MAX7219 для управления. Этот вариант был отвергнут, так как с ним затруднительно выводить не только время, но и будильники, дату, температуру, ит.д. Стало появляться множество дополнительных светодиодов помимо восьмерок, так что индикация стала неочевидной и тяжело читаемой.
Вариант 2. Сделать свою плату индикации на цифровых светодиодах WS2812, расположив их матрицей. Успешно провалился, ибо WS2812 (ещё старые, шестивыводные) внезапно умирали во время работы. Скорее всего из-за перегрева при пайке феном, ну либо на Алиэкспрессе продали отбраковку.
Вариант 3. Готовые самоклеящиеся модули цифровых светодиодов 8×8, уже знакомые многим по лампе Гувера, например. Два модуля идеально вписываются в размер по горизонтали, по вертикали при этом остается зазор 5 мм с двух сторон, что некритично. Однако у светодиодов невысокая плотность и модули требуют подложки, например, печатной платы без элементов с одной стороны и отверстиями под провода. Остались сомнения.
Можно ещё найти самоклеящийся вариант с нераспаянными проводами
Вариант 4. Последним, на что я обратил внимание, стали светодиодные модули P2.5 для рекламы. Они бывают того же размера, что и у двух светодиодных квадратиков — 160 мм*80 мм, плюс небольшая цена, разъемы вместо пайки и в 4 раза больше плотность светодиодов. К этим достоинствам прилагается в комплекте протокол HUB75, а значит и динамическая индикация, которая требует непрерывной работы с выводом изображения. Было довольно сложно найти чертеж с расположением гаек, штырьков и прочих крепежных элементов на корпусе модуля, но в конечном итоге это удалось. Четвертый вариант остался финальным.
На Алиэкспрессе же был заказан кусочек полупрозрачного плексигласа для передней панели.
Выбор основного процессора
… или контроллера, или одноплатного компьютера, или чего-нибудь еще для центрального управления всем.
Если посмотреть характеристики таких светодиодных панелей (например здесь), бросается в глаза непонятный сходу параметр 1/16 scan — он означает динамическую индикацию, а именно, что в один момент времени горит только одна строка из 16. Иначе говоря, микросхем-драйверов светодиодов с регистром, который хранит текущие данные, в 16 раз меньше, чем необходимо для картинки всей панели: одна микросхема имеет 16 выходов, а всего их 24. Следовательно мы имеем 384 токовых вывода с буффером, и этого достаточно для 128 светодиодов в один момент времени, но у нас их 32×64 = 2048.
То есть, мы должны по последовательному протоколу загрузить изображение для первых строк из 16, а в нашем случае двух (всего 32 строки), выбрать номер строки, дать некоторое время на индикацию этой строки и затем начать показывать следующие. Так все 16, и делать это достаточно быстро, чтобы пользователь не видел мельтешения — то есть успеть отрисовать полную картинку хотя бы за 1/25 сек. Однозначно нам нужен некий аппаратный интерфейс для этого, иначе нам придется выделить отдельное ядро для такой операции — любой другой процесс, который отъест процессорное время, остановит смену строк, и это будет заметно.
Картинка загружается в модуль последовательно, и каждый цвет и имеет свой отдельный пин R1/B1/G1. Пины — R2/B2/G2, — у больших панелей для ускорения ввода нижняя половина имеет отдельные пины данных, что позволяет грузить картинки строк верхней и нижней половинок параллельно, в нашем случае никуда не подключены. Пины A-E — выбор текущей строки, то есть максимум для протокола — 1/32 scan, одна строка из 32 в моменте.
Итак, нам нужен контроллер с некими тремя последовательными интерфейсами, достаточным размером оперативной памяти, чтобы поместить в нее всю картинку. А еще лучше две, чтобы мы могли рисовать одну, пока показывается другая — и поменять их, когда полностью закончилм рисовать, — double buffer. При разрядности цвета 8 бит это 32×64 * 3 байт — 6144 байт.
Вариант 1. STM — старшие модели с 3 SPI, но такие разновидности довольно редкие и дорогие.
Вариант 2. Одноплатные компьютеры. Имея некий параллельный порт, из коробки или через расширитель, мы можем написать драйвер, который будет выводить фреймбуфер. Проблема в том, что обычная ОС не дает нам гарантий вызова нашего обработчика через заданное время, а значит динамическая индикация может срываться. Готовые устройства для этого используют ПЛИС, которая реализует не только вывод, но и буферизацию полного изображения.
Вариант 3. В процессе поиска мне попалась библиотека ESP32-HUB75-MatrixPanel-DMA — это библиотека для ESP32, которая использует I2S-интерефейс в camera mode (параллельный 16-битный ввод-вывод) и DMA (копирование данных из блока оперативной памяти в блок периферии без участия ядра). Плюс такого решения в том, что отрисовка экрана идет почти без участия процессора, минус — в оперативной памяти. Для такого варианта необходимо держать в памяти массив — результат рендера картинки на цифровой сигнал, 2 байта на каждое состояние пинов, то есть 2 байта на каждый бит цветности и изменение сигналов OE (включить индикацию) и LAT (сохранить текущие данные для индикации). Библиотека очень прожорлива в плане RAM, поэтому там даже есть excel калькулятор, сколько памяти займет библиотека с текущими параметрами. Я решил пока остановиться на этом варианте.
Не все ESP32 чипы поддерживают camera mode — те, что с ядром RISC-V, например ESP32-C*, не поддерживают. Я выбрал модуль WROOM 32D 16MB — он построен на оригинальном двухядерном ESP32 с архитектурой ядер Xtensa фирмы Tensilica.
Неплохая и актуальная таблица вариантов ESP32 есть в Википедии.
На роль фреймворка выбрал Arduino, чтобы компиляция не была особо сложной или платформозависимой, но в качестве среды разработки вместо Arduino IDE был выбран VSCode с плагином Arduino, так как там есть IntelliSense, подсветка синтаксиса цветом, настраиваемые хоткеи и другие привычные современному разработчику приятности, и оно полностью совместимо и компилируется в Arduino IDE.
Единственная проблема — IntelliSense ругается на отсутствующие файлы. Решается следующим образом:
после того, как установлен Board configuration и все необходимые библиотеки добавлены через library manager (они перечислены в arduino_libs.txt),
открыть файл .vscode/c_cpp_properties.json,
продублировать конфигурацию Arduino с новым именем, например Arduino2 (конфигурация Arduino перегенерируется например при ребилде или изменении библиотек, так что изменять её бессмысленно),
в includePath добавить:
«C:\\Users\\Administrator\\AppData\\Local\\Arduino15\\packages\\esp32\\tools\\esp32-arduino-libs\\idf-release_v5.1-bd2b9390ef\\esp32\\**»,
заменив имя пользователя, версию ESP фреймворка и версию esp32 (папки esp32c3, esp32h2 и т.д.) на свои,выбрать новый профиль в правом нижнем VSCODE:
нажать Ctrl+Alt+I, чтобы пересоздать IntelliSense кэш.
Пока писалась статья, Microsoft внезапно решили прекратить поддержку плагина и выпилить его из репозитория 1 октября.
Но в этом есть и хорошая сторона: если какой-нибудь из форков, например vscode-arduino, действительно будет поддерживаться и пул реквесты будут рассматриваться, я попробую исправить генерацию includePath и заслать им, и тогда возможно больше не придется делать это вручную.
Питание
USB Type-C, никаких других вариантов не рассматривалось, современный и популярный вариант. Спасибо Arya Voronova за обзор протокола, который не требует многочасового чтения всей документации, ведь она действительно огромна — только документация на PD часть это 1113 страницы.
Поскольку максимальный ток потребления довольно большой — 2.5А у панели, плюс примерно 500 мА на все чипы, плюс ток динамиков, — я добавил в схему поддержку 9В и 12В, добавив понижатель напряжения на TPS54623 и USB PD-контроллер FUSB302 — это позволило запитывать часы в том числе и от роутерных блоков питания.
Тут возникла внезапная, но логически предсказуемая проблема: когда на входе 5В и мы хотим получить 5В на выходе, TPS54623 уходит в защиту — ибо эта микросхема использует бутстрапный конденсатор для питания верхнего ключа, и его периодически надо перезаряжать. Заряжается он, когда открыт нижний ключ, следовательно, TPS54623 не может на выходе выдать то же самое напряжение, что и на входе — для этого нужно держать верхний ключ всегда открытым. В случае отсутствия понижения конденсатор рано или поздно разрядится и TPS54623 отключится.
Транзистор верхнего плеча и его драйвер внутри микросхемы. Плюс питания драйвера — выход BOOT, а минус драйвера — выход на катушку
Решение в лоб — добавить умножитель напряжения на LM2664, который будет фактически внешним дополнительным бутстрапом.
Внешнее питание драйвера верхнего ключа в TPS54623 — на его выходе должно быть не менее 8В: 5В — минус питания драйвера и 3В минимум питания драйвера.
Новая внезапная, но логически предсказуемая проблема: напряжение на входе может быть меньше 5В (некоторые USB-кабели имеют сопротивление 1 Ом и выше) и TPS54623 снова уходит в защиту, но уже по обрыву обратной связи — напряжение на VSENSE не достигает референсного за приемлемое время. Что можно сделать с этим.
Вариант 1: заменить архитектуру преобразователя с Step-Down на Buck-Boost, и тогда можно будет питать напряжением как выше 5В, так и ниже (да, вы правильно подумали, 1S Li-Ion аккумуляторы). Но я сходу не нашел подходящей микросхемы.
Вариант 2: понизить выходное напряжение до заведомо ниже входного.
Какое же минимальное напряжение нас устроит? Для этого оценим, какое минимальное напряжение питания у тех микросхем или модулей, которые питаются от 5В.
Аудио-усилитель TS4962 — Operating from VCC = 2.4 V.
Датчик углекислого газа T6703 — 4.5–5.5 VDC.
Я абсолютно уверен, что пятиногая микросхема — это стабилизатор на 3.3В, а от 5 питается только лампочка, но рисковать не будем, проверить точность все равно нечем
Светодиодная панель.
Поскольку мы не знаем производителя, а у продавцов документация либо отсутствует, либо цифры разнятся, оценим сами.
Драйверы светодиодов ICN2037 — Min 3.3V.
Буферы логических сигналов SN74HC245 — Wide Operating Voltage Range of 2 V to 6 V.
Демультиплексор c высокотоковыми выходами для выбора строк SM5166P.
工作电压: 3.0V ~ 5.5V
Я уверен, что это напряжение питания)
Светодиоды — точный тип мы не знаем, поэтому посмотрим на lcsc.com аналогичные в таком же корпусе, например XL-B1010RGBT-HF
Их параметры:Падение на драйвере на максимальном значении — 0.6В, следовательно минимум 3.2 + 0.6 = 3.8В нам необходимо подавать, плюс некий запас на ток светодиода выше 20 мА, ну и на потенциально другой тип светодиодов конечно же.
Окей, выбираем напряжение на выходе ШИМа 4.6В (все резисторы у меня имеют разброс 1% и сам ШИМ имеет разброс регуляции, на это нужно оставлять запас) и убираем наш внешний бутстрап, он больше не нужен.
Лирическое отступление про аккумулятор
Если не ставить датчик углекислого газа, то и в текущей схеме можно сделать резервное питание от 1S Li-Ion аккумулятора с некоторой потерей максимальной яркости в разряженном состоянии, и в версии на фото такая часть схемы действительно есть:
Схема зарядки аккумулятора и коммутации питания
Однако в релизной версии эту схему пришлось убрать, потому что она приводила к следующим проблемам.
Текущий ШИМ для 3.3В TPS563208 работает только начиная с 4.5В.
Необходимо как-то отличать питание от батареи и питание от USB без PD-протокола, то есть использовать некий чип-коммутатор, а не диод (это было сделано) и выход состояния коммутатора питания завести на модуль (это не было сделано).
В идеале необходима гибкая настройка зарядного тока. У нас может быть разный бюджет тока на USB, например, для 5B это может быть 500 мА — значение по умолчанию со времен usb 2.0, 1.5 и 3A выставляются резисторами по линиям CC со стороны источника, или 5А — такое позволяет только цифровой PD-протокол. Как правило, вылезти в небольших пределах за лимиты ничем не грозит, но вот попытка взять 1.5А с 500 мА USB-порта скорее всего стриггерит самовосстанавливающийся предохранитель на материнской плате. В текущей версии добавлены измерение тока и напряжения USB, но алгоритмы изменения яркости и зарядки в зависимости от разрешенного тока ещё предстоит написать.
Необходима защита батареи на случай подключения банки без своей защиты, например, 18650 из старой батареи ноутбука. Китайцы сейчас производят микросхемы — готовые схемы защиты (например XB4908A), можно было бы их попробовать.
Ну и датчик углекислого газа очень хочется
В итоге резервное питание от аккумулятора было убрано из релизной ветки и отложено на следующие версии.
PD-протокол
Я выбрал чип FUSB302, ибо он неплохо описан в статье про USB-C, есть библиотека для Arduino и open-source проекты с ним, например паяльник Pinecil, куда можно подглядывать, если что-то непонятно.
Проблема 1: скорость ответа
При обмене данными с источником питания нам надо успевать отвечать хотя бы за 10 мС, иначе нас посчитают зависшими, и в лучшем случае обмен прекратится, в худшем источник сделает hard reset со сбросом питания. Это может быть проблемой, когда на I2C шине висят ещё и другие датчики, с которыми идет обмен большим объемом данных. Да, у этой микросхемы есть выход прерывания, по которому мы резко можем уйти в работу с FUSB302, но я не уверен, что это хорошая идея — бросить на середине пакета обмен с другой микросхемой. Так можно сделать, если для FUSB302 выделить отдельный I2C интерфейс, благо у ESP32 их два.
Решение: первые 2 секунды после запуска, пока процедура обмена не закончена, обрабатываем только FUSB302, а уже потом остальные устройства на шине I2C тоже. Плюс, некоторые библиотеки, например для DS3231, заменены на свой, заточенный под конкретные задачи код для более компактного обмена данными.
Проблема 2: китайские кабели.
При отладке была обнаружена ситуация, когда напряжение на СС линии соответствует нужному для цифрового обмена, но самого обмена не идет. Как оказалось, китайцы собрали кабель, используя 4-жильный провод, то есть линии СС не проброшены с одного конца на другой, и при этом один из разъемов содержит ID-чип, что создает правильное напряжение на линии CC.
Попытка цифрового обмена предпринимается только при последних двух вариантах (и при первом, если мы решим стать мастером и подать питание сами, но это не наш случай)
Решение: один файл библиотеки был переписан на свой код, и заодно это позволило сохранять себе лимит тока, заданный напряжением на линии CC (в библиотеке этого нет).
Датчик температуры, влажности и давления
Я выбрал BME280, многократно обкатанный сборщиками домашних метеостанций. Единственная проблема — модули, заказанные на Алиэкспрессе врали по влажности до 30%. Голые чипы, заказанные на LCSC, такой проблемы не имеют.
Еще я рассматривал вариант AHT10 как более дешевой альтернативы при очень неплохой точности, но то ли в силу бага, то ли очень странной фичи, этот датчик отвечает на любой ID на шине I2C, о чем в документации мелким шрифтом написано так:
Баги на проде у чипов выглядят так
Аудио
На Алиэкспресс попался модуль DY-SV17F, который имеет функцию воспроизведения MP3-файлов и их загрузки по USB.
1. Support MP3 WAV decoding.
2. Support sampling rate (KHz):8/11.025/12/16/22.05/24/32/44.1/48.
3. 24bit DAC output, support dynamic range 90dB, SNR 85dB.
Модуль очень похож на DFPlayer Pro, но поддерживает меньше форматов файлов при большем выборе способов управления.
После тщательных тестов модуля был выбран именно он потому, что протокол управления по UART у него бинарный и с контрольной суммой. Поскольку тот же самый UART используется для просмотра логов, AT команды, как, например, сделано у DFPlayer Pro, могут как-то совпасть с логами, да и бинарный протокол занимает меньше времени обмена.
Основной чип — DY1703A, документацию на который найти не удалось. Подозреваю, это в силу того, что китайцы не хотят платить отчисления за формат. Однако есть документация на модули и даже готовая библиотека.
Схема модуля была успешно срисована и перенесена (если надо отдельно — вот), микросхема проблем не доставила, и даже более того, вполне успешно заработала с памятью W25Q256 (256M-BIT flash memory) — несмотря на то, что у этой флеш-памяти 32-битная адресация, а не 24, как у тех, что стоят на модуле. Один чип, правда, умер в процессе отладки, но я подозреваю, что из-за перегрева при пайке — пришлось накидывать перемычки на пины конфигурации.
Внезапно проблемы доставил ESP32. Скорость UART, даже если все отправки завершены, не меняется сразу, а меняется почему-то через какое-то время, причем может поменяться и в середине передачи байта. Предположение, что это из-за того, что шина периферии работает медленнее, чем ядро, не оправдалось, ибо добавленный цикл: запись скорости в регистр— чтение — проверка — повторить, пока не совпадает, ситуацию не исправил. Пришлось добавить delay везде после изменения скорости UART. В причинах этой магии ещё предстоит разобраться.
Кстати, в природе существует модуль с этим чипом, где файлы хранятся на SD-карте — это DY-SV5W, а в документации на команду получения текущего источника есть ещё и USB:
Впрочем, мои экземпляры на эту команду возвращают 4, так что документация не гарантированно корректная.
Датчик яркости окружения
Необходим для того, чтобы менять яркость в зависимости от освещенности. Обычный аналоговый GL5549 вставляется в выемку сверху корпуса, чтобы исключить засветку от индикации. Впрочем, на плате есть разводка и под цифровой MAX44009.
Взаимодействие с человеками (не удаленное)
У ESP32 9 входов, которые могут работать как сенсоры прикосновения, причем с программной точки зрения это похоже на аналоговый вход — мы получаем некое 16-битное значение (у ESP32S 32-битное, но это уже другой случай), которое тем меньше, чем ближе рука. Мы сами должны определить порог, ниже которого будем засчитывать срабатывание. Например, хранить максимальное значение за всё время и сравнивать с ним, как собственно я и сделал.
Поверху платы была проложена дорожка, которая подключена к одному из таких пинов — пока плата была без корпуса, касания сверху отлично детектировались. В силу того, что в корпусе расстояние от платы до касания сверху получилось 10 мм, также надежно детектировать не удалось — пришлось приклеить изнутри и подключить к верхней части корпуса медную фольгу.
Моя медная фольга очень тонкая, ибо из аккумуляторов, и даже если припаять очень тонкий провод, при малейшей нагрузке отрывается кусками. Шайба в силу большей площади держится крепче и позволяет подпаиваться к ней без проблем. Канал под датчик освещенности ещё не просверлен.
В качестве клея использовал клей для тачскринов B-7000 — он хорошо держится на любом пластике, стекле и керамике, а его излишки легко удаляются.
Взаимодействие с человеками (удаленное)
Поскольку чип имеет Wi-Fi, изначально предполагалось, что настройка будет происходить через веб. Причем, если часы настроены как точка доступа, то страница настроек будет открываться при подключении — спасибо CDFER за библиотеку Captive Portal для ESP32. Однако:
после добавления веб-сервера, даже ещё без страниц, свободная оперативная память практически полностью закончилась;
ESP32 не имеет раздельных радиотрактов внутри чипа для BT и WiFi, следовательно будет невозможно например собирать данные с BLE датчиков. Даже одновременная работа как BLE клиент и сервер под большим вопросом.
На данный момент все настройки изменяются через правку файла settings.hpp в исходном коде и последующей компиляцией и прошивкой. Принимаю идеи на этот счет
Сборка
Я заказал плату в JLCPCB, поскольку это было дешевле всего. Плюс, у них есть gerber-просмотровщик на сайте, а значит можно убедиться, что файлы для производства сгенерировались правильно.
Хотя в одном месте плата не соответствует их минимальным требованиям, они все равно взяли на производство и сделали без проблем:
Расстояние 0.1 при их требованиях 0.15 мм
Прошивка происходит так же, как и, например у ESP-WROOM-32 DevKit. Плата определяется как USB UART (придется поставить драйвер на USB-UART конвертер CH340) и шьется через Arduino IDE или VSCODE — часть схемы, которая относится к прошивке, полностью и в точности скопирована с девкита (неоригинальному, у оригинального USB-UART конвертер CP2102, но разница только в драйвере). Единственный нюанс — переключение USB между UART и MP3 с помощью перемычек в моей, или переключателя в текущей версии. Для прошивки через внешний USB-UART конвертер (это может быть полезно для отладки USB PD — когда питание может пропадать при неправильном коде) на пины EN и IO0 модуля добавлены кнопки — при прошивке через встроенный ими управляет CH340.
Как оказалось, некоторые версии чипа CH340 не требуют внешнего кварцевого резонатора, и его можно не запаивать. Обратите внимание, какое количество версий этого чипа сейчас существует — только в выбранном корпусе SOP-16 их три, и все можно использовать. CH340 выбран потому, что в силу его дешевизны подделки встречаются редко, хоть мне и такие попадались. Плюс, он легко добывается с китайской Arduino Micro.
Корпус — я оставил от родного корпуса только 26 мм толщины. Заднюю крышку, вставки-распорки и заполнитель ненужного нам теперь выреза сверху (да, вкладка с надписью touch просто закрывает вырез, а touch там по всей поверхности) печатаем на 3д принтере. Отверстия внизу можно просто закрыть чем-нибудь. Ножки по желанию, в целом часы и так довольно устойчивы и сами по себе не падают.
Аккуратно обрезал плексиглас — оказалось, что углы корпуса не совсем 90 градусов, лучше приложить корпус и обвести — и приклеил на тот же B-7000.
Что в итоге
Реализовано:
Что планируется в текущей версии печатной платы:
Что планируется в следующей версии печатной платы:
SPIRAM, например APS6404 — 64Mb SPI/QPI PSRAM. Да, очень медленный вариант внешней оперативной памяти, не подойдет по скорости даже для индикации, о чем есть упоминание в библиотеке HUB75:
For the ESP32-S3 only, you can use SPIRAM/PSRAM to drive the HUB75 DMA buffer when using an ESP32-S3 with OCTAL SPI-RAM (PSTRAM) (i.e. ESP32 S3 N8R8 variant). However, due to bandwidth limitations, the maximum output frequency is limited to approx. 13Mhz, which will limit the real-world number of panels that can be chained without flicker. Please do not use PSRAM as the DMA buffer if using QUAD SPI (Q-SPI), as it's too slow.
Однако это единственный вариант сделать веб без фундаментальных изменений в работе с панелью.
Приемник и, возможно, передатчик на 433МГц для внешних датчиков температуры и всякого разного. Скорее всего это будет SYN500R, он имеет неплохую чувствительность, возможно с дополнительными фильтрами на входе.
Возможно, FM-радио, в том числе как будильник. Но еще предстоит придумать, как будет происходить его настройка.
Возможно, USB хаб вместо выбора перемычками или кнопкой между USB — UART и USB — MP3
Возможно, резервное питание от Li-Ion аккумулятора.
Спасибо за внимание! Все исходники можете найти здесь.
Софт для разработки плат — DipTrace 4.3.0.6. Лицензия для некоммерческого использования.