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, because the sketch will not tell you.
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 /.
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 above 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.
The code
The same wiring as the counting sketch, 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 (printing towards you, IN is top left):
// 12 V supply + -> 12V two-pin header
// 12 V supply - -> GND two-pin header
// Uno 5V -> 3V3/5V five-pin header
// Uno GND -> GND five-pin header
// Uno D8 -> DATAIN (SER)
// Uno D11 -> LATCH (RCLK)
// Uno D12 -> CLOCK (SRCLK)
//
// 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)
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);
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
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.
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.
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.
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.
Five symptoms, and the one measurement that settles each of them before you rewire anything.
When nothing lights →Edit this page — content/books/4-inch-7-segment/show-any-number.mdx
Questions about this product
See what other owners have asked, and read their solutions.
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.