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.

arduino PWM sine generation 20Hz

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 and averages produced by smoothing

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.

    Uno, Nano, Mini - use the same pins as here.

Other Arduino chips all use PWM frequencies from 700Hz to 1000Hz.

Arduno Analog Output Pins for PWM

Arduino Nano PWM pins (these are also Arduino Uno PWM pins):

    3, 5, 6, 9, 10 and 11.

Arduino analog output pins 5 and 6 use 980Hz  (Timer 0 shared).

Arduino analog output pins 3, 9, 10 and 11 use 490Hz (the default frequency).

Arduino Pins, Timers and ATMega labels

Arduino
PWM Pins
Timer used
328P label
328P chip pin
PWM Frequency
  3
Timer 2
OC2B
1
490Hz
  5
Timer 0
OC0B
9
980Hz *
  6
Timer 0
OC0A
10
980Hz *
  9
Timer 1
OC1A
13
490Hz
  10
Timer 1 OC1B
14
490Hz
  11 Timer 2
OC2A
15
490Hz
* Timer 0 is used as the millisecond and delay timer, timing source, so its frequency must be kept at 980 Hz for these functions to operate correctly.

Pins 5 and 6   use Timer 0 [An 8 bit timer].
Pins 9 and 10 use Timer 1 [A 16 bit timer].
Pins 3 and 11 use Timer 2 [An 8 bit timer].

Arduino AnalogWrite

The function to set any of the PWM pins to a specific mark to space ratio is:

    analogWrite(pin, msvalue);

The argument  pin is any of the valid PWM pins.
The argument msvalue (mark to space value) can take a value from 0 to 255.

Setting the Mark to Space ratio


M:S ratio
Arduino PWM value
0%0
25%64
50%128
75%191
100%255

A PWM value of 0 means the output is fully off.
A PWM value of 255 means the output is fully on.

Capacitive smoothing Example

Lets design a filter for the PWM Arduino analog output.

For a single pole filter the centre frequency is given by:

    fc = 1.0/(2 * PI * R * C)

We know that the frequency of the PWM signal is 490Hz so lets choose a filter center frequency 10 times lower as a 1st estimate.

Lets also choose a resistance of 10k. Then the capacitance is:

    FC = 49

    C  = 1.0/( 2 * 3.14159 * 10e3 * 49) = 3.24e-7 = 324nF

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.

Arduino analog output filtered wihth 10k and 1uF

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.

arduino pwm smoothing r=100k C=1uF

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 PWM smoothing two 10k 220nF

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.

Arduino analog output PWM 480Hz

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.

Arduino Analog Output PMW associated with timer 0 980Hz

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.

int PWMpin = 5;

void setup()
{
   Serial.begin(115200);
   Serial.println("PWM Sine...");
}

void loop()
{
static int pwm;
static float x = 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:

arduino PWM sine generation 20Hz

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):

Arduino PWM smoothing min delay 134 Hz

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.


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.





Privacy Policy | Contact | About Me

Site Map | Terms of Use