This project uses the
Arduino PWM Uno or other systems to realize a fully isolated analog output with
a range of 0-5 volts or more, changing only the reference voltage.
Introduction
This project completes the series of my
articles about the Arduino analog I/O with the aim to use it as a controller of
small automation systems.
In control systems of the industrial plants it
is always advisable to isolate both the inputs and the outputs coming from the
field. This prevents disturbances caused by power surges, lightning strikes or
other EMI sources and also by ground potential differences.
Arduino Uno, or systems based on the ATmega328
chip has no a true analog output, but it may be realized using a PWM output averaged
with a low-pass filter.
The use of an averaged PWM signal with 8-bit
setting is not comparable with a real DAC, but in the insulation case presents
undoubted advantages of simplicity since it is sufficient to use an optocoupler
for isolating the PWM digital signal. Recently I designed another circuit to
generate a 4-20 mA current with Arduino, that experience gave me the idea for
this new project.
The
Arduino PWM
Arduino Uno has several pins (3, 5, 6, 9, 10, and
11) that can be configured for PWM output. For this project I used pin 9
because the others were used by various devices (LCD, SD and RTC) in my Arduino
system.
The PWM signal on pins D9 and D10 is generated
by Timer# 1 of ATmega328. It has a prescaler which divides by 1, 8, 64, 256, 1024,
controlled by the three least significant bits of the register TCCR1B. The
default value of the prescaler set by the Arduino IDE is equal to Np= 64 (TCCR1B,
bits 2-0= 110), which provides an output frequency:
PWM frequency =
CPUClock/(2´Np´TOP) =
16000000/(2´64´255)= 490.196 Hz
Where the TOP value is he maximum Timer/Counter
value.
The following table shows the frequencies
generated by Timer# 1 of an Arduino Uno (Atmega 328) on pins 9 and 10,
with a 16 MHz clock and in “phasecorrect PWM” mode. In this
mode, the timer counts from 0 to 255 and then back
down to 0.
Prescaler divider (Np)
|
Prescaler code
|
PWM frequency
|
1
|
B001
|
31372.549
|
8
|
B010
|
3921.569
|
64
|
B011
|
490.196
|
256
|
B100
|
122.549
|
1024
|
B101
|
30.637
|
The prescaler code must be put in the three
least significant bits of the register TCCR1B – Timer/Counter1 Control Register
B. For example, to generate a PWM of 3921 Hz, the following instruction must be
inserted in the setup function:
TCCR1B = TCCR1B & B11111000 | B00000010;// set
timer 1 prescaler to 8
Using a common optocoupler with a
phototransistor, as 4N25, the frequency is limited because of the high
transition times, so I used a faster optocoupler with photodiode and with an open
collector output, such as the 6N136.
To eliminate the output noise I utilized a
second order active low-pass filter, Sallen-key type, with a cut-off frequency
of about 11.2 Hz. The isolation is achieved with an optocoupler, of course you
must use for this circuit a power supply different from the one used for
Arduino. If the insulation is not required, things become even simpler and
connect the filter to the PWM output, in this case not even need the reference
source U2.
The circuit diagram, shown in Figure 1, is
quite simple. I recommend using for U1 a double operational amplifier suitable
for single-rail power supply, such as LM358.
The LM358 chip must be powered with a voltage
higher than 7 V (and lower than 32) to have in output a maximum voltage of 5V
and also the regulator has a 2 V dropout.
The advantage of the open collector of the
optocoupler is that you can easily obtain a different output range, for
example, using a 10V reference voltage and R2=10 kohm the output range became 0-10V. In
this case the LM78L05 must be replaced with a LM317 with an appropriate
circuitry.
In figure 2 you can see the arrangement of the
components of my prototype.
Hardware components
1x Arduino board,
Components list
R1= 330 ohm ±5%
|
R2= 5.1 kohm ±5%
|
R3= 100kohm ±5%
|
R4= 100 kohm ±1% metal film
|
C1= 100nF ceramic
|
C2 = 10 MF,50V Electrolytic
|
C3= 200 nF Mylar ±2%
|
C4 = 100 nF Mylar ±2%
|
U1= LM358 dual op amp
|
U2= LM78L05 regulator
|
OPT1= 6N136
|
The capacitors used for the filter must be
measured with a capacimeter, for my prototype I selected for C3 some 220 nF
capacitors to search for a value that approached 200nF and C4 have selected a
value half of C3.
The test on the circuit
The Figure 3 shows the results of the linear
regression on the 14 measurements points made on my prototype. The test
conditions are:
·
PWM
frequency = 490.196 Hz;
·
Vin
= 12V;
·
Vref
= 5.00 V
The standard error is about 6.1 mV, so the
results are very good at the default PWM frequency.
I also tested the system with a frequency of 3921.569
Hz, but with a standard error of 39 mV. The largest errors are found for high duty cycle values, in this area
the pulses are narrow and the rise time is high and this phenomenon creates
non-linearity. The period is: T = 1/3921.569 = 255 µs. The more narrow pulse
has a duration of about 1 µs, approximately the same value as the rise time of
the pulses, the cause of non-linearity is due just to this phenomenon. Using
the default frequency of 490.196 Hz, the minimum pulse has a duration eight
times larger, so it greatly improves the linearity.
The program list
To test the system I used an Arduino Uno with
a LCD display and the analog input A0 connected to a potentiometer to vary the
duty cycle of the PWM.
//
program to test Arduino Uno PWM at 3.9 kHz
// G. Carrera 30 sett 2016
#include <LiquidCrystal.h>
int
PWMpin = 9; // PWM out on digital
pin 9
int
analogPin = 0; // potentiometer
connected to A0
int
val = 0; // variable to store the
read value
char
spacestring[17] =" ";
//
initialize the library with the numbers of the interface pins
LiquidCrystal
lcd(7, 6, 5, 4, 3, 2);
void
setup() {
pinMode(PWMpin,
OUTPUT); // sets the pin as output
lcd.begin(16, 2);// set up number of columns
and rows
lcd.setCursor(0, 0);// set the cursor to
column 0, line 0
lcd.print("Stalker PWM");// Print a
message to the LCD
// set timer 1 prescaler to 8 for PWM
frequency of 3921.57 Hz
//TCCR1B = TCCR1B & B11111000 |
B00000010;
}
void
loop() {
val = analogRead(analogPin) >>
2;// read the potentiometer as 8 bit
analogWrite(PWMpin, val);
val =
255-val;// complement
lcd.setCursor(0, 1);
lcd.print(spacestring);
lcd.setCursor(0, 1);
lcd.print(val);
delay(500);
}
References
2. “Atmel 8-bit
Microcontroller with 4/8/16/32KBytes In-System Programmable Flash”, 8271G–AVR–02/2013