Skocz do zawartości

[C] Atmega8 Timer2 CTC problem z przerwaniem - czujnik zbliżeniowy na TOPS


stanekcr

Pomocna odpowiedź

Witam! Chciałbym wam opisać moją batalię z czujnikiem zbliżeniowym( na TOPS + dioda podczerwona na Atmega8 1MHz) i prosić o pomoc.

Więc pierwszy program:

#include <avr/io.h>             
#include <avr/interrupt.h>     
#include <stdlib.h>
#include <util/delay.h>
#define F_CPU 1000000L

void init(void)
{
 /* PB0,PB1 - wyścia */
 DDRB  = 0x03;
 PORTB = 0x00;
}
void play(int nots[][2], unsigned int start, unsigned int stop)
{
  int n;

  for(n=start; n <= stop; n++)
       beep(nots[n][0], nots[n][1]);
} 

void beep(unsigned int frequency, unsigned int duration)
{  
 unsigned int i,t,n;  
 t = 125000/frequency;  
 n = (250UL*duration)/t;

 PORTB |= 0x01;
 PORTB &= ~0x02;
 for(i=0; i < n; i++) 
 {
   PORTB ^= 0x01;  
   PORTB ^= 0X02;  
   _delay_loop_2(t);
 } 
}


int main(void)
{int k;
init();
DDRB|=0x08;  



while(1)
{

for(k=0;k<1000;k++)
{
PORTB=0x08;
_delay_us(14);
PORTB=0x00;
_delay_us(12);

if(!(PINC&0x02))
{if(!(PINC&0x02))
play(melodyjka,0,3);
}

}
_delay_ms(50);

}
}

Generuje w przybliżeniu sygnał 36k Hz, zasięg max ok 40 cm i wszystko działa jak należy, wykrywając sygnał pojawia się melodyjka na głośniku.

Chcę zrobić to samo tylko na timerze2. Oto co udało mi się zrobić, jednak nie działa do końca poprawnie:

// funkcja inicjalizująca
void init_timer2(void)
{

DDRB|=(1<<3);                                   // ustawienie portu PB3 (OC2) jako wyjscie
TCCR2|=(1<<CS20)|(1<<WGM21)|(1<<COM20); //preskaler 1 ; wlaczony tryb CTC; zmiana stanu OC2 na przeciwny gdy nastapi porownanie
OCR2=12;                     // Załączenie przerwania gdy TCNT2 = OCR2
TIMSK |= (1<<OCIE2); 
sei();
}

ISR (TIMER2_COMP_vect)
{
i++;
if(i==999)
{
_delay_ms(50);
i=0;
}
}

int main(void)
{
init();
init_timer2();

while (1)
{

if(!(PINC&0x02))
{if(!(PINC&0x02))
play(melodyjka,0,3);
}

} 

} 

Jeśli usunę z niego przerwania, to program działa w ten sposób, że wykrywa sygnał z małych odległości ok 5 cm, a przy szybkim ruchu obiektów do 30-40 cm.

Chcę dodać przerwania i wyłączać lub zmieniać częstotliwość diody co ok 50 ms co powinno sprawić, że program zadziała prawidłowo.

Jednak wprowadzając przerwania nie działa mi w ogóle głośnik, tylko lekko charczy i czujnik nie łapie z większych odległości.

Czy ktoś mógłby doradzić mi wkwestii ustawienia przerwań??

ps

fragment kodu dotyczący głośnika zaczerpnięty ze strony http://hobby.abxyz.bplaced.net

Link do komentarza
Share on other sites

Zacznijmy od tego, że twój kod jest napisany w bardzo brzydki sposób:

Po pierwsze dobrze by było dodać linijki ustawiające port C, bo widać że korzystasz z niego jako wejście a nie masz nigdzie opisane co jest do niego podłączone (domyślam się, że czujnik). Szczególnie że prosisz o odpowiedź osoby trzecie, które nie znają schematu układu ani twojej koncepcji. Poza tym mógł byś dodać informację o częstotliwości taktowania. Pewnie jest to standardowe 1MHz, ale też dobrze by było to wiedzieć a nie domyślać się.

Poza tym umieszczanie w kodzie z timerem funkcji delay jest bardzo złą praktyką. W końcu timer jest dużo lepszym narzędziem do odmierzania czasu, a dodatkowo działa w tle. Natomiast umieszczanie delay w funkcji przerwania timera jest kompletnie chybionym pomysłem. Ustawiłeś OCR2 na 12 i preskaler na 1. Tak więc przy częstotliwości 1MHz (1us na takt) przerwanie wystąpi po 12us. Program wykona się 999 razy czyli upłynie około 12ms i po tym czasie w przerwaniu timera nastąpi delay 50ms. Ta funkcja na pewno coś robi, ale chyba obaj zgodzimy się, że nie jest to odmierzanie 36kHz.

Natomiast jeśli chodzi o to, że melodyjka się nie gra problem leży po raz kolejny w źle napisanym przerwaniu. w ciągu 12ms przerwanie wykonywane jest prawie tysiąc razy za każdym razem zabierając kilka taktów mikroprocesora, a potem jeszcze jest przerwa cztery razy większa niż ten wcześniejszy cykl. W takim wypadku program nie ma kiedy dokończyć odgrywania melodyjki. Ogólnie nie ma opcji żeby dokończyć odgrywanie melodyjki jeśli jej funkcja też jest napisana za pomocą delay.

Musisz całkowicie zmienić kod, ustawić ten timer tak aby dobrze odmierzał 36kHz (1 okres na 28us). Czyli przez około 14us powinien dawać 0 i przez drugie tyle dawać 1. Proponował bym do tego zwiększyć częstotliwość taktowania. Poza tym musiał byś dodać drugi timer odgrywający melodyjkę. Kiedyś pisałem program tego typu program i funkcja ustawiająca timer wyglądała tak:

int settimer(int freq)

{
int wynik;
wynik = 2*FM/(PRE*freq)-1;
if(wynik>0)
return wynik;
else return -1;
}

zwraca ona wartość której ma się równać OCR i zwraca -1 kiedy dostanie zły argument. A to funkcja przerwania:

ISR(TIMER0_COMP_vect)
{
if(timer == 0) TCCR0 &= ((0<<CS01) | (0<<CS00) | (0<<CS02));
else 
{

		PORTB ^= 0x01;  
		PORTB ^= 0X02;  
}
}

zmienna timer mówi, czy licznik ma być wyłączony (przy wartości 0) czy włączony. Funkcje włączania i wyłączania timera:

	if(!(PINC & 0x80)) 
{
		if(blokada == 0)
		{
		blokada = 1;
		aktualny_dzwiek = C;

		//aktywacja timera
		timer = 1;
		OCR0 = settimer(C);
		TCCR0 |= ((1<<CS01) | (1<<CS00));
		}
}

if(PINC & 0x80) 
	{
		if(blokada == 1 && aktualny_dzwiek == C)
		{
		blokada = 0;
		//deaktywacja timera
		timer = 0;
		}
	}

C to stała odpowiadająca częstotliwości dźwięku. Natomiast zasada działania jest taka: jeśli na pinie C.3 jest 0 i nie ma blokady funkcja włącza blokadę, definiuje aktualnie odgrywany dźwięk i ustawia timer. Natomiast kiedy na pinie jest odwrotna wartość a grany jest ten dźwięk, dezaktywujemy blokadę i wyłączamy timer.

Mam nadzieję, że pomogłem

Link do komentarza
Share on other sites

Witaj 😉 Mógłbyś napisać jakie typu czujnika używasz ? Od dawna zbieram się do zrobienia czegoś z czujnikiem ruchu, szukałem teraz po necie czegoś ale znalazłem tylko gotowe urządzenia na sprzedaż. Z góry dzięki 😉

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Trafilem na kolejny problem. Oto fragment kodu:

volatile uint8_t wOCR2;
void init_timer2(void)
{

DDRB|=(1<<3);                                   // ustawienie portu PB3 (OC2) jako wyjscie
TCCR2|=(1<<CS20)|(1<<WGM21)|(1<<COM20); //preskaler 1 ; wlaczony tryb CTC; zmiana stanu OC2 na przeciwny gdy nastapi porownanie
OCR2=6;                     // Załączenie przerwania gdy TCNT2 = OCR2
TIMSK |= (1<<OCIE2); 
sei();
}
ISR (TIMER2_COMP_vect)
{
OCR2=wOCR2;//mozliwość sterowania częstotliwością timera2
}


Kiedy wprowadzę przerwania funkcja _delay_ms() przestaje działać prawidłowo,dioda świeci się około 10 razy dłużej. Czy ma to związek z operacjami uC wykonywanymi podczas przerwań? Jak to obejść?

Link do komentarza
Share on other sites

No tak jak pisałem wyżej _delay_ms nie będzie działać dobrze z przerwaniami. Funkcja delay zajmuje ileś tam linijek w asemblerze, gdzie każda linijka odpowiada jednej operacji CPU i nie jest atomowa (czyli jeśli przyjdzie przerwanie to funkcja przestanie być wykonywania na czas obsłużenia go). Tak więc używając delay razem z przerwaniem, szczególnie takim wykonywanym często, tracisz kompletnie kontrolę nad czasem delaya. Możesz albo dorobić jakąś zmienną która wygasi lampkę w odpowiednim przerwaniu timera albo napisać swoją własną funkcję od opóźnienia na drugi timer.

Link do komentarza
Share on other sites

Aha, dzięki wielkie, spróbuje napisać własną funkcję delay. Czy funkcja delay z biblioteki także nie będzie działać dla timera1(atmega8) z przerwaniami?? Zrobiłem sterowanie silnikami przez pwm i nie zauważyłem tak dużego opóźnienia w tej funkcji jak w tym przypadku.

Link do komentarza
Share on other sites

funkcja delay może źle działać z każdym przerwaniem nie ważne czy od timera, adc, usarta czy czegokolwiek innego. A to że czasem działa dobrze jest bardziej szczęśliwym przypadkiem niż jakąś regułą. Żeby dobrze panować nad czasem przy używaniu delaya z przerwaniami trzeba by było pisać w asemblerze i dobrze przeanalizować wszystkie przypadki ale i tak nie zawsze było by to możliwe.

Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.