Некоторое время назад участвовал в обсуждении DIY проекта матричных светодиодных часов
И что меня удивило — в качестве устройства отображения использовались древние одноцветные светодиодные матрицы 8х8 с шагом 5 миллиметров. Причём под них разводились сложные печатные платы, делалась софтовая динамическая индикация. И это в то время, когда уже давно доступны по цене в районе 10-20$ готовые полноцветные LED панели 64х32 с шагом 3 мм. А общий ассортимент подобных панелей очень большой и имеет шаг пикселя от 2 до 10 мм и практически любой размер.
В то же время использовать такие панели в DIY конструкциях достаточно непросто — готовые контроллеры стоят довольно больших денег и не имеют нормального API. Сделать же достаточно быстрое сканирование панели на обычно используемых в DIY микроконтроллерах достаточно сложно. Причём временные интервалы должны выдерживаться с высокой точностью — иначе начинается заметная неравномерность яркости.
Есть неплохие решения на Adafruit, но они все достаточно дорогие и сложные.
После некоторого раздумья возникла мысль — а почему бы не сделать предельно недорогую плату, которая будет мостом между обычной копеечной платой типа ардуино и LED панелью? После какой-то пары месяцев возни родилось что-то работающее.
В данной статье описана вторая, улучшенная версия контроллера.
В качестве базовой задачи хотелось иметь возможность управлять сборной панелью общим размером хотя бы 64х64, имея при этом возможность работы хотя бы в Highcolor (RGB565) с сохранением приемлемой частоты обновления экрана (не менее 50Гц). В первой версии контроллера базовая задача была полностью реализована, но возникла идея реализации задачи другим, весьма многообещающим методом, из чего и родилась вторая версия.
На входе интерфейс HUB75:
![](https://habrastorage.org/r/w780q1/webt/2d/yn/gt/2dyngtqqelfmxsb9iseipe2c29u.jpeg)
На каждом входе цвета стоит цепочка регистров типа HC595 (но специальные 16-ти канальные версии для светодиодов). Регистров столько, чтобы хватало на ширину панели. Входы клока, параллельной загрузки и разрешения выхода общие для всех регистров. Входы ABCDE — это выбор ряда — идут на обычный дешифратор.
Принцип работы:
То есть классическая динамическая индикация. Понятно, что при таком методе за один такой цикл мы можем каждый конкретный светодиод только включить/выключить.
Для того, чтобы получить градации яркости классическим PWM, такой цикл приходится повторять N-1 раз, где N — число градаций яркости (256 для RGB888). А учитывая, что при этом это всё еще и мерцает — всё это надо делать очень-очень быстро.
Есть обходной вариант — Bit Angle Modulation (BAM). При этом время свечения в каждом цикле пропорционально весу отображаемого бита. То есть для RGB888 надо всего 8 циклов отображения. Чуть более детально — здесь.
В первой версии контроллера использовался классический PWM, что накладывало жёсткое ограничение на количество циклов сканирования. Во второй версии реализован BAM, что дало огромный выигрыш в скорости.
Совершенно очевидным было то, что обычный микроконтроллер тянет только маленькие панели — на большие просто не хватает скорости. Поэтому без CPLD или FPGA здесь не обойтись — выдавать десятки МБ/сек на недорогих микроконтроллерах физически невозможно.
В качестве памяти мне на форуме IXBT порекомендовали очень интересную FIFO память Averlogic AL422B, которая имеет примерно 400кбайт памяти и может работать на частотах до 50МГц.
Учитывая, что моим основным требованием была максимальная дешевизна компонентов, чтобы готовая платка была доступна самодельщикам — была выбрана Altera EPM3064 — CPLD c 64мя макроячейками. В то же время столь малое количество макроячеек не позволяет сделать динамически конфигурируемую плату — конфигурацию необходимо компилировать непосредственно в CPLD.
→ Получившаяся схема лежит здесь
Детали:
Итого по деталям получается 1,5+1,5+0,7=3,7$ и 40+3+8+7+8*1+1=67 р. Всё вместе в пределах 5$ — копейки.
→ Исходный рисунок платы лежит здесь
→ Подготовленные gerber файлы для заказа
Плата подготовлена для первой версии, в которой не было управления RE. Для использования её со второй версией надо разрезать перемычку между выводами 23 и 24 AL422B и бросить проводок от вывода 28 EPM3064 (он выведен на контактную площадку) на вывод 24 AL422B.
При пайке платы не забыть запаять перемычки на питание на обратной стороне платы.
![](https://habrastorage.org/r/w780q1/webt/y6/hy/i6/y6hyi6obrqqyyybfvractvmtuqy.jpeg)
![](https://habrastorage.org/r/w780q1/webt/u1/n1/jr/u1n1jr2zzs7sqfwpzxji2xpg2pg.jpeg)
Расчёты нужных параметров довольно сложны.
Дело в том, что в контроллере параллельно выполняются два процесса — загрузка данных следующей строки / индикация уже загруженной строки.
Запускаются процессы одновременно, но заканчиваются в разное время, поэтому процесс, выполненный быстрее, ждёт завершения работы более длинного процесса.
→ Для расчёта была сделана Excel-табличка
Исходные данные:
Подбираемые параметры:
Например, у нас панель 32х32, имеющая 8 линий сканирования и 2 RGB входа. Такая панель имеет два HUB75E разъема, то есть физически это две панели 32х16. Мы соединяем эти панели последовательно, то есть логически эта панель будет выглядеть как 64х16.
PRE_DELAY и POST_DELAY — это интервалы гашения до и после output enable (OE) для того, чтобы мультиплексоры успели переключить выходы и ключи открыться/закрыться. Без них будут «тянучки» от горящих пикселей на соседние строки. Значения подбираются экспериментально для конкретной панели. Обычно достаточно 15 тактов (задаются в тактах).
Тут возникает вопрос выбора prescaler — как его выбрать.
Малое значение прескейлера даёт малое время отображения кадра, но уменьшает общую яркость. Большое значение прескейлера увеличивает время отображения кадра, то есть при переборе приводит в мерцанию экрана.
Попробуем PRESCALER = 1
Получим:
OE_EFFICIENCY — 8,3% то есть панель будет работать всего на 8.3% от возможной максимальной яркости
FRAMES_PER_SECOND — 2034 к/с — зато частота обновления картинки будет огромной — больше 2000 к/с.
Потеря яркости уж очень большая.
Попробуем PRESCALER = 16
Получим:
OE_EFFICIENCY — 72,9% то есть панель будет работать на 72,9% от возможной максимальной яркости
FRAMES_PER_SECOND — 1117 — и частота обновления картинки очень хорошая — больше 1000 к/с.
Ну вот, вполне нормально — эффективность больше 50% вполне нормальная и частота кадров очень хороша.
Общее эмпирическое правило — PRESCALER примерно в 8 раз меньше произведения PIXEL_COUNT*RGB_INPUTS
Ну и дальше считать и проверять.
Все панели соединяются последовательно. Схема соединения: сперва справа-налево, потом снизу-вверх. То есть сперва соединяем горизонтали последовательно, потом выход нижнего ряда ко входу второго снизу ряда и т.д. до верхнего ряда.
Контроллер цепляется к правой нижней панели.
Бывают панели, которые имеют по два входных и два выходных разъёма. Такие панели по сути являются просто механической сборкой двух панелей по вертикали. Коммутируются как две независимые панели.
После сборки надо посчитать общую длину цепочки в пикселях — для этого смотрим — сколько всего панелей получилось в цепочке и умножаем это число на ширину панели в пикселях. Это число потом надо будет вбить в значение PIXEL_COUNT при конфигурации CPLD и в калькулятор таймингов.
Все необходимые файлы лежат на github. Скачивать нужно прямо папкой.
С сайта Altera после регистрации необходимо скачать и установить Quartus II 13.0sp1. Качать надо ИМЕННО ЭТУ версию — более новые версии уже не поддерживают серию MAX3000. Ломать её не надо — достаточно Web edition (free) версии. При скачивании не забудьте поставить галочки на поддержке MAX3000 и Programmer. На всякий случай предупреждаю — пакет большой, порядка двух гигов. Еще понадобится Altera USB Blaster — обычная цена на ali порядка 3$.
Открываем проект al422_bam.qpf. Слева открываем закладку файл и открываем файл al422_bam.v — это основной файл проекта. В нём надо настроить параметры:
Сколько RGB входов на панели — на панелях со входом HUB75 может быть 1 или 2 входа RGB. Выяснить — сколько именно входов можно таким способом — берём количество пикселей на панели по вертикали. Делим её на число линий сканирования (указано в обозначении панели как 8S, например). Делим на количество входных разъёмов (1 или 2). Например — у меня панель 32х32, сканирование 8S и два входных разъёма — 32/8/2=2 — значит два входа RGB.
Сколько линий сканирования на панели — так как поддерживается стандарт HUB75E, то может быть до 32х. Количество линий сканирования обычно есть в названии панели в виде 8S/16S/32S соответственно.
Должна быть раскомментирована только одна нужная строка:
Общее число пикселей по горизонтали в цепочке. Считаются пиксели во всей цепочке панелей — см. раздел выше «Коммутация LED панелей»
Фазы выходных сигналов. Наиболее типичная конфигурация такая — OE активен по низкому уровню (коммент снят), CLK работает по фронту (коммент стоит), LAT активен по высокому уровню (коммент стоит). Возможны всякие странные варианты. Выяснять какой именно у вас только экспериментальным способом или снятием схемы и поиском даташитов на используемые микросхемы).
Пред и пост задержки сигнала OE относительно LAT и прескейлер для основного счётчика. См. выше.
Всё, нажимаем ctrl-L — проект компилируется. Если нигде не напортачили — будет несколько warnings, но не должно быть никаких ошибок. Дальше цепляем спаянную плату к USB Blaster, подаём питание на плату. В Quartus идём в tools — programmer. Выбираем в Hardware setup USB-blaster, нажимаем Start. Всё, CPLD запрограммирована.
Выдача данных на контроллер, в общем-то, предельно проста — сбрасываем адрес записи и потом последовательно выдаём байты данных, стробируя их сигналом WCLK. И вроде бы даже банальной ардуинки хватит для работы. Но есть две проблемы:
а) Надо много памяти. Даже небольшая панель 32х32 в режиме RGB888 требует 3кБайта памяти под экранный буфер. Обычные ардуино на базе Atmega328 содержат всего 2кбайта оперативки. Можно, конечно, использовать плату Mega на базе Atmega2560, которая содержит аж 8 кБайт оперативки, но даже этого мало для панелей нормального размера — панель 128х64 в режиме RGB565 требует 16кБайт памяти.
б) В процессе работы с AL422B вылез не документированный нигде глюк — при записи данных со скоростью меньше 2МБ/сек счётчик адресов работает некорректно и пишет данные «не туда». Возможно это глюк имеющейся у меня партии. Возможно нет. Но этот глюк приходится обходить. Учитывая, что AVR8 работает на 16МГц, то получить с неё данные на нужных скоростях почти нереально.
Предлагаемое решение — уйти на дешёвые платки на базе 32-х битного контроллера STM32F103C8T6. Такая платка стоит на Ali примерно 2.5$ поштучно или около 1.7$ при покупке десятком, то есть дешевле даже Arduino Nano. При этом мы получаем полноценный 32-х битный микроконтроллер, работающий на 72 МГЦ и имеющий 20 кБ оперативной памяти и 64 кБ флеша (сравните с 2кБ/8кБ Atmega328, которая стоит на Nano).
При этом такие платы вполне успешно программируются в среде Arduino. Про это есть неплохая статья на гиктаймс, поэтому я не буду её дублировать. В общем — делайте всё, как описано в статье.
В среде ардуино выбирайте плату Generic STM32F103C, variant STM32F103C8. Данные идут через DMA, поэтому можно использовать любой вариант оптимизации.
Коммутация происходит по следующей схеме:
Жёстко прибиты в библиотеке:
A0..A7 → DI0..DI7 AL422B
B0 → WCLK AL422B
B1 → WRST AL422B
Назначается в скетче на контроллер:
B10 → WE AL422B
Общий провод:
G → GND
Ну и не забудьте подать питание 5В/GND от панели на соответствующие пины контроллера.
Распиновку разъема на контроллере брать со схемы.
![](https://habrastorage.org/r/w780q1/webt/nh/p7/_y/nhp7_ygm9hqmkawess06ypt1oy4.jpeg)
Так как ставилась задача сделать всё максимально просто и доступно, то весь софт сделан под среду Arduino и оформлен в виде библиотеки LED_PANEL.
Библиотека LED-PANEL активно использует библиотеку Adafruit GFX, поэтому она должна быть инсталлирована.
Я настоятельно рекомендую не ставить библиотеку LED_PANEL в каталог libraries, а оставить её в папке со скетчем. Дело в том, что там много железно привязанных параметров и если вы захотите перенести работу на более «жирный» микроконтроллер, то придётся много чего менять в самом коде.
Инициализация идёт примерно в таком виде:
то есть создаём экземпляр класса LED_PANEL, для которого указываем параметры:
width — общая ширина панели в пикселях (всего)
height — общая высота панели в пикселях (всего)
bpp — байт на пиксель, 3 для RGB888. BAM версия работает только в RGB888
scan_lines — количество линий сканирования — 8/16/32. Должно соответствовать режиму, прошитому в контроллер.
RGB_inputs — количество RGB входов в разъеме HUB75 — 1/2. Должно соответствовать режиму, прошитому в контроллер.
we_out_pin — пин, к которому подцеплен вывод WE
Обращаю внимание, что при инициализации задаётся только пин WE. Все остальные пины прописаны жёстко в коде, так как они привязаны к используемым каналам таймера и DMA и их изменение повлечёт существенные изменения в коде.
Запуск и очистка экрана в разделе setup:
begin инициализирует нужные пины на выход, подключает таймер и DMA
clear очищает буфер
Для рисования можно использовать все стандартные процедуры библиотеки Adafruit GFX — от простейшего drawPixel до вывода текста. Для выдачи нарисованного в буфер используется процедуры:
В таком виде show инициирует передачу данных на контроллер по DMA и немедленно возвращает управление. Узнать — закончилась ли передача можно с помощью функции led_panel.OutIsFree() — если она говорит true, то значит передача закончилась. Есть особенность — если вызвать show, когда передача еще не завершилась — она будет просто проигнорирована.
аналог show(), но если вызвать show(false), а передача еще не завершилась, то процедура подождёт завершения передачи, потом начнёт новую передачу и вернёт управление:
аналог show(false), но если вызвать show(true), то после начала новой передачи процедура не вернёт управления до завершения передачи.
В общем-то — всё.
![](https://habrastorage.org/r/w780q1/webt/pn/-a/of/pn-aofhwnhuuzszuk_mcf9upqje.jpeg)
Некоторые замечания по софту:
а) Гамма-коррекция вводится при пересчёте цвета из RGB565 (который использует библиотека) функцией ExpandColor. Во всех прочих случаях используется линейная трансфер-функция, то есть яркость прямо пропорциональна значению.
б) Софт позволяет подключать несколько LED-контроллеров к одной микроконтроллерной плате. Для этого надо отдать на контроллеры параллельно шину данных, линии RST и CLK. Нужный контроллер выбирается через линию WE. В софте нужно создать отдельный экземпляр класса LED_PANEL для каждого контроллера, при этом у каждого экземпляра при инициализации должны быть указаны разные линии WE (последний параметр).
— Разобраться с «наводкой» цветов на соседние ряды. Похоже на плохую разводку самой панели (ключи мусорят), но надо проверить. Только приехала новая панель — буду проверять;
— Сделать новую версию платы — с уже разведённым RE и добавлением выходных преобразователей уровней в 5В;
— Сделать класс META_LED_PANEL, который позволит объединять несколько LED_PANEL в один виртуальный экран — это даст возможность создавать очень большие экраны с несколькими контроллерами;
— В перспективе уйти на более мощную серию CPLD, например CycloneIV. Это бы существенно расширило возможности при сохранении невысокой стоимости (EP4CE6E22 стоит у китайцев порядка 5$ штука, при этом там в 100 раз больше макроячеек и порядка 32 кБ встроенной памяти). Но этим я буду заниматься когда-нибудь потом. Если захочу. Так как подобные разработки отнимают уж слишком много времени.
И что меня удивило — в качестве устройства отображения использовались древние одноцветные светодиодные матрицы 8х8 с шагом 5 миллиметров. Причём под них разводились сложные печатные платы, делалась софтовая динамическая индикация. И это в то время, когда уже давно доступны по цене в районе 10-20$ готовые полноцветные LED панели 64х32 с шагом 3 мм. А общий ассортимент подобных панелей очень большой и имеет шаг пикселя от 2 до 10 мм и практически любой размер.
В то же время использовать такие панели в DIY конструкциях достаточно непросто — готовые контроллеры стоят довольно больших денег и не имеют нормального API. Сделать же достаточно быстрое сканирование панели на обычно используемых в DIY микроконтроллерах достаточно сложно. Причём временные интервалы должны выдерживаться с высокой точностью — иначе начинается заметная неравномерность яркости.
Есть неплохие решения на Adafruit, но они все достаточно дорогие и сложные.
После некоторого раздумья возникла мысль — а почему бы не сделать предельно недорогую плату, которая будет мостом между обычной копеечной платой типа ардуино и LED панелью? После какой-то пары месяцев возни родилось что-то работающее.
В данной статье описана вторая, улучшенная версия контроллера.
Задача
В качестве базовой задачи хотелось иметь возможность управлять сборной панелью общим размером хотя бы 64х64, имея при этом возможность работы хотя бы в Highcolor (RGB565) с сохранением приемлемой частоты обновления экрана (не менее 50Гц). В первой версии контроллера базовая задача была полностью реализована, но возникла идея реализации задачи другим, весьма многообещающим методом, из чего и родилась вторая версия.
Базовые пояснение по устройству типовой LED панели
На входе интерфейс HUB75:
![](https://habrastorage.org/webt/2d/yn/gt/2dyngtqqelfmxsb9iseipe2c29u.jpeg)
На каждом входе цвета стоит цепочка регистров типа HC595 (но специальные 16-ти канальные версии для светодиодов). Регистров столько, чтобы хватало на ширину панели. Входы клока, параллельной загрузки и разрешения выхода общие для всех регистров. Входы ABCDE — это выбор ряда — идут на обычный дешифратор.
Принцип работы:
- выставляем данные на RGB входы, щелкаем клоком CLK. Повторяем, пока не загрузим всю строку
- выключаем выходы OE = 1 (чтобы помех не было)
- выдаём на дешифратор номер загруженного ряда
- щелкаем параллельной загрузкой LAT — данные строки переносятся в выходные регистры
включаем выходы OE = 0 - повторяем для следующего ряда
То есть классическая динамическая индикация. Понятно, что при таком методе за один такой цикл мы можем каждый конкретный светодиод только включить/выключить.
Для того, чтобы получить градации яркости классическим PWM, такой цикл приходится повторять N-1 раз, где N — число градаций яркости (256 для RGB888). А учитывая, что при этом это всё еще и мерцает — всё это надо делать очень-очень быстро.
Есть обходной вариант — Bit Angle Modulation (BAM). При этом время свечения в каждом цикле пропорционально весу отображаемого бита. То есть для RGB888 надо всего 8 циклов отображения. Чуть более детально — здесь.
В первой версии контроллера использовался классический PWM, что накладывало жёсткое ограничение на количество циклов сканирования. Во второй версии реализован BAM, что дало огромный выигрыш в скорости.
Реализация
Совершенно очевидным было то, что обычный микроконтроллер тянет только маленькие панели — на большие просто не хватает скорости. Поэтому без CPLD или FPGA здесь не обойтись — выдавать десятки МБ/сек на недорогих микроконтроллерах физически невозможно.
В качестве памяти мне на форуме IXBT порекомендовали очень интересную FIFO память Averlogic AL422B, которая имеет примерно 400кбайт памяти и может работать на частотах до 50МГц.
Учитывая, что моим основным требованием была максимальная дешевизна компонентов, чтобы готовая платка была доступна самодельщикам — была выбрана Altera EPM3064 — CPLD c 64мя макроячейками. В то же время столь малое количество макроячеек не позволяет сделать динамически конфигурируемую плату — конфигурацию необходимо компилировать непосредственно в CPLD.
→ Получившаяся схема лежит здесь
Детали:
- CPLD EPM3064ATC44-10 — цена на Ali примерно 13-15$ за десяток
- FIFO RAM AL422B — цена на Ali примерно 15$ за десяток
- Кварцевый генератор на 50МГц. На плате предусмотрена установка в корпусах DIP14/DIP8/7050. Цена на Ali примерно 6-7$ за десяток
- Стабилизатор на 3.3В в корпусе SOT223. Цена в Чип и Дип — 40р за штуку
- Разъем IDC-10MS. Цена в Чип и Дип — 3 р/штуку
- Разъем IDC-16MS. Цена в Чип и Дип — 8 р/штуку
- Разъём IDC-14MS. Цена в Чип и Дип — 7 р/штуку
- Конденсаторы 1мкФ 0805 — 8 штук примерно по 1 р/штуку
- Конденсатор 0,1мкФ 0805 — примерно по 1 р/штуку
- Резистор 10к 0805 — копейки
Итого по деталям получается 1,5+1,5+0,7=3,7$ и 40+3+8+7+8*1+1=67 р. Всё вместе в пределах 5$ — копейки.
→ Исходный рисунок платы лежит здесь
→ Подготовленные gerber файлы для заказа
Плата подготовлена для первой версии, в которой не было управления RE. Для использования её со второй версией надо разрезать перемычку между выводами 23 и 24 AL422B и бросить проводок от вывода 28 EPM3064 (он выведен на контактную площадку) на вывод 24 AL422B.
При пайке платы не забыть запаять перемычки на питание на обратной стороне платы.
![](https://habrastorage.org/webt/y6/hy/i6/y6hyi6obrqqyyybfvractvmtuqy.jpeg)
![](https://habrastorage.org/webt/u1/n1/jr/u1n1jr2zzs7sqfwpzxji2xpg2pg.jpeg)
Расчёты
Расчёты нужных параметров довольно сложны.
Дело в том, что в контроллере параллельно выполняются два процесса — загрузка данных следующей строки / индикация уже загруженной строки.
Запускаются процессы одновременно, но заканчиваются в разное время, поэтому процесс, выполненный быстрее, ждёт завершения работы более длинного процесса.
→ Для расчёта была сделана Excel-табличка
Исходные данные:
- CRYSTAL_FRQ (MHz) — частота генератора (50 МГц)
- PIXEL_COUNT — количество пикселей в строке загрузки. Более детально в разделе коммутации
- RGB_INPUTS — количество RGB входов, используемых в HUB75E интерфейсе используемой панели. 1 или 2
- BYTES_PER_PIXEL — байт на пиксель. В нашем случае всегда 3 — RGB888
- SCAN_LINES — количество линий сканирования в используемой панели. 8/16/32
Подбираемые параметры:
- PRE_DELAY — задержка от сигнала LAT до включения OE, задаётся в тактах
- PRESCALER — прескейлер для основного счётчика. То есть если прейскейлер 8 и вес текущего бита 4, то OE будет включено на 8*4 = 32 такта
- POST_DELAY — минимальная задержка от выключения OE до следующего сигнала LAT, задаётся в тактах
Например, у нас панель 32х32, имеющая 8 линий сканирования и 2 RGB входа. Такая панель имеет два HUB75E разъема, то есть физически это две панели 32х16. Мы соединяем эти панели последовательно, то есть логически эта панель будет выглядеть как 64х16.
PRE_DELAY и POST_DELAY — это интервалы гашения до и после output enable (OE) для того, чтобы мультиплексоры успели переключить выходы и ключи открыться/закрыться. Без них будут «тянучки» от горящих пикселей на соседние строки. Значения подбираются экспериментально для конкретной панели. Обычно достаточно 15 тактов (задаются в тактах).
Тут возникает вопрос выбора prescaler — как его выбрать.
Малое значение прескейлера даёт малое время отображения кадра, но уменьшает общую яркость. Большое значение прескейлера увеличивает время отображения кадра, то есть при переборе приводит в мерцанию экрана.
Попробуем PRESCALER = 1
Получим:
OE_EFFICIENCY — 8,3% то есть панель будет работать всего на 8.3% от возможной максимальной яркости
FRAMES_PER_SECOND — 2034 к/с — зато частота обновления картинки будет огромной — больше 2000 к/с.
Потеря яркости уж очень большая.
Попробуем PRESCALER = 16
Получим:
OE_EFFICIENCY — 72,9% то есть панель будет работать на 72,9% от возможной максимальной яркости
FRAMES_PER_SECOND — 1117 — и частота обновления картинки очень хорошая — больше 1000 к/с.
Ну вот, вполне нормально — эффективность больше 50% вполне нормальная и частота кадров очень хороша.
Общее эмпирическое правило — PRESCALER примерно в 8 раз меньше произведения PIXEL_COUNT*RGB_INPUTS
Ну и дальше считать и проверять.
Коммутация LED панелей
Все панели соединяются последовательно. Схема соединения: сперва справа-налево, потом снизу-вверх. То есть сперва соединяем горизонтали последовательно, потом выход нижнего ряда ко входу второго снизу ряда и т.д. до верхнего ряда.
Контроллер цепляется к правой нижней панели.
Бывают панели, которые имеют по два входных и два выходных разъёма. Такие панели по сути являются просто механической сборкой двух панелей по вертикали. Коммутируются как две независимые панели.
После сборки надо посчитать общую длину цепочки в пикселях — для этого смотрим — сколько всего панелей получилось в цепочке и умножаем это число на ширину панели в пикселях. Это число потом надо будет вбить в значение PIXEL_COUNT при конфигурации CPLD и в калькулятор таймингов.
Прошивка FPGA
Все необходимые файлы лежат на github. Скачивать нужно прямо папкой.
С сайта Altera после регистрации необходимо скачать и установить Quartus II 13.0sp1. Качать надо ИМЕННО ЭТУ версию — более новые версии уже не поддерживают серию MAX3000. Ломать её не надо — достаточно Web edition (free) версии. При скачивании не забудьте поставить галочки на поддержке MAX3000 и Programmer. На всякий случай предупреждаю — пакет большой, порядка двух гигов. Еще понадобится Altera USB Blaster — обычная цена на ali порядка 3$.
Открываем проект al422_bam.qpf. Слева открываем закладку файл и открываем файл al422_bam.v — это основной файл проекта. В нём надо настроить параметры:
Сколько RGB входов на панели — на панелях со входом HUB75 может быть 1 или 2 входа RGB. Выяснить — сколько именно входов можно таким способом — берём количество пикселей на панели по вертикали. Делим её на число линий сканирования (указано в обозначении панели как 8S, например). Делим на количество входных разъёмов (1 или 2). Например — у меня панель 32х32, сканирование 8S и два входных разъёма — 32/8/2=2 — значит два входа RGB.
`define RGB_outs 2
Сколько линий сканирования на панели — так как поддерживается стандарт HUB75E, то может быть до 32х. Количество линий сканирования обычно есть в названии панели в виде 8S/16S/32S соответственно.
Должна быть раскомментирована только одна нужная строка:
`define SCAN_x8 1
//`define SCAN_x16 1
//`define SCAN_x32 1
Общее число пикселей по горизонтали в цепочке. Считаются пиксели во всей цепочке панелей — см. раздел выше «Коммутация LED панелей»
`define PIXEL_COUNT 64
Фазы выходных сигналов. Наиболее типичная конфигурация такая — OE активен по низкому уровню (коммент снят), CLK работает по фронту (коммент стоит), LAT активен по высокому уровню (коммент стоит). Возможны всякие странные варианты. Выяснять какой именно у вас только экспериментальным способом или снятием схемы и поиском даташитов на используемые микросхемы).
//`define LED_LAT_ACTIVE_LOW 1
`define LED_OE_ACTIVE_LOW 1
//`define LED_CLK_ON_FALL 1
Пред и пост задержки сигнала OE относительно LAT и прескейлер для основного счётчика. См. выше.
`define OE_PRESCALER 16
`define OE_PREDELAY 31
`define OE_POSTDELAY 31
Всё, нажимаем ctrl-L — проект компилируется. Если нигде не напортачили — будет несколько warnings, но не должно быть никаких ошибок. Дальше цепляем спаянную плату к USB Blaster, подаём питание на плату. В Quartus идём в tools — programmer. Выбираем в Hardware setup USB-blaster, нажимаем Start. Всё, CPLD запрограммирована.
Микроконтроллерная часть
Выдача данных на контроллер, в общем-то, предельно проста — сбрасываем адрес записи и потом последовательно выдаём байты данных, стробируя их сигналом WCLK. И вроде бы даже банальной ардуинки хватит для работы. Но есть две проблемы:
а) Надо много памяти. Даже небольшая панель 32х32 в режиме RGB888 требует 3кБайта памяти под экранный буфер. Обычные ардуино на базе Atmega328 содержат всего 2кбайта оперативки. Можно, конечно, использовать плату Mega на базе Atmega2560, которая содержит аж 8 кБайт оперативки, но даже этого мало для панелей нормального размера — панель 128х64 в режиме RGB565 требует 16кБайт памяти.
б) В процессе работы с AL422B вылез не документированный нигде глюк — при записи данных со скоростью меньше 2МБ/сек счётчик адресов работает некорректно и пишет данные «не туда». Возможно это глюк имеющейся у меня партии. Возможно нет. Но этот глюк приходится обходить. Учитывая, что AVR8 работает на 16МГц, то получить с неё данные на нужных скоростях почти нереально.
Предлагаемое решение — уйти на дешёвые платки на базе 32-х битного контроллера STM32F103C8T6. Такая платка стоит на Ali примерно 2.5$ поштучно или около 1.7$ при покупке десятком, то есть дешевле даже Arduino Nano. При этом мы получаем полноценный 32-х битный микроконтроллер, работающий на 72 МГЦ и имеющий 20 кБ оперативной памяти и 64 кБ флеша (сравните с 2кБ/8кБ Atmega328, которая стоит на Nano).
При этом такие платы вполне успешно программируются в среде Arduino. Про это есть неплохая статья на гиктаймс, поэтому я не буду её дублировать. В общем — делайте всё, как описано в статье.
В среде ардуино выбирайте плату Generic STM32F103C, variant STM32F103C8. Данные идут через DMA, поэтому можно использовать любой вариант оптимизации.
Коммутация происходит по следующей схеме:
Жёстко прибиты в библиотеке:
A0..A7 → DI0..DI7 AL422B
B0 → WCLK AL422B
B1 → WRST AL422B
Назначается в скетче на контроллер:
B10 → WE AL422B
Общий провод:
G → GND
Ну и не забудьте подать питание 5В/GND от панели на соответствующие пины контроллера.
Распиновку разъема на контроллере брать со схемы.
![](https://habrastorage.org/webt/nh/p7/_y/nhp7_ygm9hqmkawess06ypt1oy4.jpeg)
Программная часть
Так как ставилась задача сделать всё максимально просто и доступно, то весь софт сделан под среду Arduino и оформлен в виде библиотеки LED_PANEL.
Библиотека LED-PANEL активно использует библиотеку Adafruit GFX, поэтому она должна быть инсталлирована.
Я настоятельно рекомендую не ставить библиотеку LED_PANEL в каталог libraries, а оставить её в папке со скетчем. Дело в том, что там много железно привязанных параметров и если вы захотите перенести работу на более «жирный» микроконтроллер, то придётся много чего менять в самом коде.
Инициализация идёт примерно в таком виде:
#include "LED_PANEL.h"
#define width 32
#define height 32
#define bpp 3
#define scan_lines 8
#define RGB_inputs 2
#define we_out_pin PB10
LED_PANEL led_panel = LED_PANEL(width, height, bpp, scan_lines, RGB_inputs, we_out_pin);
то есть создаём экземпляр класса LED_PANEL, для которого указываем параметры:
width — общая ширина панели в пикселях (всего)
height — общая высота панели в пикселях (всего)
bpp — байт на пиксель, 3 для RGB888. BAM версия работает только в RGB888
scan_lines — количество линий сканирования — 8/16/32. Должно соответствовать режиму, прошитому в контроллер.
RGB_inputs — количество RGB входов в разъеме HUB75 — 1/2. Должно соответствовать режиму, прошитому в контроллер.
we_out_pin — пин, к которому подцеплен вывод WE
Обращаю внимание, что при инициализации задаётся только пин WE. Все остальные пины прописаны жёстко в коде, так как они привязаны к используемым каналам таймера и DMA и их изменение повлечёт существенные изменения в коде.
Запуск и очистка экрана в разделе setup:
led_panel.begin();
led_panel.clear();
begin инициализирует нужные пины на выход, подключает таймер и DMA
clear очищает буфер
Для рисования можно использовать все стандартные процедуры библиотеки Adafruit GFX — от простейшего drawPixel до вывода текста. Для выдачи нарисованного в буфер используется процедуры:
led_panel.show();
В таком виде show инициирует передачу данных на контроллер по DMA и немедленно возвращает управление. Узнать — закончилась ли передача можно с помощью функции led_panel.OutIsFree() — если она говорит true, то значит передача закончилась. Есть особенность — если вызвать show, когда передача еще не завершилась — она будет просто проигнорирована.
led_panel.show(false);
аналог show(), но если вызвать show(false), а передача еще не завершилась, то процедура подождёт завершения передачи, потом начнёт новую передачу и вернёт управление:
led_panel.show(true);
аналог show(false), но если вызвать show(true), то после начала новой передачи процедура не вернёт управления до завершения передачи.
В общем-то — всё.
![](https://habrastorage.org/webt/pn/-a/of/pn-aofhwnhuuzszuk_mcf9upqje.jpeg)
Некоторые замечания по софту:
а) Гамма-коррекция вводится при пересчёте цвета из RGB565 (который использует библиотека) функцией ExpandColor. Во всех прочих случаях используется линейная трансфер-функция, то есть яркость прямо пропорциональна значению.
б) Софт позволяет подключать несколько LED-контроллеров к одной микроконтроллерной плате. Для этого надо отдать на контроллеры параллельно шину данных, линии RST и CLK. Нужный контроллер выбирается через линию WE. В софте нужно создать отдельный экземпляр класса LED_PANEL для каждого контроллера, при этом у каждого экземпляра при инициализации должны быть указаны разные линии WE (последний параметр).
TO DO
— Разобраться с «наводкой» цветов на соседние ряды. Похоже на плохую разводку самой панели (ключи мусорят), но надо проверить. Только приехала новая панель — буду проверять;
— Сделать новую версию платы — с уже разведённым RE и добавлением выходных преобразователей уровней в 5В;
— Сделать класс META_LED_PANEL, который позволит объединять несколько LED_PANEL в один виртуальный экран — это даст возможность создавать очень большие экраны с несколькими контроллерами;
— В перспективе уйти на более мощную серию CPLD, например CycloneIV. Это бы существенно расширило возможности при сохранении невысокой стоимости (EP4CE6E22 стоит у китайцев порядка 5$ штука, при этом там в 100 раз больше макроячеек и порядка 32 кБ встроенной памяти). Но этим я буду заниматься когда-нибудь потом. Если захочу. Так как подобные разработки отнимают уж слишком много времени.