Skocz do zawartości

[C] Atmega8 nadajnik IR 36kHz i Timer0 CTC


byxu

Pomocna odpowiedź

Witam, robię właśnie nadajnik IR 36kHz dla odbiornika tsop1736, i mam problem z zasięgiem, który wynosi około 20cm jeśli dioda i tsop ustawione są na przeciwko siebie. Sygnał narazie nadaje jedną atmegą8 timerem0 a drugą atmegą podłączoną mam do wyświetlacza który wyświetla stosowny napis. Dodam że np. jeśli pilotem od telewizora poświece na odbiornik to zasięg jest ponad 3 metry. Myślę że coś jest nie tak z kodem programu. Schemat podłączenia nadajnika poniżej.

#define F_CPU 8000000UL
#include <stdio.h> 
#include <avr/io.h> 
#include <util/delay.h> 





int main(void) 
{ 





DDRD = 0xff;
DDRC  = 0x00;
DDRB = 0xff;
PORTB = 0xff;


TCCR1A |= 1<<COM1A0; // toggle OC1A on Compare Match
TCCR1A |= 1<<WGM12; // CTC mode
TCCR1B = 1<<CS11; //prescaler clk/8 przykladowo

OCR1A = 13;	 


while(1)
{


_delay_ms(80);   
PORTD = 0x01;
_delay_ms(12);
PORTD = 0x00;
_delay_ms(100);  


} // koniec nieskonczonej petli       
} // koniec procedury glownej   

Link do komentarza
Share on other sites

A po co masz na schemacie aż 2 tranzystory W zupełności wystarczy jeden, podłączony do wyjścia OCn Timera.

Co do zasięgu to zależy on od zastosowanej diody, napięcia i prądu przez nią płynącego. Ja zauważyłem także że rozmiar diody tez ma pewien wpływ, mniejsze diody maja gorszy zasiąg, ale ważny jest tu też kąt świecenia. Dokładność wygenerowania 36KHz też tu ma pewne znaczenie, ponieważ TSOPy są na to wrażliwe.

Przy zasilaniu 5V i spadku napięcia na tych 2 tranzystorach wynoszącym ok 1,2V i oporniku 80Ω masz 3,8V / 80Ω = 0,0475A (47,5mA) czyli tak w granicy max prądu przeciętnej diody, ale prąd impulsowy może być większy, spróbuj sobie dać 40Ω (47Ω z szeregu np.) opornik, tylko pamiętaj aby na nim nie zapalić diody na stałe.

Link do komentarza
Share on other sites

Ja posiadam diodę o średnicy 5mm, kącie świecenia 15°, o długości fali 940nm, prądzie 50mA i napięciu 1,5V. 5-1,5/0,05=70Ω. Czy ten timer jest poprawnie skonfigurowany? Bo np. patrząc na tą diode przez aparat cyfrowy wygląda ona jakby migała z częstotliwością ok 15Hz, więc jak np. zmienie OCR1A = 13 na OCR1A = 200 lub OCR1A = 2 to nie widze żadnej różnicy w częstotliwości migania. znalazłem jeszcze taki kod który działa, teraz zasięg mam spokojnie 1-2m, w ostateczności mogę go użyć do budowy robota ale chciałbym mieć to zrobione elegancko na tych timerach. Więc mam pewność że problem jest nie z hardware tylko z software. Jak ten problem rozwiązać? Teraz mam problem bo ten zasięg jest zbyt duży będę musiał chyba zwiększyć wartość opornika aby zmniejszyć moc diody.

#define F_CPU 8000000L 
#include <avr/io.h> 
#include <util/delay.h> 

int main(void){ //początek funkcji main 
   DDRD=0xFF; 
   DDRB=0xFF; 
   PORTD=0xFF; 
   //generacja fali nośnej 36kHz 
   while(1){ 

PORTD=0x01;
for(int ii=0; ii<=15; ii++){ 
       for(int i=0; i<=38; i++){ 
               PORTB=0xFF; 
               _delay_us(13.5); 
               PORTB=0x0; 
               _delay_us(13.5); 
   } 
   _delay_ms(1); 
   } 
   _delay_ms(170); 
   } 
return 0; 
}   

Pozdrawiam.

Link do komentarza
Share on other sites

Wzrokowo nie za obserwujesz częstotliwości migania diody, musiał byś ją zmierzyć oscyloskopem. Oko człowieka nie wyrabia więcej niż 10-15 Hz, dla tego np. przyjmuje się że minimalny Frame Rate w grach to 30FPS, 60FPS w bijatykach, ale tam to wynika z czego innego. Do tego dochodzi ci jeszcze szybkość samej kamery w telefonie, co powoduje że to co widzisz jest mocno uśrednionym obrazem rzeczywistości.

Co do Timerów to trochę szkoda używać do generowania częstotliwości 16 bitowego T1, lepiej użyć 8 bitowych T0 (programowo w przerwaniach na dowolnym pinie CPU) lub T2 sprzętowo.

Z kodem trudno mi ci pomóc bo ja raczej w BASCOM siedzę, C tak rozumiem po łebkach.

Ale wartości musisz sobie policzyć na kartce dla timera. Mi dla 8 MHz, i preskalera = 1 wyszło że dla 8 bitowego Timera wartość porównywana powinna mieć 111 wtedy powinno być 36036Hz (36,036KHz)

  • Lubię! 1
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

Widać, że BlackJack musi w Bascomie programować a nie w C bo nie zauważył jednej istotnej rzeczy w twoim kodzie, która właśnie jest prawdopodobną przyczyną problemów. Ty tak naprawdę nie używasz timera tylko funkcji delay. Jest ona niedokładna i odmierza czas poprzez wykonanie przez procesor określonej ilości pustych rozkazów o znanej długości. W ten sposób w międzyczasie procesor nie może robić nic innego, funkcja staje się niedokładna jeśli zastosujesz przerwania a poza tym dla różnych argumentów ma różną dokładność co wynika ze sposobu jej implementacji.

Generalnie zasada jest taka, że jeśli korzystasz z timera do odmierzania czasu to funkcja delay w ogóle nie powinna się znaleźć w twoim kodzie. Timer możesz zrealizować przy pomocy odpytywania w pętli jeżeli mikrokontroler nie ma w twoim systemie wiele roboty poza obsługą podczerwieni. Jeżeli chciał byś w międzyczasie wykonać jakieś inne operacje, powinieneś skorzystać z przerwań.

W swoim kodzie powinieneś wtedy zawrzeć:

-załączenie biblioteki przerwań: #include

-inicjalizacje timera według datasheeta: ustawienie w tryb CTC, dobranie preskalera, uaktywnienie przerwań od timera

-inicjalizację przerwań globalnych: instrukcja C sei();

-pętlę główną programu: tutaj zawierasz główny program, ewentualnie możesz modyfikować ustawienia timera w celu zmiany jego działania w trakcie programu, możesz też dać pustą pętlę jeśli masz zamiar realizować wszystkie zadania w funkcji przerwania.

-funkcję obsługi przerwania: postaci ISR(nazwa przerwania z wektora przerwan) {treść funkcji}

ISR oznacza Interrupt Service Routine. Nie deklarujesz przed nią prototypu ani nie określasz typu zwracanej wartości. Kompilator wszystko już wie dzięki dołączeniu biblioteki interrupt.h w nawiasie jako argument dajesz nazwę przerwania z wektora przerwań z dodanym _vect. Znajdziesz ją w datasheecie w sekcji interrupts. Do twojego CTC z OCR1A będzie to wiec ISR(TIMER1_COMPA_vect)

Dobrym nawykiem jest, żeby funkcja przerwania była w miarę krótka ponieważ przerywa ona działanie głównego programu. Jeżeli w przerwaniu chcesz używać tej samej zmiennej co w głównym programie, musisz deklarować ją jako globalną (przed funkcją main) i z przedrostkiem volatile. Czyli na przykład:

volatile unsigned char zmienna_do_timera;

Jeżeli znasz angielski polecam świetny artykuł o timerach znajdujący się na równie świetnej stronce gdzie znajdziesz dużo podobnych tutoriali:

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=50106&sid=20d788f89772f7a108c5d6a8f8655654

Link do komentarza
Share on other sites

Delaye jako separatory bloków danych jeszcze można przeżyć, ale w jednym kodzie były używane też do samej generacji częstotliwości.

Patrzyłem na kod z pierwszego posta tak na szybko i nie zauważyłem jeszcze jednej rzeczy. Obsługa timera na przerwaniach nadawała by się dobrze do bardziej skomplikowanej aplikacji, gdzie np w głównej pętli programu modyfikujesz wypełnienie i ilość impulsów. Ty natomiast chciałeś zrobić to na pinie OC1A. W takim wypadku powinieneś zrobić samą inicjalizację timera a następnie zapomnieć w dalszej części kodu zarówno o timerze, jak i tym porcie wyjściowym. W takiej postaci jakiej jest, program hardware'owo zmienia wartość na wyjściu OC1A, a delaye tylko mu przeszkadzają. Po pierwsze powinieneś wywalić tranzystor sterowany przez PD0, a delay w twojej funkcji jest niepotrzebny i spokojnie możesz go też wyrzucić.

Link do komentarza
Share on other sites

Widać, że BlackJack musi w Bascomie programować a nie w C bo nie zauważył jednej istotnej rzeczy w twoim kodzie, która właśnie jest prawdopodobną przyczyną problemów. Ty tak naprawdę nie używasz timera tylko funkcji delay. Jest ona niedokładna i odmierza czas poprzez wykonanie przez procesor określonej ilości pustych rozkazów o znanej długości. W ten sposób w międzyczasie procesor nie może robić nic innego, funkcja staje się niedokładna jeśli zastosujesz przerwania a poza tym dla różnych argumentów ma różną dokładność co wynika ze sposobu jej implementacji.

Generalnie zasada jest taka, że jeśli korzystasz z timera do odmierzania czasu to funkcja delay w ogóle nie powinna się znaleźć w twoim kodzie. Timer możesz zrealizować przy pomocy odpytywania w pętli jeżeli mikrokontroler nie ma w twoim systemie wiele roboty poza obsługą podczerwieni. Jeżeli chciał byś w międzyczasie wykonać jakieś inne operacje, powinieneś skorzystać z przerwań.

W swoim kodzie powinieneś wtedy zawrzeć:

-załączenie biblioteki przerwań: #include

-inicjalizacje timera według datasheeta: ustawienie w tryb CTC, dobranie preskalera, uaktywnienie przerwań od timera

-inicjalizację przerwań globalnych: instrukcja C sei();

-pętlę główną programu: tutaj zawierasz główny program, ewentualnie możesz modyfikować ustawienia timera w celu zmiany jego działania w trakcie programu, możesz też dać pustą pętlę jeśli masz zamiar realizować wszystkie zadania w funkcji przerwania.

-funkcję obsługi przerwania: postaci ISR(nazwa przerwania z wektora przerwan) {treść funkcji}

ISR oznacza Interrupt Service Routine. Nie deklarujesz przed nią prototypu ani nie określasz typu zwracanej wartości. Kompilator wszystko już wie dzięki dołączeniu biblioteki interrupt.h w nawiasie jako argument dajesz nazwę przerwania z wektora przerwań z dodanym _vect. Znajdziesz ją w datasheecie w sekcji interrupts. Do twojego CTC z OCR1A będzie to wiec ISR(TIMER1_COMPA_vect)

Dobrym nawykiem jest, żeby funkcja przerwania była w miarę krótka ponieważ przerywa ona działanie głównego programu. Jeżeli w przerwaniu chcesz używać tej samej zmiennej co w głównym programie, musisz deklarować ją jako globalną (przed funkcją main) i z przedrostkiem volatile. Czyli na przykład:

volatile unsigned char zmienna_do_timera;

Jeżeli znasz angielski polecam świetny artykuł o timerach znajdujący się na równie świetnej stronce gdzie znajdziesz dużo podobnych tutoriali:

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=50106&sid=20d788f89772f7a108c5d6a8f8655654

Czyli jeśli dobrze rozumiem mam zrobić coś takiego?

#define F_CPU 8000000UL 
#include <stdio.h> 
#include <avr/io.h> 
#include <interrupt.h>




int main(void) 
{ 

   DDRD = 0xff; 
   DDRC  = 0x00; 
   DDRB = 0xff; 
   PORTB = 0xff; 


TCCR1A |= 1<<COM1A0; // toggle OC1A on Compare Match 
TCCR1B |= 1<<WGM12; // CTC mode 
TCCR1B = 1<<CS11; //prescaler clk/8 przykladowo 

OCR1A = 13;   // f=36kHz
sei();



while(1) 
{

} // koniec nieskonczonej petli        
} // koniec procedury glownej    

ISR(TIMER1_COMPA_vect)
{
// tutaj jakieś opreracje np. obsługa czujnika tsop1736 lub sterowanie silnikami przez //mostek-h i nigdy nie używać funkcji delay
}  


Link do komentarza
Share on other sites

W inicjalizacji musisz jeszcze dodać linijkę ustawiającą bit Output Compare Interrupt Enable 1A

TIMSK |= (1 << OCIE1A); //inicjalizacja przerwania od CTC1A

Bez tego program nigdy nie przejdzie do funkcji przerwania od CTC.Poza tym preskaler bym ustawił na 1. Wtedy masz większą rozdzielczość licznika i możesz dokładniej przybliżyć zadaną częstotliwość. Przy preskalerze 8 też powinien działać.

Link do komentarza
Share on other sites

No tylko to coś mi się nie bardzo chce skompilować. Używam Avr Studio 4

Build started 29.5.2011 at 23:15:43
avr-gcc  -mmcu=atmega8 -Wall -gdwarf-2 -std=gnu99     -DF_CPU=8000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT main.o -MF dep/main.o.d  -c  ../main.c
../main.c:4:24: error: interrupt.h: No such file or directory
../main.c: In function 'main':
../main.c:24: warning: implicit declaration of function 'sei'
../main.c: At top level:
../main.c:35: warning: return type defaults to 'int'
../main.c: In function 'ISR':
../main.c:35: warning: type of '__vector_6' defaults to 'int'
../main.c:37: warning: control reaches end of non-void function
make: *** [main.o] Error 1
Build failed with 1 errors and 4 warnings...
#define F_CPU 8000000UL 
#include <stdio.h> 
#include <avr/io.h> 
#include <interrupt.h> 




int main(void) 
{ 

   DDRD = 0xff; 
   DDRC  = 0x00; 
   DDRB = 0xff; 
   PORTB = 0xff; 


TIMSK |= (1 << OCIE1A); //inicjalizacja przerwania od CTC1A    
TCCR1A |= 1<<COM1A0; // toggle OC1A on Compare Match 
TCCR1B |= 1<<WGM12; // CTC mode 
TCCR1B = 1<<CS11; //prescaler clk/8 przykladowo 

OCR1A = 13;   // f=36kHz 
sei(); 



while(1) 
{ 

} // koniec nieskonczonej petli        
} // koniec procedury glownej    

ISR(TIMER1_COMPA_vect) 
{ 
// tutaj jakieś opreracje np. obsługa czujnika tsop1736 lub sterowanie silnikami przez //mostek-h i nigdy nie używać funkcji delay 
}  
Link do komentarza
Share on other sites

W include zmień na wtedy się powinno się skompilować. I jeszcze dobrze by było zamienić miejscami OCR1A i ustawienie preskalera, bo zmiana wartości preskalera z 0 startuje licznik i dobrze żeby już wtedy miał wartość do porównania wpisaną do OCR1A.

Link do komentarza
Share on other sites

no skompilowało się tylko że problem z pierwszego postu pozostał, czyli mam zasięg na przeciwko siebie nadajnika i odbiornika jakieś 30cm, a jeśli wiązka ma sie od czegoś odbić to max 10cm, gdy przedmiot ciemniejszy to wogule nie wykrywa. Coś mi się wydaje że tu nie ma 36kHz 😥

#define F_CPU 8000000UL 
#include <stdio.h> 
#include <avr/io.h> 
#include <avr/interrupt.h>




int main(void) 
{ 

   DDRD = 0xff; 
   DDRC  = 0x00; 
   DDRB = 0xff; 
   PORTB = 0xff; 


TIMSK |= (1 << OCIE1A); //inicjalizacja przerwania od CTC1A    
TCCR1A |= 1<<COM1A0; // toggle OC1A on Compare Match 
TCCR1B |= 1<<WGM12; // CTC mode 
OCR1A = 13;   // f=36kHz
TCCR1B = 1<<CS11; //prescaler clk/8 przykladowo 


sei(); 



while(1) 
{ 

} // koniec nieskonczonej petli        
} // koniec procedury glownej    

ISR(TIMER1_COMPA_vect) 
{ 
PORTD=0x01;

// tutaj jakieś opreracje np. obsługa czujnika tsop1736 lub sterowanie silnikami przez //mostek-h i nigdy nie używać funkcji delay 
}
Link do komentarza
Share on other sites

A zmieniłeś schemat na taki z jednym tranzystorem gdzie na bazę podajesz pin OC1A? Bo głównym problemem w twoim przypadku jest prawdopodobnie właśnie ten drugi tranzystor który wprowadza tylko zamęt. Ten kod który napisałeś zmienia wyjście OC1A z częstotliwością 36kHz, natomiast twój warunek w przerwaniu tak naprawdę cokolwiek robi tylko za pierwszym razem bo zawsze ustawiasz na nim 1. Poza tym połączenie trybu przerwaniowego i hardwarowego jest dość dziwne. Nigdy czegoś takiego nie używałem. Jesteś pewny, że tak miał działać twój program?

Proponował bym abyś zmienił schemat na taki jak opisałem na początku, a potem przerobił kod albo na obsługę czysto hardwarową albo czysto przerwaniową. Na szybko edytowałem twój kod według tych dwóch opcji. Najlepiej sprawdź z datasheetem czy wszystko jest ok.

Opcja pierwsza:

#define F_CPU 8000000UL 
#include <avr/io.h>

int main(void)
{
DDRD = 0xff;
DDRC  = 0x00;
DDRB = 0xff;
PORTB = 0xff; 

TCCR1A |= 1<<COM1A0; // toggle OC1A on Compare Match
TCCR1B |= 1<<WGM12; // CTC mode
OCR1A = 13;   // f=36kHz
TCCR1B = 1<<CS11; //prescaler clk/8 przykladowo

while(1) {}
}

Tutaj robisz metodą bez przerwań z hardwareową zmianą pinu OC1A. Dlatego niepotrzebne części kodu usuwasz. Nie potrzebujesz w tym sposobie przerwań, dlatego nie musisz nawet dodawać biblioteki interrupt.

Opcja druga:

#define F_CPU 8000000UL
#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>




int main(void)
{

   DDRD = 0xff;
   DDRC  = 0x00;
   DDRB = 0xff;
   PORTB = 0xff;


TIMSK |= (1 << OCIE1A); //inicjalizacja przerwania od CTC1A   
TCCR1B |= 1<<WGM12; // CTC mode
OCR1A = 13;   // f=36kHz
TCCR1B = 1<<CS11; //prescaler clk/8 przykladowo

sei();

while(1)
{
} // koniec nieskonczonej petli       
} // koniec procedury glownej   

ISR(TIMER1_COMPA_vect)
{
PORTB^=0x02; //odwraca wartość PB1

// tutaj jakieś opreracje np. obsługa czujnika tsop1736 lub sterowanie silnikami przez //mostek-h i nigdy nie używać funkcji delay
}

Tutaj w funkcji przerwania negujesz PB1, nie potrzebujesz natomiast ustawiać COM1A0.

Mam nadzieję, że teraz będzie ok.

Link do komentarza
Share on other sites

A spróbuj sobie zamiast 13 do OCR1A wstawić 14. Bo z obliczeń wychodzi 13,8 czyli bliżej 14 a nie 13.

Na razie masz bliżej 38KHz niż 36KHz.

1 000 000 / 13 = 76923,076 / 2 (bo zmieniamy stan 2 razy) = 38461,538 czyli 38,461 KHz

dla 14 będzie:

1 000 000 / 14 = 71428,571 / 2 = 35714,285 czyli 35,714 KHz a to bliżej 36KHz.

#define F_CPU 8000000UL

#include

#include

#include

int main(void)

{

DDRD = 0xff;

DDRC = 0x00;

DDRB = 0xff;

PORTB = 0xff;

TIMSK |= (1 << OCIE1A); //inicjalizacja przerwania od CTC1A

TCCR1B |= 1<

OCR1A = 14; // f=36kHz

TCCR1B = 1<

sei();

while(1)

{

} // koniec nieskonczonej petli

} // koniec procedury glownej

ISR(TIMER1_COMPA_vect)

{

PORTB^=0x02; //odwraca wartość PB1

}

// tutaj jakieś opreracje np. obsługa czujnika tsop1736 lub sterowanie silnikami przez //mostek-h i nigdy nie używać funkcji delay

Ogólnie można przyjąć że jak z obliczeń wychodzą ułamki to do 0,5 (łącznie z 0,5) zaokrąglamy w dół np 13,5 przyjmujemy 13, a powyżej 0,5 zaokrąglamy do góry np. 13,7 przyjmujemy 14.

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.