Dziękuję bardzo za ten kod. Przykład z wykorzystaniem union oraz struct również bardzo cenny!
Twój kod, tak na szybko, bez union i struct (chodzi o samą ideę):
SIGNAL (SIG_INPUT_CAPTURE3) { icr = ICR3;
if (ETIFR & _BV(TOV3)) { overflow++; ETIFR |= _BV(TOV3); ff++; // licznik zdarzeń tego typu }
actualDuration = ((unsigned long)icr + overflow * 65536UL) - pp; pp = (unsigned long)icr + overflow * 65536UL; }
SIGNAL (SIG_OVERFLOW3) { overflow++; }
Wyświetlam sobie na LCD wartość ff. Na ogół ma wartość "1" i tak już zostaje. Wynika z tego, że bardzo rzadko mam sytuację z jednoczesnymi przerwaniami.
Wydaje mi się, że możliwe jest jeszcze pewne uproszczenie -- w praktyce mi ono zdaje egzamin (a przynajmniej tak mi się wydaje na podstawie obserwacji). Chodzi o to, że u Ciebie "overflow" inkrementowany jest nieustannie. To nie jest błąd -- wszystko działa jak należy. Niemniej ja chciałem mieć "overflow" zerowany, gdyż zależnie od jego wartości chciałem zrobić odcinanie niskich częstotliwości mierzonego sygnału. Oto przeróbka:
*** KROK 1
SIGNAL (SIG_INPUT_CAPTURE3) { icr = ICR3;
if (ETIFR & _BV(TOV3)) { overflow++; ETIFR |= _BV(TOV3); }
actualDuration = ((unsigned long)icr + overflow * 65536UL) - pp; pp = (unsigned long)icr + overflow * 65536UL;
pp -= overflow * 65536UL; overflow = 0; }
powyższe można uprościć do takiej postaci:
*** KROK 2
SIGNAL (SIG_INPUT_CAPTURE3) { icr = ICR3;
if (ETIFR & _BV(TOV3)) { overflow++; ETIFR |= _BV(TOV3); }
actualDuration = ((unsigned long)icr + overflow * 65536UL) - pp; pp = (unsigned long)icr;
overflow = 0; }
Zatem wydaje mi się (może się mylę, ale w praktyce działa i wygląda OK), że w "pp" nie musimy trzymać wartości licznika software'owego, a wystarczy trzymać ICR. Co o tym myślisz?
A oto kompletny kod -- odcinamy poniżej ok. 2Hz (w szczególności, gdy sygnał w ogóle jest zerowy).
SIGNAL (SIG_INPUT_CAPTURE3) { icr = ICR3;
if (ETIFR & _BV(TOV3)) { overflow++; ETIFR |= _BV(TOV3); }
actualDuration = ((unsigned long)icr + overflow * 65536UL) - pp; pp = (unsigned long)icr;
overflow = 0; }
SIGNAL (SIG_OVERFLOW3) { overflow++; }
int main(void) { char buf[16]; unsigned long actualDurationLatch;
TCCR3A = 0; TCCR3B |= _BV(ICES3) | _BV(ICNC3) | _BV(CS32) | _BV(30); // prescaler 64 ETIMSK |= _BV(TICIE3) | _BV(TOIE3);
sei();
while (1) { cli(); actualDurationLatch = actualDuration; sei();
if ((overflow >= 5) || (actualDurationLatch > 150000)) { LCDwriteString("-------------"); } else { sprintf(s, "%ld ", actualDurationLatch); LCDwriteString(s); } } }
W moim pierwotnym liście podałem kod z wykorzystaniem SIG_OUTPUT_COMPARE2 oraz SIG_INTERRUPT7. Kod tamten miał (w porównaniu do tego z wykorzystaniem input capture) tę wadę, że zwiększanie zmiennej "timer" 100000 razy na sekundę obciążąło procesor. Niemniej miał on też pewną zaletę -- chodzi o to, że dla niskich częstotliwości "odcinanie" miałem zawsze w określonym czasie (np. gdy timer osiągnął wartość 100000; timer był resetowany po każdym zboczu narastającym -- zatem wartość 100000 mówiła, że minęło 100000 ticków zegara od ostatniego zbocza narastającego sygnału mierzonego). Tu (chodzi o kod z input capture) niestety jest problem. Gdy sygnał zaniknie nagle, to "overflow" musi doliczyć do 5 (taką wartość sobie przyjąłem), ale przecież ICR może mieć różną wartość i czas, w którym "overflow" doliczy do
5 może być różny (gdy ICR było bliskie 0, to będzie to czas prawie pięciu przepełnień ICR; gdy ICR było prawie 65000, to będzie to czas nieco ponad czterech przepełnień ICR). Aby mieć dokładne odcinanie (po upływie dokładnie określonego czasu), musiałbym zrobić dodatkowy timer (np. dający impuls co 1 sekundę), resetowany w SIG_INPUT_CAPTURE3. Jeśli timer da impuls (czyli nie został zresetowany w ciągu ostatniej sekundy), to znaczy, że sygnał jest poniżej 1Hz i robimy odcinanie.
R.