Arduino Analog Output:Impossible Analog; using Digital Pins. How to generate an Analog Output varying from 0V to 5V using only
Digital pins! It sounds impossible as digital pins output only 5V or 0V.
The secret is... Averaging...
Arduino Analog Output:
How to create Analog Output using only digital pins with analogWrite.
The secret is PWM (Pulse Width Modulation) and averaging.
Find out why there are only 6 PWM outputs
Find out what Internal Modules are needed to Implement PWM.
Why one PWM output pair only allows a low frequency period (490Hz).
Find out the Speed Capabilities of PWM e.g. what frequency sine wave can you create?
See real life PWM results measured on my bench using an oscilliscope. Observe the real effect of different smoothing capacitors resistor pairs.
Arduino Analog Output pins do not generate a true analog output in
the sense that the microcontroller does not have a resistive divider to
create the voltage (The exception is the Arduino Due). Instead it uses a
digital PWM signal that can be smoothed to create an average voltage,
which does result in an analog output.
The difference between a true analog output voltage and a PWM voltage
is that the latter is created using varying
mark to space ratio within the digital output signal. In the waveform
above you can see the M:S ratio changing as the sine wave is generated.
The PWM signal is repeated at a constant frequency (the start of each
PWM signal is separated from the next starting edge by the same period).
Since a frequency
is involved there will be some feed through of the fundamental frequency
to the output (after smoothing).
So if you are looking for absolute accuracy you should use a
dedicated DAC that does have a complete set of resistive dividers inside
e.g. MCP4725. If you need a fairly good voltage then the PWM outputs are very useful.
For the standard Arduino Uno there are six analog output pins you can use. The
really useful thing about these outputs is that they use internal
hardware modules attached to the timers to automatically output the
chosen M:S ratio without further programming. So your program only
writes them once and they keep generating the PWM signal allowing your
program to do other useful tasks.
How Arduino Analog Output PWM works
Varying the Mark to Space (M:S) ratio alters the average voltage at
the output which can be smoothed into a steady D.C. voltage to create the Arduino analog output. It is a
simple way of generating a continuously varying output voltage using
only digital signals.
The following diagram shows the difference in average voltage for low, middle and high M:S ratios:
PWM Output Smoothing
There are several ways to smooth a PWM signal:
Capacitive Resistive pair.
Inductive Resistive pair.
Persistence of vision.
Capacitive Resistive pair
To make the output signal a true D.C. output all noise and frequency
have to be filtered out and the easiest way is to create a low pass
filter formed from a resistance and a capacitance.
Inductive Resistive Pair
PWM signals are often used to drive motors (a large inductance with
winding resistance). The inductance and resistance filter the PWM signal
to give power control of the motor. It does not really matter that the
signal is a square wave. All that matters is the amount of current fed
to the motor winding.
Persistence of vision
This is all about fading an LED. If you want to do it just connect an
LED and current limit resistor to a PWM pin. Your eye will see a
smoothly fading LED output (in reality the light is pulsing on and off) -
if you adjust the PWM from 1 to 255 and back again over a set period of
time.
Your eye cannot react faster than 20ms (the 490Hz signal has period 2ms), and therefore sees a continuous output light level.
Arduino PWM Frequency
There are two main frequencies used in Arduino boards:
490Hz (The default frequency).
980Hz (Used when pin is also shared with Timer 0).
Warning: To allow functions delay() and millis() to operate correctly - do not change the PWM frequency of Timer 0 - keep it at 980Hz.
The reason for the different frequencies is that Timer 0 is shared with
functions delay() and millis(). These, on the Uno, are on pins 5 and 6.
Note: The reason for the 980Hz clock for Timer 0 is shown here. It is basically that Timer 0 is setup to provide a near 1ms timer tick. See Arduino millis() for how this is corrected to an average (and accurate) 1ms timer tick.
The following Arduino boards follow this scheme:
Uno, Nano, Mini, Mega, Leonardo, Micro, Yun, 101 - actual pins used vary.
I don't have that exact value to hand so lets choose a 1uF capacitor giving a center frequency of:
FC = 1.0/( 2 * 3.14159 * 10e3 * 1e-6)= 15.9Hz
Warning: For these values 10k, 1uF smoothing may not be enough.
Now lets see the resulting output:
Smoothing R=10k C=1uF
The time constant is 10ms.
You can see that the A.C. ripple left over from smooting on the
Arduino analog output is large at 253mV (top blue line). The average
D.C. level is
where it should be at half the supply at 2.428V [lower right reading
'V') (The supply is ~4.8V on
this Arduino Uno USB power connection).
The oscilloscope screen capture shows the PWM output on the bench with a mark to space ratio set at 1:1 and RC=10ms.
So if you want a more stable DC Arduing analog output, extra filtering is needed. You can either
Use a higher pole filter (requires opamp).
Increase R or C or both.
Or increase the PWM frequency - program the PWM registers directly.
If you increase the capacitance you will get a smaller ripple
but the response to a step input will be slower (the capacitor has to
charge to the final voltage).
Smoothing R=100k C=1uF
Time constant is 100ms.
Here the ripple is 25mV (top blue line) - reduced from 253mV in
the previous bench scope capture. The average voltage is 2.38V (half
the supply voltage
~4.8V).
The oscilloscope screen capture shows the top waveform is a
scope stored waveform at the same voltage per division as in the
previous image (for comparison 500mV/Div). The lower waveform is a
zoomed in 50mV/Div view of the same waveform in scope A.C. mode to show
the ripple in detail.
Smoothing R=20k C=220nF
Time constant is 4.4ms (using two 10ks in series and a 220nF).
This is shown as an example of more typical values that you might use
or find online. It is a compromise in smoothing and speed of response.
For the next screen capture from the bench oscilloscope, the
(T=4.4ms) ripple is 496mV. The average voltage is again 2.38V (half the
supply voltage
~4.8V).
Arduino Analog Output Resolution
The resolution of the PWM signal is 8 bit. So there are 256 distinct
output values of mark to space ratio, and when averaged results in 256
different voltage levels.
The PWM value 0 results in zero output.
The PWM value 255 results in the output turning fully on.
There are three timers in the Arduino Uno and Nano chips (ATmega328P)
and in general each timer is capable of supporting 2 PWM outputs hence
why there are 6 PWM outputs.
In fact Timer 1 is capable of being used as a 16 bit PWM but is
limited in software to a 8 bit output to keep consistency with other PWM
outputs.
PWM Resolution Waveforms
The following waveform show the operation of both 490Hz and 980Hz PWM
signals. The higher speed output has a bit period half the lower speed
but the higher speed one also has a different output for the 1st bit
period.
PWM frequency 490Hz (default)
This frequency is the "normal" frequency of PWM signals for Timer 1 and Timer 2.
You can see the bit period is 8.1us. Zero output is at the top
followed by incrementing M:S values from 1 to 4 with each waveform
incrementing by 8.1us.
The screen capture from the bench oscilloscope shows real waveforms for
single increments of the M:S ratio for Timer 1 or 2 i.e. increments of
the PWM register by 1.
PWM frequency 980Hz
This frequency is the PWM frequency associated Timer 0 shared outputs.
Note: The reason for the 980Hz clock and exact value is shown here.
To make it work the first bit period is extended to 8.1us, while all the others are 4.05us long.
The Zero output is at the top
followed by incrementing M:S values from 1 to 4 with each waveform
incrementing by 4.5us except the 1st. You can also see the last value
(254) is correctly positioned at the end of the waveform (before the
starting pulse - in yellow before the low period of the signal).
The screen capture from the bench oscilloscope shows real waveforms for
single increments of the M:S ratio for Timer 0 i.e. increments of the
PWM register by 1.
In the Arduino documentation it states that the PWM signal on pins 5 and
6 may not go fully off. This looks to have been corrected by extending
the 1st bit output. You can see that waveform 0 is fully off and the 254
output (yellow) is correct.
Example Sketch Sine Generation
The following Arduino analog output example uses a PMW output (pin 5) with smoothing capacitor
220nF and resistor 20k to generate the Arduino analog output. This is not the best way to create a sine wave
output as the output frequency is too low (It also depends on the loop
time of the code - here ~185us plus the 1ms delay added on).
Warning: The PWM sine wave output is low frequency.
You need a small time delay to allow the smoothing RC pair to reach the
output so its a trade off between frequency, amplitude and resolution.
// Generate (slow) sine using PWM.
intPWMpin=5;
voidsetup()
{
Serial.begin(115200);
Serial.println("PWM Sine...");
}
voidloop()
{
staticintpwm;
staticfloatx=0;
x+=15;
// Let one period take 100 iterations.if(x>100*2*3.141)x=0;// Position in middle of PWM with a peak of half the max. op.
pwm=int(128+127*sin(x/100));
analogWrite(PWMpin,pwm);
delay(1);// When x += 15 - f = 20Hz, Vpp = 1.5V
// delayMicroseconds(200); // When x += 15 - f = 61Hz, Vpp = 500MmV
// delayMicroseconds(500); // When x += 15 - f = 34Hz, Vpp = 898mV
}
Loop delay 1ms Output
You can't really generate a fast Arduino analog output unless you program the PWM
hardware directly. Using the standard PWM frequencies and smoothing
results in an output frequency of 20Hz ~ 60Hz.
Note that x is skipped along by 15 to increase the frequency but the output waveform resolution is reduced.
The waveform below, showing real word measurements using my bench oscilloscope shows the case when using a delay of 1ms:
Changing the 'x' increment to 1 in the program will increase the resolution but slow the output down further.
Loop delay 185us Output
The loop delay (185us) was measured previously. This is the time to repeat the loop function excluding the delay(1) time.
The waveform below, showing real word measurements using my bench
oscilloscope: With no delay the output is not generated long enough for
it to
update so the amplitude reduces and distorts (all delays commented out).
The advantage is increased output frequency (135Hz):
The comments at the end of the code shows these results:
delay(1);// When x += 15 - f = 20Hz, Vpp = 1.5V
// delayMicroseconds(200); // When x += 15 - f = 61Hz, Vpp = 500MmV
// delayMicroseconds(500); // When x += 15 - f = 34Hz, Vpp = 898mV
To make a better output use program the PWM directly for a higher output frequency and use smaller RC pair
Note: It should be possible to get better quality output signals by programming the PWM i.e. increase the PWM frequency.
Frequently asked questions
Can you write an analog output to A0~A5?
The short answer is no. The only extra internal hardware attached to these pins is the AC. These pins are either:
An analog input (multiplexed to the ADC).
A digital input.
A digital output.
Can you use Analog pins for digital I/O?
Yes : See above.
Remember to set the pinMode for each pin as INPUT or OUTPUT or INPUT_PULLUP when using them as digital I/O.
You can't use digital I/O for A6 & A7 on the Arduino Nano (they
are analogue only). A6 & A7 are not available on the Arduino
Uno.
How to output a true Arduino Analog Output voltage.
See above page using the Arduino PWM or use an MCP4725.
What's the difference between AnalogWrite and DigtialWrite.
AnalogWrite, used here, creates a digital PMW signal that when smoothed,
results in an analog output voltage between 0V and the supply voltage
with 255 steps of resolution.
DigitalWrite outputs either 5V (logic 1) or 0V (logic zero) and nothing in between.
Written by John Main who has a degree in Electronic Engineering.
How to get accurate DHT22 digital humidity sensor readings with an Arduino. Did you know it also measures temperature as Well? Find out why, in this page...
A PIR sensor lets your Arduino sense movement without contact. This tutorial covers PIR sensor basics, connecting one to an Arduino board and coding a motion detector.
Arduino Hall Effect Sensor: Add magnetic sensing superpowers to your Arduino projects with an easy-to-use hall effect sensor. With full code and layout...
Comments
Have your say about what you just read! Leave me a comment in the box below.
Don’t see the comments box? Log in to your Facebook account, give Facebook consent, then return to this page and refresh it.