Рубрики
Проекты на ESP32

POV дисплей высокого разрешения на ESP32

Постоянство зрения — это фундаментальный принцип, который позволяет нашим глазам воспринимать непрерывное движение на отдельных изображениях. Эта концепция имеет решающее значение для иллюзии движения в кино. Когда я был ребенком, меня всегда это увлекало. Итак, в этом проекте мы собираемся использовать это увлекательное оптическое явление для создания подобного POV дисплея. POV дисплеи также называются дисплеями постоянного видения или голографическими дисплеями. Также их иногда называют вращающимися светодиодными дисплеями.

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

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

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

Особенности нашего POV дисплея:

  • Разрешение 128 пикселей.
  • Частота кадров 20 кадров в секунду.
  • Легко построить.
  • Легко контролировать.
  • На базе ESP32.
  • Полностью открытый исходный код.
  • Сопутствующее веб-приложение для легкого преобразования изображений.

Необходимые компоненты

  • Модуль ESP32 WROOM — x1 (купить на AliExpress).
  • Регистр сдвига 74HC595D – x16 (купить на AliExpress).
  • CH340K USB-контроллер UART – x1 (купить на AliExpress).
  • TP4056 — микросхема зарядного устройства для литий-ионных аккумуляторов — x1 (купить на AliExpress).
  • AMS1117 3,3 В LDO – x1 (купить на AliExpress — можно купить не в виде модуля, а в виде отдельной микросхемы (от 5 до 7 рублей за штуку), но на момент публикации данной статьи не нашел на алиэкспрессе магазина с дешевой доставкой данной микросхемы).
  • AO3401 P — МОП-транзистор — x1 (купить на AliExpress).
  • 2N7002DW двойной N — МОП-транзистор – x1
  • Датчики Холла – x2
  • Диод SS34 – x1 (купить на AliExpress).
  • USB-разъем типа C, 16 контактов — x1 (купить на AliExpress).
  • Светодиод SMD Синий 0603 — x128
  • 775 Мотор – x1
  • Контроллер скорости двигателя постоянного тока — x1
  • SMD резисторы и конденсаторы
  • SMD светодиоды
  • SMD Тактильные переключатели
  • Ползунковый переключатель SDM
  • Разъемы
  • Пользовательская печатная плата
  • Деталь, напечатанная на 3D-принтере, и крепежные винты.
  • Прочие инструменты и расходные материалы.

Реклама: ООО «АЛИБАБА.КОМ (РУ)» ИНН: 7703380158

Схема проекта

Принципиальная схема POV дисплея высокого разрешения на основе модуля ESP32 представлена на следующем рисунке. Ее также можно скачать в виде PDF файла по ссылке в конце статьи.

Порт USB типа C в схеме используется как для зарядки, так и для целей программирования. Питание от USB-порта подается на схему контроллера цепи питания, построенную на основе P-канального MOSFET U7 и диода D67. Разъем J4 можно использовать для подачи внешнего напряжения 5 В. Тот же порт можно использовать для подключения модуля беспроводного зарядного устройства, если мы хотим, чтобы он работал непрерывно без зарядки. Для регулирования напряжения мы использовали популярный LDO AMS1117 3,3 В, который способен выдавать ток до 1 А с падением напряжения примерно 1,1 В при полной нагрузке. Разъем J3 используется для подключения внешнего переключателя включения и выключения всей схемы. Для зарядки внутренней батареи мы используем контроллер заряда TP4056, который может заряжать батарею максимальным током заряда 1 А. Теперь, если мы посмотрим на схему программирования, она построена на чипе CH340K от WCH. Для автоматического сброса мы использовали двойной МОП-транзистор 2N7002DW. Теперь мозгом всей схемы является модуль ESP32-WROOM. Мы выбрали этот модуль SoC, потому что он дешев, легко доступен и достаточно мощный, чтобы обеспечить достаточную частоту кадров на дисплее. Мы соединили пиксели как две секции. Каждая секция имеет 64 пикселя или светодиода. Итак, в общей сложности мы имеем разрешение 128 пикселей. Мы также использовали два датчика Холла для измерения оборотов и определения положения. На печатной плате мы добавили для него место для поверхностного монтажа, но позже решили использовать обычный датчик A3144 в корпусе TO-92, поскольку его легче приобрести и легко установить с помощью конструкции держателя тока.

Для управления светодиодами мы использовали сдвиговые регистры 74HC595D. Поскольку один s74HC595 может управлять до 8 светодиодами, мы использовали в общей сложности 16 таких чипов для управления всеми 128 светодиодами. Мы использовали резистор сопротивлением 1 кОм для ограничения тока, но вы можете изменить это значение в зависимости от необходимой вам яркости. Поскольку у нас две секции, нам достаточно половины оборота, чтобы нарисовать целый кадр или изображение. Одна секция будет рисовать половину изображения, а другая — вторую половину изображения. Сделав это, мы смогли удвоить частоту кадров. Дисплей может обеспечить приблизительную частоту кадров 20 кадров в секунду.

Каждый пиксель будет управляться через эти последовательно соединенные регистры сдвига. 

Печатная плата для POV дисплея

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

А вот 3D-вид печатной платы.

И вот полностью собранная плата.

Gerber файлы для изготовления печатной платы вы можете скачать по ссылке в конце статьи.

Напечатанные на 3D-принтере детали для POV дисплея

Мы разработали классный напечатанный на 3D-принтере POV-дисплей с помощью Fusion360. Файлы для всех напечатанных на 3D-принтере деталей можно загрузить по ссылке GitHub, приведенной в конце статьи, вместе со эскизом Arduino и растровым файлом. Вы также можете узнать больше о 3D-печати и о том, как начать с ней работать.

Вот держатель печатной платы вместе с самой платой.

А вот 3D-вид POV дисплея вместе с монтажной подставкой. Все модели, которые вы видите здесь, созданы с использованием fusion360. 

А вот полностью собранный светодиодный POV дисплей.

Как работает постоянство видения?

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

На нашем дисплее мы делим каждое изображение на 360 частей по радиусу. Это означает, что мы должны нарисовать 360 линий с интервалом в 1 градус при каждом повороте, чтобы нарисовать изображение. В каждой строке будет 64 пикселя или светодиода (всего 128 светодиодов с обоих плеч), которыми мы должны манипулировать в соответствии с данными пикселей. Проблема с отображением обычного изображения заключается в том, что они используют декартову систему координат. Но для того, чтобы отобразить изображение на вращающемся дисплее POV, нам необходимо изображение с полярными координатами. В декартовой системе координат пиксели имеют квадратную форму, а положение определяется расстоянием по горизонтали (x) и расстоянием по вертикали (y). Полярные координаты, с другой стороны, основаны на круговой сетке. Пиксели выглядят как клинья или трапецеидальные искажения, а положение определяется радиусом (r) и углом от горизонтали (θ). Таким образом, пиксели неоднородны: по мере увеличения расстояния от начала координат пиксели увеличиваются в площади и меняют форму.

Чтобы получить пиксельные данные из изображения, мы должны использовать некоторые тригонометрические вычисления и некоторую интерполяцию. Однако выполнение этого для каждого пикселя каждого изображения займет много времени и постепенно увеличит время отклика пикселя. Чтобы избежать этого и получить минимальное время покоя пикселей и, следовательно, максимальную частоту обновления, мы будем использовать заранее вычисленное значение для обработки изображения, которое будет объяснено ниже в статье. При нашей нынешней настройке рисование одного кадра или изображения с разрешением 128 пикселей и 360 сегментами займет около 50 мс, что дает эффективную частоту 20 кадров в секунду.

Следующей задачей было оптимизировать способ хранения изображения. Потому что даже при использовании обычных массивов изображений, преобразованных традиционными инструментами, каждый пиксель будет занимать 1 байт пространства. То есть изображению размером 128×128 потребуется 16384 байта или 16,384 Кбайт места. Но при этом мы будем ограничены количеством изображений, которые мы можем хранить в пространстве кода. Чтобы преодолеть эту проблему, а также улучшить оптимизацию, мы использовали новый подход. Каждая строка пикселей изображения будет храниться в 16 байтах. Каждый из этих байтов будет содержать данные из 8 пикселей в виде единицы или нуля, т.е. черного или белого цвета. Затем эти данные будут декодированы с помощью простой функции для получения фактических данных пикселей. При использовании этого метода одному изображению размером 128×128 пикселей потребуется всего 2048 (128×16) байт или 2,048 килобайт пространства. Используя этот метод, нам удалось уменьшить размер изображения в 8 раз. Чтобы преобразовать изображение в такой формат, мы также создали веб-приложение, ссылка на которое приведена в следующем разделе статьи.

Как преобразовать изображение в код для отображения POV?

Чтобы преобразовать изображение, сначала убедитесь, что ваши изображения имеют разрешение 128×128 пикселей и имеют черно-белый формат. Оттенки серого могут обрабатываться неправильно. Вы можете конвертировать столько изображений, сколько захотите. Для этого откройте следующий конвертер изображений. После того, как изображения выбраны, нажмите «Конвертировать». Он создаст соответствующие массивы. Если вы выберете несколько изображений, выходные данные будут иметь одинаковое количество массивов с такими именами, как Image_1, Image_2, Image _3 и т. д. Вы можете скопировать и вставить этот массив в свой код, чтобы использовать их. Если вы хотите, там будет флажок для инвертирования цвета. Для этого установите флажок и еще раз нажмите кнопку конвертировать. Он создаст новые массивы для копирования. 

Объяснение кода программы

Теперь давайте посмотрим на код Arduino для POV дисплея. Как обычно, мы включили в код все необходимые библиотеки с помощью функции include, при этом единственной сторонней библиотекой, которая понадобилась, была MultiShiftRegister, которая используется для управления сдвиговыми регистрами 74HC595. Вы также можете видеть, что мы также включили два заголовочных файла. Первый заголовочный файл Images.h содержит все изображения, хранящиеся в виде оптимизированных массивов данных. Заголовочный файл Precompute.h содержит данные поиска, которые используются для вычисления данных пикселей с использованием полярных координат. Вы можете скачать все необходимые файлы из репозитория GitHub, ссылка на который приведена внизу этой статьи.

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

Далее у нас есть функция getValueFromAngle, которая принимает 3 параметра, включая имя 2D-массива, угол и радиус, и возвращает данные пикселей. Функция получает угол и обрабатывает его, используя значение смещения, чтобы получить скорректированный угол. Затем этот скорректированный угол используется с радиусом и значениями из массивов precomputedCos и PrecomputedSin для расчета данных X и Y для соответствующего пикселя. Затем он извлекает значение этого пикселя и возвращает его.

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

Позже у нас есть функция DisplayFrame. Эта функция обрабатывает выборку и рисование изображения. Она рисует изображения построчно: первое плечо используется для рисования первой половины, а второе плечо используется для одновременного рисования противоположной половины изображения. Она также синхронизирует время отрисовки кадра, используя данные RPM из процедуры прерывания.

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

В функции loop() мы рисуем все анимации и изображения одно за другим. Переменная anim используется для циклического просмотра каждого изображения и анимации. Переменная frameHoldeTime управляет скоростью анимации. Если вы установите для этого параметра значение два, анимация будет воспроизводиться на половине скорости, а если вы установите четыре, она будет воспроизводиться на скорости ¼ и так далее. Другая переменная, repeatvalue (значение повтора), определяет, сколько времени вам понадобится для воспроизведения анимации. Единица означает, что она будет сыграна только один раз перед воспроизведением следующей, а если равна 2, то она будет сыграна дважды и так далее. Каждая из этих переменных должна быть использована в предыдущем изображении или анимации. 

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

Файлы для проекта

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

Исходный код программы

Видео, демонстрирующее работу проекта

Источник статьи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *