Two builds and a check · 14 of 15

Show any number

Two operators pull a number apart into digits, and the loop runs backwards for a reason. What to do when the number has more digits than you have modules, and why a correct number can still show nothing.

Taking a number apart

A chain does not accept a number. It accepts one byte per module, so the number has to be split up first, and the two operators that do it are % and /.

Peeling digits off the right
73 across 2
Modules in the chain
The number to show73
Highest this chain holds
99
Peels done
0 of 2
Display shows
—
t % 10 is the rightmost digit and t / 10 throws it away. Whole-number division on an unsigned long keeps no fraction, so 73 / 10 is 7, not 7.3. Two lines and the number has been taken apart from the right.

t % 10 is the remainder after dividing by ten, which is the rightmost digit. t /= 10 divides and keeps no fraction — on an unsigned long, 73 / 10 is 7, not 7.3 — which throws that digit away. Repeat, and the number comes apart from the right.

The loop counts down through the array because the digit that comes off first is the ones digit, and the ones digit has to be the last byte sent. That is the rule from the previous article showing up in code.

The number that is too big

Set the slider past 99 on a two-module chain. Nothing errors. The loop runs twice, takes two digits, and the rest are simply never looked at — so 1234 shows as 34 and your sketch carries on believing it displayed 1234.

That is why showNumber() in the sample returns a bool. One comparison against chainLimit() turns a silent wrong answer into something you can handle:

if (!showNumber(reading)) {
  // too wide for this chain — show an error, or scale it
}

Blank is a digit too

0x00 lights nothing, which reads as a gap rather than a nought. That gives you leading-zero suppression for free — walk the array from the left blanking zeros until you hit something else — and it gives you somewhere to put a minus sign, which is segment G on its own, 0x40.

Both cost a digit position. A two-module chain that can show a sign covers −9 to 99, and there is no arrangement of seven bars that avoids that.

A right number nobody can see

showNumber() returning true means the registers hold your number. It does not mean the digits are lit: that is the PWM pad's decision, and the board ships with it open. Sketch and board have to agree on which of the two ways you chose, as in the counting sketch: D9 wired to PWM and PWM_PIN 9, or the pad soldered shut and PWM_PIN -1. Set PWM_PIN to 9 on a shorted pad and the pin is driving a wire that is held high.

The code

Show_A_Number.ino

The same wiring as the counting sketch, PWM included, with one function you can call with any value. showNumber() refuses a number too wide for the chain instead of quietly truncating it.

// Show any number on chained 4" seven-segment modules with a 74HC595.
//
// Wiring, at the module's IN edge (reading the back, IN is on the left):
//   12 V supply +   -> 12V            two-pin header
//   12 V supply -   -> GND            two-pin header
//   Uno 5V          -> 3V3/5V         six-pin header
//   Uno GND         -> GND            six-pin header
//   Uno D8          -> DATAIN (SER)
//   Uno D11         -> LATCH (RCLK)
//   Uno D12         -> CLOCK (SRCLK)
//   Uno D9          -> PWM            or leave PWM empty and solder the
//                                     PWM pad shut (then PWM_PIN is -1)
//
// The board ships with the PWM pad OPEN. With nothing on PWM the digit
// has no supply and stays dark, however correct the number is.
//
// The 12 V supply's ground and the Uno's ground must be the same wire.
//
// Arduino IDE: Tools > Board > Arduino AVR Boards > Arduino Uno, then
// Tools > Port and pick the port the board appears on. No libraries.

#define NUM_DIGITS 2

#define SER  8         // DATAIN (SER)
#define LAT 11         // LATCH  (RCLK)
#define CLK 12         // CLOCK  (SRCLK)
#define PWM_PIN 9      // PWM; use -1 if you shorted the PWM pad instead

const uint8_t d[] = {
  0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
};

const uint8_t BLANK = 0x00;   // no segments
const uint8_t MINUS = 0x40;   // segment G on its own
const uint8_t POINT = 0x80;   // the decimal point

void updateDisplay(const uint8_t *buf) {
  digitalWrite(LAT, LOW);
  for (int i = 0; i < NUM_DIGITS; i++)
    shiftOut(SER, CLK, MSBFIRST, buf[i]);
  digitalWrite(LAT, HIGH);
}

// Highest number this chain can hold: 9, 99, 999 …
unsigned long chainLimit() {
  unsigned long limit = 1;
  for (int i = 0; i < NUM_DIGITS; i++) limit *= 10;
  return limit - 1;
}

// true if it was shown, false if the number needs more modules than you
// have. Without this check the top digits vanish and nothing says so.
bool showNumber(unsigned long value, bool leadingZeros = true) {
  if (value > chainLimit()) return false;

  uint8_t b[NUM_DIGITS];
  unsigned long t = value;
  for (int i = NUM_DIGITS - 1; i >= 0; i--) {
    b[i] = d[t % 10];       // rightmost digit of what is left
    t /= 10;                // drop it
  }

  if (!leadingZeros) {      // blank the noughts in front of the number
    for (int i = 0; i < NUM_DIGITS - 1 && b[i] == d[0]; i++) b[i] = BLANK;
  }

  updateDisplay(b);
  return true;
}

void blank() {
  uint8_t b[NUM_DIGITS];
  for (int i = 0; i < NUM_DIGITS; i++) b[i] = BLANK;
  updateDisplay(b);
}

void setup() {
  pinMode(SER, OUTPUT);
  pinMode(LAT, OUTPUT);
  pinMode(CLK, OUTPUT);
  if (PWM_PIN >= 0) {
    pinMode(PWM_PIN, OUTPUT);
    digitalWrite(PWM_PIN, HIGH);   // high = the digit is supplied
  }
  blank();

  if (!showNumber(42, false)) blank();   // 42 on a two-module chain
}

void loop() {
  // nothing: the display holds what it was last given
}

blank() and showNumber() are the two you will keep. Everything else is the same table and the same three-line update.

When it does not work

A big number shows its last digits only

That is the failure this sketch's return value exists to catch. The peeling loop runs once per module and stops, so 1234 on a two-module chain shows 34 and nothing reports a problem. Check the value against chainLimit() before you send it.

Leading zeros look wrong on a clock

Pass false for leadingZeros and the noughts in front of the number become blanks instead. For a clock you usually want the opposite — 09:05 reads better than 9:5 — so leave it true for the minutes and decide for the hours.

How do I show a negative number?

Put MINUS in the leftmost slot and the digits in the rest, which costs you one module's worth of range: a two-module chain shows -9 to 99. There is no other way round it, because a minus sign needs a digit position to live in.

The number is right and the display is dark

showNumber() returned true, so the registers hold 42, and a dark digit is a supply question. The PWM pad ships open: wire PWM to D9 with PWM_PIN 9, or solder the pad shut and set PWM_PIN to -1. If the pad is shut and the digit is still dark, measure 12 V at the 12V pin against the Uno's GND.

The display keeps the old number when the sketch resets

It would. The output register holds its contents until something latches new ones into it, and a board reset does not touch the module. Call blank() at the top of setup() so a restart always clears what was there.

Where this goes next

Seven faults in the order to look, starting with the pad that ships open, and the one check that settles each of them before you rewire anything.

When nothing lights →

Edit this page — content/books/seg4/show-any-number.mdx

Community

Questions about this product

See what other owners have asked, and read their solutions.

Ask a question ↗

4″ 7-Segment LED Display with 74HC595

Loading discussions…

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse Modules and blocks on the forum →