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 |
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 |
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 0 1 0 4 0 1 1 8 1 0 0 16 1 0 1 32 1 1 0 64 1 1 1 128 ADPS Bits
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 its not bit 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 the next highest Division Factor.
ATmega8:Processor Clock Speed / 100k = x(round up to next division factor)eg. Using a 8Mhz Clock8MHrz / 100k =8,000,000 / 100,000 = 80next highest = 128ATmega168/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 |
ADTS2 ADTS1 ADTS0 Trigger Source 0 0 0 Free Running 0 0 1 Analog Comparator 0 1 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
工作於CTC模式時,不管是OC1A 還是OC1B,都隻能通過設置OCR1A 來控制,如果OC1A 和OC1B都打開的話,兩個通道得到的波形是一樣的。如果你隻想用OC1B,但不設置OCR1A ,而隻設置OCR1B時,OCR1B會沒有波形輸出!
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 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 |
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:
Lets 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 continues scanning and have it trigger a an interrupt when its 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 (Single Conversion Mode)
#include
#include
// 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)); // 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, ATmega128Code:
// this code continually scans ADC0 for an analog signal, using 8Mhz processor clock (Free Running Mode)
#include
#include // needed for uint8_t
#include
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 then 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 (Free Running mode)
#include
#include // needed for uint8_t
#include
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
}
}
沒有留言:
張貼留言
注意:只有此網誌的成員可以留言。