Fully-Working Seven Segments

All Eight Digits

The questions below are due on Wednesday September 18, 2024; 11:59:00 PM.
 
You are not logged in.

Please Log In for full access to the web site.
Note that this link will take you to an external site (https://shimmer.mit.edu) to authenticate, and then you will be redirected back to this page.

Recall the seven segment display from last week:

seven segment pattern

Seven Segment Pattern

We previously used the eight digits (i.e. one of the eight number displays on the board) on the seven-segment display grouped into two identical sets of four. This is a product of how the LEDs (each little line on each digit that lights up red) are connected. We are only able to control:

  • Which digits are enabled at a given time, via the anode pins (ss0_an[3:0] and ss1_an[3:0]). Driving an anode high disables a given digit, driving it low enables it. (note this is backwards from normal since there is a PNP driving transistor controlling the anode.)
  • Which LEDs are enabled on all of the digits at a given time, via the cathode pins which control the "0" through "6" segments of the four digits in each LED bank (pins ss0_c[6:0] and ss1_c[6:0] are used for banks 0 and 1, respectively). Driving a cathode high would disable a given segment, driving it low enables.

In week one, we labeled the segment cathodes "A" through "G". You can also just label them with numbers, which we'll do here. Same pattern: A=0, 1=B, etc...

seven segment schematic

Seven Segment Digit Schematic. Each digit is a common anode comprising eight independently controllable cathode pins (including the decimal point one that we're ignoring).

Across each bank of four digits, the "0/A" segments of each are tied together for example, same with "1/B", etc... Therefore we are never able to apply a signal to a specific "1/B" segment on any digit. However, since an LED only glows if the anode and cathode are the correct values, by setting this global "1/B" signal to a value we want and then selecting which digits to activate by using certain an pins, we can at least control which "1/B" pins on particular digits actually light up. The same logic applies for the remaining cathode pins/segments as well like "0/A", "2/C", etc...

seven segment schematic

Control Wiring for all Eight Digits. Every digit shares the same seven (or eight if you include the DP) cathode connections, but each digit has a separate controllable anode connection. Notice how we can only change what number is displayed at a given time on all of the digits, since the LEDs all share the per-segment cathode pins. On a given bank of four digits, there is no way to display the number "1" on one digit and the number "2" on another, for example; you can have a "1" on the first digit and the others off, but not two different numbers.

Still, while we can turn off particular digits by varying which values of an are on and off, there is no way using only combinational logic that we could ever show two separate hex symbols on two separate digits. If I needed to show the numbers 0000_1986, many of these digits are using different combinations of segments and there'd be no way to do this with combinational only logic.

The question that should be jumping out to you is how do we make the different segments say different things at the same time if they're all tied together in this way? The answer is we don't. Or we sorta do, but it is complicated, and relies on the same inadequacies in human vision that we used for PWM control of the RGB LEDs last week. What we can do with our LEDs is light up each of the eight digits with the appropriate pattern for its digit in sequence, while keeping the others off. If we do this fast enough (keeping each digit lit for something like ~1 ms) and repeatedly for each digit, we can make it look like all eight digits are illuminated to our inferior human eyes.

This should seem possible to you. We have access to the anodes (which "activate" a given digit) and we have a system which can make decisions 100 million times per second, about 5 orders of magnitude faster than the 1 ms requirement.

We put together a nice-ish cartoon to show this concept a little bit more clearly:

seven segment schematic

Tricking the eyes into seeing all digits at once.

This may seem like a lot of work just to make a display. Why not just independently control each cathode/segment pin, for example, instead of having all of them share a common control pin? Well the device isn't built that way. And there's a good reason for it. As it turns out, controlling the display by "strobing" each segment drastically cuts down on the number of pins. If we did it the brute-force way, you'd have eight pins per seven-segment display (one common anode, and seven cathodes), times eight segments, that'd be 64 pins needed. No thank you. Doing it this way requires only 15 pins. Pins equate to circuit board and chip real estate and this usually equates to money. As Method Man tells us, Cash Rules Everything Around Me(Us), so strobing LEDs is beneficial.

Let's now build a module called seven_segment_controller which will be responsible for controlling all eight digits of the seven-segment displays. To make this module as functional as possible (for later usage) it will be responsible for displaying hex on all 8 digits, meaning it can represent 32 bits of information (4 bits per hex symbol) even though we're only using it for 16 in counting button pushes at first (in part 3 of lab we will use all 32 bits!). Consequently, this module should have three inputs:

  • val_in[31:0]: A 32 bit input which will get rendered on the display (in hexadecimal)
  • clk_in: An external system clock for driving the synchronous logic in the module
  • rst_in: A reset control in case you'd like to make your module resettable

Additionally, the module will have two sets of outputs:

  • cat_out[6:0]: Pins intended to drive the cathode pins in our system
  • an_out[7:0]: Pins for selecting/deselecting the individual segments via their anodes (note, again, that driving high an anode disables a given digit)

The Seven-Segment Controller

For the initial deployment, we're counting button pushes. btn_count is only 16 bits, but the input to the seven segment module is 32 bits. As a result, the upper 16 bits of val_in will be default 0 and you'll only be able to control the lower four hex digits for this first demo; that's ok. If you really want to push the button more than 65,536 times that's...I mean that's great, but I'm sure there's better things you could do with your time.

We're going to provide an almost-complete version of seven_segment_controller below. Study this code and get it working. It does not need much more to work like we just described...perhaps ten lines max. The missing piece is effectively to route the appropriate seven-segment pattern to led_out given the current digit being activated! (See the sequential image above and try to match what it has for values with the code below!).

This module is a very basic form of state machine, which we'll be covering in upcoming lectures/labs! Note that it uses an instance of bto7s from week 1 so you should bring a copy of that into your project as well (you can either put it in a separate file or include it below in the same file. The choice is yours.)! Other than wiring everything together, this module should just work! When ready, make sure to uncomment the instance of seven_segment_controller in your top_level file.

`default_nettype none
module seven_segment_controller #(parameter COUNT_PERIOD = 100000)
  (input wire           clk_in,
   input wire           rst_in,
   input wire [31:0]    val_in,
   output logic[6:0]    cat_out,
   output logic[7:0]    an_out
  );

  logic [7:0]   segment_state;
  logic [31:0]  segment_counter;
  logic [3:0]   sel_values;
  logic [6:0]   led_out;

  //TODO: wire up sel_values (-> x_in) with your input, val_in
  //Note that x_in is a 4 bit input, and val_in is 32 bits wide
  //Adjust accordingly, based on what you know re. which digits
  //are displayed when...

  bto7s mbto7s (.x_in(sel_values), .s_out(led_out));
  assign cat_out = ~led_out; //<--note this inversion is needed
  assign an_out = ~segment_state; //note this inversion is needed

  always_ff @(posedge clk_in)begin
    if (rst_in)begin
      segment_state <= 8'b0000_0001;
      segment_counter <= 32'b0;
    end else begin
      if (segment_counter == COUNT_PERIOD) begin
        segment_counter <= 32'd0;
        segment_state <= {segment_state[6:0],segment_state[7]};
      end else begin
        segment_counter <= segment_counter +1;
      end
    end
  end
endmodule // seven_segment_controller

/* TODO: drop your bto7s module from lab 1 here! */
module bto7s(input wire [3:0]   x_in,output logic [6:0] s_out);

endmodule // bto7s

`default_nettype wire

Notice the reset signal in seven_segment_controller, rst_in. It is almost always a good idea to give your sequential circuits a reset signal both for regular operation but also for debugging since it can give you the ability to get your circuit back to a known starting state.

For your own local debugging, we've provided a testbench here: test_ssc.py. You should use this to help develop this module. Before posting on piazza about why your code isn't passing the checker, you should make sure to run your simulations locally and study the waveforms.

When the above code is working, update seven_segment_controller.sv. Make sure to uncomment the instantiation of the seven_segment_module in the top_level file and delete any unneeded lines. Build, and upload. Hopefully you're getting a video like below! If so, get the checkoff!

Checkoff 2:
When you have assembled all these pieces, show to a staff member for the second lab checkoff. Note, because it a subset of this checkoff, you can get Checkoff 1 by demonstrating Checkoff 2, but make sure to add yourself to the queue for each.