ATMega128 with ADC and Timer:understanding problem

Go To Last Post
9 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello there!

I wrote a program in C with the help of all the great Tutorials from AVR-Freaks. It suppose to do several things. I'll connect a switched capacitor filter to my schematic and this part needs a clock source. I decided to produce that clock with the ATMega 128 Timer. This works already. Furthermore I need to do an analog digital conversion of the output of that filter and decided to use the ADC within the ATMega128 in 10BIT mode, continuous sampling. This works as well. For test reasons I put the result of the conversion on a LCD. I get the ADC register (ADCL/ADCH) printed correct on the LCD and when I measure the voltage I get the right result if I multiply my ADC with the reference Voltage and divide this by 1024. So I put that equation into my program but I just don't get the right result on my display. Later on I want to send the result on a UART but one step at a time, right?

Another thing is that the code in main.c is not executed anymore as soon as the ADC is running. Is there a possibility to run that code as well? Here there are just a few LED's flashing for test reaons. They flash with the activated Timer but not with the activated ADC anymore. Any suggestions on this topic? I'm pretty new to interrupts so maybe I am missing something important.

EDIT:
Almost forgot:

CPU: Atmega 128 at 16MHz external Clock source
Language: C
Software: AVR Studio 4.14 using WinAVR 20080610
Fuses: see attached Image.

END of EDIT.

Here is my code:


#define VREF 2480 //measured VRef pin in mV
#define F_CPU 16000000 // important for delay.h 
#include 
#include 
#include 
#include 
#include 
#include "lcd.h"


int main (void)
{
   
   int i = 0; 
   

	// Port E als Ausgang (als Kontrolle, ob die CPU noch arbeitet)
   	DDRE = 0xFF; 
 

   	// TIMER DEFINITION 
   	//=================
   
	// Setze Port 5 als Ausgang (filter_CLK fuer LTC1067)
	DDRB |= (1 << 5); 
 
	// Timer 1 in CTC mode konfigurieren
   	TCCR1B |= (1 << WGM12); 
 
   	// Timer 1 Compare Output channel A in Toggle Modus aktivieren
	TCCR1A |= (1 << COM1A0); 
 
	// Setze CTC Vergleichswert auf 20kHz bei 16MHz AVR Takt, Mit einem Vorteiler von 8
   	OCR1A   = 49; 
 
	// Starte Timer 1 mit Fcpu/8
   	TCCR1B |= (1 << CS11); 


	// ADC DEFINITION
	//===============
	
	// Setze ADC Vorteiler auf 128 - 125KHz Sample Rate @ 16MHz 
	ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); 
	


	// Setze ADC Referenzspannung auf AVCC
	ADMUX |= (1 << REFS0); 

	// Setze ADC Referenzspannung auf Intere 2.56V Referenz mit ext. Kondensator am AREF Pin
	ADMUX |= (1 << REFS1) | (1 << REFS0);


   	// Keine MUX Werte muessen geaendert werden um ADC0 zu verwenden

	// Setze ADC in den Free-Running Modus
   	ADCSRA |= (1 << ADFR);  
   
   	// Aktiviere ADC
   	ADCSRA |= (1 << ADEN);  

	// Aktiviere ADC Interrupt
   	ADCSRA |= (1 << ADIE);  
   
   	// Start A/D Konvertierung
	ADCSRA |= (1 << ADSC);  


	//LCD Initialisierung
	lcd_init();


   // Aktiviere Globale Interrupts
   	sei();   




   
   while (1) //loop forever
   {


		//some kind of "Cyclone Eye" to show that the CPU is not stuck
		for(i = 1; i < 64; i = i*2)
		{
			PORTE = i;
			_delay_ms(70);

		}
	
		for(i = 64; i > 1; i -= i/2)
		{
			PORTE = i;
			_delay_ms(70);
		}
		// End of Cyclone Eye

   }
} // Ende von main



ISR(ADC_vect)
{
	
	char temp [9];

	uint16_t num;
	uint32_t voltage;


	// erst ADCL auslesen, dann ADCH , WICHTIG! Reihenfolge beachten...
   	
	// bei ATMega 128 kann auch einfach ADC ausgelesen werden, 16BIT breit
	//ADC_Ergebnis |= ADCL;
   	//ADC_Ergebnis |= (ADCH << 8);
	
	num = ADC;
   
	voltage = 	(num * VREF) / 1024;
	
	itoa (num,temp,10);
	set_cursor(0,1);
	lcd_data('A');
	lcd_data('D');
	lcd_data('C');
	lcd_data(':');
	lcd_data(91);
	lcd_string(temp);
	lcd_data(93);
	lcd_data(32);

	itoa (voltage,temp,10);
	set_cursor(0,2);
	lcd_data(91);
	lcd_string(temp);
	lcd_data(32);
	lcd_data('m');
	lcd_data('V');
	lcd_data(32);
	lcd_data(93);
	lcd_data(32);
	_delay_ms(50);

} 

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Nicht putting ein code im das ISR!

Sorry about the poor German! It is not wise to do your LCD stuff in the ADC isr - it takes too long, thus the AVR is always processing the ISR code and nothing else. Keep the ISR code simple and fast, so move your LCD stuff into the main.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:
Nicht putting ein code im das ISR!

Sorry about the poor German! It is not wise to do your LCD stuff in the ADC isr - it takes too long, thus the AVR is always processing the ISR code and nothing else. Keep the ISR code simple and fast, so move your LCD stuff into the main.

Sorry for my poor English ;)

So you mean it is better to use global variables instead to get the data to the main routine? I'll take care of this. But the first part her:

...

*SNIP*
 itoa (num,temp,10);
   set_cursor(0,1);
   lcd_data('A');
   lcd_data('D');
   lcd_data('C');
   lcd_data(':');
   lcd_data(91);
   lcd_string(temp);
   lcd_data(93);
   lcd_data(32); 
...
*SNIP*

does already work. My Display shows "ADC: [XXX]" where XXX is a number that represents the result of the A/D conversion. But the calculation of the voltage does not work at all.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

(num * VREF) can not be computed in 16 bits (which is the size of int and what will be used for any expression that does not include wider types).

  voltage =    ((uint32_t)num * VREF) / 1024; 

Takes care of that problem.
/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lajon:

Yes it fixed the problem, thank you! Next step will be to use volatile variables and take that stuff out of the ISR routine.

Does anybody know, why the program in int main (void) is not executed as soon as the ADC runs?

I appreciate all your help.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Does anybody know, why the program in int main (void) is not executed as soon as the ADC runs?
Yes, at least Kartman and I know:
Quote:
AVR is always processing the ISR code and nothing else
So take that stuff out of the ISR and you should be ok.
/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I can add that the main code is actually executing but only one instruction at a time in between the ADC interrutps. Due to the long time spent in the ISR the next ADC conversion will be ready when the ISR exists. One instruction is however always executed but your delay loops in main will be really slow.
/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Finally I understand what the two of you mean. Gosh, sometimes "it" takes a little longer! :) Thank you for your help, I got some code to re-write now ;)

Josef

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is also no need to update the LCD every conversion, you can't possibly read it that fast.

Regards,
Steve A.

The Board helps those that help themselves.