Хочу поделиться опытом создания светодиодного табло 8x8 пикселей, 262к комбинаций цветов (18 бит), частотой кадров 180 FPS и подключением к USB. Также готов выслушать предложения по оптимизации и доработке. В дальнейшем планирую использовать наработки для создания дисплея домашней метеостанции.

Предисловие


    Началось все с простейшей схемы управления линейкой из 8 светодиодов через LPT-порт. Следующей версией было табло 5x8 из трехцветных светодиодов, которое также подключалось к 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 раз, то буфер таймера сбрасывается в минимальное значение и сдвигается указатель выбранной колонки.
В итоге получается следующий алгоритм динамической индикации:
  1. Берем первую группу 3 байт из трех массивов и помещаем в буферы каждого цвета в столбце.
  2. Активируем таймер с минимальным временем задержки в 128 тактов.
  3. Берем следующую группу 3 байт из трех массивов и помещаем в буферы каждого цвета в столбце.
  4. Активируем таймер удвоенной задержкой относительно предыдущего шага.
  5. Повторяем выборку еще 4 раза и каждый раз удваиваем время задержки.
  6. Сбрасываем таймер и начинаем обработку следующего столбца с п.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 — скачать
Графика
основной модуль сторона 1


основной модуль сторона 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Гц.

Градиент


Визуализатор

музыка: Tape Five — Soulsalicious

Примечание: Видео снимал через стекло от солнцезащитных очков (так не видно черных точек кадровой развертки) и без матового стекла (оно мешает фокусировке). Т.к. светодиоды у меня не матовые, я сточил линзу на них и обработал гравером.

Фото в собранном виде





Что можно улучшить


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

Комментарии (26)


  1. Hellsy22
    13.04.2016 03:53
    +1

    Это делалось из спортивного интереса или у подобной схемы есть преимущества перед массивом из WS2812+5050?


    1. Michael314
      13.04.2016 09:58

      в первую очередь спортивный интерес
      само устройство я собрал несколько лет назад, но улучшил только сейчас
      перед WS2812 особых преимуществ на первый взгляд нет, если только по цене и кастомизируемости/производительности
      а у вас есть опыт работы с WS2812? заявлено 16млн цветов, интересно посмотреть, как это выглядит в живую


      1. IronHead
        13.04.2016 10:52
        +1

        Вот, запускал для пробы https://www.youtube.com/watch?v=w-08sPrO-pA
        А это уже то, ради чего покупал (снято на телефон) https://www.youtube.com/watch?v=l7EDhozF0PI


        1. Michael314
          13.04.2016 16:33

          а чем управляется бегущая строка?


          1. IronHead
            13.04.2016 16:37

            stm32 + hc05 (bluetooth) + приложение под андроид для смены текста, его цвета и скорости прокрутки. Все никак руки не дойдут оформить все это в корпус со встроенным dc-dc для питания от 12вольт.


            1. Michael314
              13.04.2016 16:45

              на stm32 реализован знакогенератор или он помнит и выводит массив данных, а содержимое массива генерирует изначально андроид?


    1. Michael314
      15.04.2016 09:18

      посмотрел цены в китае, на матрице 8х8 еще сопоставимо, но например для 32х8 уже в 1.5-2 раза дороже. но с другой стороны, и пайки меньше. по скорости работы они не уступают судя по даташиту.


  1. ElectricFromUfa
    13.04.2016 07:03

    Существуют похожие серийные решения в наружной рекламе. Без микроконтроллера и USB правда, только система управления на логике и массив светодиодов. Интерфейс последовательный.

    Градации серого, правда, получают там очень частым обновлением индикатора (своеобразный ШИМ поверх последовательного интерфейса).
    Модули для управления такими модулями тоже есть с разными интерфейсами (на базе микроконтроллеров или ПЛИС)


    1. kail
      13.04.2016 10:07
      +1

      Серийные модули для рекламы не обеспечат такую скорость.

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

      Контроллеры тоже есть, большей частью неуправляемые, но есть и управляемые модели, видел у Onbon, например BX-5QL, есть SDK или API.

      Тут уже вопрос цены и кастомазации.


      1. Michael314
        17.04.2016 17:35

        да вроде особой разницы нет. а вот выбор у них поменьше. в основном P10


  1. Ares_ekb
    13.04.2016 09:52
    +1

    Прикольный микроконтроллер: с поддержкой USB и в DIP-корпусе. AVR таких нет…


    1. olafnew
      13.04.2016 10:43
      +1

      32u4 — tqfp44, т.е. паяется руками легко. Большой выбор готовых кроваток, превращающий его в DIP. Да и быстро можно сделать(для нужд прототипирования) we.easyelectronics.ru/AVR/plata-perehodnik-tqfpdip-dlya-mikrokontrollerov-avr-atmega16x-atmega32x-atmegaxx4x-smk-mmk-01p1.html

      Но, честное слово — не совсем понимаю смысл DIP корпусов, вижу их применение только в прототипировании, не более того. Даже распайку SMD деталей проще делать, нежели плату вертеть «туда-сюда» в случае с выводными. Да и быстрее распаять плату на 100элементов в SMD исполнении, нежели чем в выводном.


      1. KPbICKA
        13.04.2016 15:36

        Поддерживаю


      1. Michael314
        17.04.2016 15:03

        честное слово — не совсем понимаю смысл DIP корпусов

        как вариант — в нависном монтаже, когда можно обойтись без платы
        Даже распайку SMD деталей проще делать, нежели плату вертеть «туда-сюда» в случае с выводными.

        еще весомый плюс — нет необходимости сверлить отверстия под ножки


        1. olafnew
          18.04.2016 14:02

          Я ужасный зануда — но навесной монтаж у меня вызывает инженерное отвращение :(… Он не выдерживает никакой критики, т.к. ни механической прочности, ни ремонтопригодности у него нет. Когда, иной раз, вижу картинки поделок на навесном монтаже с использованием ИС( а мы говорим о 44ножечном чудище в DIP, в соответствии со статьей) — меня оторопь берет, как ЭТО, можно называть вообще монтажом! ))))

          О да… сверловка это отдельный, малопередаваемый кайф. Когда почти полностью перешел на SMD, я до сих пор с нервным тиком правого глаза вспоминаю плату декодера, где сверлил 2000+ отверстий, и запорол пару штук в конце :(…

          P.S. Но вообще — в современных реалиях, сверлить/не сверлить — задача вообще не стоит, т.к. заказ 2х слойки, а то и 4х слойки — стоит очень вменяемых денег, которые по плечу любому хоббисту — зато на выходе и нормальная маска, и шелкография, и аккуратная металлизация межслойная, и готовый трафарет для пасты — красота! И получаешь красивое изделие на выходе, а не тот ужас, как раньше, где 2х слойная плата в домашних условиях — это был ад и мучения.


  1. Costic
    13.04.2016 14:52
    +1

    Вы просите предложения по оптимизации.
    Сейчас у китайцев в продаже 8х8 RGB матрицы примерно по 300р. Компактно, паять самому диоды не требуется. И даже платы управления (шилды) к ним есть, но неадекватные цены.
    Для своего контроллера я присмотрел MAX7219 или даже TLC5940, т.к. последняя 4096-градации поддерживает.
    В принципе для «ардуинщиков» есть готовые аппаратные и программные решения, в том числе и массивы из таких матриц.


    1. Michael314
      13.04.2016 16:23

      я правильно понимаю, что для организации RGB матрицы 8х8 потребуется 12 шт TLC5940 или 3 шт MAX7219?
      когда начинал, искал подходящее что-то у MAX, но для контроллеров RGB-матриц у них цены запредельные


      1. Costic
        15.04.2016 16:20

        Микросхема TLC5940 может 16 ногами управлять. В матрице 32 вывода. Я думаю 2шт. будет достаточно. Хотя по цене простенький микроконтроллер подошёл бы с 40+ ногами. Но вот по току не выдержит.
        Получается что LCD дешевле и выгоднее.


        1. Michael314
          17.04.2016 14:20

          Микросхема TLC5940 может 16 ногами управлять

          судя по даташиту, она умеет управлять 16 светодиодами и я не вижу варианта организации матриц.
          Получается что LCD дешевле и выгоднее.

          LCD тоже планируется для вывода различной информации. LED будет для циферблата и возможно каких то эффектов.


      1. IronHead
        15.04.2016 16:36

        «или 3 шт MAX7219»
        Эти максы рулят матрицей только в режиме вкл/выкл отдельного светодиода + 8 градаций яркости всей матрицы сразу.
        Сейчас просто как раз на них один проект под заказ делаю, поэтому вникал в тонкости управления.
        Но даже так, вы сможете получить на rgb матрице 7 основных цветов, что для бытового применения — более чем достаточно.


  1. Delsian
    13.04.2016 17:18

    рукалицо…
    AS1130
    HT1632


    1. Costic
      15.04.2016 16:40

      USD 24, это перебор.


      1. Delsian
        15.04.2016 16:54

        "Ван доллар, мистер, онли ван доллар!" (с)


        1. Delsian
          15.04.2016 16:58

          А это вообще за гранью добра и зла


          1. Michael314
            17.04.2016 14:22

            как выше уже написали, он не умеет RGB и яркость только для всей матрицы целиком


    1. Michael314
      17.04.2016 14:42

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