Tuesday 17 January 2017

A better display than LEDs

Now is a good time to cover a little more VHDL, and use it to efficiently implement a design that controls the seven segment display.

The VHDL case statement

Much like "switch()" in C, VHDL has the CASE statement that allows you to choose between multiple different paths through your code based on the value of a signal. Although it is largely functionally equivalent to nested ’IF’s it is far easier to write, and is implemented more efficiently within the FPGA.

It looks much like this:

CASE input(2 downto 0) IS
WHEN "000" =>
output1 <= ’1’;
output2 <= ’1’;
WHEN "001" =>
output1 <= ’0’;
output2 <= ’1’;
WHEN "110" =>
output1 <= ’1’;
output2 <= ’0’;
WHEN OTHERS =>
output1 <= ’0’;
output2 <= ’0’;
END CASE;

It differs from most similar constructs in programming languages in that all possible cases must be covered, so it pays to remember that a STD_LOGIC signal can have other states than just ’1’ or ’0’ - most designers choose to use the ’least harmful’ actions on an unexpected value. Like an "IF" statement, "CASE" can only be used inside a process - and remember to include the signals being tested in the process’s sensitivity list when a "CASE" statement is used outside of an "IF RISING_EDGE(clk) THEN" block.

Note that a CASE block must be inside a PROCESS block.

Excellent practice for using the CASE statement is driving the seven segment display - you can use it twice. One CASE statement decodes which segments to light, and a second CASE statement selects which digit is active at any time.

Project - Displaying digits

These projects are a lot of work and might take a couple of sittings, but you will build up a great understanding of the seven segment displays. If you are feeling confident, combine a few of the steps and race through.

• Add "anodes(3 downto 0)" and "sevenseg(6 downto 0)" as outputs to your top level design, and then add the following constraints
to your ucf file:

# Constraints for Papilio One
NET "anodes<0>" LOC="P18";
NET "anodes<1>" LOC="P26";
NET "anodes<2>" LOC="P60";
NET "anodes<3>" LOC="P67";
NET "segments<6>" LOC="P62";
NET "segments<5>" LOC="P35";
NET "segments<4>" LOC="P33";
NET "segments<3>" LOC="P53";
NET "segments<2>" LOC="P40";
NET "segments<1>" LOC="P65";
NET "segments<0>" LOC="P57";
NET "dp" LOC="P23";
# Constraints for the Basys2
NET "sevenseg<0>" LOC = "L14";
NET "sevenseg<1>" LOC = "H12";
NET "sevenseg<2>" LOC = "N14";
NET "sevenseg<3>" LOC = "N11";
NET "sevenseg<4>" LOC = "P12";
NET "sevenseg<5>" LOC = "L13";
NET "sevenseg<6>" LOC = "M12";
NET "dp" LOC = "N13";
NET "anodes<3>" LOC = "K14";
NET "anodes<2>" LOC = "M13";
NET "anodes<1>" LOC = "J12";
NET "anodes<0>" LOC = "F12";

• In your top level design, connect the outputs sevenseg and dp directly to the inputs from the switches. Within your design set anodes to "1110" then build the design. As the anodes are "active low" this value should enable only the rightmost digit of the sevenseg displays.
• Work out and document the switch patterns required to give the digits 0 through 9, and the letters A through F.
• Build a CASE statement to decode the binary of switches(3 downto 0) and display it on the first seven segment display - remember that at least switches(3 downto 0) has to be included in the sensitivity list of the process acting on them, as there is no clock being used.

Multiplexing digits

If each digit is displayed in quick succession the eye can be fooled into seeing all four displays as being lit at the same time. As we have four digits we can use two bits of a suitably sized counter to select which is to be lit. If the design switches digits too fast it will not give them enough time to light up, and too slow will cause flickering. Something around 200Hz to 1kHz seems to work best.

Counter bits               Value for anodes                           Values for sevenseg()
00                                   1110                                                 Digit 0
01                                   1101                                                 Digit 1
10                                   1011                                                 Digit 2
11                                   0111                                                  Digit 3

You can either decide to decode the four digits in each option of the CASE statement (using nested CASE statements), or maybe create a signal "thisdigit : STD_LOGIC_VECTOR(3 downto 0)" with the digit to be decoded within the case, and then just decode that signal.

Project - Using the Seven segments

• Update your project to multiplex all four displays and show the values of switches(3 downto 0) on all digits
• Update your project to multiplex all four displays and show the value of switches(3 downto 0) on digits 0 and 1, and the value of switches(7 downto 4) on digits 2 and 3
• Update your project to show the highest 16 bits of a counter over all four digits.
• Create a new module that can display four digits on the seven segment display. This will be useful for any project you design that uses the sevenseg displays. Its interface signals should look something like:
clk : in std_logic
digit0 : in std_logic_vector(3 downto 0)
digit1 : in std_logic_vector(3 downto 0)
digit2 : in std_logic_vector(3 downto 0)
digit3 : in std_logic_vector(3 downto 0)
anodes : out std_logic_vector(3 downto 0)
sevenseg : out std_logic_vector(6 downto 0)
dp : out std_logic

Challenges

• Can you make the display count only in decimal rather than hexadecimal?
• Can you make the display count in minutes and seconds?

No comments:

Post a Comment