logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[ATmega128L][AVR-GCC] Zamienione TIMER0 i TIMER2

damiano713 25 Sty 2011 23:51 2529 13
  • #1 9057783
    damiano713
    Poziom 15  
    Na początku zaznaczę, że siedziałem nad tym ostatnie 3 dni. Jestem z tego powodu CHOLERNIE WKURZONY.

    Ostatnie doświadczenia z kompilatorem AVR-GCC i obsługą TIMER0 i TIMER2 doprowadziły mnie do konkluzji, iż w Nocie Atmegi128 lub w headerach jest błąd.

    Spójrzmy na tabele preskalera TIMER2.
    [ATmega128L][AVR-GCC] Zamienione TIMER0 i TIMER2

    Oto prosty programik:
    #include <avr/io.h>
    #include <inttypes.h>
    #include <avr/interrupt.h>
    #include <util/delay.h>
    
    ISR(TIMER2_OVF_vect)
    {
    	PORTC &= ~_BV(PC2);
    	asm volatile("nop");
    }
    
    int main(void)
    {
    	DDRC = 0xFF;
    	PORTC |= _BV(PC2);
    	_delay_ms(1000);
    	PORTC &= ~_BV(PC2);
    	_delay_ms(1000);
    	PORTC |= _BV(PC2);
    	_delay_ms(1000);
    	TCNT2 = 100;
    	TCCR2 = _BV(CS22)|_BV(CS21)|_BV(CS20);
    	//TIMSK = 0b1000000;
    	//sei();
    	for(;;)
    	{
    		_delay_us(10);
    		PORTC ^= _BV(PC3);
    		if (TCNT2 > 100)
    			PORTC &= ~_BV(PC2);
    	}
    }

    Uruchamia timer 2 i zapala diodę jeżeli wartość TCNT2 będzie większa od 100.
    Dzielnik ustawiony na 1024.

    Czy program zadziała? Nie!
    Nie działa, w żaden sposób.

    Idźmy dalej...
    #include <avr/io.h>
    #include <inttypes.h>
    #include <avr/interrupt.h>
    #include <util/delay.h>
    
    ISR(TIMER2_OVF_vect)
    {
    	PORTC &= ~_BV(PC2);
    	asm volatile("nop");
    }
    
    int main(void)
    {
    	DDRC = 0xFF;
    	PORTC |= _BV(PC2);
    	_delay_ms(1000);
    	PORTC &= ~_BV(PC2);
    	_delay_ms(1000);
    	PORTC |= _BV(PC2);
    	_delay_ms(1000);
    	TCNT2 = 100;
    	TCCR2 = _BV(CS22)|_BV(CS20);
    	//TIMSK = 0b1000000;
    	//sei();
    	for(;;)
    	{
    		_delay_us(10);
    		PORTC ^= _BV(PC3);
    		if (TCNT2 > 100)
    			PORTC &= ~_BV(PC2);
    	}
    }

    Zmienił się tylko preskaler. Zgodnie z tabelą, będzie to 128.

    Czy program zadziała? Tak! Jaki będzie preskaler? Nie... nie 128
    Będzie to 1024.

    Następna tabela wyjaśni czemu tak jest. Jest to preskaler TIMER0:
    [ATmega128L][AVR-GCC] Zamienione TIMER0 i TIMER2
    [ATmega128L][AVR-GCC] Zamienione TIMER0 i TIMER2

    Teraz popatrzmy co się stało.
    Ustawiając preskaler na 1024 tak naprawdę ustawiliśmy go na zewnętrzny sygnał zegarowy T0.

    Poniższy program udowadnia, że to najprawdopodobniej błąd w nocie katalogowej.
    Przerwanie wykonuje się poprawnie.
    #include <avr/io.h>
    #include <inttypes.h>
    #include <avr/interrupt.h>
    #include <util/delay.h>
    
    ISR(TIMER0_OVF_vect)
    {
    	PORTC &= ~_BV(PC2);
    	asm volatile("nop");
    }
    
    ISR(TIMER2_OVF_vect)
    {
    	asm volatile("nop");
    }
    
    int main(void)
    {
    	DDRC = 0xFF;
    	PORTC |= _BV(PC2);
    	_delay_ms(1000);
    	PORTC &= ~_BV(PC2);
    	_delay_ms(1000);
    	PORTC |= _BV(PC2);
    	_delay_ms(1000);
    	TCNT0 = 100;
    	TCCR0 = _BV(CS02)|_BV(CS00);
    	TIMSK = _BV(TOIE0);
    	sei();
    	for(;;)
    	{
    		_delay_us(10);
    		PORTC ^= _BV(PC3);
    	}
    }
    


    Widziałem kilka wątków gdzie ludzie mieli problem z TIMER'ami. Czasami działały, czasami nie. Także chodziło tylko i wyłącznie o preskaler.

    Pozdrawiam
    Damian Kmiecik
  • #2 9057933
    Konto nie istnieje
    Konto nie istnieje  
  • #3 9058269
    damiano713
    Poziom 15  
    Jest komentarzem ponieważ w tym przykładzie nie używam przerwań. sprawdzam czy licznik w ogóle ruszył:

    if (TCNT2 > 100)
             PORTC &= ~_BV(PC2); 


    Nie spodziewałem się, że w tak krytycznej sekcji datasheetu może być błąd.
    W mojej wersji noty katalogowej (pobranej ze strony atmela, jako najnowsza) najpierw jest opisany licznik TIMER2(8bit), potem TIMER1(16bit) i TIMER3 (dodatkowy 16bit), a na końcu TIMER0. Od początku coś mi nie pasowało.

    Pozatym w opisie rejestrów TIMER2 i TIMER0 na początku leci (dla TIMER2):
    TCCR2
    TCNT2
    OCR2
    a tutaj zamiast TIMSK i bitów TOIE2 jest przedstawiony bit TOIE0

    to samo z kolejnością rejestrów w opisie TIMER0:
    TCCR0
    TCNT0
    OCR0
    i tutaj TOIE2

    Wskazuje to, że Atmel pomylił rejestry i nazwy timerów. Programując zgodnie z notą, ustawiamy złe wartości preskalera.
  • #4 9058307
    mirekk36
    Poziom 42  
    Ja mam notę PDF do tego procka z 2004 roku i jest w niej wszystko OK ;) .... ale teraz aż z ciekawości zajrzałem do tej noty ze strony Atmela z roku 2010 i rzeczywiście jest "drobna" pomyłka - polega tylko i wyłącznie na tym, że w spisie treści opisany jest Timer2 a wszystkie informacje opisane dotyczą timera0 ;) .... i później opisany jest Timer0 a za to wszystkie informacje dotyczą timera2 .... mała masakra im wyszła bo nawet nazwy rejestrów są pozamieniane w tych rozdziałach ale już np numery tabelek się zgadzają z tą starą prawidłową notą z 2004 roku ;) .... czyli wystarczy czytając o Timer2 myśleć o Timer1 (podobnie z kolejnym timerem 8bit) i będzie wszystko dobrze albo....

    .... albo zassać starszą notę PDF

    ale to wszystko i tak nie wyjaśnia dlaczego w swoim programie popełniłeś takie błędy już nie związane z samym preskalerem bo tu rozumiem, że nota wprowadziła w błąd.

    W pierwszym swoim programie napisałeś tak:

    
    	TCNT2 = 100;
    	TCCR2 = _BV(CS22)|_BV(CS21)|_BV(CS20);
    	//TIMSK = 0b1000000;
    	//sei();


    oczywiście rozumiem, że te dwie linie nie były zakomentowane i to tylko omyłkowo wkleiłeś taki kod - bo wtedy na prawdę by nie działało przerwanie. Ale jeśli były odkomentowane to i tak powinno to błędnie działać w związku z tym co napisałeś poniżej:

    damiano713 napisał:
    Uruchamia timer 2 i zapala diodę jeżeli wartość TCNT2 będzie większa od 100.
    Dzielnik ustawiony na 1024.


    Po pierwsze to korzystaj w programie np z tych makr _BV albo zapisu tego typu, gdy ustawiasz bity

    TIMSK = (1<<OCIE2);


    Po drugie - wybrałeś przerwanie typu "CompareMatch" a odpalasz wektor przerwania "TIMER2_OVF_vect" od przepełnienia czyli niewłaściwy.... w efekcie program się resetuje wciąż a dioda nie miga - czyli program działa bardzo dobrze zgodnie z tym jak został napisany. Ale powiem więcej - powyżej napisałeś, że dioda będzie się zapalać gdy TCNT2 będzie większe od 100 to już niestety całkiem bzdura przy takim zapisie :( .... podejrzewam tylko, że niezbyt dokładnie przeczytałeś o 2 podstawowych trybach pracy timerów: Tryb Licznika i Tryb CTC. Zachciało ci się tutaj chyba skorzystać z dobrodziejstw trybu CTC ale całkowicie poplątałeś rejestry i ustawienia co właśnie tłumaczy, że nie zrozumiałeś do końca działania tego trybu.... musiałoby to być ustawione tak:

    
    	OCR2 = 100;
    	TCCR2 = _BV(CS22)|_BV(CS21)|_BV(CS20);
    	TIMSK = (1<<OCIE2);
    	sei();

    (tylko ustaw preskaler teraz poprawnie)

    a do tego takie przerwanie:

    
    ISR(TIMER2_COMP_vect)
    {
    	PORTC &= ~_BV(PC2);
    }
    


    i wtedy ci zadziała. A jak chcesz korzystać uparcie z przerwania od przepełnienia to w samym przerwaniu trzeba za każdym razem przeładowywać TCNT2 a nie tak jak to ty niefrasobliwie sobie zrobiłeś ;) tylko przy inicjalizacji timera2

    Poza tym skąd u ciebie w kodzie i po co te wszystkie _delay_ms() ???? do czego ci one tutaj??? tak samo do czego ci ten NOP w asemblerze w przerwaniu ??? to jakieś nieporozumienie chyba albo nie wiem co tu miałeś na myśli.
  • #5 9058336
    damiano713
    Poziom 15  
    Cytat:
    Ja mam notę PDF do tego procka z 2004 roku i jest w niej wszystko OK

    Najchętniej otukł bym komuś głowę, z tego Atmela. :P
    Zwykle najnowsza wersja czegoś, nie psuje tego, co już było poprawne w wersji poprzedniej. ....grrrr....

    Cytat:
    oczywiście rozumiem, że te dwie linie nie były zakomentowane i to tylko omyłkowo wkleiłeś taki kod - bo wtedy na prawdę by nie działało przerwanie.

    Po prostu zostały z poprzednich wersji programu. Gdzie miały sens. Tych linii nie powinno być. Poza tym wpisanie wartości na sztywno było po to, żeby wywnioskować czy definicje w AVR-LIBC są poprawne.

    Cytat:
    Poza tym skąd u ciebie w kodzie i po co te wszystkie _delay_ms() ???? do czego ci one tutaj??? tak samo do czego ci ten NOP w asemblerze w przerwaniu ??? to jakieś nieporozumienie chyba albo nie wiem co tu miałeś na myśli.

    To jest związane ze specyfikacją sprzętu na jakim pracuje. Jest tam zewnętrzny watchdog, który musze resetować pinem PC3. Dioda na PC2 służy do sygnalizacji, czy układ żyje. Po włączeniu migam nią, żeby zobaczyć, czy układ żyje.
    Wszystkie nop'y są użyte jako znaczniki w kodzie asm. Łatwo w listningu widać czy kompilacja zgadza się z tym co napisałem w C.
  • #6 9058349
    mirekk36
    Poziom 42  
    A no to rozumiem już te delay'e i nop'y ;)

    Ale co z tym trybem CTC - tu ci się właśnie coś nie pomieszało ? Bo to w kodzie wyglądało jako całkiem odrębny błąd poza tymi preskalerami.
  • #8 9058399
    mirekk36
    Poziom 42  
    no tak ;) jeszcze jak się napisze tak:

    0b1000000

    zamiast tak

    0b01000000

    to nie łatwo o pomyłkę ;)

    ale w takim razie pozostaje jeszcze sprawa przeładowywania wartości TCNTx w przerwaniu skoro nie korzystasz z CTC.
  • #9 9058409
    damiano713
    Poziom 15  
    Cytat:
    ale w takim razie pozostaje jeszcze sprawa przeładowywania wartości TCNTx w przerwaniu skoro nie korzystasz z CTC.

    Nie przeładowuje, ponieważ jeżeli w ogóle wejdzie w przerwanie to znaczy, że timer działa. Dioda się zaświeci i będzie w takim stanie przez wieczność. :)

    Pozdrawiam
  • #10 9058425
    Konto nie istnieje
    Konto nie istnieje  
  • #11 9058439
    damiano713
    Poziom 15  
    Mnie ten błąd dużo problemów nie sprawił. Jestem doświadczony w programowaniu tych ich AVR, potrafię sobie radzić.
    Natomiast, dla kogoś kto zaczyna taki błąd jest nierozwiązywalny. Mikrokontroler zachowuje się niezgodnie z oczekiwaniami. Leci wtedy po nowy egzemplarz do sklepu, który działa dokładnie tak samo.

    Napisałem już w tej sprawie do szanownej firmy Atmel.
  • Pomocny post
    #12 9058492
    Konto nie istnieje
    Konto nie istnieje  
  • #13 9058499
    mirekk36
    Poziom 42  
    damiano713 napisał:
    Jestem doświadczony w programowaniu tych ich AVR, potrafię sobie radzić..


    Nie chcę tu oceniać kolegi doświadczenia (dlatego to co dalej napiszę to nie do ciebie), ale niestety takie pisanie programów gdzie wykorzystuje się zapisy o których mowa wyżej, np:

    Cytat:
    rejestr = 0b10000;


    czy np:

    Cytat:
    rejestr = 0x0a;
    rejestr = 0xfd;

    (takich przykładów i złych nawyków można by tu jeszcze mnożyć bez końca)

    należą zdecydowanie do najgorszych praktyk programowania, to wręcz niedbalstwo żeby nie powiedzieć niechlujstwo ;) przy całych możliwościach jakie daje język C żeby pisać bardzo przejrzyście kod programu. A szczególna zgroza w tego typu pisania programów wypływa wtedy gdy się zaczyna pracować w zespole a nie samemu w pojedynkę. Na szczęście praca w zespołach szybko albo uczy porządku albo eliminuje z pracy zespołowej takich niereformowalnych "programistów".

    Nie wspomnę już że stwierdzenie iż tylko początkujący mają problemy z takim zapisem, tzn z jego odczytywaniem - świadczy niestety że pisze to początkujący albo zaawansowany ale z całym bagażem złych nawyków ;)
  • #14 9058650
    Konto nie istnieje
    Konto nie istnieje  
REKLAMA