Digital Electronics

PHYS 432 - Analog to Digital Conversion

Updated Wednesday May 05, 2021

Introduction

This task introduces the internal ADC on the Arduino. The ADC can be used to interface with a wide range of analog sensors or record data from any voltage source. The ADC is a 10-bit successive approximation ADC that can be tied to several different reference voltages. In this task we will use the ADC to read the voltage from a potentiometer and use this to set the brightness of an LED. Along the way, we will see how to use the ADC, and also explore its performance and stability. This task is largely based off the example found in "File : Examples : Analog : Analog Input".

Wiring

We will use an LED attached as before (so if you still have it wired in the breadboard, just leave it there) along with a potentiometer to supply a variable voltage. Wire up the circuit as shown below. If you also have the button from Task 1, just leave it, as it will do no harm. As with Task 1, it is best to directly wire the potentiometer from the +5V and GND pins rather than setting up a rail. I had to use pairs of leads to get everything to fit in the drawing below, but you should just use single leads to connect the potentiometer pins to the Arduino. Make sure the wiper (middle pin) of the potentiometer goes to pin A0.

fritz

Sketch

Put the following sketch into the Arduino IDE or else download it from task3.ino. In addition to the new code for the ADC, we are also using the Serial commands from Task 2 to give us an easy way to see the direct ADC readings.


/*
 * Task 3 - ADC
 * 
 * Read an analog voltage from a potentiometer and use the result to flash an ADC.
 * 
 * Code adapted from Analog Input and Read Analog Voltage examples.
 * 
 */

// LED connection
#define LED_PIN 2

// ADC connection
#define ADC_PIN A0

// Voltage reference value
#define MAX_VOLTAGE 5.0

// the setup routine runs once when you press reset:
void setup() {
  
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);

  // Set up the ADC reference voltage
  // DEFAULT uses the USB 5V power as a reference
  // The other possibility for a Nano is INTERNAL which is a 1.1V bandgap reference
  analogReference(DEFAULT);  // Don't actually neeed this line to specify default

  // Pin to control the LED
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);  // Start with LED off

}

// the loop routine runs over and over again forever:
void loop() {
  
  // read the input on analog pin 0 (range: 0-1023)
  int sensorValue = analogRead(A0);

  // Convert this to a voltage, scaled by Vmax 
  float voltage = sensorValue * (MAX_VOLTAGE/1023.0);

  // Print this value and conversion to Volts to the Serial port
  Serial.print(sensorValue);
  Serial.print(" -> ");
  Serial.print(voltage);
  Serial.println(" V");
  
  // Now have some fun.  Light up our LED based on the ADC reading
  digitalWrite(LED_PIN, HIGH);
  delay(sensorValue/8);
  digitalWrite(LED_PIN, LOW);
  delay(sensorValue/8);
  
}

How it Works

The voltage present on any of the Analog input pins is digitized using the analogRead() function. As the Arduino uses a 10-bit ADC, we find values in the range from 0-1023. The Arduino ADC knows nothing about the actual reference voltage used, so we must convert this back to volts using the appropriate calibration factor. The analogReference() function in setup() specifies the external 5V USB voltage as the reference. If you change to the internal 1.1V reference, you will also need to change MAX_VOLTAGE accordingly. Note that if you were to divide sensorValue (which is an integer) by 1023, you would get zero, as integer division in C produces an integer by default. We use Serial to write the raw ADC reading and converted voltage to the serial port. Finally, the external LED is lit for an amount of time proportional to the ADC value.

Compiling and Uploading

Follow the same procedure described on the Arduino IDE page to compile and upload your sketch. Don't forget to open the Serial Monitor to see the printed ADC readings. Vary the potentiometer and watch the LED flashing change, and also watch the ADC value in the Serial Monitor change. Vary the potentiometer and verify that the raw readings range from 0 to 1023.

Improvements

The ADC readings seem rather stable, with at most a 1 bit change if you just leave the potentiometer alone. The printed voltages, however, don't have enough precision to show this 1-bit fluctuation (that should be around 5 mV). Printing numbers with different precision can be controlled in detail using String formatting and some of this is built into the Serial.print() command. Change Serial.print(voltage) to Serial.print(voltage, 3) to see what happens.

To improve the ADC precision, you need to reduce the range the ADC is digitizing. Change the voltage reference to the internal 1.1V reference with analogReference(INTERNAL); and change the max voltage with #define MAX_VOLTAGE 1.1. Upload the sketch again to see the difference. You may need to further increase the precision used to print out the calibrated voltages, as now each bit corresponds to approximately 1 mV. You could also use an external voltage reference supplied on the REF pin if you had some other stable reference. The regulated 3.3V output on the board could be used for instance. Other styles of Arduinos have different options for internally generated reference voltages. Details can be found in the documentation for analogReference.

Going Further

Using a potentiometer knob as an interface to control something is about as old as electronics itself. This technique could be extended to light up two different colored LEDs for complimentary lengths of time to create a color fader. There is a tri-color LED in your kit that could make this more satisfying than two LEDs side by side, but either could work.

You may have noticed that the LED delays impact the speed at which the ADC acquires data. This is due to the blocking wait() command that prevents the Arduino from doing anything else while waiting. There is custom circuitry attached to the digital output pins to pulse signals on and off automatically known as Proportional Wave Modulation (PWM). The PWM runs as a fixed frequency around 500 Hz, but changes the duty cycle of the on pulses to the off pulses. If you have any effective low-pass filtering, this essentially produces an analog output. You can use PWM by replacing the on/wait/off/wait commands with analogWrite(LED_PIN, sensorValue/4);. Not all pins on any given Arduino have the PWM circuitry, and the Nano doesn't have this on pin 2 as you can see in the analogWrite reference. If you move the LED to pin 3, this works great. The maximum value for the PWM is 255 (8 bits) so we need to divide our 10 bit ADC reading by 4. Because the PWM frequency is around 500 Hz, we don't see the pin flashing, but the brightness of the light varies as the duty cycle is changed by the PWM circuitry.

There is also a rudimentary plotting function built into the Arduino IDE. If you close the Serial Monitor and instead open the Serial Plotter, you can see a time history of numbers send over the serial port. To make this work well, you should print only the calibrated voltage to the serial port. This isn't honestly that useful, but to quickly visualize some data it can be pretty handy.

When you are done playing around, save your sketch somewhere you can find it, and move on to Task 4.