Прототип светодиодного табло на 262.144 комбинации цветов и 64 пикселя
Предисловие
Началось все с простейшей схемы управления линейкой из 8 светодиодов через LPT-порт. Следующей версией было табло 5×8 из трехцветных светодиодов, которое также подключалось к LPT и по сути представляло собой массив из пятнадцати 8-битных буферов с дешифратором для их адресации.
Позже, после знакомства с микроконтроллерами, задался целью создать аналогичное табло, но с подключением к USB. Изначально рассчитывал использовать только 8 цветов. Впоследствии нашел способ управления яркостью каждого диода с помощью таймера по аналогии с PWM, и после доработки программной части получился текущий девайс. Теоретически можно работать и с 16млн цветов, но обычные светодиоды не подойдут для такого режима по цветопередаче и повторимости. К тому же проблемы с цветом разных диодов уже на текущей конфигурации заметны.
Описание работы
В основе устройства лежит микроконтроллер PIC18F4550, работающий на частоте 48МГц. Используются встроенный USB-контроллер и готовая библиотека для работы с ним, Timer0 в режиме 8 бит, реализующий динамическую индикацию. Для хранения трех цветов в одном столбце использованы три 8-битных триггера на 74F374. Использование такого буфера позволяет сократить время отображения одного кадра в 3 раза. Примечание: Когда я выбирал буфер 74F374, не обратил внимания на разводку его ножек, а понял это только уже на монтажном стенде, поэтому пришлось существенно усложнить плату. Лучше использовать более удобные аналоги. Например, 74HC574.
Светодиоды подключаются через ключи ULN2803 и UDN2982. Токоограничивающие сопротивления стоят только в красном канале, т.к. их напряжение питания ниже синего и зеленого. Для синего и зеленого сопротивления не установлены, т.к. достаточно падения напряжения на ключах. Примечание: Для более точной цветопередачи лучше подобрать более точные токоограничивающие сопротивления в каждый канал.
Микроконтроллер в бесконечном цикле выполняет опрос состояния USB и, при поступлении пакета данных, в зависимости от команды, запускает/останавливает индикацию или подготавливает данные для индикации. В связи с ограничением размера одного пакета в 64 байта, данные для каждого цвета передаются отдельным пакетом в 48 байт — по 6 байт на каждый из 8 столбцов, кодирующие яркость каждого светодиода в столбце. После получения каждого пакета он копируется из памяти USB в массив своего цвета.
После поступления команды запуска индикации МК активирует таймер в режиме 8 бит и делителем на 128. Таймер использует в качестве тактовых импульсов рабочую частоту микроконтроллера. Увеличение счетчика таймера происходит каждые 4 такта. Минимальный период таймера составляет 10,6 мкс (1/48*4*128), что примерно в 2,8 раза больше времени обработки прерывания (46 операций, против 128 отсчетов таймера).
При переполнении таймера выполняется прерывание по высокому вектору. Обработчик прерывания отключает индикацию, выполняет обновление данных в буферах, перенося по 1 байту из каждого массива цвета согласно курсору, затем включает индикацию. Заносит новое значение в таймер из временного буфера, декрементирует курсор, сдвигает временный буфер для таймера. Если буфер таймера превысил максимальный показатель, т.е. сдвигался больше 5 раз, то буфер таймера сбрасывается в минимальное значение и сдвигается указатель выбранной колонки.
В итоге получается следующий алгоритм динамической индикации:
- Берем первую группу 3 байт из трех массивов и помещаем в буферы каждого цвета в столбце.
- Активируем таймер с минимальным временем задержки в 128 тактов.
- Берем следующую группу 3 байт из трех массивов и помещаем в буферы каждого цвета в столбце.
- Активируем таймер удвоенной задержкой относительно предыдущего шага.
- Повторяем выборку еще 4 раза и каждый раз удваиваем время задержки.
- Сбрасываем таймер и начинаем обработку следующего столбца с п.1.
Таким образом мы можем задать 2^6=64 варианта яркости для каждого диода в столбце. Комбинируя яркость каждого из трех базовых цветов, получаем 64*64*64=262144 цвета. Время обработки одного столбца составляет (2^6-1)*10,6мкс=672мкс. Время на один кадр из 8 столбцов — 672*8=5.4мс, что примерно соответствует 186 кадрам в секунду.
Использованные компоненты
- PIC18F4550 — Микроконтроллер
- 74F374 — Триггер для хранения текущих значений столбца
- ULN2803 — Ключ для управления катодами
- UDN2982 — Ключ для управления анодами
- 4-х выводные RGB светодиоды с общим катодом (можно использовать любые светодиоды)
Схема
Схема в формате dsn — скачать
Плата
Чертежи в формате lay6 — скачать
основной модуль сторона 2
модуль светодиодов (обратите внимание, что синим отмечена проволочная перемычка, соединяющая столбцы)
матрица крепления светодиодов
Прошивка
Исходники и собранный HEX в MPLABX X IDE v2.30 — скачать
#ifndef MAIN_C #define MAIN_C // Local includes #include "config.h" #include "usb.h" #include "HardwareProfile.h" #include "usb_function_hid.h" #include "genericHID.h" #define UdnOn LATA&=0b11111110 #define UdnOff LATA|=0b00000001 #define UlnOn LATD #define UlnOff LATD =0b00000000 #define LineBufer LATB #define WriteR LATE|=0b00000001 #define WriteG LATE|=0b00000010 #define WriteB LATE|=0b00000100 #define WriteRst LATE =0b00000000 #define Columns 8 #define BrightLevels 6 #define BlockSize (Columns*BrightLevels) #define MinBright 0b11111111 unsigned char cursor; unsigned char bright; unsigned char column; unsigned char dataR[BlockSize]; unsigned char dataG[BlockSize]; unsigned char dataB[BlockSize]; void ProcessIO(void) { unsigned char temp = BlockSize + 1; // If we are not in the configured state just return if ((USBDeviceState < CONFIGURED_STATE) || (USBSuspendControl == 1)) return; //Check if data was received from the host. if (!HIDRxHandleBusy(USBOutHandle)) { switch (ReceivedDataBuffer[0]) { case 0x80: // get red packet while (--temp) dataR[temp-1] = ReceivedDataBuffer[temp]; break; case 0x81: // get green packet while (--temp) dataG[temp-1] = ReceivedDataBuffer[temp]; break; case 0x82: // get blue packet while (--temp) dataB[temp-1] = ReceivedDataBuffer[temp]; break; case 0x90: // start column = 0b00000001; cursor = BlockSize; bright = MinBright; TMR0ON = 1; SWDTEN = 0; break; case 0x91: // stop UdnOff; UlnOff; TMR0ON = 0; SWDTEN = 0; break; case 0x92: // power off UdnOff; UlnOff; TMR0ON = 0; SWDTEN = 0; SLEEP(); break; } // Re-arm the OUT endpoint for the next packet USBOutHandle = HIDRxPacket(HID_EP, (BYTE*) & ReceivedDataBuffer, 64); } } void main(void) { // Set all port as digital input/output PCFG3 = 1; // Clear all ports // 76543210 PORTA = 0b00000000; PORTB = 0b00000000; PORTC = 0b00000000; PORTD = 0b00000000; PORTE = 0b00000000; // Configure ports (1 - inputs; 0 - outputs) // 76543210 TRISA = 0b00000000; TRISB = 0b00000000; TRISC = 0b00000000; TRISD = 0b00000000; TRISE = 0b00000000; // Configure interrupts for Timer0 // 76543210 INTCON = 0b10100000; // Configure Timer0 as 8bit and 128 prescaler // 76543210 T0CON = 0b01000110; USBDeviceInit(); while(1) { // Check bus status and service USB interrupts. USBDeviceTasks(); // Application-specific tasks. ProcessIO(); }; } void interrupt tc_int() // High priority interrupt { UdnOff; UlnOff; LineBufer = dataR[cursor-1]; WriteR; LineBufer = dataG[cursor-1]; WriteG; LineBufer = dataB[cursor-1]; WriteB; UdnOn; UlnOn = column; WriteRst; TMR0L = bright; if (!--cursor) cursor = BlockSize; bright <<= 1; asm("BTFSS _bright, 5, 0"); asm("RLNCF _column, 1, 0"); asm("BTFSS _bright, 5, 0"); bright = MinBright; TMR0IF = 0; } #endif
Устройство в работе
Для управления я использую плеер интернет радио, написанный на Си, в основе которого библиотека BASS.DLL. Демо с градиентом по всей доступной палитре цветов работает во время паузы, частота обновления кадров (передаваемых пакетов в устройство) — 20Гц. При проигрывании музыки работает визуализатор, использующий FFT-массив, получаемый средствами BASS.DLL, частота обновления кадров (передаваемых пакетов в устройство) в этом режиме — 29Гц.
Примечание: Видео снимал через стекло от солнцезащитных очков (так не видно черных точек кадровой развертки) и без матового стекла (оно мешает фокусировке). Т.к. светодиоды у меня не матовые, я сточил линзу на них и обработал гравером.



Что можно улучшить
- заменить ключи на более быстрые (особенно это касается UDN)
- реализовать работу с USB через прерывания
- использовать матовые светодиоды или smd для упрощения рассеивания и смешивания света
- в место 74F374 лучше использовать 74HC574, что значительно упростит разводку платы
- добавить емкости к каждой схеме 74F374 для защиты от помех
- для выборки столбцов можно использовать дешифратор 74HC138, что позволит сэкономить ножки МК
- для удешевления схемы можно использовать светодиоды с общим анодом и использовать 3 более дешёвых ключа ULN, вместо UDN


ИИ находит вдвое больше программных уязвимостей, но хакеры их почти не используют
Rocket Lab открыла четвёртый космодром на Аляске для расширения сети запусков
Китайские астрономы открыли уникальный пульсар с аномальным замедлением и нулевым гамма-излучением
Раскрыты характеристики глобальной версии Redmi Note 17 Pro Max: батарея 9210 мА·ч, зарядка 100 Вт, защита IP69K и чип Snapdragon 6 Gen 5
Запущен первый спутниковый флот для обнаружения лесных пожаров на ранней стадии
Россия открывает доступ к квантовому процессору SnowDrop 8Q на 8 кубитов
Смартфон Samsung Galaxy A37 перевели на квартальные обновления спустя всего четыре месяца после релиза
Кабмин увеличит финансирование достройки первых серийных Ил-114-300