Fixing PWM flicker on the Arduino UNO R3
Hooked up an RGB LED to an Arduino and it started to flicker when combining colours? This is how I found the solution.
Table of Contents
What happened?
Since I'm trying to tell a story here, let's start at the beginning.
I have an Alarm clock that reliably wakes me up every workday, featuring a piezo beeper and an RGB backlit LCD connected to an ATmega328P Microcontroller that cosplays as an Arduino UNO R3.
The tone()
function messes up the PWM on the Arduino Pins 3 and 11. (They are using the same timer that also generates the beeper frequency.)
No problem I though, I'll hook up my RGB backlight up to Pins 5, 9 and 10 …
Note: My previous setup had one LED on pin 3 instead of pin 5 because I wasn't aware of the tone()
functions side effects when building the hardware.
The Problem
The problem was now that the display seemed to be flickering, turning up one colour at a time to the maximum doesn't result in flicker.
My first suspicion was a software bug … Nope, cant be — setting the LEDs just once a second also makes it flicker.
Also the flicker only occurs when the LED on pin 5 was involved, mixing the LEDs on pins 9 and 10 worked fine.
So looking up the Pinout which I was reminded that there are three timers inside the ATmega328P, each capable of driving two PWM channels. Pins 9 and 10 are on timer 1, while pin 5 is on timer 0 … suspicious.
The obvious difference was that one uses an 8-bit counter and the other 16 bit one, so maybe some thing wrong with the 16-bit timer? (Nope.)
At this point I found an answer on the Electronics stack exchange pointing out that what I already knew as timer 0 runs at a different PWM frequency.
Second Theory: They are not on the same clock and I see some fancy interference pattern … So will the flickering when I reprogram timer 0 to the same setting as the others?
Some digging in the Arduino code I found where it configures the timers with a helpful comment:
on the ATmega168, timer 0 is also used for fast hardware PWM (using phase-correct PWM would mean that timer 0 overflowed half as often resulting in different millis() behaviour on the ATmega8 and ATmega168)
and a bit further down the code:
timers 1 and 2 are used for phase-correct hardware PWM this is better for motors as it ensures an even waveform
The ATmega168 is the predecessor and quite similar to the ATmega328P so I guessed that it still applies. With timer 0 being responsible for the correct behaviour of millis()
reconfiguring it isn't a path I want to go down.
But can I switch timer 1 to that non-phase correct mode that timer 0 is using?
If you want to follow the code: The cbi()
and sbi()
functions clear and set bits in values, in this case the processors registers. All the cryptic symbols of upper case characters and numbers are the names of registers and bits and explained in the Datasheet.
Consulting the ATmega328P Datasheet (PDF) to find out what all the options mean:
- Both timers are on the System clock divided by 64
- timer 1 is in Phase correct mode (already knew that)
- timer 1 is using only 8 of its 16 bits
- timer 0 is in a mode called fast PWM, which just means really simple and barebones PWM
- The Fast-PWM effectively doubles the PWM frequency
The Solution
So the only difference is one Timer is in Fast PWM and the other is in Phase Correct PWM.
If you skipped here: The ATmega328P in the UNO R3 has three timers, PWM pins 5 and 6 are hooked up to timer 0 which is in a different mode than the other timers for the millis()
function to work.
So lets try the pretty obvious and switch timer 1 to Fast PWM, which is just one bit according to the Datasheet.
So I add the following code to my setup()
function:
// Set timer/counter 1 from 8-bit phase correct to 8-bit fast
;
And … the flickering is gone. Problem solved.
If you are now interested in more details on how PWM works and what those timers actually do:
Why it flickers?
Two PWM signals one running at double the frequency of the other shouldn't noticeably interfere when the slower one is running at 490Hz.
The problem is that they aren't really double with how the two involved modes work.
The Fast PWM mode simply counts up and wraps from 255 to 0, making one iteration 256 cycles long.
The Phase Correct PWM mode counts up until 255 and then starts counting down again, but to arrive at 0 it takes 510 cycles. An off by two error resulting in not quite double the frequency.
Fast | 0 | 1 | 2 | 3 | 0 | 1 | 2 | 3 | 0 |
---|---|---|---|---|---|---|---|---|---|
Phase Correct | 0 | 1 | 2 | 3 | 2 | 1 | 0 | 1 | 2 |
So the actual frequencies are:
Mode | Frequency | Calculation |
---|---|---|
Fast | 976.6Hz | 16Mhz / 64 / 256 |
Phase correct | 490.2Hz | 16MHz / 64 / 510 |
If you were to plot those two frequencies overlapping each other you will notice that the interference matches a 3.8Hz wave, which should roughly match the low frequency flicker you were observing.