Товары из Китая

Делаем дисплей из адресуемых светодиодов


Делаем дисплей из адресуемых светодиодов

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

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

Делаем дисплей из адресуемых светодиодов

Много лет назад Nokia мечтала получить светодиоды с интеллектуальным управлением. Со светодиодами тогда не срослось, но кое-что она получила — это была микросхема LP5521, которая потребляя очень мало энергии, моргала светодиодиком по программе внутри, не требуя никакого внимания со стороны основного процессора телефона.

В итоге почти все производители телефонов ставили такую микросхему и она побила все рекорды продаж. Года за 2-3 было продано 100 миллионов микросхем.

Фирма разработчикам с таких барышей даже по кофточке выдала 🙂

Делаем дисплей из адресуемых светодиодов

Кофточка, кстати, оказалась очень хорошей — теплая и уютная.

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

Адресуемые светодиоды — это, конечно, совершенно другое. Просто цепь светодиодов, которыми можно управлять по одному проводу.

Делаем дисплей из адресуемых светодиодов

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

Делаем дисплей из адресуемых светодиодов

В WS2813 эта проблема до какой-то степени решена с использованием двух линий данных — одна от выхода предыдущего светодиода, а вторая — от его входа. Если от выхода ничего нет — считаем, что предыдущий светодиод сдох и используем сигнал от его входа с задержкой на 1 пиксель. В итоге на экране будет одна неработающая точка — терпимо. Но если подряд умрет 2 светодиода — то погаснет вся линия.

Делаем дисплей из адресуемых светодиодов

Ставить конденсатор и резистор около каждого светодиода — головная боль. Но у производителя есть решение — светодиод со встроенным резистором и конденсатором. Но тут другая головная боль — как узнать при заказе, есть внутри конденсатор или нет? На названии светодиода наличие конденсатора никак не сказывается. Светодиоды размером 2020 с высокой вероятностью будут иметь конденсатор внутри.

Делаем дисплей из адресуемых светодиодов

Для программирования каждого пикселя нужно 24 бита — 8 бит на цвет. Управление яркостью производится с помощью ШИМ с частотой 2 кГц.

Делаем дисплей из адресуемых светодиодов

Светодиоды обычно бывают с двух типов по току — около 15-18 мА на светодиод или 5-7 мА. Для дисплеев 5мА подходят лучше, 15мА — слишком ярко. Приходится убирать лишнюю яркость за счет ШИМ. В итоге от 8 битов на цвет остается 6-7. Плюс человеческое зрение логарифмическое, а регулировка линейная. Для того, чтобы не искажать цвета при снижении яркости, необходимо использовать гамма-коррекцию.

Делаем дисплей из адресуемых светодиодов

От цветовой разрешающей способности вообще ничего не остается.

Делаем дисплей из адресуемых светодиодов

Недавно эта проблема была решена дико избыточно. Появились новые светодиоды — WS2816. У них уже 16 бит на цвет, частота ШИМ 10 кГц и впридачу ко всему встроенная гамма-коррекция. Но получается, что загружать их можно в 2 раза медленнее — при прежней скорости обмена передать уже нужно 48 бит вместе прежних 24.

Если было бы 10 бит на цвет со встроенной коррекцией — меня лично это устроило бы гораздо больше.

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

Делаем дисплей из адресуемых светодиодов

Если вдруг соберетесь дисплей такого типа делать — выбирайте адекватного производителя панелей. Вот что пишут в спецификации:

Делаем дисплей из адресуемых светодиодов

Многие на это внимания не обращают, но это очень серьезно. Из-за влажности могут быть дефекты пайки. А когда последовательно стоит тысяча светодиодов и отказ любого из них приведет к отказу всей панели, причем, не сразу, а через какое-то время — панель придется демонтировать и ремонтировать. Это грустный опыт.

Теперь начнем считать. Скажем, мы хотим отображать 30 кадров в секунду. Значит, у нас есть время для загрузки линии 33.3 мсек. Частота сигнала для светодиодов — 800кГц. Для одной точки нужно 24 импульса — по 8 на каждый цвет. На загрузку пикселя уходит 30 микросекунд. Значит, за время отображения кадра можно загрузить 1111 пиксель. С учетом возможных временнЫх допусков, реально я использовал в линии до 1300 светодиодов.

Если используем готовые панели 16х16 точек — можно подключить к одной линии до 5 панелей — 1280 светодиодов. Можно и 4-мя ограничиться — будет 1024 точки.

Теперь поговорим про управление. Я с товарищем несколько лет назад делал систему управления такими дисплеями. Самый большой был вморожен в лед, размер — 9 на 9 метров, но разрешающая способность довольно низкая — 288х288 пикселей. Были дисплеи меньше размерами, но с бОльшим разрешением. Каждый микроконтроллер управлял или 8 или 16 линиями светодиодов, одновременно работало несколько контроллеров. Самый тяжелый случай был — 14 контроллеров по 8 линий. Но это была разработка на заказ, поэтому дополнительной информации по ней не будет. Кое-что без деталей реализации можно почитать здесь:

Большой дисплей. Замороженный проект

Делаем дисплей из адресуемых светодиодов

А сейчас вернемся к моей недавней статье про 5-баксовый Raspberry Pi Pico на базе RP2040. Может, припоминаете — мне хотелось сделать именно управление дисплеем на ее базе, из-за наличия возможности программирования машины состояния для управление вводом-выводом на собственном ассемблере, мне показалось, что реализовать управление таким дисплеем будет не просто, а очень просто. В деталях повторяться не буду, обзор можно прочитать тут

Будем делать систему управления для 16 линий — этого хватит для 20480 светодиодов. Если мало — добавляем микроконтроллеры, благо копейки стоят. Для небольшого рекламного дисплея должно хватить.

Не забудьте про блок питания — если у вас светодиоды на 15мА, то понадобится чуть больше 900 ампер — обычной USB зарядки может не хватить :), все-таки почти 5 киловатт. А если взять 5 миллиамперные светодиоды — то каких-то 1.5 киловатт. Если белым светом не злоупотреблять, то мощность можно существенно уменьшить.

Под спойлером самое важное в статье, остальное все — от лукавого и можно не читать. А этот код — это все, что действительно надо, остальное любой разработчик легко сам доделает.

Дополнительная информация
Итак, код для PIO:

.define public T1 2

.define public T2 5
.define public T3 3
.wrap_target
out x, 32
mov pins, !null [T1-1]
mov pins, x [T2-1]
mov pins, null [T3-2]
.wrap

Все!

Забираем из входного регистра данные — до 32 бит, реально надо только 16. Выбрасываем в 16-разрядный порт все единицы и задержка. Выбрасываем на выход содержимое введеное из входного регистра — и задержка. И, наконец, выбрасываем нули и задержка.

Все это будет работать абсолютно независимо от процессора и сформирует сигналы управления светодиодом для всех (у нас будет их 16) линий.

Вместе с загрузкой программы PIO и настройкой выводов это будет выглядеть так (файл ws2812.pio)



.program ws2812_parallel
.define public T1 2
.define public T2 5
.define public T3 3
.wrap_target
out x, 32
mov pins, !null [T1-1]
mov pins, x [T2-1]
mov pins, null [T3-2]
.wrap
% c-sdk {
#include "hardware/clocks.h"
static inline void ws2812_parallel_program_init(PIO pio, uint sm, uint offset, uint pin_base, uint pin_count, float freq) {
for(uint i=pin_base; i<pin_base+pin_count; i++) pio_gpio_init(pio, i);
pio_sm_set_consecutive_pindirs(pio, sm, pin_base, pin_count, true);
pio_sm_config c = ws2812_parallel_program_get_default_config(offset);
sm_config_set_out_shift(&c, true, true, 32);
sm_config_set_out_pins(&c, pin_base, pin_count);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
int cycles_per_bit = ws2812_parallel_T1 + ws2812_parallel_T2 + ws2812_parallel_T3;
float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
sm_config_set_clkdiv(&c, div);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
%}

Идем сюда и воспользуемся онлайн компилятором

Скомпилированный текст сохраняем в файле ws2812.pio.h



// -------------------------------------------------- //
// This file is autogenerated by pioasm; do not edit! //
// -------------------------------------------------- //
#pragma once
#if !PICO_NO_HARDWARE
#include "hardware/pio.h"
#endif
// --------------- //
// ws2812_parallel //
// --------------- //
#define ws2812_parallel_wrap_target 0
#define ws2812_parallel_wrap 3
#define ws2812_parallel_T1 2
#define ws2812_parallel_T2 5
#define ws2812_parallel_T3 3
static const uint16_t ws2812_parallel_program_instructions[] = {
// .wrap_target
0x6020, // 0: out x, 32
0xa10b, // 1: mov pins, !null [1]
0xa401, // 2: mov pins, x [4]
0xa103, // 3: mov pins, null [1]
// .wrap
};
#if !PICO_NO_HARDWARE
static const struct pio_program ws2812_parallel_program = {
.instructions = ws2812_parallel_program_instructions,
.length = 4,
.origin = -1,
};
static inline pio_sm_config ws2812_parallel_program_get_default_config(uint offset) {
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset + ws2812_parallel_wrap_target, offset + ws2812_parallel_wrap);
return c;
}
#include "hardware/clocks.h"
static inline void ws2812_parallel_program_init(PIO pio, uint sm, uint offset, uint pin_base, uint pin_count, float freq) {
for(uint i=pin_base; i<pin_base+pin_count; i++) pio_gpio_init(pio, i);
pio_sm_set_consecutive_pindirs(pio, sm, pin_base, pin_count, true);
pio_sm_config c = ws2812_parallel_program_get_default_config(offset);
sm_config_set_out_shift(&c, true, true, 32);
sm_config_set_out_pins(&c, pin_base, pin_count);
//sm_config_set_set_pins(&c, pin_base, pin_count);
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
int cycles_per_bit = ws2812_parallel_T1 + ws2812_parallel_T2 + ws2812_parallel_T3;
float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit);
sm_config_set_clkdiv(&c, div);
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
#endif

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



#include <Arduino.h>
#include "ws2812.pio.h"
#include "hardware/dma.h"
#include "hardware/pio.h"
#define LEDS_IN_STRING 1280
#define DMA_CHANNEL 0
uint16_t DataArray[LEDS_IN_STRING*24];
// 30720
void dma_init(PIO pio, uint sm)
{
// (X/Y)*sys_clk, where X is the first 16 bytes and Y is the second
// sys_clk is 125 MHz unless changed in code
// we need 800 kHz - divider 156.25 x=4 y=625( 0x271)
dma_hw->timer[0] = 0x00040271;
dma_channel_config c = dma_channel_get_default_config(DMA_CHANNEL);
channel_config_set_dreq(&c, 0x3b); // 0x3b -> Select Timer 0 as TREQ
channel_config_set_transfer_data_size(&c, DMA_SIZE_16);
channel_config_set_read_increment(&c, true);
channel_config_set_write_increment(&c, false);
dma_channel_configure(
DMA_CHANNEL, // Channel to be configured
&c, // The configuration we just created
&pio->txf[sm], // The initial write address
NULL, // The initial read address - set later
LEDS_IN_STRING*24, // Number of transfers;
false // Start immediately?
);
}
void setup()
{
PIO pio = pio0;
int sm = 0;
uint offset = pio_add_program(pio, &ws2812_parallel_program);
ws2812_parallel_program_init(pio, sm, offset, 0, 16, 800000);
dma_init(pio, sm);
// здесь заполните буфер чем-нибудь сами
}
void loop()
{
dma_hw->ch[DMA_CHANNEL].al3_read_addr_trig = (uintptr_t) DataArray;
delay(50);
}

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

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

Делаем дисплей из адресуемых светодиодов

Если есть видео — его надо разложить по кадрам. При необходимости добавить переходные эффекты по вкусу. У меня этим занимается написанная на Python программа — но это уже выходит за рамки статьи, иначе она никогда не кончится.

Делаем дисплей из адресуемых светодиодов

Раньше у меня была проблема — в гараже стояло несколько больших светодиодных панелей, место занимали. Не так давно все-таки удалось их отдать хозяевам. Естественно, спустя небольшое время такая панель понадобилось. Пришлось заказать пару дешевых панелей на Али. Размер 16х16, итого 256 светодиодов. За 10 евро неплохо — всего 4 цента за точку и паять ничего не надо. Я как-то покупал готовые ленты — там цена вообще получалась 3 цента за светодиод.

Сколько отдельно стоят светодиоды в небольших партиях (в районе 100 тысяч штук) — я точно не знаю, заказывал не я. Думаю, что в районе 2 центов.

Делаем дисплей из адресуемых светодиодов

К платке Raspbery Pi Pico понадобилось подключить только SD карточку. По хорошему, на выход нужно ставить преобразователь уровня в худшем случае или драйвер дифференциальной линии в лучшем. Но для теста программного обеспечения и так сойдет.

Делаем дисплей из адресуемых светодиодов

Новая система управления в окружении предшественников:

Делаем дисплей из адресуемых светодиодов

Совершенно случайно нашелся рояль в кустах старый переделаный источник питания, он может 60 Ампер по 5 Вольтам выдать. А двум панелям в динамике 10 ампер хватает. Системы выглядит даже как-то убого:

Делаем дисплей из адресуемых светодиодов

Две панели — 512 точек. Панель реально маленькая для такой системы управления — на каждую линию можно подключить до 5 таких панелек, имеем 16 линий — итого 80 панелей. А у меня всего 2 🙁 — только попробовать хватит. Вот что получилось:

Делаем дисплей из адресуемых светодиодов

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

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

Делаем дисплей из адресуемых светодиодов

Видео с одним из старых дисплеев:

В моей старой системе данные, кадр за кадром, гонятся по проводному Ethernet.

Сейчас я попробовал скопировать блок данных на SD. Вылезла обычная ардуиновская проблема — все работает абы как. Быстро чтение данных не работает, я не могу считывать 30 кадров в секунду. Все можно переделать — когда я делал оригинальную систему, то тоже использовал Ethernet библиотеки, заимствованные из Ардуино. Ethernet работал, но очень медленно. Пришлось весь код перерывать — ошибки нашлись в очень мелких деталях. Сейчас копать библиотеки просто не хочется, да и нужды нет. Я хотел проверить только, как вывод на светодиоды работает, функционирующее изделие никому не нужно. «Будет хлеб — будут и песни» — как говорил дорогой Леонид Ильич.

Ну господь с ней, с Ардуиной. Есть же компания, которая продвигает этот проект. Должна же быть у Raspberry поддержка SD карт в их SDK. Как бы не так, как они пишут:

The pico_sd_card code is SDIO only at the moment. It is not really yet in shape for prime-time unless you are feeling brave.

ссылка

Файловой системой там даже не пахнет — сам прикручивай.

Нет, я не трус, но я боюсь. Бежать впереди паровоза — не самое благодарное занятие. Мне не к спеху, подожду, пока они допилят свои библиотеки. Или для Ардуино кто-то допилит, на что надежда очень маленькая. Какая-то библиотека есть, а для большинства применений достаточно и низкой скорости доступа.

P.S. Если меня сильно заносит на поворотах и я уже совсем в сторону ушел от темы шопоголизма — дайте знать в комментариях, постараюсь исправиться 🙂


СМОТРИ ТАКЖЕ

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

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