Hoe maak je zelf een LED matrix display?

Hoe maak je zelf een LED matrix display?

Gepubliceerd op: 31-08-2012 18:09 - Read this in English

Hé, jij houdt niet van advertenties maar wél van kwaliteitscontent? Dat mag, maar misschien wil je dan een donatie doen via PayPal of creditcard?

Of doe je aankopen via één van onze winkelpartner-links in de zijbalk.

Wordt jouw aandacht ook altijd getrokken door grote LED schermen? Indrukwekkende RGB videowalls, of een bescheidener single color lichtkrant, ik wilde wel eens kijken hoe moeilijk het is om er zelf een te bouwen.

Mega Arduino RGB LED matrix

Laat ik jullie allereerst uit de droom helpen, met één Arduino ga je geen reusachtige RGB LED matrix bouwen die 16 miljoen kleuren kan weergeven, die kun je beter gewoon hier kopen. Een 8 bij 8 matrix zal nog wel lukken, maar de 16 MHz Atmel processor op je Arduino is niet snel genoeg om nog meer pixels flikkervrij aan te sturen.

Omdat ik liever een wat groter display wil bouwen laat ik de kleuren voor wat ze zijn, en bouw ik een single color matrix met 192 rode LED’s. Maar hoe stuur je met een Arduino, die maar 16 digitale poorten heeft, 192 LED’s aan? Het magische woord is multiplexing.

Multiplexing maakt gebruik van het feit dat het menselijk oog hele snelle veranderingen niet kan waarnemen, hierdoor lijken tv-beelden vloeiend, terwijl je in werkelijkheid naar 25 foto’s per seconde kijkt. Ik gebruik dezelfde truc bij het multiplexen van de matrix: er wordt steeds één rij LED’s tegelijk aangestuurd, maar dit gaat zo snel dat het lijkt alsof alle rijen tegelijk branden.

192 LED's Arduino matrix

192 LED's met de hand solderen

Mijn LED matrix bevat 192 LED’s, opgedeeld in 8 rijen en 24 kolommen. De kolommen stuur ik aan met 74HC595 shiftregisters. Shiftregisters kun je beschouwen als een soort serieel naar parallel converters. Op de enkele input klok je de bits serieel in, waarna ze op de uitgangen tegelijk verschijnen. Zo maak je dus van één Arduino output acht outputs. Een erg goede tutorial over shiftregisters vind je op Arduino.cc.

De 8 rijen worden niet aangestuurd met een shiftregister en wel om de volgende reden: de shiftregisters kunnen per uitgang net genoeg stroom verwerken om één LED aan te sturen. Een rij bestaat uit 24 LED’s, als je dit direct aanstuurt gaat je Arduino of je shiftregister letterlijk in rook op! De oplossing bestaat uit een component dat met een klein stroompje een grote stroom kan sturen: een transistor. Omdat ik hier 8 transistors nodig heb is het makkelijker om een ULN2803 driverarray te gebruiken, dit is één chip met 8 transistors erin, dat scheelt wat soldeerwerk.

Schema van de Arduino LED matrix

LED-stroom berekenen

Je kunt de LED's niet zomaar aan de uitgangen van de shiftregisters hangen, dan gaan er dingen stuk. De stroom die door de LED loopt moet begrenst worden met een weerstand om te voorkomen dat de LED doorbrandt. De waarde van de weerstand bereken je met de wet van Ohm:

U = I x R

In mijn matrix maak ik gebruik van rode LED's, deze vragen een standaardstroom van 20 milliAmpère, en veroorzaken een spanningsval van 1,8 Volt. De voedingsspanning van de matrix is 5 Volt, de weerstand zal dan dus nog 3,2 Volt moeten verwerken.

R = (Us - Ul) / I

Vullen we de voedingsspanning, de spanningsval, en de LED-stroom in dan krijgen we:

R = (5 - 1,8) / 0,02 = 160 Ohm

De benodigde voorschakelweerstand is dus 160 Ohm. Door het multiplexen van de 8 rijen branden de LED's maar 1/8e van de tijd, dit zou je nog mogen compenseren met de weerstandswaarde als je vind dat de LED's te zwak branden. Bedenk echter wel dat wanneer er iets mis gaat en je matrix blijft hangen op een rij, deze hele rij LED´s afgeschreven kan worden!

Het bouwen van een 192 LED Arduino matrix

Als laatste is het van belang dat je een goede voeding gebruikt. Je wilt geen 192 LED’s via je USB-poort voeden, of via de ingebouwde spanningsregelaar van je Arduino, dit levert ook die onprettige rook op. Met een LM7805 en wat randcomponenten bouw je een stabiele spanningsregelaar die 1 Ampère kan leveren, mits je hier een goede adapter op aansluit natuurlijk.

C++ voor Arduino

Om het display aan te kunnen sturen en ondertussen ook nog andere dingen te kunnen doen met je processor kun je de matrix het beste via een timerinterrupt aansturen. Het timerinterrupt zorgt ervoor dat het display stabiel blijft verversen, zodat er geen haperingen in het beeld optreden. De Timer1 library helpt bij het eenvoudig aanmaken van deze timerinterrupts.

Het display wordt rij voor rij aangestuurd met behulp van multiplexing. Omdat dit veel sneller moet gebeuren dan zichtbaar voor het menselijk oog kon ik niet uit de voeten met Arduino’s standaard digitalWrite() funcitie. Deze functie heeft ongeveer 54 clockcycles nodig om een poort te schakelen, dat is te traag. Daarom stuur ik de complete poorten direct aan met C++, een portwrite is in ongeveer 3 clockcycles gebeurd, 18 keer sneller dus!

Nog een 8x8 unit te gaan voor de matrix compleet is

De onderstaande code is de writeScreen routine die door het timerinterrupt wordt aangeroepen. Voor elke rij worden de 24 bits uit de buffer in de shiftregisters geklokt, en vervolgens worden de uitgangen van de shiftregisters geactiveerd evenals de bijbehorende rij-transistor. De delayMicroseconds() zorgt ervoor dat de LED’s ook zichtbaar kunnen opgloeien, de brightness waarde beïnvloed hoe fel het display lijkt te zijn voor de toeschouwer. Deze mag uiteraard niet te groot zijn omdat dan de verversingsfrequentie van het scherm te laag wordt.

void writeScreen() {
  for (row = 0; row <= 7; row++) {
    for (col = 0; col <= 23; col++) {
      PORTB &= B00; // clock and data low
      PORTB |= buffer[row][col]; // set databit from buffer
      PORTB |= B10; // clockpulse high to clock in databit
    }
    PORTB |= B100; // latch high to show outputs
    PORTB &= B011; // latch low
    PORTD = 1 << row; // enable rowdriver
    delayMicroseconds(brightness); // show outputs
    PORTD = 0; // disable rowdriver
  }
}

Buiten de writeScreen routine heb je natuurlijk nog allerlei functies nodig die de juiste bits in de buffer plaatsen om bijvoorbeeld letters of een klok op het display te tonen. Onderstaande code gebruik ik om 4 cijfers van 5 kolommen breed te tonen.

void UberKlok() {
  klok[0] = hour()/10;
  klok[1] = hour()%10;
  klok[2] = minute()/10;
  klok[3] = minute()%10;

  for (x = 0; x <= 7; x++) {
    for (karakter = 0; karakter <= 1; karakter++) {
      for (y = 0; y <= 4; y++) {
        buffer[x][karakter * 6 + y] = bitRead(pgm_read_byte_near(&cijfers_uber[klok[karakter]][y]), x);
      }
      buffer[x][karakter * 6 + 5] = 0;
    }

    buffer[x][12] = 0; // space between chars

    for (karakter = 2; karakter <= 3; karakter++) {
      for (y = 0; y <= 4; y++) {
        buffer[x][karakter * 6 + y + 1] = bitRead(pgm_read_byte_near(&cijfers_uber[klok[karakter]][y]), x);
      }
      buffer[x][karakter * 6 + 6] = 0;
    }
  }
}

De cijferkarakters zijn als byte arrays opgeslagen in het flashgeheugen met behulp van de pgmspace library. De "realtime" klok wordt verzorgd door de Time library waardoor ik eenvoudig het uur en de minuten kan ophalen. Met behulp van de modulo-operator % splits ik het uur en de minuten in twee losse cijfers.

Hoog op mijn verlanglijst staat de mogelijkheid om de buffer direct vanuit de computer, via USB te kunnen vullen, tot op heden is dat helaas nog niet gelukt. Enig idee hoe ik dat voor elkaar kan krijgen? Laat het me weten in de reacties hieronder!

Meer van dit soort artikelen lezen?

Reageren