Arduino internal block diagram for ADC.
Fig. 1

Arduino Analog Digital Conversion Voltmeter

by Lewis Loflin

Earlier we learned how to use Arduino's PWM output with a LM358 voltage amplifier to make variable DC power supply. Now will combine that idea with a LM311 comparator to demonstrate how to read a voltage greater than 5-volts.

The idea is to understand how analog to digital conversion is done.

See Arduino Pulse-Width Modulation Digital to Analog Conversion

Fig. 1 shows the basic building block of most modern analog to digital converters in this case Arduino. This consists mainly of a voltage reference, a sample and hold comparator, and a digital to analog converter. (DAC).

LM311 comparator operation.
Fig. 2

In this case the DAC output is proportional to the binary input value from the control logic. At some point the binary value to voltage output from the DAC on the comparator inverting (-) will be equal to input voltage on the non-inverting (+) input. The output from the comparator will go HIGH or LOW, the count to the DAC is halted and returned as a 10-bit value with a analogRead() function.

There are two ways to control the input value to the DAC: ramp and successive approximation. In this demo I'll use the ramp method.

Fig. 2 shows how this would work with a LM311 type comparator.

Arduino DAC based voltmeter.
Fig. 3

In Fig 3 we are using a DAC based on the LM358 whose analog output is proportional to the 8-bit duly cycle of the PWM out of Arduino digital pin 11.

The PWM DAC output is connected to the LM311 non-inverting (+) input while the voltage to read is connected to the inverting (-) input of the comparator. The program uses a FOR loop counting from 0-255 and writing that value to control the duty cycle PWM output on DP11.

As the count increments or "steps" the voltage output increases in steps.

Between every count Arduino polls the status of DP4 which is connected to the LM311 output waiting for a low when the two inputs to the LM311 are equal. When this goes LOW the count is halted, the voltage value is calculated, then written to the LCD display.

The voltage range is set by Rset based on 5-volts (255 written to DP11) on the LM358 pin 3. The range can be no greater than the voltage on LM358 pin 8 minus 2-volts. Note the higher the range voltage the less accurate the resolution. A 0-10 volt range has a resolution of 10 / 255 = 39.2mV / step; at 20-volts it's 78mV / step. Rset can be used as a fine adjustment.

In this case I was reading 0-5 volts.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// set the LCD address to 0x27
// 2-line 16-char numbered 0-15
LiquidCrystal_I2C lcd(0x27,16,2); 

float voltage;
int i;

void setup(void) {
  pinMode(4, INPUT);
  lcd.init(); // initialize the lcd

void loop(void) {
 i = 0;
 while(digitalRead(4))   {
   analogWrite(11, i);

  lcd.print("Count = ");
  lcd.print("  ");
  // scaler value = Vcc / 255
  voltage = .01961 * i;
  lcd.print("Vin = ");

  analogWrite(11, 0);

Web site Copyright Lewis Loflin, All rights reserved.
If using this material on another site, please provide a link back to my site.