AVR Analog to Digital Conversion
Most of the real world data is analog. There are sensors to measure light intensity, temperature, pressure, voltage etc. But these sensors give their output relative to voltage scale. When programming the MCU, we have to do some calculation on their output to get the actual value. The MCU component that is very helpful in this process is called ADC (Analog to Digital Converter). The conversion process happens in 3 steps.
- Sensor senses the real world physical parameter and convert it to eqivalent analog electrical signal.
- Analog signal is converted to digital signal using a Analog to Digital Converter (ADC).
- This digital value is then fed into the CPU and processed accordingly.
Almost all MCUs have ADC. In Atmega32A, PORTA contains the ADC pins. These 8 ADC pins are multiplexed together. Some of the features of ADC are given below.
- 10-bit Resolution
- 0.5 LSB Integral Non-Linearity
- ±2 LSB Absolute Accuracy
- 13 - 260μs Conversion Time
- Up to 15ksps at Maximum Resolution
- 8 Multiplexed Single Ended Input Channels
- 7 Differential Input Channels
- 2 Differential Input Channels with Optional Gain of 10x and 200x
- Optional Left Adjustment for ADC Result Readout
- 0 - VCC ADC Input Voltage Range
- 2.7 - VCC Differential ADC Voltage Range
- Selectable 2.56V ADC Reference Voltage
- Free Running or Single Conversion Mode
- ADC Start Conversion by Auto Triggering on Interrupt Sources
- Interrupt on ADC Conversion Complete
- Sleep Mode Noise Canceler
The ADC implemented inside the AVR MCU is of Successive Approximation type.
ADC Prescaler
The ADC of the AVR converts analog signal into digital signal at some regular interval. This interval is determined by the clock frequency. In general, the ADC operates within a frequency range of 50kHz to 200kHz. But the CPU clock frequency is much higher. So to achieve it, frequency division must take place. For example, a prescaler of 64 implies F_ADC = FCPU / 64. High prescale value gives high accuracy.
ADC Registers
-
ADMUX – ADC Multiplexer Selection Register
-
REFS1 ,REFS0 (Reference Selection Bits) - These bits are used to choose reference voltage. Following
are different configurations.
REFS1 REFS0 Voltage Reference Selection 0 0 AREF, Internal Vref turned off 0 1 AVCC with external capacitor at AREF pin 1 0 Reserved 1 1 Internal 2.56V Voltage Reference with external capacitor at AREF pin
- ADLAR (ADC Left Adjust Result) - Make it ‘1’ to Left Adjust the ADC Result.
-
MUX4:0 (Analog Channel and Gain Selection Bits) - There are 8 ADC channels (PA0...PA7). We choose a channel
by setting these bits. There are 5 bits and 32 different conditions available. But we are considering only
first 8 conditions.
MUX[4:0] Single Ended Input 00000 ADC0 00001 ADC1 00010 ADC2 00011 ADC3 00100 ADC4 00101 ADC5 00110 ADC6 00111 ADC7
-
ADCSRA – ADC Control and Status Register
- ADEN (ADC Enable) - It enables the ADC feature.
- ADSC (ADC Start Conversion) - Write this to ‘1’ before starting any conversion. This 1 is written as long as the conversion is in progress, after which it returns to zero.
- ADATE (ADC Auto Trigger Enable) - Setting it to ‘1’ enables auto-triggering of ADC. ADC is triggered automatically at every rising edge of clock pulse.
- ADIF (ADC Interrupt Flag) - When a conversion is finished, this bit is set to '1' automatically. This is used to check whether the conversion is complete or not.
- ADIE (ADC Interrupt Enable) - Set this to '1' to enable ADC Interrupts.
-
ADPS2:0 (ADC Prescaler Select Bits) - The prescaler is determined by selecting the proper combination
from the following. F_ADC = F_CPU / Prescaler
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
-
ADCL and ADCH (ADC Data Registers)
-
SFIOR (Special Function I/O Register)
-
ADTSn (ADC Auto Trigger Source) - If ADATE in ADCSRA is written to one, the value of these bits selects
which source will trigger an ADC conversion.
ADTS[2:0] Trigger Source 000 Free Running mode 001 Analog Comparator 010 External Interrupt Request 0 011 Timer/Counter0 Compare Match 100 Timer/Counter0 Overflow 101 Timer/Counter1 Compare Match B 110 Timer/Counter1 Overflow 111 Timer/Counter1 Capture Event
The result of the ADC conversion is stored here. ADC has resolution of 10-bits. It requires 10-bits to store ADC result. Two registers, ADCL and ADCH are used to store the result. You can set ADLAR=1 to make conversion result left adjusted.
Sample Code
Following example demonstrates how light intensity is measured using ADC.
Without Interrupts
#include <avr/io.h> #include <avr/delay.h> #include "lcd.h" // initialize ADC void adc_init() { // AREF = AVcc // Use external voltage as reference ADMUX = (1<<REFS0); // ADC Enable and prescaler of 128 // 16000000/128 = 125000 ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0); } // read ADC value uint16_t adc_read(uint8_t ch) { ch &= 0b00000111; // select the corresponding channel 0~7 // AND operation with 7 ADMUX = (ADMUX & 0xF8)|ch; // clears the bottom 3 bits before setting channel selection bits // start single conversion // write '1' to ADSC ADCSRA |= (1<<ADSC); // wait for conversion to complete // ADSC becomes '0' again // till then, run loop continuously while(ADCSRA & (1<<ADSC)); return (ADC); } int main(void) { // initialize LCD lcd_init(LCD_DISP_ON); // initialize ADC read adc_init(); char* read_buffer; while (1) { int read = adc_read(0); // Read ADC from channel 0 itoa(read, read_buffer, 10); // Convert read value into string lcd_puts(read_buffer); // Display on LCD _delay_ms(50); // Delay for 50ms } }
With Interrupts
To enable ADC interrupt feature, you have to do following.
- Enable ADC interrupts in ADCSRA.
- Implement ADC Conversion complete Interrupt Service Routine.
- Enable global interrupts.
#include <avr/io.h> #include <avr/interrupt.h> #include "lcd.h" //ADC Conversion Complete Interrupt Service Routine (ISR) ISR(ADC_vect) { char* read_value; int read = ADC; // Read ADC value itoa(read, read_value); // Convert read value to string lcd_puts(read_value); // Display on LCD ADCSRA |= 1 << ADSC; // Start conversion } void adc_init(){ ADCSRA = (1 << ADEN) | (1 << ADIE); // Enable ADC // Enable ADC Interrupts ADCSRA |= (1 << ADPS0) | (1 << ADPS1) || (1 << ADPS2); // Use prescaler of 128 ADMUX = (1 << REFS0); // AREF = AVcc // Use external voltage as reference } int main(void) { lcd_init(LCD_DISP_ON); // Initialize LCD adc_init(); // Initialize ADC sei(); // Enable global interrupts ADCSRA |= 1 << ADSC; // Start conversion while(1); // Wait forever }
Comments
Post a Comment