ATMega16, GCC, problem z przerwaniami od timera

Jul 28, 2005 13 Replies

Prubuję uruchomić taki program:



<code>

#include <avr/io.h>



#include <avr/interrupt.h>



#include <avr/signal.h>



SIGNAL(SIG_OVERFLOW0) { PORTB = 0xff; PORTD = 0xff; __asm__ __volatile__ ("reti" ::); }



int main() { MCUCSR |= 0x80; MCUCSR |= 0x80; DDRA = 0xFF; DDRB = 0xFF; DDRC = 0xFF; DDRD = 0xFF; PORTB = 0x00; PORTD = 0x00; TCNT0 = 0x00; /* CTC; clk_IO/1024 (From prescaler) */ TCCR0 = 0 | (0 << WGM00) | (1 << WGM01) | (5 << CS00); /* 4194304Hz / (2 * 1024 * (1 + (OCR0)1)) = 1024Hz */ OCR0 = 1; /* Timer/Counter0 Output Compare Match Interrupt Enable */ TIMSK = 0 | (1 << OCIE0) | (0 << TOIE0); SREG |= (1 << SREG_I); while(1) { asm volatile("nop" ::); } return 0; }



</code>

Niestety stan linii portu B i D nie zmienia się (pozostaje 0 na wszystkich liniach). Porty są sprawne, bo po umieszczeniu PORTB = 0xff i PORTD = 0xff w main() wszystko działa poprawnie (porty przechodzą w stan H).



Docelowo program ma podawać odpowiednio spreparowane dane z częstotliwością ok. 1000Hz (1024Hz jest ok). Utknąłem jednak w trakcie "odpalenia" przerwań.



Z góry dziękuję za wszelkie sugestie i informacje. Być może robię jakiś trywialny błąd, ale siędzę nad tym już od dłuższego czasu i brak mi pomysłów.



Pozdrawiam!


[...]

Trochę poprawiłem, powinno ruszyć:

#include <avr/io.h>

#include <avr/interrupt.h>

#include <avr/signal.h>

SIGNAL(SIG_OVERFLOW0) { TCNT0=256-4; PORTB = 0xff; PORTD = 0xff; }

void main(void) { MCUCSR |= 0x80;

DDRA = 0xFF; DDRB = 0xFF; DDRC = 0xFF; DDRD = 0xFF;

PORTB = 0x00; PORTD = 0x00;

TCNT0 = 256-4; // przerwanie co 4*1024 cykle ~1024Hz

/* CTC; clk_IO/1024 (From prescaler) */ TCCR0 = (5 << CS00);

/* Timer/Counter0 Output Compare Match Interrupt Enable */ TIMSK = (1 << TOIE0);

sei();

while(1); }

Daniel Jaworski napisał(a):

jeśli używasz licznika z porównywaniem wartości OCR0 to powinieneś raczej obsługiwać SIG_OUTPUT_COMPARE0, nie przepełnienie licznika. nieobsłużone przerwanie spowoduje zatrzymanie programu.

to nie jest potrzebne. co więcej, prolog funkcji na pewno odłożył trochę danych na stosie, więc na jego szczycie na pewno nie ma adresu powrotu tylko coś w stylu zawartość rejestrów w momencie wywołania przerwania. program pójdzie w maliny.

co prawda to nie błąd, ale masz od tego makro sei().

wystarczy opuścić main(), program sam się zapętli.

w.

Genialnie. Ruszyło :-). Dalej jednak nie wiem dlaczego licznik nie chciał liczyć w trybie CTC.

Pozdrawiam!

I dokładnie o to chodziło... dziękuję wszystkim za pomoc. Źle zrozumiałem sposób działania licznika i myślałem, że zawsze zgłaszane jest SIG_OVERFLOW0, a to co je wywołuje zależy od ustawienia rejestrów. Teraz wszystko jest jasne. Jeszcze raz dziękuję.

Racja. Ale w akcie "desperacji" próbowałem wszystkiego i to się po tych próbach ostało.

OK - jest już via makro.

Pozdrawiam!

Korzystając z okazji chciał bym spytać się jeszcze o jedno.

Zgodnie z notą Atmela. Częstotliwość wyjściowa licznika w trybie CTC wynosi: f_OCn = f_clkIO / (2 * N * (1 + OCRn))

Zgodnie z tym wnioskuję, że częstotliwość przerwań będzie wynosić 2 razy tyle. Na jeden okres przebiegu trzeba dwa przerwania w celu wygenerowania obu zboczy.

Zatem z w/w wzoru wywalam 2.

Podstawiam to co mam: f_clkIO = 4194304Hz N = 1024 (preskaler) OCR0 = 204

4194304 / (1024 * (1 + 204)) = 4096 / 205 = 20 Hz

Tymczasem przerwania pojawiają się 4 razy mniejszą częstotliwością (ok.

5Hz). Te 5Hz pochodzi z obserwacji organoleptycznych :-) więc może być niedokładne. O co chodzi?

Bardzo proszę o wskazówkę.

Pozdrawiam!

Daniel Jaworski napisał(a):

wygląda dobrze, ale może w kodzie jest błąd? pokaż, jak ustawiasz timer.

w.

TCNT0 = 0x00; /* CTC; clk_IO/1024 (From prescaler) */ TCCR0 = 0 | (0 << WGM00) | (1 << WGM01) | (1 << CS02) | (0 << CS01) | (1 << CS00); /* 4194304Hz / (1024 * (1 + (OCR0)1)) = 1024Hz */ OCR0 = 204; /* Timer/Counter0 Output Compare Match Interrupt Enable */ TIMSK = 0 | (1 << OCIE0) | (0 << TOIE0);

Tak ustawiam timer.

Upewniej siê, ¿e procek pracuje z zewnêtrznego oscylatora, a nie z generatora RC (domy¶lnie 1MHz). Od tego s± odpowiednie fusebity...

EM wrote: [...]

To jest bardzo rozsądny trop. Tylko czy te bity przestawię moim programatorem...?

Pozdrawiam,

ispprog ma mozliwosc modyfikacji fuse bitow.

Spróbuj ISP Programmerem:

formatting link

Tylko 10x przed ich przestawieniem sprawdz ich ustawienie, bo kolejny post zatytulujesz - Ratunku!! Moja ATMega zdechla;) Uwaga praktyczna - czasami bit=1 to bit skasowany w ustawieniach programatora (0) i vice versa.

Atmel w swojej dokumentacji nazywa bitem skasowanym taki o wartości '1' a bitem zaprogramowanym bit o wartości '0'. Wynika to z budowy komórki pamięci Flash, której skasowanie nadaje wartość '1'. Tego się trzeba trzymać i nie będzie problemów. Jeżeli na przykład piszą, że CLKOUT=1 to taką wartość trzeba ustawić w programatorze. ISP Programmer "ptaszkami" oznacza wartość fusebitu '1', dodatkowo podaje bajt fusebitów szestnastkowo aby każdy mógł zweryfikować to przed zaprogramowaniem.

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required