ANALOG INPUTS (ANALOG TO DIGITAL CONVERTER)
INTRODUCTION:
Digital inputs are great, but sometimes devices output an analog signal. An analog signal is a signal of varying voltage levels. Temperature sensors, light sensors, potentiometers and flex sensors are just some of the devices that output analog voltages that the AVR could read. Analog sensors are generally a resistor that changes its value when a given condition is met (light level, temperature, flexibility, stretching ... etc.).
This is by far my favorite part of the AVR because you could find so many uses for it.
HARDWARE:
Figure 1: ATmega8 - ADC Pins
Different AVRs have a different number of analog Inputs the ATmega8 and the ATmega168/328 has 6 different device pins but only 1 ADC (Analog to Digital Converter) converter. The 6 input pins are connected to a multiplexer which basically allows a single input to be connected to the ADC in order to get a reading. So we have to be smart about how to use our ADC and how to prioritize our readings.
The data sheet on the ADC can be a bit overwhelming, it talks about timing, noise cancellation, filters, uses inductors and is 10 bit ... 10 bit! I mean come on. The truth is that it is not as complicated as it may seem. Most of us hobbyists do not need a NASA level of precision. Lets do some quick math, 90% of the time we will use 5V, the ADC is a 10bit ADC so this means it can do 1024 bits of resolution. 5V / 1024 ~= 0.005V. We will never need that kind of accuracy. All we really need is a small capacitor or 2 and we are good to go.
Figure 2: Recommended ADC Power Hookup
If you want to be fancy you can add a 10uH inductor for extra filtering between the AVCC pin and Vcc (5V on the schematic). Most hobbyist's however, don't bother. Lastly, most hobbyist's don't bother with C2 and simply leave AREF disconnected. I however, have had a lot of luck with a nice stable ADC using the schematic above.
Figure 3: 3-pin Analog Sensor Hookup
In order for an analog sensor to work, it must create some sort of voltage divider. 3 pin type sensors that have a GND, VCC and Output connection have a voltage divider build in and can be connected to an ADC pin directly as per Figure 3.
Figure 4: 2-pin Analog Sensor Hookup
2 pin type sensors on the other hand work by changing their resistance and will not work without the use of a resistor (because you need a minimum of 2 resistors to build a voltage divider network. You could use any value for R1 but you have to make sure that you do not cause a short circuit and/or that it doesn't snuff out (for a lack of a better term) your sensor.
2 Pin Resistive Type Sensor Calculations:
Ok this section has a bit of math, so if your not interested here is a shorthand rule. Make R1 the same value as the highest value of the sensor. This gives you the best power to sensor voltage range ratio. The rest of this subsection just proves that statement.
I use 2 calculations to make sure the Resistor value will work out well. Max current draw and the Difference in voltage between sensor min and max.
If you think back to V=IR, dropping the resistance will cause your current to rise if your voltage stays the same. So if we add the value of R1 and the lowest value of our sensor together we will find the highest current draw for our sensor. We also have to figure out the difference in voltage between the highest and lowest sensor resistance value to figure out your window, the bigger the window the more data the higher your resolution.
So lets use the Mini Photocell from Sparkfunas our sensor. It ranges form 1k - 10k Ohms:
Using 1k Resistor | Using 10k Resistor | Using 1M Resistor |
---|---|---|
V = I * R I = V / R I = V / (R1 + R_sensorlow) I = 5 / (1k + 1k) I = 5 / 2k I = 2.5 mA |
V = I * R I = V / R I = V / (R1 + R_sensorlow) I = 5 / (10k + 1k) I = 5 / 11k I = 0.45 mA |
V = I * R I = V / R I = V / (R1 + R_sensorlow) I = 5 / (1M + 1k) I = 5 / 1001k I = 4.5 mA |
Notice how we use 5 times less power if we use the 10k resistor over the 1k, and the 1 Ohm uses 10 times more power then the 10k. Another thing to note, is that if your using 1/8th watt resistors you will be limited to 25mA @ 5V so knowing how much current your sensors draw isn't a stupid idea.
Now for the resolution calculation. We do some hard math or we could just create 2 voltage divider networks, 1 using the min value of the sensor and the other using the max values, and then subtract the difference to get our working voltage range.
1k Resistor Min Sens R | 1k Resistor Max Sens R | Conclusion |
---|---|---|
Vout = R2 / (R1 + R2) * Vcc Vout = 1k / (1k+1k) *5V Vout = 1k / 2k * 5V Vout = 2.5V |
Vout = R2 / (R1 + R2) * Vcc Vout = 10k / (1k + 10k) * 5V Vout = 10k / 11k * 5V Vout = 4.5V |
Using a 1k Resistor the Photocell has a 2V difference between Min and Max. Min 2.5V sensed Max 4.5V sensed |
10k Resistor Min Sens R | 10k Resistor Max Sens R | Conclusion |
---|---|---|
Vout = R2 / (R1 + R2) * Vcc Vout = 1k / (10k+1k) * 5V Vout = 1k / 11k * 5V Vout = 0.45V |
Vout = R2 / (R1 + R2) * Vcc Vout = 10k / (10k + 10k) * 5V Vout = 10k / 20k * 5V Vout = 2.5V |
Using a 10k Resistor the Photocell has a 2V difference between Min and Max. Min 0.45V sensed Max 2.5V sensed |
1M Resistor Min Sens R | 1M Resistor Max Sens R | Conclusion |
---|---|---|
Vout = R2 / (R1 + R2) * Vcc Vout = 1k / (1M+1k) * 5V Vout = 1k / 1001k * 5V Vout = 0.0049V |
Vout = R2 / (R1 + R2) * Vcc Vout = 10k / (1M + 10k) * 5V Vout = 10k / 1010k * 5V Vout = 0.049 |
Using a 1M Resistor the Photocell has a 0.04V difference between Min and Max. So our resolution has really shrunk |
I was always thought in school to have a conclusion for complicated or long example, so here it is, If your resistor is too low, you risk an overload and use more power, if its too high you will lose resolution. Using the min value of your resistive sensor will come up to the same resolution as using your max value, so use the max value (since higher R means less I).
Signal Noise:
Our air filled with Electra-Magnetic noise; Radio waves, wireless networks power lines all emit electro-magnetic noise. This noise can be picked up by the ADC because the traces on the circuit board and any wires between the board and sensor will act like an antenna and pickup this noise. The longer the antenna the greater the noise. So when your designing your circuit board make sure to keep the traces from the ADC as short as possible to reduce noise. But what if your sensor needs to be far away from your board? Well you could use some fancy programming to try to "guesstimate" the true signal of you could use a shielded cable.
Shielded cables have a ... well shield around the conductors that will keep the noise from being felt by the conductors. The shield is a metal braid (or foil) material that surrounds the wires. Any magnetic noise that comes from the air will be captured by the shield.
Figure 5: Shielded Cable
In order to make this work, you have to connect the shield to the ground BUT only one 1 end. Connecting both ends of a shield to ground could cause ground loop faults. If you ever hooked up a high end stereo system and have heard hum coming from the speakers, you have heard a ground fault loop in action.
Using the ADC For Extra Buttons:
This is a really cool trick, Say you want to build something, but you don't have enough Digital Inputs. If you build a voltage divider network and use the buttons to short out the resistors you could read use the ADC to figure out what button was pressed. The downsides to doing this is that it takes a lot more time to read an ADC than it does to read a Digital Input and that you could not read multiple keys being pressed at the same time.
Figure 6: Multiple Buttons On the ADC
THEORY OF OPERATION:
7 bit | 6 bit | 5 bit | 4 bit | 3 bit | 2 bit | 1 bit | 0 bit | |
---|---|---|---|---|---|---|---|---|
ADMUX | REFS1 | REFS0 | ADLAR | - | MUX3 | MUX2 | MUX1 | MUX0 |
ADC Multiplexer Selection Register
REFS1 | REFS0 | Voltage Reference Selection |
---|---|---|
0 | 0 | AREF, Internal Vref turned off |
0 | 1 | AVcc with external capacitor on AREF pin |
1 | 0 | Reserved |
1 | 1 | Internal 1.1V (ATmega168/328) or 2.56V on (ATmega8) |
REF Bits
MUX 3...0 | Single Ended Input |
---|---|
0000 | ADC0 |
0001 | ADC1 |
0010 | ADC2 |
0011 | ADC3 |
0100 | ADC4 |
0101 | ADC5 |
0110 | ADC6 |
0111 | ADC7 |
1000 | Temp Sensor (ATmega168/328 only) |
1001 - 1101 | (reserved) |
1110 | 1.1V (ATmega168/328) 1.30V (ATmega8) |
1111 | 0V (GND) |
MUX Bits
If ADLAR in the ADMUX Register is LOW (0)7 bit | 6 bit | 5 bit | 4 bit | 3 bit | 2 bit | 1 bit | 0 bit | |
---|---|---|---|---|---|---|---|---|
ADCH | - | - | - | - | - | - | ADC9 | ADC8 |
ADCL | ADC7 | ADC6 | ADC5 | ADC4 | ADC3 | ADC2 | ADC1 | ADC0 |
ADC Data Register
ADLAR in the ADMUX Register is HIGH (1)7 bit | 6 bit | 5 bit | 4 bit | 3 bit | 2 bit | 1 bit | 0 bit | |
---|---|---|---|---|---|---|---|---|
ADCH | ADC9 | ADC8 | ADC7 | ADC6 | ADC5 | ADC4 | ADC3 | ADC2 |
ADCL | ADC1 | ADC0 | - | - | - | - | - | - |
ADC Data Register
7 bit | 6 bit | 5 bit | 4 bit | 3 bit | 2 bit | 1 bit | 0 bit | |
---|---|---|---|---|---|---|---|---|
ADCSRA | ADEN | ADSC | ADFR* | ADIF | ADIE | ADPS2 | ADPS1 | ADPS0 |
ADC Control and Status Register A
ADPS2 | ADPS1 | ADPS0 | Division Factor |
---|---|---|---|
0 | 0 | 0 | 2 * |
0 | 0 | 1 | 2 |
1 | 0 | 0 | 4 |
0 | 1 | 1 | 8 |
1 | 0 | 0 | 16 |
1 | 0 | 1 | 32 |
1 | 1 | 0 | 64 |
1 | 1 | 1 | 128 |
ADPS Bits
* not a typo, check datasheet if you don't believe me :P
Reference Voltage Sources:
The first thing we need to do is select a reference source (this is the high voltage) using the REFS1 and REFS0 bits in the ADMUX register. We could chose to use the voltage on the AREF pin, this voltage has to be a minimum of 2.0V on the ATmega8 or 1.0V on the ATmega168/328 and a max of VCC. So when would you use this? Say your using a 5V to power your AVR. And you're sensors are on a 3.3V power supply. If you want to use the full range you could hook up the 2nd power supply to the AREF pin. How do I know that the min/max on the AREF pin? It's buried in the datasheet under the "ADC Characteristics" table in the "Electrical Characteristics" section of the Datasheet. The other References are AVcc or an internal test voltage. Most of the time you will find yourself using the voltage on AVcc for two reasons. First of all most analog devices are low power consumption devices (so you really don't need to run them off of another power supply for any reason) and secondly because using a reference on VREF prevents us from using the interior sources.
ADC Multiplexer Source:
Because we only have a single ADC but 6 ADC pins to choose from we need to feed the signal into the ADC using the built-in multiplexer. To do this by setting the MUXn bits in the ADMUX register. The neat thing is that if you change these values while the conversion is running, the conversion will finish before the change takes (so if you ever find yourself having problems with bad data you might be switching before the ADC finishes).
ADC Data Register Control:
This really got me when I first started playing with micro controllers. Here is the deal. The ADC has 10bit of resolution. However, the AVR is an 8 bit micro controller, so we are always working with 8 bit registers. So in order to get 10bit data we have to split it into 2 registers ADCH and ADCL. We can use a 16 bit register or we could use an 8 bit register and dump the bottom 2 bits (which are the least significant bits anyways). If you choose to use the full 10 bits resolution you should leave ADLAR LOW(0) and make sure you read the ADCL register first because reading the ADCH causes to ADC to update. If you only want to use 8 bit resolution you will want to set the ADLAR to HIGH(1), this way you only have to read ADCH. I guess I could have said, the ADLAR bit lets you control how the AVR is going to output the data. Set ADLAR HIGH(1) if you only want to 8 bit of resolution.
ADC Conversion Timing:
The next thing requires a bit of math (and for once it's not a bit of math). In order to get the best conversion results the ADC clock needs to be between 50k - 100k on the ATmega8 and 50K-200k on the ATmega168/328. So take your processor clock frequency divide by 100k on the ATmega8 (or 200k on the ATmega168/328). When you get the result move the next highest Division Factor.
ATmega8:
Processor Clock Speed / 100k = x (round up to next division factor)
eg. Using a 8Mhz Clock
8MHrz / 100k =
8,000,000 / 100,000 = 80
next highest = 128
ATmega168/328:
Processor Clock Speed / 200k = x (round up to next division factor)
eg. Using a 1Mhz Clock
1MHrz / 200k =
1,000,000 / 200,000 = 5
next highest = 8
Modes of Operation:
Now we need to figure out what kind of mode we want to run our ADC in. We can choose to do a single conversion or free running mode. In single mode we do 1 conversion and we are done. In free running mode we run continually, when one conversion finishes we automatically start the next. The default is single conversion mode so we don't have to do a thing. To use Free running mode set the ADFR bit HIGH (1) in the ADCSRA register on the ATmega8 or set the ADATE bit in the ADCSRA register on the ATmega168/328 and leave the ADSTn bits LOW (0) in the ADCSRB register (more on this register later on).
There are advantages and disadvantages of both modes. In single scan mode, we tie up the processor while the ADC is doing its conversion, but its simple to use with multiple Analog Inputs because we trigger one at a time. In free running mode, we have to have a bit of extra code to figure out what Analog input just finished and have to make sure we switch the MUX at the right time, it's not too bad, but it is not as simple as single scan mode.
ADC Interrupt:
Whatever mode you choose to use can be used with, or without interrupts, which will trigger the ADC vector.
Special Modes of Operation for the ATmega168/328:
7 bit | 6 bit | 5 bit | 4 bit | 3 bit | 2 bit | 1 bit | 0 bit | |
---|---|---|---|---|---|---|---|---|
ADCSRB | - | ACME | - | - | - | ADTS2 | ADTS1 | ADTS0 |
ADC Control and Status Register B
ADTS2 | ADTS1 | ADTS0 | Trigger Source |
---|---|---|---|
0 | 0 | 0 | Free Running |
0 | 0 | 1 | Analog Comparator |
1 | 0 | 0 | External Interrupt Request 0 |
0 | 1 | 1 | Timer/Counter0 Compare Match A |
1 | 0 | 0 | Timer/Counter0 Overflow |
1 | 0 | 1 | Timer/Counter1 Compare Match B |
1 | 1 | 0 | Timer/Counter1 Overflow |
1 | 1 | 1 | Timer/Counter1 Capture Event |
ADTS Bits
Isn't this cool, on the ATmega168/328 we can use the Timer/Counters and the Analog Comparator as a trigger source to start the ADC conversions. Man does this ever cut down on the coding. Set the ADATE bit in the ADSCRA register to HIGH (1) and set the ADTSn bits in the ADCSRB register the values in the Table, and we are good to go.
7 bit | 6 bit | 5 bit | 4 bit | 3 bit | 2 bit | 1 bit | 0 bit | |
---|---|---|---|---|---|---|---|---|
DIDR0 | - | - | ADS5D | ADC4D | ADC3D | ADC2D | ADC1D | ADC0D |
Digital Input Disable Register 0
This register is designed to reduce power consumption of the AVR by giving you the ability to turn of the Digital Input circuitry on the 6 ADC pins while your using the ADC. If power consumption is a problem set the bits to HIGH (1) if not leave them at LOW (0).
SOFTWARE:
ADC on The ATmega8:
Let's write a bit of code. The two most common ways you will use the ADC is to do a single conversion at a given time in the program and/or to do continue scanning and have it trigger a interrupt when it's done. I'll demonstrate both for each AVR type.
ATmega8 & ATmega168/328 Code:
// this code scans ADC1 for an analog signal upon request, using 8Mhz processor clock #include <avr/io.h> #include <stdint.h> // needed for uint8_t int ADCsingleREAD(uint8_t adctouse) { int ADCval; ADMUX = adctouse; // use #1 ADC ADMUX |= (1 << REFS0); // use AVcc as the reference ADMUX &= ~(1 << ADLAR); // clear for 10 bit resolution ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // 128 prescale for 8Mhz ADCSRA |= (1 << ADEN); // Enable the ADC ADCSRA |= (1 << ADSC); // Start the ADC conversion while(ADCSRA & (1 << ADSC)); // Thanks T, this line waits for the ADC to finish ADCval = ADCL; ADCval = (ADCH << 8) + ADCval; // ADCH is read so ADC can be updated again return ADCval; } int main(void) { int ADCvalue; while (1) { ADCvalue = ADCsingleREAD(1); // ADCvalue now contains an 10bit ADC read } }
ATmega8 Code:
// this code continually scans ADC0 for an analog signal, using 8Mhz processor clock #include <avr/io.h> #include <stdint.h> // needed for uint8_t #include <avr/interrupt.h> volatile uint8_t ADCvalue; // Global variable, set to volatile if used with ISR int main(void) { ADMUX = 0; // use ADC0 ADMUX |= (1 << REFS0); // use AVcc as the reference ADMUX |= (1 << ADLAR); // Right adjust for 8 bit resolution ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // 128 prescale for 8Mhz ADCSRA |= (1 << ADFR); // Set free running mode ADCSRA |= (1 << ADEN); // Enable the ADC ADCSRA |= (1 << ADIE); // Enable Interrupts ADCSRA |= (1 << ADSC); // Start the ADC conversion while (1) { // main loop } } ISR(ADC_vect) { ADCvalue = ADCH; // only need to read the high value for 8 bit }
ADC on the ATmega168/328:
The ATmega168/328 code isn't much different from the ATmega8 code. If you remember the only difference is that we have a ADATE (ADC Auto Trigger Enable) bit in the ADCSRA register that we use in order that we could then use to set different trigger types (such as Free running mode). For single conversion the code is exactly the same so please read above.
ATmega168/328 Code:
// this code continually scans ADC0 for an analog signal, using 16Mhz processor clock #include <avr/io.h> #include <stdint.h> // needed for uint8_t #include <avr/interrupt.h> volatile uint8_t ADCvalue; // Global variable, set to volatile if used with ISR int main(void) { ADMUX = 0; // use ADC0 ADMUX |= (1 << REFS0); // use AVcc as the reference ADMUX |= (1 << ADLAR); // Right adjust for 8 bit resolution ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // 128 prescale for 16Mhz ADCSRA |= (1 << ADATE); // Set ADC Auto Trigger Enable ADCSRB = 0; // 0 for free running mode ADCSRA |= (1 << ADEN); // Enable the ADC ADCSRA |= (1 << ADIE); // Enable Interrupts ADCSRA |= (1 << ADSC); // Start the ADC conversion sei(); // Thanks N, forgot this the first time =P while (1) { // main loop } } ISR(ADC_vect) { ADCvalue = ADCH; // only need to read the high value for 8 bit // REMEMBER: once ADCH is read the ADC will update // if you need the value of ADCH in multiple spots, read it into a register // and use the register and not the ADCH }
Changing MUX While In Free Running Mode With Interrupts:
The last thing I want to cover is what if you have sensors on ADC0, ADC1 and ADC2 and you want to run in free running mode. The problem is that you only have 1 ADC and only 1 Interrupt vector. So when a interrupt triggers you need to know what sensor is being read AND you need to switch to the next sensor. This could all be done in the ISR routine, so I will just post below. And don't forget to make the Sensor Data Registers volatile.
ATmega8 & ATmega168/328 Code:
ISR(ADC_vect) { uint8_t tmp; // temp register for storage of misc data tmp = ADMUX; // read the value of ADMUX register tmp &= 0x0F; // AND the first 4 bits (value of ADC pin being used) ADCvalue = ADCH; // read the sensor value if (tmp == 0) { // put ADCvalue into whatever register you use for ADC0 sensor ADMUX++; // add 1 to ADMUX to go to the next sensor } else if (tmp == 1) { // put ADCvalue into whatever register you use for ADC1 sensor ADMUX++; // add 1 to ADMUX to go to the next sensor } else if (tmp == 2) // put ADCvalue into whatever register you use for ADC2 sensor ADMUX &= 0xF8; // clear the last 4 bits to reset the mux to ADC0 } }
That's it, its over man, that's all I know about ADCs. It's bedtime.
Cheers
Q