The byte behind a digit
Ten bytes are the whole font. Which bit reaches which bar, what the ULN2803A does with a 1, and what one wrong word in shiftOut does to every digit.
Ten bytes and you are done
The sketch's font is one line:
const uint8_t d[] = {
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
};Index 0 is the byte for a zero, index 9 for a nine. Bit 0 is segment A, bit 1 is B, and so on up to bit 6 for G. Bit 7 is the decimal point, so d[3] | 0x80 is a 3 with a point after it.
0x3F and 0b00111111 are the same number.Slide through the digits. A 1 in the byte lights that bar, and the path it takes is short: the shift register output for that bit goes to one input of U2, a ULN2803A.
What U2 does with a 1
The ULN2803A is eight transistor stages, one per segment, and each one is inverting: when its input is high it sinks current from its output pin to GND. Its output goes through a resistor (75 ohm for A to G, 510 ohm for the point) to that segment's pin on the display, and the display's other end is the common anode, on the 12 V side.
So a 1 does not push current into a segment. It completes a path: 12 V through the dimmer to the anode, through the segment, through its resistor, through U2 to GND. A 0 leaves that path open.
How much current flows in a lit segment is not in any file we have, and we did not measure it. The shape of the sum is the 12 V, minus about 1 V across U2 (the datasheet gives 1.0 V typical at 200 mA), minus the segment's own forward voltage, divided by 75 ohm. U2 is rated for 500 mA per channel, and a 3.3 V logic high is enough to switch it: its input needs at most 2.4 V at 200 mA.
The one word that ruins it
shiftOut() takes a bit order, and both spellings are valid:
shiftOut(SER, CLK, MSBFIRST, d[n]); // bit 7 goes out first
shiftOut(SER, CLK, LSBFIRST, d[n]); // bit 0 goes out firstThis board wants MSBFIRST: the bit sent first travels furthest, to QH, which is the decimal point, and the bit sent last stays in QA, which is segment A. Send the byte the other way and every bit lands on its mirror segment.
Switch the figure to LSBFIRST. The result is not a blank display or a broken one but a different, valid pattern, sometimes another digit. That is why the mistake survives: nothing looks wrong enough to suspect one word in one call.
When it does not work
Swap MSBFIRST for LSBFIRST, or the other way round. Both spellings compile, and the wrong one mirrors every byte: 0x3F becomes 0xFC, a real pattern that lights real segments and looks like a broken display rather than a bit order.
It is the six segments that draw a zero, A to F, which are bits 0 to 5. In binary that is 0b00111111, the same number. Hexadecimal is shorter to type and nothing more.
Set bit 7 on top of the digit's byte: d[3] | 0x80 is a 3 with the point. 0x80 on its own is the point with no digit, and 0x00 is a blank.
A minus is segment G alone, 0x40. Work a letter out on the drawing: E is A, D, E, F and G, which are bits 0, 3, 4, 5 and 6, which is 0x79. Put 0x40 in the leftmost module of a chain and digits in the rest to write a negative number.
Because of U2. Its stages invert: an input that goes high makes the output sink current, and the display's other end is the common anode on the 12 V side. A table written for a different display can be the other way round. Use the table in this book as it stands.
A new module with nothing on its PWM pin stays dark. Why, and the three ways to use the pin.
The PWM pad →Edit this page — content/books/seg4/the-byte-behind-a-digit.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.