Przerwanie overflow - duża ilość instrukcji

To forum jest dla wszystkich pasjonatów mikrokontrolerów AVR Atmela. Wymiana doświadczeń i pomoc dla początkujących w pisaniu programów zarówno w C, Asemblerze jak i BASCOM. Zapraszam znawców tematu, aby pomogli wszystkim początkującym!
ODPOWIEDZ
owoc0
-
Posty: 13
Rejestracja: 26 cze 2011, 14:11
Lokalizacja: Gdańsk

Przerwanie overflow - duża ilość instrukcji

Post autor: owoc0 » 26 cze 2011, 14:19

Buduję sobie prosty układ z matrycą led (5x7). Sterowanie nią realizuję poprzez multipleksowanie. Wszystko działa, ale mam pytanko. Obecnie wszystkie sprawy, dotyczące timera, wykonuję sobie w przerwaniu i zastanawiam się, czy to przypadkiem nie jest zła praktyka. Ogólnie wygląda to tak:

Kod: Zaznacz cały

ISR(TIMER0_OVF_vect) //overflow
{
	switch(licznik)
	{
	
	case 1:
		PORTD = 0x00;
		PORTB = 0x00;
	
		PORTD |= (1<<2);
		
		for(int i=0; i<7; i++)
		{
			if(wyswietlacz[i] == 1)
			{
				PORTB |= (1<<i);
			}
		}
	break;
	
	case 10:
		PORTD = 0x00;
		PORTB = 0x00;
	
		PORTD |= (1<<3);
		
		for(int i=7; i<14; i++)
		{
			if(wyswietlacz[i] == 1)
			{
				PORTB |= (1<<(i-7));
			}
		}
	break;
	
	case 19:
		PORTD = 0x00;
		PORTB = 0x00;
	
		PORTD |= (1<<4);
		
		for(int i=14; i<21; i++)
		{
			if(wyswietlacz[i] == 1)
			{
				PORTB |= (1<<(i-14));
			}
		}
	break;
	
	case 28:
		PORTD = 0x00;
		PORTB = 0x00;
	
		PORTD |= (1<<5);
		
		for(int i=21; i<28; i++)
		{
			if(wyswietlacz[i] == 1)
			{
				PORTB |= (1<<(i-21));
			}
		}
	break;
	
	case 37:
		PORTD = 0x00;
		PORTB = 0x00;
	
		PORTD |= (1<<6);
		
		for(int i=28; i<35; i++)
		{
			if(wyswietlacz[i] == 1)
			{
				PORTB |= (1<<(i-28));
			}
		}
	break;
	
	case 46:
		licznik = 0;
	break;
	
	}
	
	licznik++;
	animacja_licznik++;
	
	if(animacja_licznik == 600)
	{
		animacja_klatka++;
		animacja_flaga = 0;
		animacja_licznik = 0;
	}
	
	if(animacja_klatka == 7)
	{
		animacja_klatka = 1;
	}
	
			switch(animacja_klatka)
		{
			case 1:
				if(animacja_flaga == 0)
				{
					*kursor = 0;
					kursor = &wyswietlacz[0];
					*kursor = 1;
					
					animacja_flaga = 1;
				}
			break;
			
			case 2:
				if(animacja_flaga == 0)
				{
					*kursor = 0;
					kursor = &wyswietlacz[1];
					*kursor = 1;
					
					animacja_flaga = 1;
				}
			break;
			
			case 3:
				if(animacja_flaga == 0)
				{
					*kursor = 0;
					kursor = &wyswietlacz[2];
					*kursor = 1;
					
					animacja_flaga = 1;
				}
			break;
			
			case 4:
				if(animacja_flaga == 0)
				{
					*kursor = 0;
					kursor = &wyswietlacz[3];
					*kursor = 1;
					
					animacja_flaga = 1;
				}
			break;
			
			case 5:
				if(animacja_flaga == 0)
				{
					*kursor = 0;
					kursor = &wyswietlacz[4];
					*kursor = 1;
					
					animacja_flaga = 1;
				}
			break;
			
			case 6:
				if(animacja_flaga == 0)
				{
					*kursor = 0;
					kursor = &wyswietlacz[5];
					*kursor = 1;
					
					animacja_flaga = 1;
				}
			break;
			
			case 7:
				if(animacja_flaga == 0)
				{
					*kursor = 0;
					kursor = &wyswietlacz[6];
					*kursor = 1;
					
					animacja_flaga = 1;
				}
			break;
		}

}	
Wydaję mi się, że jest to stanowczo zbyt dużo i może w przyszłości prowadzić do problemów, kiedy to procesor się po prostu "nie wyrobi". Co Panowie o tym myślą?

keruseykaryu

Post autor: keruseykaryu » 26 cze 2011, 17:06

Stanowczo za dużo się dzieje w tym przerwaniu.

Kod: Zaznacz cały

        for(int i=0; i<7; i++) 
         { 
             if(wyswietlacz[i] == 1) 
             { 
                 PORTB |= (1<<i); 
             } 
         }
Tragedia. Pętle w przerwaniu.

Kod: Zaznacz cały

         switch(animacja_klatka) 
         { 
             case 1:
Cały ten switch można przerobić co by był krótszy, bo wiele się powtarza.

owoc0
-
Posty: 13
Rejestracja: 26 cze 2011, 14:11
Lokalizacja: Gdańsk

Post autor: owoc0 » 26 cze 2011, 18:03

tak myślałem... w takim wypadku, odczyt z tablicy z grafiką powinienem realizować w main, a w przerwaniu jedynie inkrementować?

keruseykaryu

Post autor: keruseykaryu » 26 cze 2011, 18:06

To przerwanie da się przepisać tak, by zajęło 1/3 tego co teraz. I wcale nie trzeba w to main() angażować. Musisz tylko bardziej pomyśleć...

owoc0
-
Posty: 13
Rejestracja: 26 cze 2011, 14:11
Lokalizacja: Gdańsk

Post autor: owoc0 » 26 cze 2011, 18:40

A co z pętlami? Dopuszczalne jest ich używanie w przerwaniach timera?

keruseykaryu

Post autor: keruseykaryu » 26 cze 2011, 20:00

A co z pętlami? Dopuszczalne jest ich używanie w przerwaniach timera?
Nie. Jak przypadkiem pętla nie będzie miała wyjścia - wpadnie w pętlę nieskończoną, to masz zwis totalny.

owoc0
-
Posty: 13
Rejestracja: 26 cze 2011, 14:11
Lokalizacja: Gdańsk

Post autor: owoc0 » 26 cze 2011, 21:02

No dobrze, ale w jaki sposób miałbym zrealizować czytanie z tablicy nie używając do tego celu pętli for? Nie mówmy już o dalszej części przerwania, bo tam faktycznie można dużo zoptymalizować. Mam na myśli samo multipleksowanie.

keruseykaryu

Post autor: keruseykaryu » 27 cze 2011, 5:24

owoc0 pisze:No dobrze, ale w jaki sposób miałbym zrealizować czytanie z tablicy nie używając do tego celu pętli for?
Czytaj po kolei co przerwanie.

atelszewski
Użytkownik
Posty: 143
Rejestracja: 12 sie 2005, 9:36
Lokalizacja: Banie

Post autor: atelszewski » 27 cze 2011, 7:59

Witam,
Nie. Jak przypadkiem pętla nie będzie miała wyjścia - wpadnie w pętlę nieskończoną, to masz zwis totalny.
Wszystko zależy od aplikacji, tzn. w przerwaniu można używać pętli, jeśli nie uda się rozwiązać problemu inaczej. A przed totalnym zwisem może chronić watchdog - przecież w main() też może znaleźć się pętla, która zawiesi program "na wieki".

owoc0
-
Posty: 13
Rejestracja: 26 cze 2011, 14:11
Lokalizacja: Gdańsk

Post autor: owoc0 » 27 cze 2011, 13:09

Dziękuję za odpowiedzi, spróbuję coś wyskrobać. Temat niech pozostanie jeszcze otwarty, gdyby pojawiły się jakieś problemy. Pomysł z czytaniem co przerwanie faktycznie jest bardzo dobry.

Edit: Mieli Koledzy rację - udało się to zrobić w dużo szybszy i bardziej zoptymalizowany sposób. Multipleksowanie wygląda teraz tak:

Kod: Zaznacz cały

volatile char wiersz;
volatile uint8_t licznik;
volatile uint8_t kolumna;

Kod: Zaznacz cały

ISR(TIMER0_OVF_vect) //overflow
{
	if(wyswietlacz[kolumna][licznik] == 1)
	{
		wiersz |= (1<<licznik);
	}
	
	licznik++;
	
	if(licznik == 7)
	{
		PORTB = wiersz;
		
		PORTD = 0x00;
		PORTD |= (1<<(kolumna+2));
		
		kolumna++;
		
		if(kolumna == 5)
		{
			kolumna = 0;
		}
		
		wiersz = 0;
		licznik = 0;
	}
}	
Myślę, że jest to już na całkiem konkretnym poziomie, ale bardzo bym prosił, gdyby któryś z Kolegów miał chwilkę, rzucić na to fachowym okiem.

keruseykaryu

Post autor: keruseykaryu » 27 cze 2011, 17:24

Możesz to jeszcze krócej napisać. Niepotrzebnie się rozpisujesz blokowo z if-ami. A takie konstrukcje porównań z preinkrementacją kompilator jeszcze potrafi nieco odchudzić w kodzie asm.

Kod: Zaznacz cały

ISR(TIMER0_OVF_vect) //overflow 
 { 
     if(wyswietlacz[kolumna][licznik] == 1) wiersz |= (1<<licznik); 
  
     if(++licznik == 7) 
     { 
         PORTB = wiersz; 
         
         PORTD = 0x00; 
         PORTD |= (1<<(kolumna+2)); 
                 
         if(++kolumna == 5) kolumna = 0; 
         
         wiersz = licznik = 0; 
     } 
 } 
Ostatnio zmieniony 27 cze 2011, 21:24 przez keruseykaryu, łącznie zmieniany 1 raz.

owoc0
-
Posty: 13
Rejestracja: 26 cze 2011, 14:11
Lokalizacja: Gdańsk

Post autor: owoc0 » 27 cze 2011, 18:47

O, nie wiedziałem, że można w ten sposób inkrementować (właściwie preinkrementować) zmienną - bardzo przydatne. Serdecznie dziękuję za pomoc. Spokoju niestety nie daje mi jeszcze jedna rzecz. Tempo np. animacji mogę sobie nadać poprzez timer. Gdzie powinienem obsługiwać ładowanie klatek, lub inne operacje na tablicy - w przerwaniu, czy może w main? Weźmy za przykład przesuwanie czegoś po wyświetlaczu. Ustalam sobie, że chcę przesuwać o jeden pixel w prawo, co 20 przerwań. Gdzie powinienem umieścić taką instrukcję, jak ta poniżej (pseudo kod)?

Kod: Zaznacz cały

if(flag == 1)
{
          przesun();
          flag = 0;
}
Flaga wysyłana oczywiście przez przerwanie.

keruseykaryu

Post autor: keruseykaryu » 27 cze 2011, 21:24

Jako, że przesuwanie obrazu i inne manipulacje na nim są nieco czasochłonne to oczywiście zrób to w main().

Jak masz dużo RAMu do dyspozycji zrób drugą tablicę "cień" danych do wyświetlania, na przykład jako kolejny wymiar albo poprzez offset. Wtedy operacje robisz na niewidocznej części, a potem szybko zamieniasz miejscami odnośnik do tablicy.
Jak chcesz mieć wypas obsługę wyświetlacza to zrób też wirtualny powrót plamki i operacje zamiany stron grafiki wykonuj tylko po narysowaniu całej "ramki".

owoc0
-
Posty: 13
Rejestracja: 26 cze 2011, 14:11
Lokalizacja: Gdańsk

Post autor: owoc0 » 27 cze 2011, 21:32

Rozumiem, chodzi mi jednak o dużo prostszą rzecz, związaną bezpośrednio z timerem. W jaki sposób zrobić opóźnienie? Mógłbym zrobić podobną funkcję do funkcji delay, korzystając z timera, i po prostu zawieszać pętlę, ale raczej nie o to tutaj chodzi. Dokładniej chciałbym, aby funkcja przesun() wykonywała się co kilkadziesiąt przerwań.

Edit: Teraz, czytając post Kolegi, wpadł mi do głowy pewien pomysł. Całość wyglądałaby tak:

main:

- czekaj na flagę
- jeśli flaga jest, ładuj do niewidocznej tablicy

przerwanie (któreś z kolei, zależne jaka szybkość przesuwania napisu):

- wystaw flagę
- zamień odnośnik do tablicy

Co Kolega o tym myśli?

ODPOWIEDZ