Sida 1 av 2

Problem med Interrupt AtMega168 @ 16MHz

Postat: 10 april 2010, 15:30:32
av myrek
Hej allihop. jag håller på med ett litet projekt där jag spelar musik med stegmotorer. Jag har dock ett problem med min kod som jag hoppas någon kan hjälpa mig med.
Lite bakgrund. Jag använder mig av en AtMega168-20 samt en extern oscillator med en klockfrekvens på 16Mhz.

Programmet fungerar som så att jag kör en interrupt med extremt hög frekvens (prescaler 1, 8bitar. 1/16MHZ * 256)
Timern har en frekvens på 16uS. Inuti denna interrupt har jag en räknare som räknar antalet interrupts som har utförts och kontrollerar hela tiden interrupt numret mot en array som innehåller de toner jag vill spela.

Tonerna är alltså lagrade i en array där deras frekvens (tonen som skall spelar) står i direkt relation till antalet interrupts.

Om interrupt talet är korrekt så stegar jag motorn. Efter varje steg kontrollerar jag ytterligare en array som innehåller den paus som skall vara efter varje spelad ton. även denna är hårdkodad.

Mitt problem är följande. Låten spelar som den ska vissa stunder för att sedan stanna av någon sekund och fortsätta. Jag trodde detta berodde på att jag hade för hög interruptfrekvens men precis samma sak händer när jag ökar prescalern.
Jag provade också att byta uC till AtMega8515 men precis samma fel kvarstår. Jag körde lite simulering i avrstudio då jag misstänkt att interrupten kanske tog för god tid på sig och avbröt sig själv eller något liknande (om det är möjligt) men hela ISR:en han slutföras i god tid innan nästa.

Jag har förmodligen något fel i min kod. hoppas någon kan ta sig tid att kolla igenom lite. Jag vet att koden är något ostrukturerad... och att variabelnamnen är kassa men jag har experimenterat så mycket med detta fram och tillbaka att det har blivit så :)

när jag kollar med oscilloskopet ser jag att alla frekvenser är korrekta men plötsligt bara dör allt och pinnen ställer sig antingen låg eller hög för att sedan fortsätta efter några sekunder. Koden rullar på som vanligt eftersom jag plötsligt befinner mig på "rätt" ställe i musikstycket.

Re: Problem med Interrupt AtMega168 @ 16MHz

Postat: 10 april 2010, 15:59:02
av sodjan
Jag försökte läsa ditt inlägg men det var praktiskt taget omöjligt p.g.a
formatteringen av din kod (långa rader). V.v fixa det så får vi se...

Re: Problem med Interrupt AtMega168 @ 16MHz

Postat: 10 april 2010, 16:08:22
av tlvb
Är inte säker på att detta är felet, men:
1) Kolla upp hur/när volatile behövs i interrupt/variabelsammanhang, t.ex. Part 5: Things you Need to Know: Parentes 2
2) Om du använder progmem så måste ändå värdena laddas in i ramminnet innan användning vilket kan ta tid (och i det här fallet kanske för mycket rammine), (det var kommentarmarkerat här men kan vara värt att nämna)

Re: Problem med Interrupt AtMega168 @ 16MHz

Postat: 10 april 2010, 16:24:47
av jesper
Ser inte något fel sådär rakt av, men om du får en såpass lång paus, låter det som att något index eller räknare får overflow och du inte kommer igång igen innan den wrappat runt.

Re: Problem med Interrupt AtMega168 @ 16MHz

Postat: 10 april 2010, 16:27:17
av sodjan
Symptomen är like likt när man har WDT påslaget av misstag.

Re: Problem med Interrupt AtMega168 @ 16MHz

Postat: 10 april 2010, 17:15:56
av Swech
Skulle det vara Watchdog så borde väl musiken starta om, inte fortsätta där den borde vara?

Swech

Re: Problem med Interrupt AtMega168 @ 16MHz

Postat: 10 april 2010, 17:16:38
av sodjan
Möjligt, men som sagt, första inlägget var lite svårläst så jag
kollade inte alla detaljer...

Re: Problem med Interrupt AtMega168 @ 16MHz

Postat: 10 april 2010, 18:36:02
av jesse
om du först har if (pause == 1) så behöver du inte senare skriva else if (pause == 0) väl? Det räcker med else ... För det finns väl inte fler alternativ? men det var inte själva problemet.

kommer pausen ofta på samma ställe i melodin eller på slumpartade ställen?
kommer pausen alltid i ett skifte från en låg ton till en högre ton?
Är pausen på 1.048 sekunder?

(jag skriver lite frågor eftersom jag inte orkar ta reda på allt själv);

jag tror att det är så att när du byter ton så hänger inte "step" med.
Har jag rätt att du har en paus mellan varje ton?
vad händer med variabeln "step" under en paus?

Du kanske borde nollställa variabeln "step" när en ton ska börja spelas, risken är att när du i programmet sätter paus = 1 så råkar step ha ett värde som är högre än maxvärdet på nästa ton.

Du fixar det antingen genom att nollställa step vid paus , eller att du i interrupten inte skriver if(step == track1) utan iställetif(step >= track1)

orsaken till pauserna är att när en ton med nr 239 tar slut (dvs. pause sätts till 1) och nästa ton är t.ex. 150 , så kan det råka slumpa sig så att variablen step råkar ha värdet 220 när detta sker. Vad händer då när den ska börja spela tonen 150? Jo den jämför step med tonen: (220 == 150) = false - och ökar sedan step till 221.... detta pågår i 1.048 sekunder - tills step räknat till 65535 och sedan slårt över till 0.

Jag har inte läst din kod, bara snabbt ögnat igenom den, så detta är bara en kvalificerad gissning.

Annars var din kod grymt smart. :tumupp:

Re: Problem med Interrupt AtMega168 @ 16MHz

Postat: 10 april 2010, 18:57:19
av myrek
Jag har städat lite i koden. hoppas det blir lite lättare att se vad som pågår nu.

Kod: Markera allt


#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>


volatile unsigned int step;  
volatile unsigned int duration;	
volatile unsigned int i=0;
volatile unsigned int p=0;

volatile unsigned int pause=0;
volatile unsigned long int PauseCounter=0;


//This array contains the number of interrupts needed to produce the correct note at interrupt frequency 16Mhz
volatile unsigned char track1[299] = {
47,47,47,60,47,40,60,80,95,71,63,67,71,80,47,40,36,45,40,47,60,53,63,60,80,95,71,63,67,71,80,47,40,36,45,40,
47,60,53,63,40,42,45,50,47,75,71,60,71,60,53,40,42,45,50,47,30,30,30,40,42,45,50,47,75,71,60,71,60,53,50,53,
60,40,42,45,50,47,75,71,60,71,60,53,40,42,45,50,47,30,30,30,40,42,45,50,47,75,71,60,71,60,53,50,53,60,60,60,
60,60,53,47,60,71,80,60,60,60,60,53,47,60,60,60,60,53,47,60,71,80,47,47,47,60,47,40,60,80,95,71,63,67,71,80,
47,40,36,45,40,47,60,53,63,60,80,95,71,63,67,71,80,47,40,36,45,40,47,60,53,63,47,60,80,75,71,45,45,71,63,36,
36,36,40,45,47,60,71,80,47,60,80,75,71,45,45,71,63,45,45,45,47,53,60,47,60,80,75,71,45,45,71,63,36,36,36,40,
45,47,60,71,80,47,60,80,75,71,45,45,71,63,45,45,45,47,53,60,60,60,60,60,53,47,60,71,80,60,60,60,60,53,47,60,
60,60,60,53,47,60,71,80,47,47,47,60,47,40,47,60,80,75,71,45,45,71,63,36,36,36,40,45,47,60,71,80,47,60,80,75,
71,45,45,71,63,45,45,45,47,53,60};


// This array contains all the delays between the notes
volatile unsigned char pause1[298] = {
8,32,32,8,32,176,56,56,56,32,32,8,32,16,16,16,32,8,32,32,8,8,56,56,56,56,32,32,8,32,16,16,16,32,8,32,32,8,8,
104,8,8,8,32,32,8,8,32,8,8,56,8,8,8,32,32,32,8,128,8,8,8,32,32,8,8,32,8,8,56,56,56,224,8,8,8,32,32,8,8,32,8,
8,56,8,8,8,32,32,32,8,128,8,8,8,32,32,8,8,32,8,8,56,56,56,176,8,32,32,8,32,8,32,8,80,8,32,32,8,	8,200,8,32,32,
8,32,8,32,8,80,8,32,32,8,32,176,56,56,56,32,32,8,32,16,16,16,32,8,32,32,8,8,56,56,56,56,32,32,8,32,16,16,16,32,
8,32,32,8,8,56,8,32,56,32,8,32,8,80,16,16,16,16,16,16,8,32,8,80,8,32,56,32,8,32,8,80,8,32,8,16,16,16,176,8,32,
56,32,8,32,8,80,16,16,16,16,16,16,8,32,8,80,8,32,56,32,8,32,8,80,8,32,8,16,16,16,176,8,32,32,8,32,8,32,8,80,
8,32,32,8,8,200,8,32,32,8,32,8,32,8,80,8,32,32,8,32,176,8,32,56,32,8,32,8,80,16,16,16,16,16,16,8,32,8,80,8,32
,56,32,8,32,8,80,8,32,8,16,16,16};

int main(void)
{

	TCCR0B = 1;		// Prescler = 1 --> counter frequency 62,5kHz (8bit counter)
	TIMSK0 = 1;		// Enable timer/counter0 overflow interrupt
	
	DDRD = 0xFF;	// All pins outputs
	DDRB = 0xFF;
	
	sei();			//enable interrupts
	
	while(1)
	{
					//infinite loop to keep interrupts running.
	}	

}


//interrupt overflow vector. This interrupt has a frequency of 16 MHz / 256 = 62500 Hz.
//The period is 16uS. (prescaler = 1, 8 bit register)

ISR(TIMER0_OVF_vect)			
{


	if (pause == 1)							//if pause=1 pause between notes
	{
		PauseCounter++;
		
		if(PauseCounter==pause1[p]*100)	//the end of pause has been reached.
		{
			pause=0;						//Clear pause flag
			PauseCounter=0;
			p++;							//jump to next pause element
			
			if(p==297)
			{
				p=0;						//if end of array has been reached. restart.
			}
		}
	
	}
	else if(pause == 0)	//if pause=0 we should play notes
	{
	
		//This loop toggles the pin to the correct frequency. the track array contains the number of 
		//interrupts to be played. This is based on an interrupt freq of 62,5 kHz.
		
		if(step == track1[i])
		{
			PORTD ^= (1<<0);
			step=0;				//clear counter.
		}
		
		step++;					//increase interrupt counter
		
		duration++;				//Increase duration counter.
		
		if(duration == 35625)	//Every note should be played for 0,57s --> 35625 * 0,16us = 0.57s 
		{
			i++;				//Go to the next tone
			duration = 0;		//Cclear duration counter
			pause=1;			//set the pause flag to pause between notes
			
			if (i==298)
			{
				i=0; 			//if end of track has been reached. restart.
			}
		}

	}	
	
}



Re: Problem med Interrupt AtMega168 @ 16MHz

Postat: 10 april 2010, 19:06:41
av sodjan
Men har du gjort något åt *radlängderna" ??

Kod: Markera allt


#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>


volatile unsigned int step; 
volatile unsigned int duration;   
volatile unsigned int i=0;
volatile unsigned int p=0;

volatile unsigned int pause=0;
volatile unsigned long int PauseCounter=0;


// This array contains the number of interrupts needed to produce the
// correct note at interrupt frequency 16Mhz
volatile unsigned char track1[299] = {
47,47,47,60,47,40,60,80,95,71,63,67,71,80,47,40,36,45,40,47,60,53,63,60,
80,95,71,63,67,71,80,47,40,36,45,40,47,60,53,63,40,42,45,50,47,75,71,60,
71,60,53,40,42,45,50,47,30,30,30,40,42,45,50,47,75,71,60,71,60,53,50,53,
60,40,42,45,50,47,75,71,60,71,60,53,40,42,45,50,47,30,30,30,40,42,45,50,
47,75,71,60,71,60,53,50,53,60,60,60,60,60,53,47,60,71,80,60,60,60,60,53,
47,60,60,60,60,53,47,60,71,80,47,47,47,60,47,40,60,80,95,71,63,67,71,80,
47,40,36,45,40,47,60,53,63,60,80,95,71,63,67,71,80,47,40,36,45,40,47,60,
53,63,47,60,80,75,71,45,45,71,63,36,36,36,40,45,47,60,71,80,47,60,80,75,
71,45,45,71,63,45,45,45,47,53,60,47,60,80,75,71,45,45,71,63,36,36,36,40,
45,47,60,71,80,47,60,80,75,71,45,45,71,63,45,45,45,47,53,60,60,60,60,60,
53,47,60,71,80,60,60,60,60,53,47,60,60,60,60,53,47,60,71,80,47,47,47,60,
47,40,47,60,80,75,71,45,45,71,63,36,36,36,40,45,47,60,71,80,47,60,80,75,
71,45,45,71,63,45,45,45,47,53,60};


// This array contains all the delays between the notes
volatile unsigned char pause1[298] = {
8,32,32,8,32,176,56,56,56,32,32,8,32,16,16,16,32,8,32,32,8,8,56,56,56,56,
32,32,8,32,16,16,16,32,8,32,32,8,8,104,8,8,8,32,32,8,8,32,8,8,56,8,8,8,32,
32,32,8,128,8,8,8,32,32,8,8,32,8,8,56,56,56,224,8,8,8,32,32,8,8,32,8,8,56,
8,8,8,32,32,32,8,128,8,8,8,32,32,8,8,32,8,8,56,56,56,176,8,32,32,8,32,8,32,
8,80,8,32,32,8,8,200,8,32,32,8,32,8,32,8,80,8,32,32,8,32,176,56,56,56,32,
32,8,32,16,16,16,32,8,32,32,8,8,56,56,56,56,32,32,8,32,16,16,16,32,8,32,
32,8,8,56,8,32,56,32,8,32,8,80,16,16,16,16,16,16,8,32,8,80,8,32,56,32,8,
32,8,80,8,32,8,16,16,16,176,8,32,56,32,8,32,8,80,16,16,16,16,16,16,8,32,
8,80,8,32,56,32,8,32,8,80,8,32,8,16,16,16,176,8,32,32,8,32,8,32,8,80,
8,32,32,8,8,200,8,32,32,8,32,8,32,8,80,8,32,32,8,32,176,8,32,56,32,8,
32,8,80,16,16,16,16,16,16,8,32,8,80,8,32,56,32,8,32,8,80,8,32,8,16,16,16};

int main(void)
{

   TCCR0B = 1;      // Prescler = 1 --> counter frequency 62,5kHz (8bit counter)
   TIMSK0 = 1;      // Enable timer/counter0 overflow interrupt
   
   DDRD = 0xFF;   // All pins outputs
   DDRB = 0xFF;
   
   sei();         //enable interrupts
   
   while(1)
   {
               //infinite loop to keep interrupts running.
   }   

}


//interrupt overflow vector. This interrupt has a frequency of 16 MHz / 256 = 62500 Hz.
//The period is 16uS. (prescaler = 1, 8 bit register)

ISR(TIMER0_OVF_vect)         
{


   if (pause == 1)                     //if pause=1 pause between notes
   {
      PauseCounter++;
      
      if(PauseCounter==pause1[p]*100)   //the end of pause has been reached.
      {
         pause=0;                  //Clear pause flag
         PauseCounter=0;
         p++;                     //jump to next pause element
         
         if(p==297)
         {
            p=0;                  //if end of array has been reached. restart.
         }
      }
   
   }
   else if(pause == 0)   //if pause=0 we should play notes
   {
   
      // This loop toggles the pin to the correct frequency. the track array contains
      // the number of interrupts to be played. This is based on an interrupt freq
      // of 62,5 kHz.
      
      if(step == track1[i])
      {
         PORTD ^= (1<<0);
         step=0;            //clear counter.
      }
      
      step++;               //increase interrupt counter
      
      duration++;            //Increase duration counter.
      
      if(duration == 35625)   //Every note should be played for 0,57s --> 35625 * 0,16us = 0.57s
      {
         i++;            //Go to the next tone
         duration = 0;      //Cclear duration counter
         pause=1;         //set the pause flag to pause between notes
         
         if (i==298)
         {
            i=0;          //if end of track has been reached. restart.
         }
      }

   }   
   
}
EDIT: OK, ser nu att det verkar vara lite kortare i ditt andra försök, men jag får i
alla fall scrolla vänster/höger fortfarande. Lite mindre dåligt, men inte bra... :-)

Skit samma, det är inte helt avgörande, du har fått en del andra frågor/tips som du kanske
ska jobba med istället. Men ta med det till nästa gång, så att du fixar formatteringen då... :-)

Re: Problem med Interrupt AtMega168 @ 16MHz

Postat: 10 april 2010, 19:17:54
av myrek
om du först har if (pause == 1) så behöver du inte senare skriva else if (pause == 0) väl? Det räcker med else ... För det finns väl inte fler alternativ? men det var inte själva problemet.
Det har du nog rätt i :) skall ändra det.
kommer pausen ofta på samma ställe i melodin eller på slumpartade ställen?
Pauserna dyker alltid upp på samma ställe när koden körs. Just var de dyker upp beror på om jag har ändrat nåt i koden. När jag väl laddat över koden till processorn stannar den alltid på samma ställe. just nu stannar den efter 18 toner.

Pauserna är inte alltid exakt lika långa men verkar ligga mellan 1.1-1.2sekunder ungefär

Det verkar vara så att pauserna dyker upp vid skifte från lägre till högre toner ja!

Jag orkar inte skriva klart detta meddelande. Jag testade lösningen du föreslog och det funkar perfekt! tusen tack!

Re: Problem med Interrupt AtMega168 @ 16MHz

Postat: 10 april 2010, 20:24:21
av jesse
Grattis! :bravo:

Jag kände nämligen igen problemet från timers... där får man se upp om de ska slå om vid ett visst värde till ett lägre.. timern kan då "missa" vid jämförelse på samma sätt som din rutin missade. Därför skriver jag alltid > "mer än" istället för "lika med" == när jag ska jämföra saker som räknar upp... för säkerhets skull.

Får vi höra hur det låter nu då? :razz:

Re: Problem med Interrupt AtMega168 @ 16MHz

Postat: 10 april 2010, 20:30:04
av myrek
Jag skall lägga upp det på nätet så fort allt är klart. Det problem jag står inför just nu är att den ena "stämman" är klar det är två stycken till som skall med :)
Jag har dock fullt i SRAM. Var bör jag lagra alla arrayerna för att få plats? Kan jag lagra dem i flash och fortfarande ha dem som globala variabler eller är detta omöjligt?

Hur bör jag göra?

Re: Problem med Interrupt AtMega168 @ 16MHz

Postat: 10 april 2010, 20:37:51
av jesse

Kod: Markera allt

static unsigned int track3[291] PROGMEM
Du kanske skulle ha char även på track3?

Det går att lagra både i EEPROM och i FLASH.
Antar att du använder AVR-GCC. I biblioteken där finns koder för detta.

Men båda dessa är mycket långsammare än SRAM, så i så fall får du ladda en char (i SRAM) med tonens värde i början av varje ton, så att du läser ur arrayen endast en gång per ton och inte som nu 62500 ggr/sekund.
Men du har ju redan lagt vissa arrayer i PROGMEM...?

kolla:
<avr/eeprom.h>: EEPROM handling och <avr/pgmspace.h>: Program Space Utilities

Hoppas du hinner med alla tre stämmorna... det kan funka, men det finns en liten risk att interrupten inte hinner med. Men det är väl bara att testa.

Re: Problem med Interrupt AtMega168 @ 16MHz

Postat: 10 april 2010, 20:54:03
av myrek
tack för snabbt svar. track3 innehåller flera element som är större än 256 så jag måste ha den som int :/ väldans slöserie med utrymme ..

Jag satt och testade lite med progmem tidigare, jag var dock inte helt säker på hur jag skulle läsa ut datat från flash dock.
att lagra det var inga problem.

har du nåt exempel på hur jag kan läsa från flash? Skall försöka läsa på lite i biblioteket.