Prototyp 262.144 LED-Farbkombinationen und 64 Pixel

    Ich möchte die Erfahrung mit der Erstellung eines 8x8-Pixel-LED-Displays, 262k-Farbkombinationen (18 Bit), einer Bildrate von 180 FPS und einer USB-Verbindung teilen. Ich bin auch bereit, Vorschläge zur Optimierung und Verfeinerung anzuhören. In Zukunft plane ich, die Best Practices zu verwenden, um eine Anzeige der Heimwetterstation zu erstellen.

Vorwort


    Alles begann mit dem einfachsten Steuerungsschema für eine Reihe von 8 LEDs über einen LPT-Port. Die nächste Version war eine 5x8-Karte mit dreifarbigen LEDs, die ebenfalls mit dem LPT verbunden war, und tatsächlich eine Anordnung von fünfzehn 8-Bit-Puffern mit einem Decoder für ihre Adressierung.
    Später, nachdem ich mich mit Mikrocontrollern getroffen hatte, machte ich mich daran, ein ähnliches Display zu erstellen, jedoch mit einer USB-Verbindung. Zunächst werden voraussichtlich nur 8 Farben verwendet. Anschließend fand er einen Weg, die Helligkeit jeder Diode mithilfe eines Timers in Analogie zu PWM zu steuern, und nach Abschluss des Softwareteils stellte sich das aktuelle Gerät heraus. Theoretisch können Sie mit 16 Millionen Farben arbeiten, aber herkömmliche LEDs sind für diesen Modus hinsichtlich Farbwiedergabe und Wiederholbarkeit nicht geeignet. Außerdem sind in der aktuellen Konfiguration bereits Probleme mit der Farbe verschiedener Dioden sichtbar.

Stellenbeschreibung


    Das Gerät basiert auf dem Mikrocontroller PIC18F4550, der mit einer Frequenz von 48 MHz arbeitet. Es werden ein eingebauter USB-Controller und eine vorgefertigte Bibliothek zum Arbeiten verwendet, Timer0 im 8-Bit-Modus, der die dynamische Anzeige implementiert. Um drei Farben in einer Spalte zu speichern, wurden drei 8-Bit-Trigger auf 74F374 verwendet. Durch die Verwendung eines solchen Puffers kann die Anzeigezeit eines Frames um das Dreifache reduziert werden. Hinweis: Bei der Auswahl des 74F374-Puffers habe ich nicht auf die Verkabelung der Beine geachtet, sondern dies nur am Montageständer verstanden, sodass ich die Platine erheblich komplizieren musste. Es ist besser, bequemere Analoga zu verwenden. Zum Beispiel 74HC574.
    Die LEDs werden über die Tasten ULN2803 und UDN2982 angeschlossen. Strombegrenzungswiderstände liegen nur im roten Kanal, weil Ihre Versorgungsspannung liegt unter blau und grün. Für Blau und Grün sind keine Widerstände installiert, weil genug Spannungsabfall an den Tasten. Hinweis: Für eine genauere Farbwiedergabe ist es besser, in jedem Kanal genauere Strombegrenzungswiderstände auszuwählen.
    Der Mikrocontroller fragt in einem Endloszyklus den USB-Status ab und startet / stoppt die Anzeige, wenn ein Datenpaket ankommt, abhängig vom Befehl oder bereitet die Daten für die Anzeige vor. Aufgrund der Begrenzung der Größe eines Pakets auf 64 Bytes werden die Daten für jede Farbe in einem separaten Paket von 48 Bytes übertragen - 6 Bytes für jede der 8 Spalten, wobei die Helligkeit jeder LED in der Spalte codiert wird. Nach Erhalt jedes Pakets wird es vom USB-Speicher in ein Array seiner Farbe kopiert.
    Nachdem der Anzeigestartbefehl empfangen wurde, aktiviert der MK den Timer im 8-Bit-Modus und einen Divisor um 128. Der Timer verwendet die Betriebsfrequenz des Mikrocontrollers als Taktimpulse. Eine Erhöhung des Timer-Zählers erfolgt alle 4 Takte. Die minimale Zeitdauer beträgt 10,6 μs (1/48 * 4 * 128), was ungefähr dem 2,8-fachen der Interrupt-Verarbeitungszeit entspricht (46 Operationen gegenüber 128 Zeitgeber-Abtastwerten).
    Wenn der Timer überläuft, wird ein Interrupt mit hohem Vektor ausgeführt. Der Interrupt-Handler deaktiviert die Anzeige, aktualisiert die Daten in den Puffern, überträgt 1 Byte von jedem Farbarray entsprechend dem Cursor und schaltet dann die Anzeige ein. Gibt einen neuen Wert aus dem temporären Puffer in den Timer ein, dekrementiert den Cursor und verschiebt den temporären Puffer für den Timer. Wenn der Zeitgeberpuffer den Maximalwert überschreitet, d.h. Mehr als fünfmal verschoben, wird der Timer-Puffer auf den Minimalwert zurückgesetzt und der Zeiger der ausgewählten Spalte verschoben.
Als Ergebnis wird der folgende dynamische Anzeigealgorithmus erhalten:
  1. Wir nehmen die erste Gruppe von 3 Bytes aus drei Arrays und legen sie in die Puffer jeder Farbe in der Spalte.
  2. Wir aktivieren den Timer mit einer minimalen Verzögerungszeit von 128 Ticks.
  3. Wir nehmen die nächste Gruppe von 3 Bytes aus drei Arrays und legen sie in die Puffer jeder Farbe in der Spalte.
  4. Wir aktivieren den Timer mit einer doppelten Verzögerung gegenüber dem vorherigen Schritt.
  5. Wiederholen Sie die Probe noch 4 Mal und verdoppeln Sie die Verzögerungszeit jedes Mal.
  6. Wir setzen den Timer zurück und beginnen mit der Verarbeitung der nächsten Spalte ab Schritt 1.

    Somit können wir für jede Diode in der Spalte 2 ^ 6 = 64 Helligkeitsoptionen einstellen. Wenn wir die Helligkeit jeder der drei Grundfarben kombinieren, erhalten wir 64 * 64 * 64 = 262144 Farben. Die Verarbeitungszeit für eine Spalte beträgt (2 ^ 6-1) * 10,6 μs = 672 μs. Die Zeit pro Frame von 8 Spalten beträgt 672 * 8 = 5,4 ms, was ungefähr 186 Frames pro Sekunde entspricht.

Verwendete Komponenten



Schema


Schema im DSN-Format - Download
Grafik


Gebühr


Lay6 Zeichnungen - herunterladen
Grafik
Hauptmodulseite 1


Hauptmodulseite 2


LED-Modul (Beachten Sie, dass das Überbrückungskabel, das die Säulen verbindet, blau markiert ist)


LED-Montagematrix


Firmware


Quellen und zusammengesetztes HEX in MPLABX X IDE v2.30 - herunterladen
Hauptcode
#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



Gerät in Betrieb


    Zur Steuerung verwende ich einen in C geschriebenen Internetradio-Player, der auf der BASS.DLL-Bibliothek basiert. Eine Verlaufsdemo über die gesamte verfügbare Farbpalette funktioniert während einer Pause. Die Aktualisierungsrate der Frames (an das Gerät übertragene Pakete) beträgt 20 Hz. Bei der Musikwiedergabe arbeitet ein Visualizer mit dem von BASS.DLL erhaltenen FFT-Array. Die Bildwiederholfrequenz von Frames (an das Gerät übertragene Pakete) beträgt in diesem Modus 29 Hz.

Farbverlauf


Visualizer

Musik: Tape Five - Soulsalicious

: ( ) ( ). .. , .







  • ( UDN)
  • USB
  • smd
  • 74F374 74HC574,
  • 74F374
  • 74HC138,
  • 3 ULN, UDN

Source: https://habr.com/ru/post/de392815/


All Articles