Veröffentlicht am: 19.02.2018
Von: JLusiardi
In Kategorie: Hardware
Als Fortsetzung zu meinen ersten Gehversuchen mit VHDL und FPGAs soll nun ein 1-Byte-Display aus 2 Siebensegmentanzeigen entstehen. So etwas ist auf besseren FPGA-Boards meist bereits integriert, nicht jedoch auf dem preiswerten Basisboard. In diesem Artikel wird z.B. beschrieben, wie auf einem Digilent Basys 3 Artix-7 FPGA Board diese Anzeige aufgebaut ist und wie man sie in Verilog anspricht. Hier werden nun leichte Abwandlungen davon vorgenommen.
Hardware
Auf dem Basys 3 Board werden Siebensegmentanzeigen mit gemeinsamer Anode und PNP-Transistoren verwendet. In meiner Bastelkiste fanden sich aber nur 2 Siebensegmentanzeigen und NPN-Transistoren (BC 337-16). Zusätzlich benötigt man die passenden Vorwiderstände. Die Vorwiderstände für die LED Segmente ergeben sich R = U/I = 3.3 V / 0.002 A = 150 Ohm (die LEDs benötigen 20mA zum Leuchten). Für die Transistoren verwende ich einen Vorwiderstand von jeweils 4k7 Ohm (Hilfe bei den Berechnungen hatte ich von Dr. Kilian). Die vorgestellte Schaltung skaliert natürlich auch für mehr als 2 Siebensegmentanzeigen.
Schaltplan
Fertig aufgebaut auf Lochraster sieht das dann so aus. Ja, 20mm Siebensegmentanzeigen sind eigentlich zu groß:
Aufgebaute Schaltung
Die Pins auf der linken Seite besitzen folgende Bedeutung:
PIN Nummer | Bedeutung |
---|---|
Pin 0 / GND (oben) | Anschluss für Masse |
Pin 1 / S0 | Für die Auswahl der rechten Siebensegmentanzeige auf High |
Pin 2 / S1 | Für die Auswahl der linken Siebensegmentanzeige auf High |
Pin 3 / A | Aktiviert Segment A der ausgewählten Siebensegmentanzeige |
Pin 4 / B | Aktiviert Segment B der ausgewählten Siebensegmentanzeige |
Pin 5 / C | Aktiviert Segment C der ausgewählten Siebensegmentanzeige |
Pin 6 / D | Aktiviert Segment D der ausgewählten Siebensegmentanzeige |
Pin 7 / E | Aktiviert Segment E der ausgewählten Siebensegmentanzeige |
Pin 8 / F | Aktiviert Segment F der ausgewählten Siebensegmentanzeige |
Pin 9 / G | Aktiviert Segment G der ausgewählten Siebensegmentanzeige |
Pin 10 / DP (unten) | Aktiviert den Punkt der ausgewählten Siebensegmentanzeige |
Zuordnung der Segmente A bis G und DP auf der Siebensegmentanzeige:
Zuordnung der LEDs
Ansteuerung
DieTrägheit des Auges wird bei dieser Schaltung ausgenutzt, da die beiden Siebensegmentanzeigen jeweils abwechselnd aktiviert werden (mit ca. 1 kHz).
Folgende VHDL Entity soll implementiert werden:
Entity byte_display Is
Port (
clk : In std_logic;
enable : In std_logic;
data : In std_logic_vector(7 Downto 0);
segments : Out std_logic_vector(1 Downto 0);
leds : Out std_logic_vector(6 Downto 0)
);
End byte_display;
Der clk-Eingang wird dem globalen Clock-Signal verbunden und wird für die Umschaltung der einzelnen Siebensegmentanzeige verwendet. Der Wert data wird intern gespeichert, wenn enable eine steigende Flanke hat und bleibt solange angezeigt, bis ein neuer Wert gesetzt wird. Die beiden ausgehenden Bits in segments aktivieren jeweils mit high-Werten eine der beiden Siebensegmentanzeigen und die Bits aus leds jeweils die entsprechende LED.
Eine Siebensegmentanzeige kann hexadezimal ein Nibble (also ein halbes Byte oder 4 Bit) darstellen. Die Umsetzung dieser 4 Bit in die 7 LEDs der Anzeige wird in einer eigenen VHDL-Prozedur durchgeführt:
PROCEDURE display_digit(
SIGNAL digit : IN std_logic_vector (3 DOWNTO 0);
SIGNAL leds : OUT std_logic_vector(6 DOWNTO 0)
) IS
BEGIN
CASE digit IS -- GFEDCBA
WHEN "0000" => leds <= "0111111"; -- 0 ABCDEF
WHEN "0001" => leds <= "0000110"; -- 1 BC
WHEN "0010" => leds <= "1011011"; -- 2 ABDEG
WHEN "0011" => leds <= "1001111"; -- 3 ABCDG
WHEN "0100" => leds <= "1100110"; -- 4 BCFG
WHEN "0101" => leds <= "1101101"; -- 5 ACDFG
WHEN "0110" => leds <= "1111101"; -- 6 ACDEFG
WHEN "0111" => leds <= "0000111"; -- 7 ABC
WHEN "1000" => leds <= "1111111"; -- 8 ABCDEFG
WHEN "1001" => leds <= "1101111"; -- 9 ABCDFG
WHEN "1010" => leds <= "1110111"; -- A ABCEFG
WHEN "1011" => leds <= "1111100"; -- B CDEFG
WHEN "1100" => leds <= "1011000"; -- C DEG
WHEN "1101" => leds <= "1011110"; -- D BCDEG
WHEN "1110" => leds <= "1111001"; -- E ADEFG
WHEN "1111" => leds <= "1110001"; -- F AEFG
END CASE;
END display_digit;
Hier werden jeweils ein Eingangs- und ein Ausgangssignal mit einem VHDL-Case-When umgesetzt. Es sind sicher noch andere Schreibweisen vorstellbar.
Die Übernahme des Eingabebytes data bei steigendem enable-Bit wird durch einen eigenen Prozess umgesetzt:
PROCESS (enable)
BEGIN
IF rising_edge(enable) THEN
FOR i IN 0 TO 1 LOOP
-- digits sind ein internes Signal
digits(i) <= data(((4 * i) + 3) DOWNTO (4 * i));
END LOOP;
END IF;
END PROCESS;
Steigt hier das enable-Signal an, so teilt die For-Schleife das anliegende data-Signal in die beiden internen digits auf. Dabei hilft die gleichzeitige Zuordnung mehrerer Signale mit der DOWNTO Schreibweise.
Ein paralleler Prozess wechselt mit 1 kHz zwischen beiden Siebensegmentanzeigen hin und her und zeigt dort jeweils das aktuelle Nibble an:
PROCESS (clk)
VARIABLE digit_cntr : INTEGER := 0;
VARIABLE digit : INTEGER := 0;
BEGIN
-- select proper digit
IF rising_edge(clk) THEN
digit_cntr := digit_cntr + 1;
IF (digit_cntr > 50000) THEN
digit_cntr := 0;
digit := digit + 1;
IF (digit > 1) THEN
digit := 0;
END IF;
END IF;
END IF;
-- display the nibble
IF (digit = 0) THEN
segments <= "01";
display_digit(digits(0), leds);
ELSE
segments <= "10";
display_digit(digits(1), leds);
END IF;
END PROCESS;
Den vollständigen Code inkl. Zählerbeispiel findet man in master.zip (den Code zum Entprellen des Tasters habe ich dem eewiki entnommen, vielen Dank für den verständlichen Artikel). In den FPGA übertragen und korrekt verkabelt ergibt das folgendes:
Viel Erfolg und Spass beim Nachvollziehen!
Update 26.02.2018
Jetzt auch im GIT:https://gitea.lusiardi.de/jlusiardi/vhdl_7seg