The Conversion Interrupts On ATMEGA168A


#1

Sharing an advanced tutorial about adding a second analogue input and use the ADC Conversion Complete interrupt. Look at the following circuit and here ATmega168A microcontroller is used. Seeing more details,please refer to the datasheet of ATmega168A.

The code is given below:

1 #include <avr/io.h> 2 #include <avr/interrupt.h> 3 #include "hd44780.h" 4 5 volatile uint16_t adc[]={0,0}; 6 uint8_t requested_adc_channel=0; 7 8 void ReadADC_Request(uint8_t __channel) 9 { 10 if (bit_is_set(ADCSRA,ADSC)) 11 { 12 //If another ADC read is in progress, display a message and halt processing 13 lcd_goto(0); 14 lcd_puts("ADC Error"); 15 abort(); 16 } 17 else 18 { 19 requested_adc_channel = __channel; //We'll need this when reading the value 20 21 ADMUX = (ADMUX & 0x11110000) | __channel; //Channel selection 22 ADCSRA |= _BV(ADSC); //Start conversion 23 } 24 } 25 26 void adc_init() 27 { 28 //Enable ADC, set 128 prescale and enable the interrupt 29 ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0) | _BV(ADIE); 30 sei(); //Enable interrupts 31 } 32 33 ISR(ADC_vect,ISR_NOBLOCK) 34 { 35 adc[requested_adc_channel]=ADC; // Store the resulting value to the adc results array 36 } 37 38 39 int main (void) 40 { 41 lcd_init(); 42 adc_init(); 43 44 unsigned long count=0; 45 char buffer[16]; 46 while (1) 47 { 48 ReadADC_Request(count % 2); 49 //Uncomment the line below to generate an error in ReadADC_Request 50 //ReadADC_Request(count % 2); 51 52 count++; 53 sprintf(buffer,"%u ", count); 54 lcd_goto(40); 55 lcd_puts(buffer); 56 57 //The lcd_puts command is pretty slow (>1ms) so by the time we get 58 //here the AD conversion would have already occurred and the 59 //interrupt service routine called. 60 61 sprintf(buffer,"%4u %4u", adc[1], adc[0]); 62 lcd_goto(0); 63 lcd_puts(buffer); 64 } 65 66 return(0); 67 }
When setting the prescaler to 128,it takes 1644-3200 clock cycles to read the analogue input.Whilst this is occuring we could be doing other things.
On line 48 we initiate an AD Converter read. Because we can only read one analogue input at a time we pass “count % 2” as the channel number. This evaluates to a 0 or 1 depending on the odd or even value of count. Whilst the analogue value is being read, we output count to the LCD display. The reason for this is to simulate the system being busy doing something. In a real system you would be doing something much more interesting than outputting a count.

The interrupt service routine reads in the ADC value and stores it in adc[0] or adc[1]. By the time we get to line 61, one of these values has been updated and we are ready to output it to the LCD display.

Best regards~