2017-02-17 19 views
5

Piszę przerwania mikrokontrolera, który musi dodać przesunięcie do jednego z jego liczników sprzętowych. Jednak ze względu na sposób, w jaki działa preskalator czasowy, podejście naiwne może wprowadzić błąd polegający na wystąpieniu pojedynczego błędu w zależności od czasu wykonania przerwania względem zegara prescalerowego.Jak mogę uniknąć tego błędu "jeden po drugim" podczas dodawania przesunięcia do preschowanego zegara sprzętowego?

timing diagram of ISR off-by-one error

używam timera 1 na ATmega328P (= Arduino) dla tego. Mam go skonfigurowany w trybie normalnym przy pomocy prescaler'a/8 i używam przerwania przechwytywania zegara, aby to uruchomić; celem przerwania jest ustawienie timera na przepełnienie dokładnie cykli period po zdarzeniu, które wyzwala wychwycenie wejścia (w przypadku, gdy wyzwalanie wystąpi podczas innego przerwania lub innej sytuacji, w której przerwania są wyłączone).

(Nadużywam wyjścia PWM do wyzwalania dwóch optotriaków sieci przy zmiennym przesunięciu fazy AC, bez potrzeby spalenia całego czasu procesora na nim, przerwanie jest wyzwalane przez detektor przejścia przez zero w fazie sieci.)

Kod dla ISR byłoby coś takiego:

uint_16 period = 16667; 

ISR(TIMER1_CAPT_vect){ 
    TCNT1 = TCNT1 - ICR1 - period + (elapsed counter ticks during execution); 
} 

krytyczny przedział tutaj jest jeden między gdy TCNT1 jest odczytywanie i gdy jest on następnie ponownie zapisywane.

Z tego co wiem, nie ma sposobu, aby bezpośrednio odczytać stan preskulatora, więc nie sądzę, że możliwe jest zastosowanie innego przesunięcia na podstawie taktowania ISR.

Mogłem tylko zresetować preskator zanim ISR (GTCCR |= _BV(TSM); GTCCR |= _BV(PSRSYNC); GTCCR &= ~_BV(TSM);) zsynchronizował, ale nadal wprowadza losowe przesunięcie do timera, który zależy od taktowania ISR.

Innym podejściem, które rozważam jest użycie timera do generowania przerwania zsynchronizowanego z preskalerem. Używam już obu rejestrów porównania wyników na zegarze 1, ale timer 0 dzieli się z preskalerem, aby mógł być użyty. Jednak wykonanie przerwania zegara może w końcu zostać odłożone przez inne przerwanie lub blok "cli", więc nie ma gwarancji, że zadziała.

Jak mogę napisać przerwanie, aby uniknąć tego błędu?

+1

Zakładam, że obliczenia 'TCNT1' nie są al wyłączone przez jeden (czas 2?), Tylko _sometimes_ (taktowanie 1)? – chux

+0

@chux tak, 'TCNT1' jest czasami wyłączony o jeden. Nie jestem pewien, jak wyjaśnić to w słowach, ale diagram czasowy, który zawierałem, pokazuje, jak to się dzieje. – AJMansfield

+0

Czy procedura ISR jest wystarczająco szybka, aby z łatwością wykryć impuls "clk_Tn"? Czy 'clk_Tn' może ustawić flagę możliwą do usunięcia przez ISR? – chux

Odpowiedz

1

Jeśli piszesz ISR jako

ISR(TIMER1_CAPT_vect){ 
    int counter = TCNT1 - ICR1 - period + 3; 
    asm("nop"); 
    asm("nop"); 
    TCNT1 = counter; 
} 

pisanie TCNT1 powinno nastąpić dokładnie 24 cykli po czym odczytać rejestr, co w tym samym preskalera „fazy”. (Liczba nopów może być skorygowana, jeżeli jest to konieczne, np. Z powodu różnic między różnymi typami mikrokontrolerów). Rozwiązanie to nie jest jednak w stanie uwzględnić zmiany w "fazie" preskaliatora między wartością ICR1 a odczytem TCNT1.

Powiązane problemy