Today, you will learn how to read analog voltages so that you can do more advanced operations other than just reading a “yes” or a “no”, you will be able to gather information from temperature sensors, photo diodes, flex sensors, and a multitude of other sensors or input sources.  We will then display the voltage as a binary number on three LED’s.

Goals

  • Advanced usage of registers
  • micro-controller configuration
  • function creation in AVR studio
  • reading analog voltages using the ATtiny85 micro-controller
  • understanding a voltage divider

Required Materials

  • Programmer
  • Attiny85
  • 3 LED’s and 3 current limiting resistors
  • two photo diodes, flex sensors, or two resistors
  • digital multimeter

Getting Started

Getting analog readings is slightly more complicated than reading or writing digital values. Your micro-controller has a built-in peripheral called an Analog-Digital Converter (ADC). You can configure your ADC by writing certain values to registers, much in the same way you did when configuring your pins to be outputs in Module 2.
 First you need to configure the chip’s ADMUX register which includes information about the precision of the result you are expecting, which pin to use as the analog input pin, and what the reference voltage for the reading should be.  Second you need to decide what mode of acquisition you wish to use.  You are probably familiar with the idea of a single reading call where you make a function call from within your program that returns the analog reading as an int or double.  The other option for getting analog readings is by setting up an interrupt’s which are a little more complicated.  Feel free to follow along using the long datasheet starting on page 126.  There is a lot of information in the datasheet about specific characteristics of the chip but with some time and patience you can sift through the mass amounts of information and figure out the basics that you need in order to understand the example that follows.
The bottom-line is that registers in your micro-controllers literally act as switches that re-direct the flow of information. Have a look at the schematic diagram of your MCU’s ADC below:

Block diagram of the built-in Analog Digital Converter in the AtTiny85

You can for example clearly see how the different bits in the ADMUX register (2nd box in the top left) are affecting the different multiplexers that choose between different input reference voltages and different input pins (ADC0-4). Although intimidating at first, the block diagrams are usually your best bet to grasp how a specific peripheral works and what certain settings can achieve.
The most important information about getting analog readings:
Section 17.3 Operation:
  • There is a reference voltage that may be selected “by writing to the REFS[2:0] bits in ADMUX”
  • You select the analog channel “by writing to the MUX[3:0] bits in ADMUX”
  • you need to make sure you choose the single ended value instead of differential value (for simplicity) simple explanations can be found here and here
  • You need to decide if you wish to get an 8 bit value (256 different levels) or a 10bit value (1024 different levels) and read the proper registers ADCL and ADCH, set using ADLAR bit in ADMUX register.
  • when getting a 10bit value make sure you read the ADCL register before the ADCH register
Section 17.4 Starting a Conversion:
  • Using single conversion mode, you start a conversion by writing a 1 to the ADSC bit in the ADSCRA register.
Section 17.11.1 single ended conversion:
  • this is the equation we need to use to calculate the voltage on the analog pin.  Voltage = collected_value*Vref/2^(resolution 8 or 10) or Voltage = collected_value*5/1024 for our example
Section 17.12 Temperature Measurement:
  • collected_value = (ADCH << 8)|ADCL
  • that shows how the data should be interpreted/combined when getting a 10bit reading.

Putting it all together:

Now that you have read and pulled out some of the information in the datasheet about how you collect analog readings, it is time to decide what you wish to do.  Lets define a goal, collect a 10bit single ended analog reading with a range of 0-5V (Vref = Vcc and Vcc = 5V) and collect the reading from PB4 or ADC2.  That way we can use PB0-PB2 to display our collected value in binary on three LED’s.  The best place to look for information about each register regarding analog reading’s is starting on page 138 section 17.13.

1. Setting up a circuit

In order to test the ADC, you can try to measure the resistance of any of the resistive sensors in your kit, for example the light-resistor, the flex-sensor or the potentiometer. All of these “sensors” (the potentiometer really is a knob) change their resistance as a function of the quantity they measure. In order to measure a resistance, you can use it as part of a voltage divider. A voltage divider consists of two resistors, one the one you want to measure and the other one with known resistance. Its function, and how to wire it to your ADC input is explained in the following screencast:

http://www.youtube.com/watch?v=O8mj7TFew54

2. Configuring the ADMUX register

The easiest to read way of setting register’s is by following the following format:

(register being set)=
(tab ->) (stuff)|
(tab ->) (more stuff)|
(tab ->) (last bit of stuff);

From reading the datasheet, we know we want to fill the ADMUX register with the following information:

ADMUX =
(0 << ADLAR)| //10bit precision
(1 << MUX1)| //use PB4 as input pin
(0 << REFS0)| //set refs0 and refs1 to 0 to use Vcc as Vref
(0 << REFS1);

which can be simplified to:

ADMUX = (1 << MUX1);

3. Configure the ADCSRA register

The only bit we need to set in this register is the bit required to enable the ADC, we don’t want to start a conversion yet so don’t set the ADSC bit or any of the interrupt related pins.

ADSCRA = (1 << ADEN); //enable the ADC

4. Time to play around

Now that you have figured out how an ADC works and the basic configurations for the ATtiny85’s internal ADC lets upload some code that uses it and outputs a 0-5 number in binary that you can determine by setting a voltage on pin PB4 of the ATtiny chip. Before plugging in your micro controller connect three LED’s in series with resistors to PB0-PB2.

#include <avr/io.h>
#include <stdint.h>
//configure ADC in this function
void initADC (){
   //Configure ADMUX register
   ADMUX =
     (1 << ADLAR)| //shift in a 1 and follow 8bit procedure
     (1 << MUX1)| //Use ADC2 or PB4 pin for Vin
     (0 << REFS0)| //set refs0 and 1 to 0 to use Vcc as Vref
     (0 << REFS1);
   //Configure ADCSRA register
   ADCSRA =
     (1 << ADEN)| //set ADEN bit to 1 to enable the ADC
     (0 << ADSC); //set ADSC to 0 to make sure no conversions are happening
}
//configure digital output pins
void initDO ()
{
   //enable pin 4-6 or PB0-PB2 for debugging/output
   DDRB = (1 << 0)|(1 << 1)|(1 << 2);
}
//function that returns an analog reading
double getAIN()
{
   //make sure we define result as a double so we can mult/divide w/o error
   double result = 0;
   int temp = 0;
   //set ADSC pin to 1 in order to start reading the AIN value
   ADCSRA |= (1 << ADSC);
   //do nothing until the ADSC pin returns back to 0;
   while (((ADCSRA >> ADSC) & 1)){}
   //for 10bit precision we must read ADCL first (lower bits)
   //temp = ADCL;
   //then read the most significant bits, shift them over, and
   //or that number with the least significant bits
   //result = ((ADCH << 0x8) | temp);
   //for 8 bit precision we can just read ADCH:
   result = ADCH;
   //to properly interpret the results as a voltage we need to
   //divide the result by the maximum range: 2^(#bits precision)-1
   //and multiply the result by the Vref value, Vcc or 5V
   result = result * 5/255; for 8bit, below for 10bit resolution
  // result = result * 5/1023;
   return result;
}
int main ()
{
 int reading = 0;
 //place all configuration code at the begining of this function
 initADC();
 initDO();
 //place any repetitive code inside the infinite for loop below
 for (;;)
 {
   //gets an analog reading
   reading = (int)getAIN();
   //saves the reading in binary to the LED's
   PORTB = reading;
  }
}

Check to make sure this code works by connecting a wire from PB4 to GND and then switching it to VCC, the LED display should change.  Be aware that it will output 0 and 4 because it won’t display 5 until the ADC senses a voltage slightly higher than 5V.

 

3 Responses to Analog Input

  1. […] The first goal is to dim/brighten an LED based on the reading of any analog sensor. […]

  2. […] am following a tutorial which spells out how to read an analog voltage on an AVR. However the tutorial uses a […]

Leave a Reply

Set your Twitter account name in your settings to use the TwitterBar Section.