ATmega8 ADC Example
This is a very simple example you probably won't see in any serious design, but it's straight forward and not hard to troubleshoot. We want to read the voltage output of a pot and show the most significant 8 bits of the conversion result on port D (the ADC has a resolution of 10 bits!).
The mega8 ADC offers 2 modes of operation, the Single Conversion Mode and the Free-Running Mode. Both have advantages and disadvantages and I will try to discuss them both well enough now.
In Free-Running Mode, a new conversion is started when a conversion finishes. The new conversion is done for the ADC channel set in ADMUX. If a new channel has to be converted, it's number has to be set *before* the new conversion starts. If an ISR is used to process the ADC results and update ADMUX, care has to be taken, as a change of ADMUX just after the conversion start can have unpredictable results (read pages 196/197 of the mega8 datasheet for more). As we only want to work with one ADC channel, this is no problem for us. Only the very first conversion has to be started by setting the ADSC bit!
In Single Conversion Mode, every conversion has to be started by setting the ADSC (ADC Start Conversion) bit in ADCSR. The advantage of this is that a new channel can be selected before the conversion is started without watching out for timing problems or unpredictable results. If the ADC Conversion Complete ISR is used for this, the loss in speed is quite small.
In this case we can use the Free-Running Mode - we don't need to change ADMUX (just one channel is used).
The recommended ADC clock range is 50 kHz to 200 kHz. If a faster ADC clock is used, the resolution will go down. The ADC clock is prescaled from the main clock by a seperate ADC prescaler. The division factor is selected with the ADPS2..0 bits in ADCSR (see pages 203/204 of the datasheet). At 4 MHz, appropriate values are 32 (resulting ADC clock is 125 kHz) and 64 (resulting ADC clock is 62.5 kHz). We'll use 32.
The mega8 interrupt vector for the ADC Conversion Complete ISR doesn't have to do much. When it is called, a fresh ADC result is available in ADCL and ADCH. It is converted to 8 bits (the two LSBs aren't used) and written to PortD. On the STK500 the LEDs are active low, so inverting the result before writing it to PortD is a good idea.
The ISR is, of course, only called if the ADIE (ADC Interrupt Enble) bit in ADCSR is set and if global interrupts are enabled.