Lets revisit the seven-segment display and this time use all four digits. To do that I will use a multiplexer circuit (MUX). The MUX will cycle through the display of the digits one at a time. The term multiplexer is just a fancy way of saying data selector. A multiplexer circuit simply selects a single data source from among several inputs.
The term multiplexer is just a fancy way of saying data selector.
You may remember from sequential circuit design that if we can cycle through the digits fast enough, it will appear as if all four digits are on at the same time due to persistence of vision (POV) effects.
Why Use a Multiplexer Circuit?
There are at least two reasons. First, it uses roughly four times as much current to drive all four digits at the same time. If you are running on battery power you’ve reduced your battery life significantly. Even if you are running from a residential power supply it is wasteful since it is not necessary. Second, and perhaps more importantly, you may not be able to safely source enough current through the device to drive all four digits at the same time.
In this case the inputs will be our seven-segment display signals. I’ll call the MUX circuit sseg_mux. In addition to a clk and rst signal, sseg_mux will take as input the signals for all four of the digits I want to display. And it will provide as output both the selected segment signal (seg) and the anode signal (an) so that we enable only one of the four digits at a time. I am going to use four of the switches to input a 4-bit binary number that will be displayed in hex on all four of the seven-segment displays.
As usual I will start with the top-level module and write the code using the interface the way that I want to use it. Later I will implement the sseg_mux module based on that interface. I assume at this point that readers know how to setup the Xilinx Design Constraints file. The top-level module, sseg_top, will take the system clk signal and the switches (sw) as inputs. The outputs are the seven-segment seg and an signals. The complete code for the top-level module is shown below.
module sseg_top(
input clk,
input [3:0] sw,
output [6:0] seg,
output [3:0] an
);
wire [6:0] seg0, seg1, seg2, seg3;
sseg_display s0(.hex(sw), .seg(seg0));
sseg_display s1(.hex(sw), .seg(seg1));
sseg_display s2(.hex(sw), .seg(seg2));
sseg_display s3(.hex(sw), .seg(seg3));
sseg_mux display(.clk(clk), .rst(1'b0), .dig0(seg0), .dig1(seg1), .dig2(seg2), .dig3(seg3), .an(an), .sseg(seg));
endmodule
On line 8 you can see that I’ve declared a four 7-bit ports. One for each digit of the seven-segment display. These are interconnects between the individual display drivers, sseg_display, which convert hex inputs to LED segment outputs. You may recall that I previously created sseg_display in the seven-segment display introduction.
On lines 10 through 13 I instantiate a display driver for each digit. Each driver takes in the same 4-bit switch input and generates a separate 7-bit segment output. The interesting part is on line 15 where I instantiate the not yet implemented sseg_mux circuit.
Implementing the Display MUX
Now it is time to create the sseg_mux circuit. The inputs and outputs were described earlier so we can declare our module signals. Next I need to figure out the frequency for updates. Anywhere above 100Hz will be sufficient. To cycle through the digits I will keep track of the number of clk cycles. When the count reaches a target value we will switch to displaying a new digit.
I will use the configurable counter circuit from earlier to keep track of time. Recall that the Basys 3 system clock runs at 100Mhz. If we use a 16-bit counter we can generate a tick at about 1500 Hz (100 Mhz / 2^16). More than fast enough. On line 9 I define a constant for the number of bits in the counter. Notice that the value is 18 instead of 16. Why is that?
Rather than use the tick event from the counter I am going to take advantage of how binary numbers work. The logic in our circuit looks at the top 2 bits (17 and 18) of the counter to decide which of the 4 digits should currently be displayed. Each time the counter counts up to 2 ^16 the bits above are incremented by 1. This cycles the bits 17 and 18 through the values 0..3 at a frequency of 2 ^16 clk ticks. Do you see how that works?
On line 13 I declare a set of wires to bring out the current counter value. And in the always block starting at line 15 I look at the value of bits 17 and 18 to determine which digit should be displayed and selecting the appropriate an and seg values as outputs.
module sseg_mux(
input clk, rst,
input [6:0] dig0, dig1, dig2, dig3,
output reg [3:0] an,
output reg [6:0] sseg
);
// refresh rate of ~1526Hz (100 MHz / 2^16)
localparam BITS = 18;
counter_n #(.BITS(BITS)) counter(.clk(clk), .rst(rst), .q(q));
wire [BITS - 1 : 0] q;
always @*
case (q[BITS - 1 : BITS - 2])
2'b00:
begin
an = 4'b1110;
sseg = dig0;
end
2'b01:
begin
an = 4'b1101;
sseg = dig1;
end
2'b10:
begin
an = 4'b1011;
sseg = dig2;
end
default:
begin
an = 4'b0111;
sseg = dig3;
end
endcase
endmodule
And that is all there is to it! To test the circuit I synthesized the circuit and programmed the Basys 3. In the image below you can see that the first four switches are on, providing a 4-bit input to our circuit of 15 (2 ^4 – 1). And the four seven-segment displays show the expected hex digit value of F. Changing the switches immediately updates the display.
That’s it for now! The source code for this project is available on github. If you have feedback, a question or a suggestion, please leave a comment!