Table of Contents
This document is licensed under a Creative Commons license.
In this article we describe how to write HDLC for displaying numbers on a 7-bit LED display. This example was done on a Spartan III Xilinx development board but a variation should work with any 7-bit display. To compile the VHDL I used the Xilinx WebPack tool.
A standard 7 segment led display has 7 LEDs arranged in the shape of an eight. Each led is driven by an individual input. By turning on various segments the display can show all 10 numerical digits as well as the letters A-F.
The Xilinx development board display has 4 identical digits, each of which shares the same 7 input wires. In this case driving the signal low lights the led while driving it high turns the led off.
In order to select a particular digit the display also has a 4-bit selector input. By rapidly changing the selector input and the 7-bit segment inputs you can display a 4 digit number.
Displaying a single digit is fairly straight forward. To do this we will
develop a entity called led_mux
. This entity
will take in a 4-bit number and output 7 bits corresponding to the
7 LEDs on the 7 segment LED display.
Table 1. 7 bit led codes
Number | Code |
---|---|
0x0 | 10000000 |
0x1 | 1111001 |
0x2 | 0100100 |
0x3 | 0110000 |
0x4 | 0011001 |
0x5 | 0010010 |
0x06 | 000010 |
0x7 | 1111000 |
0x8 | 0000000 |
0x8 | 0010000 |
0X | 0001000 |
0CB | 0000011 |
0xx | 1000110 |
0CD | 0100001 |
0Xe | 0000110 |
0CF | 0001110 |
Obviously all we need here is a multiplexer.
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity led_mux is Port ( DIGIT_IN : in STD_LOGIC_VECTOR (3 downto 0); LED_OUT : out STD_LOGIC_VECTOR (6 downto 0)); end led_mux; architecture Behavioral of led_mux is begin with DIGIT_IN SELect LED_OUT<= "1111001" when "0001", --1 "0100100" when "0010", --2 "0110000" when "0011", --3 "0011001" when "0100", --4 "0010010" when "0101", --5 "0000010" when "0110", --6 "1111000" when "0111", --7 "0000000" when "1000", --8 "0010000" when "1001", --9 "0001000" when "1010", --A "0000011" when "1011", --b "1000110" when "1100", --C "0100001" when "1101", --d "0000110" when "1110", --E "0001110" when "1111", --F "1000000" when "0000", "1100011" when others; --0 end Behavioral;
Displaying 2 full bytes is slightly more complicated but not by much. Basically what we need to do is rapidly switch between each of the four digits in the display.
The led_persist entity take in 4 sets of 7-bit led inputs as well as a clock and outputs a 4-bit digit selector and a 7 bit led segment selector. This is slightly more complicated than the led multiplexer, but not by much. In this case we need a counter and a multiplexer.
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity led_persist is Port ( CLOCK_IN : in STD_LOGIC; LED3_IN : in STD_LOGIC_VECTOR (6 downto 0); LED2_IN : in STD_LOGIC_VECTOR (6 downto 0); LED1_IN : in STD_LOGIC_VECTOR (6 downto 0); LED0_IN : in STD_LOGIC_VECTOR (6 downto 0); LED_OUT : out STD_LOGIC_VECTOR (6 downto 0); LED_SELECT_OUT : out STD_LOGIC_VECTOR (3 downto 0)); end led_persist; architecture Behavioral of led_persist is signal count:std_logic_vector(1 downto 0) := "00"; begin process (CLOCK_IN) begin if(CLOCK_IN = '1') and CLOCK_IN'event then count <= count + 1; end if; end process; with count select LED_OUT <= LED0_IN when "00", LED1_IN when "01", LED2_IN when "10", LED3_IN when others; with count select LED_SELECT_OUT <= "0111" when "00", "1011" when "01", "1101" when "10", "1110" when others; end Behavioral;
All you have to do now, is create 4 led_mux entities and one led_persist entity, hook it up to a clock and the 7 segment led display and you're golden. Unfortunately there is one hitch. The Spartan III development kit comes with a 50MHz clock. If you try an run the led_persist entity at that rate the LED display will not be able to refresh fast enough to keep up.
Unfortunately the documentation doesn't tell you how fast to go, so it's a bit of trail an error. As it turns out dividing down by 2^15 about does it.
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity led_display is Port ( CLOCK_IN : in STD_LOGIC; LED_OUT : out STD_LOGIC_VECTOR (6 downto 0); LED_SELECT_OUT : out STD_LOGIC_VECTOR (3 downto 0)); end led_display; architecture Behavioral of led_display is component led_mux is Port ( DIGIT_IN : in STD_LOGIC_VECTOR (3 downto 0); LED_OUT : out STD_LOGIC_VECTOR (6 downto 0)); end component; component led_persist is Port ( CLOCK_IN : in STD_LOGIC; LED3_IN : in STD_LOGIC_VECTOR (6 downto 0); LED2_IN : in STD_LOGIC_VECTOR (6 downto 0); LED1_IN : in STD_LOGIC_VECTOR (6 downto 0); LED0_IN : in STD_LOGIC_VECTOR (6 downto 0); LED_OUT : out STD_LOGIC_VECTOR (6 downto 0); LED_SELECT_OUT : out STD_LOGIC_VECTOR (3 downto 0)); end component; signal led0, led1, led2, led3: std_logic_vector (6 downto 0); signal led_clock: std_logic; signal led_clock_count: std_logic_vector(15 downto 0) := "0000000000000000"; begin process (CLOCK_IN) begin if(CLOCK_IN = '1') and CLOCK_IN'event then led_clock_count <= led_clock_count + 1; end if; end process; mux1:led_mux port map("0000", led0); mux2:led_mux port map("0001", led1); mux3:led_mux port map("0010", led2); mux4:led_mux port map("0100", led3); persist:led_persist port map(led_clock, led3, led2, led1, led0, LED_OUT, LED_SELECT_OUT); led_clock <= led_clock_count(15); end Behavioral;