3 mal 8 ergibt 16

Einführung

Früher oder später stößt man auf das Problem, dass der verwendete Mikrocontroller, CPLD oder FPGA zu wenige IOs hat. Es sind nur noch drei freie IOs verfügbar, aber es sollen fünf oder mehr LEDs angeschlossen werden.

Man kann nun einfach einen größeren (und damit teureren) Chip kaufen, oder man nutzt die Hilfe eines Schieberegisters.

Da es viele fertige Bibliotheken und Demoprogramme für Mikrocontroller gibt, wird hier ein CPLD verwendet und die Beispiele werden in VHDL ausgeführt.

Bits verschieben

Die zwei typischen Vertreter von Schieberegistern haben entweder einen seriellen Eingang und mehrere parallele Ausgänge oder mehrere parallele Eingänge und einen seriellen Ausgang.

Natürlich gibt es noch mehrere andere Versionen, aber in diesem Beispiel wird ein Schieberegister mit einem seriellen Eingang und acht parallelen Ausgängen verwendet. Die Wahl fällt auf den 74LS595, einen sehr häufig verwendeten Typ.

Dieses Schieberegister hat insgesamt zwei Register: das Schieberegister selbst und zusätzlich ein Speicherregister.

Der Vorteil besteht darin, dass die Daten bereits in das Schieberegister geladen werden können, während die Daten aus dem Speicherregister noch an den Ausgängen anliegen.

Beide Register sind auf diesem Chip 8-Bit groß.

Wie auf dem Bild zu sehen, gibt es auf der linken Seite fünf Eingänge und auf der rechten Seite neun Ausgänge.

Belegung der Pins

Eingänge:

  • PIN 14: SER – Serieller Eingang des Schieberegisters. Hier werden die Daten Bit für Bit in das Schieberegister geladen.
  • PIN 11: SRCLK – Der Takt zum Laden der einzelnen Bits in das Schieberegister. Bei jeder steigenden Flanke wird der jeweilige Zustand am seriellen Eingang (HIGH/LOW) übernommen.
  • PIN 10: SRCLR – Wird diese Leitung auf LOW oder „0“ gezogen, wird das Schieberegister sofort gelöscht.
  • PIN 12: RCLK – Wenn dieser Eingang auf HIGH oder „1“ gesetzt wird, wird der Inhalt des Schieberegisters in das Speicherregister übertragen und die Ausgänge entsprechend gesetzt.
  • PIN 13: OE – Der Ausgangsaktivierungseingang. Dies ermöglicht, das Schieberegister zu „aktivieren“, sobald der Eingang auf LOW oder „0“ gezogen wird. Andernfalls ist der Chip deaktiviert und die Ausgänge befinden sich im High-Z-Zustand.

Ausgänge:

  • PIN 1-7 & 15: Qa-h – Dies sind die acht parallelen Ausgänge des Speicherregisters. Hier liegen die Bits parallel in der Reihenfolge vor, in der sie in das Schieberegister geladen und in das Speicherregister übertragen wurden.
  • PIN 9: Qh’ – Dies ist der Überlauf oder das Übertragsbit. Damit besteht die Möglichkeit, mehrere Schieberegister zu kaskadieren, also hintereinander zu schalten. So können nicht nur acht parallele Ausgänge, sondern 16, 24, 32 usw. verwendet werden. Acht Ausgänge pro 74xx595.

Stromversorgung:

  • PIN 16: VCC – Hier wird die +5V Stromversorgung angeschlossen.
  • PIN 8: GND – Der Masseanschluss.

Funktionsweise eines Schieberegisters

Ein Schieberegister kann man sich wie ein Förderband vorstellen, auf dem genau acht Pakete Platz finden. Dieses Band wird durch den Takt angetrieben. Mit jedem Takt bewegt sich das Band um eine Position weiter. Da auf diesem Band nur acht Positionen frei sind, fällt der Inhalt der ersten Position vom Band und verschwindet.

Genau so funktioniert ein Schieberegister. Man legt vorne ein Paket mit dem Inhalt „1“ oder „0“ auf und bewegt das Band mit Hilfe des Takts.

Bei der steigenden Flanke des Takts wird der Inhalt dieser Position gespeichert. Das 8-Bit-Schieberegister kann den Inhalt von genau acht Positionen speichern. Wenn alle acht Positionen belegt sind und der Takt den nächsten Schlag ausführt, wird der Inhalt der ersten Position vergessen und der zuletzt hinzugefügte Inhalt wird stattdessen gespeichert.

Wie auf dem Bild zu sehen ist, wurde der Inhalt 11010111 in das Schieberegister geladen. Jedoch passiert noch nichts an den Ausgängen. Denn das Schieberegister muss noch in das Speicherregister übernommen werden. Dazu wird der RCLK-Eingang kurz auf HIGH gezogen. Dann wird der Inhalt in das Speicherregister übernommen und steht am Ausgang zur Verfügung.

Das Timing ist dabei natürlich wichtig. Denn wenn der Takt wieder HIGH ist, bevor die Daten übernommen wurden, geht das zuerst geladene Bit verloren. Daher muss das Timing korrekt sein.

Apropos Timing: Laut Datenblatt sollte der 74LS595 nicht höher als mit 20 MHz getaktet werden. Oftmals schafft er mehr (Tests bis zu 50 MHz waren erfolgreich), jedoch muss dann mit Datenverlusten oder Fehlern gerechnet werden.

8-Bit sind einfach nicht genug

Es stellt sich sicherlich die Frage, wie 16 LEDs mit nur 8 Ausgängen und das auch noch unabhängig voneinander, geschaltet werden können. Dass dies funktioniert, ist der Verdienst von Charlie Allen, der 1995 eine großartige Idee hatte. Die Methode wurde daher später auch als Charlieplexing bezeichnet.

Hierbei wird eine Matrix aus Reihen und Spalten gebildet, an deren Kreuzungspunkten eine LED (oder ein anderes zu steuerndes oder abzufragendes Element) platziert wird.

Wenn alle Ausgänge denselben Spannungspegel haben, kann kein Strom fließen. In Bezug auf das Schieberegister würde keine der LEDs bei „11111111“ oder „00000000“ leuchten.

Nun werden diese 8 Bits in 4 Reihen und 4 Spalten aufgeteilt. Die ersten 4 Bits entsprechen den Reihen, die nächsten 4 Bits den Spalten.

Wird nun eine „1“ an Reihe 1, oder Ausgang Qa, angelegt, was +5V entspricht, und eine „0“ an Spalte 1, was Massepotential entspricht, kann durch diese eine LED ein Strom fließen, und die LED leuchtet.

Damit nun die gesamte Reihe 1 leuchtet, müssen die anderen Reihen auf „0“ geschaltet werden, ebenso wie die verbleibenden Spalten auf „1“.

Somit entsprechen die ersten 4 Bits „1000“ und die nächsten 4 Bits „0111“, was dann „10000111“ am Ausgang des Schieberegisters entspricht.

Nun leuchtet genau diese eine LED, und die anderen bleiben dunkel. So kann man mit jeder LED fortfahren.

Auf diese Weise kann eine Tabelle erstellt werden, die die Zuordnung der LEDs zu den gesetzten Bits des Schieberegisters enthält:

  • LED 1 -> 10000111
  • LED 2 -> 10001011
  • LED 3 -> 10001101
  • LED 4 -> 10001110
  • LED 5 -> 01000111
  • LED 6 -> 01001011
  • LED 7 -> 01001101
  • LED 8 -> 01001110
  • LED 9 -> 00100111
  • LED 10 -> 00101011
  • LED 11 -> 00101101
  • LED 12 -> 00101110
  • LED 13 -> 00010111
  • LED 14 -> 00011011
  • LED 15 -> 00011101
  • LED 16 -> 00011110

Sobald diese Bitsequenz seriell in das Schieberegister geladen und dann in das Speicherregister übertragen wird, kann die jeweilige LED gesteuert werden.

Mehr als eine LED gleichzeitig

Wer versucht, das Beispiel auf einem Steckbrett nachzubauen, was auch ohne Schieberegister möglich ist, wird schnell feststellen, dass nur eine LED gleichzeitig oder mehrere auf einmal leuchten können.

Denn sobald eine weitere Reihe oder Spalte hinzugefügt wird, leuchten alle entsprechenden LEDs, die dort angeschlossen sind. Was also tun?

Das träge Auge

Hier kommt das eher träge Auge ins Spiel. Wie aus den alten Tagen des Kinos, als der Film nicht aus dem Projektor kam, wurden 24 Bilder pro Sekunde gezeigt, was in unseren Augen ein stabiles und einigermaßen flüssiges Bild ergab.

Auf diese Weise wird es auch hier gemacht. Die LEDs werden einfach nacheinander zum Leuchten gebracht. Wenn dies schnell genug geschieht, entsteht der Eindruck, dass verschiedene LEDs gezielt aufleuchten.

Da dies mit einer recht hohen Frequenz durchgeführt wird, scheinen die LEDs unabhängig voneinander steuerbar zu sein. Tatsächlich wird immer nur eine LED gleichzeitig eingeschaltet, dann wieder ausgeschaltet und die nächste stattdessen eingeschaltet, und so weiter. Diese Sequenz wird so oft wiederholt, bis das Auge nur noch leuchtende LEDs wahrnimmt.

Die Schaltung

Die Schaltung, um 16 LEDs mit nur 3 Leitungen zu schalten, sieht so aus:

Wie zu sehen ist, werden nun 16 LEDs letztendlich nur durch 3 Leitungen gesteuert.

Wer das Datenblatt gelesen hat, wird bemerkt haben, dass die Ausgänge nicht sehr belastbar sind. Daher können keine stromhungrigen LEDs angeschlossen werden. Glücklicherweise gibt es heutzutage LEDs, die mit 2-3mA auskommen.

Steuerung mit einem CPLD

Nun wird noch ein CPLD benötigt. Persönlich wird hier gerne die XC9500XL-Serie von Xilinx verwendet. Diese sind noch sehr gut erhältlich und sehr günstig. In China kostet ein Chip umgerechnet weniger als einen Dollar.

Diese sind auch recht unzerstörbar. ESD ist praktisch kein Problem, und es bedarf großer Anstrengungen, um einen IO-Port oder den gesamten Chip zu zerstören.

Der größte Vorteil ist jedoch, dass sie noch 5V tolerant sind. Das bedeutet, dass der Chip selbst mit 3,3V betrieben wird und auch die IOs nur 3,3V ausgeben (was dann LVTTL – Low Voltage TTL genannt wird), aber eingehende 5V problemlos toleriert. Somit können diese CPLDs wunderbar in TTL-Schaltungen integriert werden.

Ideal also für kleine Basteleien. Es wurde vor langer Zeit ein kleines Entwicklungsboard für solche Dinge erstellt, bei dem die IOs alle herausgeführt und zusätzlich der Spannungswandler für die 3,3V sowie ein Oszillator integriert sind.

Natürlich kann jedes andere Board verwendet werden, solange es 5V tolerant ist.

VHDL-Code

Der Code wurde so einfach wie möglich gehalten. Das Ganze könnte wesentlich optimierter und kürzer sein. Dies wäre jedoch für Anfänger weniger verständlich.

Erforderlich sind 3 Ausgänge und ein Taktgenerator. Ein 16MHz-Oszillator wird empfohlen. Sollte die Frequenz höher als 20MHz sein, wird einfach entsprechend heruntergeteilt, bis eine maximale Frequenz von 20MHz erreicht ist.

entity SR_LED_Test is

    Port    (
            CLK               : in  std_logic;
            SR_CLK            : out std_logic;
            SR_DATA_LOAD      : out std_logic;
            SR_SERIAL_DATA    : out std_logic
            );

end SR_LED_Test;

CLK ist der Eingang des Oszillators, und die 3 Ausgänge gehen zu den entsprechenden Leitungen des Schieberegisters (siehe obiges Schaltbild).

LED_PATTERN(0)  <= "10000111";
LED_PATTERN(1)  <= "10001011";
LED_PATTERN(2)  <= "10001101";
LED_PATTERN(3)  <= "10001110";
LED_PATTERN(4)  <= "01000111";
LED_PATTERN(5)  <= "01001011";
LED_PATTERN(6)  <= "01001101";
LED_PATTERN(7)  <= "01001110";
LED_PATTERN(8)  <= "00100111";
LED_PATTERN(9)  <= "00101011";
LED_PATTERN(10) <= "00101101";
LED_PATTERN(11) <= "00101110";
LED_PATTERN(12) <= "00010111";
LED_PATTERN(13) <= "00011011";
LED_PATTERN(14) <= "00011101";
LED_PATTERN(15) <= "00011110";

Wie oben bereits erwähnt, wurde das Bitmuster für jede der 16 LEDs einfach in ein 8-Bit breites Element des Typs „STD_LOGIC_VECTOR“ geschrieben.

Das Ganze wird mit dem Signal LED gesteuert. Dieses Element ist vom Typ „STD_LOGIC_VECTOR“ und ist 16 Bit breit. LED(0) entspricht der ersten LED und LED(15) der 16. und letzten LED.

Soll eine LED leuchten, muss beispielsweise nur LED(0) <= '1'; geschrieben werden.

if (LED(LED_POSITION) = '1') then
    SR_DATA <= LED_PATTERN(LED_POSITION);
else
    SR_DATA <= (others => '0');
end if;
LED_POSITION <= LED_POSITION + 1;

Nun wird der Status der einzelnen gesetzten Zustände der LEDs einfach abgefragt.

In Zeile 1 wird überprüft, ob die LED an der jeweiligen Position (beginnend bei 0) eine ‘1’ enthält. Wenn ja, dann wird in Zeile 2 das entsprechende LED_PATTERN (mit der entsprechenden Position) in das Element „SR_DATA“ übernommen, welches ebenfalls vom Typ „STD_LOGIC_VECTOR“ mit einer Breite von 8 Bit ist.

In Zeile 3 wird dann verzweigt, wenn die Abfrage aus Zeile 1 eine ‘0’ ergab, um anschließend in Zeile 4 einfach SR_DATA mit „00000000“ zu füllen ((others => '0') bedeutet, dass unabhängig von der Bitbreite des Signals alle Bits mit dem entsprechenden Wert, hier also „00000000“, gefüllt werden).

Zeile 5 beendet die Schleife, um anschließend in Zeile 6 den Zähler für die LED-Nummer um eins zu erhöhen. Somit ist nun in SR_DATA ersichtlich, welche Bits im Schieberegister gesetzt werden müssen, damit die entsprechende LED eingeschaltet werden kann.

SR_DATA_LOAD <= '0';
SR_SERIAL_DATA <= SR_DATA(SR_COUNTER);
SR_COUNTER <= SR_COUNTER + 1;
if (SR_COUNTER = 8) then
    SR_DATA_LOAD <= '1';
    SR_COUNTER <= 0;
end if;

Nun wird das Schieberegister gefüllt, um es anschließend in das Speicherregister zu übertragen. Dazu wird im ersten Schritt der Latch in Zeile 1 auf LOW gezogen, damit die Daten während des Ladens nicht bereits die Ausgänge beeinflussen.

In Zeile 2 wird dann das zuvor mit 8 Bits gefüllte Element „SR_DATA“ mithilfe eines Zählers Bit für Bit auf den seriellen Eingang des Schieberegisters gelegt. Danach wird in Zeile 3 der Zähler um eins erhöht, damit das nächste Bit eingelesen werden kann. Da der Zähler erst nach dem Lesen inkrementiert wird, steht im Zähler eine ‘8’, wenn alle Bits für eine LED eingelesen wurden, was dann auch in Zeile 4 abgefragt wird.

Sind alle 8 Bits eingelesen, wird in Zeile 5 der Latch auf HIGH gesetzt, wodurch der Inhalt des Schieberegisters in das Speicherregister übernommen wird und diese Bitfolge dann auch entsprechend an den Ausgängen gesetzt wird. Die LED leuchtet. Damit das Ganze wiederholt werden kann, wird in Zeile 6 der Zähler zurückgesetzt und in Zeile 7 diese Abfrageschleife beendet.

Nun fehlen noch zwei wichtige Punkte: das Takten, damit die einzelnen Bits gelesen und die Zähler überhaupt weiterzählen können, sowie eine Sperre, damit das Setzen des Bitmusters und das Laden in das Schieberegister nicht gleichzeitig erfolgt.

Am besten wird hierfür eine sogenannte Zustandsmaschine verwendet. Ähnlich wie bei einer Ampel wird eine Abhängigkeit untereinander geschaffen.

Kombiniert sieht das Ganze dann so aus, einschließlich der letzten beiden Codeausschnitte:

process (SR_CLOCK)
begin
    if (rising_edge(SR_CLOCK)) then
        case SR_STATUS is
            when LOADING =>
                SR_DATA_LOAD <= '0';
                SR_SERIAL_DATA <= SR_DATA(SR_COUNTER);
                SR_COUNTER <= SR_COUNTER + 1;
                if (SR_COUNTER = 8) then
                    SR_DATA_LOAD <= '1';
                    SR_COUNTER <= 0;
                    SR_STATUS <= IDLE;
                end if;
            when IDLE =>
                if (LED(LED_POSITION) = '1') then
                    SR_DATA <= LED_PATTERN(LED_POSITION);
                else
                    SR_DATA <= (others => '0');
                end if;
                LED_POSITION <= LED_POSITION + 1;
                SR_STATUS <= LOADING;
        end case;
    end if;
end process;

Zum einen wurde das Ganze in einen Prozess eingebettet, damit die Anweisungen darin nicht parallel abgearbeitet werden. Der gesamte Prozess wird getaktet. Wann immer das Signal „SR_CLOCK“ von LOW auf HIGH wechselt, also bei einer steigenden Flanke, wird dieser Prozess durchlaufen. Zeile 3 ist dafür verantwortlich.

Dann wird eine CASE-Schleife ausgeführt, und damit die Zustandsmaschine. Dafür wurden zwei Zustände definiert (die Deklaration der Signale und Typen wird später erläutert), LOADING und IDLE. Mit LOADING werden Daten in das Schieberegister geladen, und mit IDLE hat das Schieberegister momentan nichts zu tun und die Daten werden vorbereitet, indem das BIT-Muster für die jeweilige LED ausgewählt wird.

Das Ganze wird durch das Signal SR_STATUS gesteuert. Es gibt entweder LOADING oder IDLE an. Ein Teil setzt den Status so, dass der andere Teil arbeiten kann.

Nach dem ersten Anlegen des Stroms an die Schaltung beziehungsweise der Initialisierung des CPLD wird IDLE gesetzt (der Grund dafür wird später erläutert).

Somit verzweigt die CASE-Anweisung in die zweite Hälfte des Prozesses. Dort wird der Teil bearbeitet, der das passende Bitmuster für die jeweilige LED heraussucht und in SR_DATA schreibt.

Am Ende erfolgt die Setzung eines neuen SR_STATUS auf LOADING. Erst dann kann der erste Teil arbeiten. Gleichzeitig wird verhindert, dass der zweite Teil ausgeführt werden kann, solange der erste Teil arbeitet.

Wiederum, nach dem Einlesen aller 8 Bits in das Schieberegister, dem Übertragen dieser Daten in das Speicherregister und dem Setzen des Latch auf HIGH, wird SR_STATUS wieder auf IDLE gesetzt. Somit kann das Bitmuster für die nächste LED eingelesen werden. Und bis dies erfolgt ist, kann der erste Teil nicht weiterarbeiten, da hierfür SR_STATUS zunächst wieder auf LOADING gesetzt werden muss.

So wird ständig zwischen diesen beiden Zuständen hin- und hergeschaltet und die LED wird ein- und ausgeschaltet. Dies geschieht so schnell, dass man nachher nicht mehr sieht, dass eigentlich immer nur eine LED gleichzeitig an ist.

Hier noch die Deklaration der intern verwendeten Signale:

type   SR_STATUS_TYPE    is  (LOADING, IDLE);
signal SR_STATUS         :  SR_STATUS_TYPE := IDLE;
signal SR_CLOCK          :  std_logic;
signal SR_DATA           :  std_logic_vector (7 downto 0) := (others => '0');
signal SR_COUNTER        :  integer range 0 to 8 := 0;
signal LED               :  std_logic_vector (15 downto 0);
signal LED_POSITION      :  integer range 0 to 15 := 0;
type   LED_PATTERN_ARRAY is array (0 to 15) of std_logic_vector (7 downto 0);
signal LED_PATTERN       :  LED_PATTERN_ARRAY;

Für die Zustandsmaschine wurden LOADING und IDLE als Schaltkriterien verwendet. Dies ist wesentlich lesbarer als 1 und 0. Solche Signaltypen gibt es jedoch nicht in VHDL. Aber man kann diese selbst definieren. Dafür wird in der ersten Zeile ein neuer Elementtyp namens SR_STATUS_TYPE definiert. Dieses Element kennt die Zustände LOADING und IDLE. Es könnten auch mehr definiert werden, aber diese wurden hier nicht benötigt.

In Zeile 2 wird dann ein Signal wie gewohnt mit dem neu definierten Elementtyp definiert. Gleichzeitig wird ein erster Initialwert gesetzt, in diesem Fall IDLE. Somit ist beim ersten Start des CPLD bereits IDLE als Wert gesetzt.

Zeile 3 ist für das Taktsignal, welches intern verwendet wird. Da das zu Beginn definierte SR_CLK als Ausgang definiert wurde, kann dieses nicht einfach auch zum Lesen verwendet werden.

Zeile 4 ist der 8-Bit breite Vektor, in den das Bitmuster der jeweiligen LED geschrieben wird.

Zeile 5 ist für den Zähler des Schieb

eregisters, der die 8 Bits nacheinander Bit für Bit verschiebt.

In Zeile 7 befinden sich die eigentlichen Signale für die LEDs. Da 16 Stück gesteuert werden sollen, ist dies auch ein entsprechender Vektor.

In Zeile 8 wird der zweite Zähler für die 16 LEDs gesetzt.

In Zeile 10 und 11 wird ein Array definiert. Da jedes Bitmuster 8 Bits hat (7 downto 0) und von diesen Bitmustern jeweils eines für jede LED existiert (ist array (0 to 15)), kann man dies vereinfacht mit einer Tabelle vergleichen, die 16 Zeilen (eine pro LED) und 8 Spalten (für die Bits pro LED) hat.

Eine Sache noch…

Es fehlt noch eine Kleinigkeit, nämlich die Zuweisungen für den Takt.

SR_CLOCK <= CLK;
SR_CLK <= SR_CLOCK;

In der ersten Zeile wird das eingehende Taktsignal einem internen Signal zugewiesen. Dies wäre im Grunde überflüssig. Wenn jedoch das Taktsignal vorher geteilt werden muss, weil es schneller als 20 MHz ist, könnte man einfach dazwischen gehen und das geteilte Taktsignal zuweisen.

In der zweiten Zeile wird dieses interne Signal an den Taktausgang weitergegeben, damit das Schieberegister funktioniert.

Der gesamte Code sieht dann so aus:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity SR_LED_Test is
    Port (
        CLK            : in  std_logic;
        SR_CLK         : out std_logic;
        SR_DATA_LOAD   : out std_logic;
        SR_SERIAL_DATA : out std_logic
    );
end SR_LED_Test;

architecture Retrotinker of SR_LED_Test is
    type   SR_STATUS_TYPE    is  (LOADING, IDLE);
    signal SR_STATUS         :  SR_STATUS_TYPE := IDLE;
    signal SR_CLOCK          :  std_logic;
    signal SR_DATA           :  std_logic_vector (7 downto 0) := (others => '0');
    signal SR_COUNTER        :  integer range 0 to 8 := 0;
    signal LED               :  std_logic_vector (15 downto 0);
    signal LED_POSITION      :  integer range 0 bis 15 := 0;
    type   LED_PATTERN_ARRAY is array (0 to 15) of std_logic_vector (7 downto 0);
    signal LED_PATTERN       :  LED_PATTERN_ARRAY;
begin
    SR_CLOCK <= CLK;
    SR_CLK <= SR_CLOCK;

    LED_PATTERN(0)  <= "10000111";
    LED_PATTERN(1)  <= "10001011";
    LED_PATTERN(2)  <= "10001101";
    LED_PATTERN(3)  <= "10001110";
    LED_PATTERN(4)  <= "01000111";
    LED_PATTERN(5)  <= "01001011";
    LED_PATTERN(6)  <= "01001101";
    LED_PATTERN(7)  <= "01001110";
    LED_PATTERN(8)  <= "00100111";
    LED_PATTERN(9)  <= "00101011";
    LED_PATTERN(10) <= "00101101";
    LED_PATTERN(11) <= "00101110";
    LED_PATTERN(12) <= "00010111";
    LED_PATTERN(13) <= "00011011";
    LED_PATTERN(14) <= "00011101";
    LED_PATTERN(15) <= "00011110";

    process (SR_CLOCK)
    begin
        if rising_edge(SR_CLOCK) then
            case SR_STATUS is
                when LOADING =>
                    SR_DATA_LOAD <= '0';
                    SR_SERIAL_DATA <= SR_DATA(SR_COUNTER);
                    SR_COUNTER <= SR_COUNTER + 1;
                    if SR_COUNTER = 8 then
                        SR_DATA_LOAD <= '1';
                        SR_COUNTER <= 0;
                        SR_STATUS <= IDLE;
                    end if;
                when IDLE =>
                    if LED(LED_POSITION) = '1' then
                        SR_DATA <= LED_PATTERN(LED_POSITION);
                    else
                        SR_DATA <= (others => '0');
                    end if;
                    LED_POSITION <= LED_POSITION + 1;
                    SR_STATUS <= LOADING;
            end case;
        end if;
    end process;

    LED(0) <= '1';
    LED(1) <= '0';
    LED(2) <= '1';
    LED(3) <= '0';
    LED(4) <= '1';
    LED(5) <= '0';
    LED(6) <= '1';
    LED(7) <= '0';
    LED(8) <= '1';
    LED(9) <= '0';
    LED(10) <= '1';
    LED(11) <= '0';
    LED(12) <= '1';
    LED(13) <= '0';
    LED(14) <= '1';
    LED(15) <= '0';

end Retrotinker;

Zusammenfassung

Im letzten Abschnitt werden die einzelnen LEDs ein- oder ausgeschaltet. Wenn eine ‘1’ an der entsprechenden LED gesetzt wird, leuchtet sie auf.

Es können hier auch Lauflichter oder andere Effekte hinzugefügt werden. Oder es kann genau so als Anzeige für ein anderes Projekt verwendet werden.

Wie zu Beginn erwähnt, ist dieser Code sehr umfangreich. Beispielsweise müssten LEDs, die überhaupt nicht leuchten sollen, nicht gesetzt werden.

Es bleibt zu hoffen, dass der Code dadurch, insbesondere für Anfänger, deutlich lesbarer wird.

Sind solche Spielereien interessant? Dann kann ein Kommentar hinterlassen werden, um solche Projekte öfter zu präsentieren.

Ein Kommentar wird natürlich immer gerne gesehen.

0 Comments
Älteste
Neueste
Nach oben scrollen