Я сделал наручные часы, которые показывают время четырьмя светодиодами в двоичном коде. Захотелось сделать подарок знакомому и заодно пройти весь цикл разработки embedded-устройства: схемотехника, четырёхслойная PCB, прошивка.
В статье расскажу про решения, ошибки (включая ту, из-за которой USB не заработал) и устройство прошивки.
Идея: время в двоичном коде
Концепция простая: берём текущее время, например 14:36, разбиваем на отдельные цифры (1, 4, 3, 6) и показываем каждую по очереди на четырёх LED в двоичном формате. Четыре бита дают 16 состояний от 0 до 15.
Вот как четыре LED кодируют цифры:
|
LED 1 |
LED 2 |
LED 3 |
LED 4 |
Двоичное |
Десятичное |
|---|---|---|---|---|---|
|
○ |
○ |
○ |
○ |
|
0 |
|
○ |
○ |
○ |
● |
|
1 |
|
○ |
○ |
● |
○ |
|
2 |
|
○ |
○ |
● |
● |
|
3 |
|
○ |
● |
○ |
○ |
|
4 |
|
○ |
● |
○ |
● |
|
5 |
|
○ |
● |
● |
○ |
|
6 |
|
○ |
● |
● |
● |
|
7 |
|
● |
○ |
○ |
○ |
|
8 |
|
● |
○ |
○ |
● |
|
9 |
● = горит, ○ = не горит
Для примера, вот как выглядит 14:36. Каждая строка соответствует тому, что пользователь видит поочерёдно:
|
Шаг |
LED 1 |
LED 2 |
LED 3 |
LED 4 |
Двоичное |
Цифра |
|---|---|---|---|---|---|---|
|
1 |
○ |
○ |
○ |
● |
|
1 |
|
2 |
○ |
● |
○ |
○ |
|
4 |
|
3 |
○ |
○ |
● |
● |
|
3 |
|
4 |
○ |
● |
● |
○ |
|
6 |
Звучит элементарно, но есть нюанс: как отличить 00:23 от 23:00? Обе последовательности содержат те же цифры, просто в другом порядке:
|
Шаг |
00:23 |
23:00 |
|---|---|---|
|
1 |
|
|
|
2 |
|
|
|
3 |
|
|
|
4 |
|
|
Без указания позиции пользователь не поймёт, какую цифру он видит, да и непонятно, где последовательность начинается, а где заканчивается. Для решения этой проблемы добавлен пятый LED, который играет роль индикатора режима.
Перед каждой цифрой индикатор мигает столько раз, какой по счёту идёт цифра: один раз перед первой, два перед второй и так далее. Чтобы различать режимы (время, дата, год), скорость мигания разная: 200 мс для времени, 400 мс для даты, 700 мс для года. После всех четырёх цифр все пять LED мигают три раза, обозначая конец цикла, после чего последовательность повторяется. Способ не самый удобный для быстрого чтения, но считывание бинарного кода входит в привычку буквально за пару дней.
Железо
Ограничения
Главное ограничение проекта — размер. Часы должны помещаться на запястье, поэтому плата не может быть больше 42 × 42 мм. Плюс нужны отверстия под ремешок (22 × 2.5 мм), которые ещё сильнее сжимают доступную площадь.
Все пассивные компоненты выбраны в корпусах 0402 и 0603, светодиоды в 0805. Паять вручную такое не хотелось, поэтому плата заказывалась как PCBA, то есть компоненты ставит производство.
Выбор компонентов
ATmega32U4 выбран как основной микроконтроллер. Компактный QFN-44, достаточно пинов для LED, кнопок и I²C. Тактируется от внешнего керамического резонатора 8 МГц.
DS3231M отвечает за хронометрирование. В отличие от обычных RTC на внешнем кварце, тут встроенный MEMS-резонатор с температурной компенсацией. Внутри DS3231M стоит 15-ступенчатый двоичный делитель, который из 32 768 Гц (= 2¹⁵) получает ровно 1 Гц. Данные отдаёт по I²C.
ME6217 используется как LDO-регулятор. Преобразует напряжение батареи (2.75–4.2 В) или USB (5 В) в стабильные 3.3 В.
MCP73831 управляет зарядкой LiPo-аккумулятора. Ток заряда задаётся одним резистором на выводе PROG по формуле I = 1000 / R. С резистором 3 кОм получается около 333 мА, что для аккумулятора на 2000 мАч составляет примерно 0.17C и вполне безопасно.
Схемотехника
Схема разбита на несколько логических блоков. Ниже рассмотрены те, которые содержат интересные решения.
USB-разъём
Micro-USB (Molex 1050170001) используется для зарядки и для связи с компьютером. Линии данных D+ и D− идут к ATmega32U4 через токоограничивающие резисторы 22 Ом, как рекомендовано для USB 2.0. Питание с USB разделяется на две шины: +5V_VBUS идёт напрямую к MCU для мониторинга, а EXT_5V проходит через диод Шоттки BAT760-7 для защиты от обратного тока. На линии EXT_5V также стоит ESD-защита (LESD5D5.0CT1G).
Автопереключение USB / батарея
Это одно из самых интересных мест в схеме. Нужно, чтобы устройство автоматически переключалось между USB и батареей, без каких-либо ручных действий.
Реализовано на P-канальном MOSFET AO3401. Когда USB подключён, на истоке и затворе MOSFET одинаковое напряжение (5 В от USB-линии). Разница между ними (Vgs) равна нулю, транзистор закрыт, батарея отключена от входа LDO, и система питается от USB.
Когда USB отключён, напряжение на затворе через резистор 100 кОм подтягивается к земле. Vgs становится отрицательным, транзистор открывается и пропускает напряжение батареи на вход LDO. Диод Шоттки RB521S30T1G не даёт току потечь обратно в USB-линию, если к ней подключено другое устройство.
Всё это работает без управляющей логики, без MCU. Чистая аналоговая схема на одном транзисторе и паре пассивных компонентов.
LDO
ME6217 получает напряжение от описанной выше MOSFET-схемы и выдаёт стабильные 3.3 В на шину +3V3_RTC. Вывод CE (Chip Enable) подключён напрямую к VIN, чтобы регулятор работал постоянно, потому что от него запитан RTC и выключать его нельзя. На входе и выходе стоят конденсаторы по 10 мкФ.
RTC
DS3231M запитан от шины +3V3_RTC через вывод VBAT, а VCC подключён к земле. Такая конфигурация рекомендована даташитом для питания от одного источника. На линии питания стоят развязывающие конденсаторы 1 мкФ и 0.1 мкФ, линии I²C (SCL и SDA) подтянуты к +3V3 через резисторы 10 кОм.
Зарядка LiPo
MCP73831 получает питание от EXT_5V. Аккумулятор подключается через JST-разъём. Один светодиод на выводе STAT горит во время зарядки, так что пользователь видит, подключено ли питание.
MCU
ATmega32U4 является центральным элементом, к которому подключено всё остальное: I²C к RTC, SPI к разъёму для прошивки, USB-линии, кнопки, светодиоды.
Четыре зелёных LED (KT-0805G) подключены к портам PD5, PD4, PD6, PD7 через токоограничивающие резисторы 5.1 кОм. Красный индикатор режима (NCD0805R1) идёт на PC7 через 560 Ом.
Три тактильных кнопки SKQGAFE010 подключены к пинам D0, D1, D11. Внешние подтяжки не нужны, используются внутренние pull-up резисторы MCU.
Энергопотребление в режиме ожидания
Переключатель питания UK-H0827G15-SP-Y коммутирует +3V3_RTC в +3V3. В выключенном состоянии MCU обесточен, но RTC и LDO продолжают работать, поэтому часы не теряют время.
Потребление складывается из тока LDO (~100 мкА) и тока RTC (~3 мкА), итого 103 мкА. С аккумулятором 2000 мАч это даёт расчётную автономность больше двух лет.
Разводка PCB
Плата четырёхслойная: верхний и нижний слои для трассировки, inner 1 это плоскость +3V3, inner 2 это GND. ATmega32U4 расположен в центре платы.
Пять LED стоят сверху над микроконтроллером. Три кнопки разнесены по углам платы (нижний левый, нижний правый и верхний правый) для удобства и симметрии. Micro-USB и JST-разъём для батареи расположены по бокам.
USB D+/D− выровнены по длине: 23.09 и 23.23 мм (для USB 2.0 ограничение составляет 101.6 мм, так что с большим запасом). Линии I²C тоже выровнены через length tuning, то есть добавлением меандров на более короткой дорожке. Для I²C на 400 кГц это не критично, но раз уж инструмент позволяет, почему бы и нет.
SPI-разъём H1 (2×3 пина) расположен над отверстием для ремешка. В 3D-модели это выглядит нормально, но после сборки становится понятно, что его нужно выпаивать перед использованием часов.
Проектировал в EasyEDA Pro, заказывал на JLCPCB. Заплатил примерно 120 евро.
Грабли
Перепутанные D+ и D−
Главная ошибка проекта. При разводке я перепутал линии D+ и D− между USB-разъёмом и ATmega32U4, и компьютер не видит устройство вообще. Обнаружилось это, конечно, уже после того, как платы приехали.
Ошибка возникла из-за того, что я не перепроверил соответствие цепей между разъёмом и MCU на схеме. Проверка заняла бы пару секунд. Урок простой: перед отправкой в производство пройдитесь по каждой дифференциальной паре вручную.
Прошивать в итоге пришлось через SPI, благо на плате есть разъём H1 для ISP-программирования. В исправленной ревизии ошибка устранена.
5 В против 3.3 В при прошивке
Arduino UNO выдаёт 5 В на SPI-линиях, а плата работает на 3.3 В. Первые попытки записать загрузчик заканчивались ошибками верификации, потому что ATmega32U4 получал слишком высокие логические уровни на MOSI, SCK и RESET.
Решение оказалось простым: последовательные резисторы 1 кОм на этих трёх линиях. MISO трогать не нужно, сигнал идёт от 3.3 В платы к 5 В UNO, это безопасное направление.
Разъём H1 над отверстием для ремешка
SPI-разъём (2×3 пина) нависает прямо над отверстием для ремешка. После прошивки его нужно выпаивать, что неудобно. Совет на будущее: при заказе PCBA не ставьте H1 вообще и прошивайте через контактные площадки напрямую, зажимая провода или используя pogo-пины.
Прошивка
Прошивка написана на Arduino C++ и занимает порядка 150 строк. Единственная внешняя зависимость это RTClib от Adafruit. Перед загрузкой прошивки нужно записать загрузчик Adafruit Feather 32U4 (8 МГц, 3.3 В) через SPI с помощью Arduino UNO в роли ISP-программатора.
В setup() инициализируются пины и I²C. Если RTC не найден, все LED мигают в бесконечном цикле, сигнализируя об аппаратной ошибке. Если RTC потерял питание, время выставляется по моменту компиляции скетча через макросы DATE / TIME.
В основном цикле прошивка считывает время из RTC, разбивает значение на четыре десятичные цифры и показывает каждую на LED с помощью побитовых операций. Вот как выглядит функция showTime(), которая собирает всю логику отображения вместе:
void showTime(DateTime now) { int hours = now.hour(); int minutes = now.minute(); int digits[4] = { hours / 10, hours % 10, minutes / 10, minutes % 10 }; for (int i = 0; i < 4; i++) { clearBinary(); blinkMode(i + 1); // индикатор режима мигает i+1 раз displayBinary(digits[i]); // выводим цифру на 4 LED побитово delayWithButtonCheck(1000); // цифра горит секунду }}
Время разбивается на четыре цифры целочисленным делением и взятием остатка. Перед каждой цифрой blinkMode() мигает индикатором нужное количество раз со скоростью, зависящей от текущего режима. Затем displayBinary() выводит цифру на четыре LED побитовым сдвигом. showDate() и showYear() устроены точно так же, разница только в том, откуда берутся цифры (день/месяц или год).
Итого
В результате получился работающий прототип наручных часов: четырёхслойная PCB 42 × 42 мм, три режима отображения (время, дата, год), зарядка через Micro-USB, расчётная автономность больше двух лет в режиме ожидания. В первой ревизии была серьёзная ошибка с перепутанными D+/D−, которая была исправлена.
Все файлы для воспроизведения (схема, Gerber, BOM, pick-and-place, прошивка) лежат на GitHub под MIT. То, как часы работают, можно увидеть на YouTube.
ссылка на оригинал статьи https://habr.com/ru/articles/1044176/