Dim it with PWM
An Uno shifts out a digit and fades it in and out with analogWrite on D9, with the PWM pad left open as it ships. What the duty cycle and the frequency each do, and the same idea on an ESP32.
This is the newer board, the one with the PWM pad. The earlier version has no PWM pin, so this sketch has nothing to drive on it.
Duty cycle and frequency
A PWM pin turns the dimmer switch on and off over and over. Two numbers describe it, and they do different jobs.
The duty cycle is the fraction of each period the pin is high. The switch is on for that fraction, the digit is lit for that fraction, and the average light is proportional to it. This is the brightness. 0 % is dark, 100 % is a pin held high.
The frequency is how many periods a second there are, and so how long each lit and dark stretch is. It does not change the average light. It changes whether you can see the switching.
The bar shows average light, which is proportional to duty because the switch is fully on or fully off. It is not what the eye reports, and that is not linear, so a 50 % duty cycle usually does not look half as bright as 100 %.
On an Uno analogWrite() gives about 490 Hz on pins 3, 9, 10 and 11, and about 980 Hz on pins 5 and 6. D11 is already the latch here, so the sketch uses D9. A low frequency can be seen as flicker, and a phone camera usually shows it sooner than an eye does. We have not found, and do not claim, a frequency above which it stops. The switch itself is not the limit: R9 times Q2's gate capacitance is about 2.7 microseconds, and the periods here are 1 to 2 milliseconds.
The sketch
The wiring is in the comment header: 12 V and its ground to the two-pin header, 5 V and ground and three signals to the six-pin one, and D9 to PWM. It does not need a library. Bring the supply's ground and the Uno's ground to the same point.
show() is the three lines from the shift register page. loop() ramps the duty cycle from 0 to 255 and back, in 6 ms steps, then moves to the next digit. Look at what happens in between: the digit is dark at the bottom of the ramp, and the next digit is sent while it is dark. When the ramp starts again the new digit is simply there. The register held its byte the whole time, and the dimmer only decided whether it was visible.
The same on an ESP32
Arduino core 3.x has analogWrite() too, on any output pin, and analogWriteFrequency(pin, hz) to set the rate, which defaults to roughly 1 kHz. One thing differs. analogWrite() attaches the pin to the LEDC hardware, and after that a plain digitalWrite() on that pin is refused. Use analogWrite(pin, 0) for low and analogWrite(pin, 255) for high on any pin that is ever PWM'd.
// Same wiring as the Uno sketch, on an ESP32 with Arduino core 3.x.
// Pick any four free GPIOs; these are examples. The PWM pad stays open.
#define SER 4
#define LAT 5
#define CLK 6
#define PWM 7
const uint8_t d[] = {
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
};
void show(uint8_t b) {
digitalWrite(LAT, LOW);
shiftOut(SER, CLK, MSBFIRST, b);
digitalWrite(LAT, HIGH);
}
void setup() {
pinMode(SER, OUTPUT);
pinMode(LAT, OUTPUT);
pinMode(CLK, OUTPUT);
analogWriteFrequency(PWM, 1000); // Hz; call before the first analogWrite
analogWrite(PWM, 0); // low. Not digitalWrite: see the text
show(d[8]);
}
void loop() {
for (int level = 0; level <= 255; level++) { analogWrite(PWM, level); delay(6); }
for (int level = 255; level >= 0; level--) { analogWrite(PWM, level); delay(6); }
}Its pins are 3.3 V logic. That runs U1 (2 V to 6 V) and is, on the arithmetic of the previous page, enough to turn Q1 on; that page says what was not measured.
The code
One module, six wires and a 12 V supply. The digit counts up one number each time it has faded out, so you can see the byte survive the dark.
// Dim a 4" seven-segment module: show a digit, then fade it in and out.
//
// Wiring, with the module's back facing you (IN is the left edge):
// 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
//
// Leave the PWM pad (SB1, between 3V3/5V and PWM) OPEN, as it ships.
// A shorted pad ties PWM to the logic rail, and D9 would fight it.
// The 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. No libraries. analogWrite on D9 is about 490 Hz.
#define SER 8 // DATAIN (SER)
#define LAT 11 // LATCH (RCLK)
#define CLK 12 // CLOCK (SRCLK)
#define PWM 9 // PWM, a hardware PWM pin on the Uno
#define STEP_MS 6 // time per brightness step, 256 steps each way
// Bit 0 is segment A, bit 6 is G, bit 7 is the point.
const uint8_t d[] = {
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
};
void show(uint8_t b) {
digitalWrite(LAT, LOW);
shiftOut(SER, CLK, MSBFIRST, b);
digitalWrite(LAT, HIGH);
}
void setup() {
pinMode(SER, OUTPUT);
pinMode(LAT, OUTPUT);
pinMode(CLK, OUTPUT);
pinMode(PWM, OUTPUT);
analogWrite(PWM, 0); // an open pad with nothing on PWM is dark anyway
show(d[0]);
}
void loop() {
static uint8_t digit = 0;
for (int level = 0; level <= 255; level++) { // fade in
analogWrite(PWM, level);
delay(STEP_MS);
}
for (int level = 255; level >= 0; level--) { // fade out
analogWrite(PWM, level);
delay(STEP_MS);
}
// The digit is dark here, and the register still holds its byte.
digit = (digit + 1) % 10;
show(d[digit]);
}Leave the PWM pad open. If you have bridged it, D9 would be driving a wire the bridge holds at the logic rail, so desolder the bridge first. Nothing here needs a library.
When it does not work
The PWM pin is not reaching the board. Check D9 goes to PWM on the IN edge, that the Uno's ground and the 12 V ground are one wire, and that the pad is open or the pin is the only thing driving it. With a meter on PWM the voltage should rise and fall, and a pin stuck at 0 V gives a dark digit whatever the code.
The pad is bridged, so PWM is held at the logic rail and D9 cannot change it. Desolder the bridge and the fade appears.
The program steps the duty cycle evenly, but the eye does not report brightness evenly, so the steps do not look even. This is normal and has nothing to do with the module. A table of duty values that rises faster at the start, or slower, evens it out to taste.
A low PWM frequency can be seen as flicker, and a phone camera usually shows it sooner than an eye does. On an Uno, pins 5 and 6 run at about 980 Hz, twice the rate of the others. On an ESP32 you can pick the frequency with analogWriteFrequency(). We have not measured a frequency above which it disappears, so try a few.
show() is called once per breath, after the fade out, with the next digit. If you see the previous one, the line that increments digit is missing or after the call. The byte itself is held by the register the whole time.
What happens to the first byte when you send a second one, and why that is the whole trick of chaining.
DATAOUT to DATAIN →Edit this page — content/books/seg4/dim-it-with-pwm.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.