Translate

Wednesday, December 19, 2012

Multiplexing 7 segment displays





       Hello and welcome back. It's time to continue with 7 segment displays. If you haven't read my previous post about such displays please read it before reading this one: 7 segment displys. As you can see in that post, we have covered dealing with displays that have separate data inputs, but the odds are that your FPGA development board has all the 7 segment displays connected to the same 7 data inputs. In this case you have to use a technique named multiplexing in order to display separate things on several 7 segment blocks at the same time. If you don't know what i'm talking about don't worry, it will be explained.

       Before we begin I will mention that my dev board (Digilent Nexys 3) is a little strange: the 7 segment display is common anode. This means that for the data inputs you need a '1' to shut down a segment and a '0' to light it up. Now that is normal but the strange thing is that the common anodes use the same logic, so a '0' to activate and '1' to deactivate. You should test on your board to see how your 7 segment display is set up and then continue reading. In this post I will explain everything as it qould be normal and when we get to the code I'll make it for my board so if you read the theory you should be able to understand how to do it for your board.
       Ok. So what's with this multiplexing? Let's say you have a dev board with 2 displays connected in common anode like in the picture below.

       If you wanted to show 2 different numbers at the same time you couldn't use the method from the previous post because the 2 displays have the same data coming in. This leaves you only one choice: use multiplexing. Multiplexing means that the 2 displays are on one at a time. This can be done by activating a common anode and setting the data with a number and then activating the other one (deactivating this one of course) and changing the number. If this is done very fast you will have the ilussion that both displays are active at the same time. It's as simple as that. 
       This trick works because the human eye can't perceive motion or things lighting up and shutting down if that happens at a rate of 50 HZ or faster (TV uses a similar technique). In the 2 display case for example a frequency of minimum 100 HZ is needed in order for you to think that they are active at the same time (50 HZ for e display * 2). If the frequency is a little lower, flickering will appear. If it's higher there's no problem; it's actually better because the displays will be a bit brighter.
       Let's take an example: we start with anode1 at '0' level and anode2 '1'. They will be changed with a frequency of 200 HZ(so 100 HZ * 2, making them brighter). The data ports are set at first with the value 1111001 (1 in decimal). After  200 HZ the anodes will interchange values and the data ports will be set with 0110000 (3 in decimal). While the displays light up at the same time, you will only see the number 13 at all time.

       Let's see how we can make this logic in VHDL. We already have the bcd to 7 segment decoder from the previous post but now we need a way to change the data inputed to that decoder and of course change the anode activation status (or cathode fo who has the common cathode setup). Another circuit we will need is a clock divider. My board has 100 MHz and I will divide the clock to 1 KHz. I could use a lower frequency but it's ok with this one to. 
       The code for the divider:

              library IEEE;
              use IEEE.STD_LOGIC_1164.ALL;
              use IEEE.std_logic_unsigned.ALL;

              entity div is
              Port ( clk : in STD_LOGIC;
                 reset:in std_logic;
                 clko : out STD_LOGIC);
              end div;

              architecture Behavioral of div is

              begin

                 process(clk)
                    variable cnt:std_logic_vector(16 downto 0);
                 begin
                    if(clk'event and clk='1') then
                       if(reset='1') then
                          cnt:=(others=>'0');
                          clko<='0';
                       else
                          if(cnt=99999) then
                             cnt:=(others=>'0');
                             clko<='1';
                          else
                             cnt:=cnt+1;
                             clko<='0';
                          end if;
                       end if;
                    end if;
                 end process;

              end Behavioral;

       The code for the 7 segment decoder can be found on my previous post about 7 segment displays.
       Now the circuit that controls the common anodes. This will have an input for the divided clkand another one for the reset signal. The output will be a 4 bit std_logic_vector (the number of bits has to be equal to the number of displays). At every clk event when it becomes '1', another anode will be activated. For example: first we start will all anodes off ("1111"). After a clk event the value will be "1110". Next it will change to "1101" and so on. When it becomes "0111", next time the value will be "1110" again. The circuit will be done with a process that has a variable (cnt). This is a counter used for selecting a specific anode each time we need to activate one. The code for this circuit is below:

              library IEEE;
              use IEEE.STD_LOGIC_1164.ALL;

              entity reg is
              Port ( clk : in STD_LOGIC;
                 reset : in std_logic;
                 a : out STD_LOGIC_VECTOR (3 downto 0));
              end reg;

              architecture Behavioral of reg is
                 signal anod:std_logic_vector(3 downto 0);
              begin
                 a<=anod;

                 process(clk )
                    variable cnt:integer;
                 begin
                    if(clk'event and clk='1') then
                       if(reset='1') then
                          anod<="1111";
                          cnt:=0;
                       else
                          if(cnt=4) then 
                             cnt:=0;
                          end if;
                             anod<="1111";
                             anod(cnt)<='0';
                             cnt:=cnt+1;
                       end if;
                    end if;
                 end process;

              end Behavioral;

       An interesting thing: the cnt counts from 0 to 3 and at 4 it resets and it is used as an index in the anode (like using vectors in a programming language).
       Now that we have sorted out the anodes we still need a way to change the data when the anode changes. This operation can be done easily with a multiplexer of course.For the selection we already have the anode (1110,1101,1011,0111) so we will use a mux with a 4 bit selection (so a 16 to 1 multiplexer). We only need 4 inputs but if we were to use a 4 to 1 mux, the selection would have needed a decoder to transform from 4 bits to 2. The code for a 4 to 1 multiplexer can be found at the following post: Concurrent statements. You just have to add more inputs and add 2 more bits to the selection.
       The final system will look like this:
       
     
       The only inputs used on the mux beside the selection are the 4 that have a value next to them.
       When you will use this circuit for an application you have to set the 4 mux port as inputs so that you can give them values from another circuit.
       The structural design code is below:

       library IEEE;
       use IEEE.STD_LOGIC_1164.ALL;

       entity sist is
       Port ( clk : in STD_LOGIC;
          reset:in std_logic;
          an : out STD_LOGIC_VECTOR (3 downto 0);
          ca : out STD_LOGIC_VECTOR (6 downto 0));
       end sist;

       architecture Behavioral of sist is

          --components
          component div is
          Port ( clk : in STD_LOGIC;
             reset:in std_logic;
             clko : out STD_LOGIC);
          end component;

          component dec is
          Port ( nr : in STD_LOGIC_VECTOR (3 downto 0);
             decod : out STD_LOGIC_VECTOR (6 downto 0));
          end component;

          component mux16to1 is
          Port ( i0 : in STD_LOGIC_VECTOR (3 downto 0);
             i1 : in STD_LOGIC_VECTOR (3 downto 0);
             i2 : in STD_LOGIC_VECTOR (3 downto 0);
             i3 : in STD_LOGIC_VECTOR (3 downto 0);
             i4 : in STD_LOGIC_VECTOR (3 downto 0);
             i5 : in STD_LOGIC_VECTOR (3 downto 0);
             i6 : in STD_LOGIC_VECTOR (3 downto 0);
             i7 : in STD_LOGIC_VECTOR (3 downto 0);
             i8 : in STD_LOGIC_VECTOR (3 downto 0);
             i9 : in STD_LOGIC_VECTOR (3 downto 0);
             i10 : in STD_LOGIC_VECTOR (3 downto 0);
             i11 : in STD_LOGIC_VECTOR (3 downto 0);
             i12 : in STD_LOGIC_VECTOR (3 downto 0);
             i13 : in STD_LOGIC_VECTOR (3 downto 0);
             i14 : in STD_LOGIC_VECTOR (3 downto 0);
             i15 : in STD_LOGIC_VECTOR (3 downto 0);
             s : in STD_LOGIC_VECTOR (3 downto 0);
             o : out STD_LOGIC_VECTOR (3 downto 0));
          end component;

          component reg is
          Port ( clk : in STD_LOGIC;
             reset : in std_logic;
             a : out STD_LOGIC_VECTOR (3 downto 0));
          end component;

          --signals
          signal clkdiv:std_logic;
          signal bcd:std_logic_vector(3 downto 0);
          signal sel:std_logic_vector(3 downto 0);
          signal d0 : STD_LOGIC_VECTOR (3 downto 0);
          signal d1 : STD_LOGIC_VECTOR (3 downto 0);
          signal d2 : STD_LOGIC_VECTOR (3 downto 0);
          signal d3 : STD_LOGIC_VECTOR (3 downto 0);
       begin

          divfreq:div port map(clk,reset,clkdiv);
          decoder:dec port map(bcd,ca);
          mux:mux16to1 port map("0000","0000","0000","0000","0000","0000","0000",d3,"0000","0000","0000",d2,"0000",d1,d0,"0000",sel,bcd);
rega:reg port map(clkdiv,reset,sel);
          d0<="0100";
          d1<="0011";
          d2<="0010";
          d3<="0001";
          an<=sel;

      end Behavioral;

       So this is the code. When using the circuit in a project don't forget to include the d0...d3 signals as inputs for the entity because they will need to connect with other circuits that generate the numbers or symbols to display.

       For questions or suggestions please email me at: fpgatutorials@gmail.com.
       Thank you for reading.

2 comments:

  1. In this project do I need to wirte the entity and architecture for the multiplexer and the decoder?
    I am a beginner.

    ReplyDelete