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

[ATmega8][C]Obsługa przerwania tryb CAPTURE vs. EXTERNAL INTERRUPT

rozekk 10 Maj 2011 14:56 2806 15
  • #1 9491692
    rozekk
    Poziom 11  
    Cześć. Napisałem swój pierwszy program na przerwaniach, ale nie działa on do końca tak jak zamierzałem.
    Układ jest taki, że pod port B mam podpiętą diodę/diody, a pod wejście wywołujące przerwanie PD3 (INT1) mam podłączony oscylator o zmiennej częstotliwości w zakresie od 0 do +/- 30Hz.
    Program miał polegać na tym, że powyżej granicznej częstotliwości zapala się dioda, a poniżej jest zgaszona.
    Gdy kręcę częstotliwość w górę dioda ładnie się zapala za każdym razem, gdy kręcę w dół powoli - dioda gaśnie, jednak gdy skręcę częstotliwość nagle do 1-2Hz dioda dalej się świeci i gaśnie po 2-3 cyklach, lub czasem nawet wcale nie gaśnie. gdy wrócę częstotliwością góra-dół powoli wszystko wraca do normy.

    Dlaczego układ może się tak zachowywać?

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • #2 9491738
    dondu
    Moderator na urlopie...
    Witaj,
    Za co jest odpowiedzialny Timer0?
    Za określenie częstotliwości granicznej?

    Czy masz wolny Timer 1?
    Jeżeli tak, to moim zdaniem strasznie sobie to komplikujesz.
    W końcu Twoim zadaniem jest zmierzenie okresu przebiegu o częstotliwości, którą podajesz z generatora, a od tego jest tryb Capture, czyli pomiar długości impulsu.
    strona 83: Input Capture Unit

    Przy okazji w GCC są rozkazy sei() oraz cli() więc nie musisz używać wstawek assemblera, a przerwania po resecie są wyłączone więc asm(cli) jest zbędne.
  • #3 9492152
    rozekk
    Poziom 11  
    tak, i w datasheecie rzeczywiście przeczytałem że jest taki tryb o którym mówisz dla timera1. Niebardzo tylko wiem jak go uzależnić od częstotliwości podawanej na jedno z wejść...

    Dajmy na to że na PD2 chcę podawać częstotliwość i chcę żeby timer1 zliczył czas trwania impulsu, zapisał a program porównał ją z wartością graniczną.

    Idąc za wzorem w datasheecie mam coś takiego, ale nigdzie nie mam powiedziane skąd pobierany jest sygnał...

    [syntax=c#include <avr/io.h>
    #include <avr/interrupt.h>

    #define WYJSCIE 0xff
    #define WEJSCIE 0x00
    volatile int zegarh,zegarl,przepelniony;

    ISR(TIMER1_OVF_vect)
    {
    przepelniony=1;
    }

    ISR(TIMER1_CAPT_vect)
    {
    if(przepelniony>=1)
    {
    zegarh=255;
    przepelniony=0;
    }
    else
    {

    zegarl = TCNT1L;
    zegarh = TCNT1H;
    }
    TCNT1H = 0x00;
    TCNT1L = 0x00;
    }


    int main()
    {
    TCCR1B = (1<<CS12)|(1<<CS10); //preskaler 1024
    TIFR = 1<<ICF1;
    TIMSK |= ((1<<TICIE1) | (1<<TOIE1)) ; //enable przerwań od przepełnienia i trybu capture
    DDRB = WYJSCIE;
    DDRD = WEJSCIE;


    sei();

    for(;;) //pętla nieskończona programu
    {
    if(zegarh<=70) //wartość graniczna dla zapalenia diody
    {
    PORTB=0x00;
    zegarh=0;
    }
    else
    {
    PORTB=0xff;
    }

    }

    }[/syntax]
  • #4 9492192
    dondu
    Moderator na urlopie...
    rozekk napisał:
    Niebardzo tylko wiem jak go uzależnić od częstotliwości podawanej na jedno z wejść...

    Dajmy na to że na PD2 chcę podawać częstotliwość i chcę żeby timer1 zliczył czas trwania impulsu, zapisał a program porównał ją z wartością graniczną.

    Idąc za wzorem w datasheecie mam coś takiego, ale nigdzie nie mam powiedziane skąd pobierany jest sygnał...


    Jak dobrze znasz angielski w zakresie rozumienia datasheetów?
  • #5 9492300
    rozekk
    Poziom 11  
    ok, wybacz. Po prostu miałem otwarty jakiś help do timerów atmega8 a nie całą atmege i tam nie bylo napisane który to pin.

    Zatem załączam bit wywołujący przerwanie od przechwycenia i tryb przechwycenia
    TIMSK |= ((1<<TICIE1) | (1<<TOIE1))

    Podłączam generator impulsów pod PB0(ICP1).

    w rejestrze TCCR1B określam prescaler i programuję ICES1 żeby reagował na zbocze narastające, włączam także reduktor szumów.

    Dalej następuje moje przerwanie w formie
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    czy to jest dobrze napisane? Bo oczywiście nadal zupełnie nie działa :)
  • #6 9492315
    dondu
    Moderator na urlopie...
    Nie pytałem by Ci dopiec, tylko chciałem stwierdzić na ile trzeba Ci tłumaczyć :)

    Nazwę pinu używaj taką jaką jego funkcje wykorzystujesz, więc nie pisz PBB0(ICP1) tylko ICP1. Wynika to z tego iż w innej obudowie lub innym procesorze może być na innym pinie niż PB0, a warto pisać tak aby było bardziej uniwersalnie.

    Napisz jak rozumiesz działanie tego trybu w Timer1?
  • #7 9492344
    rozekk
    Poziom 11  
    Gdy na wejściu ICP1 pojawia się impuls timer zaczyna liczyć czas.
    Jak pojawi się nowy impuls, rejestr ICR przechwytuje wartość timera i zeruje timer który zaczyna liczyć od nowa, itd.

    Gdy mam wartość timera w ICR przepisuję sobie ją do swoich zmiennych i na nich operuję.
  • #9 9492414
    rozekk
    Poziom 11  
    tak tak, mam też przerwanie od przepełnienia. gdy wystąpi ustawia ono flagę 'przepełnienie' na 1, a od tej flagi zależy co wysyłam na wyjście jak widać w ostatnim kodzie.

    (do tego jeszcze doczytałem ;) )
  • #11 9492497
    rozekk
    Poziom 11  
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Proszę :)

    diody palą się cały czas niezależnie od częstotliwości

    prescaler 1024
    częstotliwość 1MHz -> 1000000us
    możliwość modulacji 1-20Hz.
    czyli 50000-1000000us
    po preskalowaniu mamy na wejściu
    48-976 taktów timera na jeden puls wejściowy
  • #12 9500693
    rozekk
    Poziom 11  
    halo? Czy ktoś mógłby mnie jeszcze jakoś nakierować gdzie szukać błędów?

    Czy timer po wywołaniu przerwania capture jest zerowany czy muszę go wyzerować w obsłudze tegoż przerwania?

    Dodano po 13 [minuty]:

    a może on nie mierzy mi czasu peak-peak tylko czas trwania stanu wysokiego/niskiego?
  • #13 9501085
    Andrzej__S
    Poziom 28  
    Z tego co zauważyłem, mikrokontroler taktujesz częstotliwością 1MHz. Przy preskalerze 1024 częstotliwość taktowania timera będzie równa 1000000Hz/1024=976,5625Hz. Z tego wniosek, że w ciągu 1 sekundy timer zlicza 976,5625 impulsów. Biorąc pod uwagę, że timer jest 16-bitowy przepełni się 1 raz na (65536/976,5625) 67,108864 sekund. Jeśli nie musisz schodzić z częstotliwością poniżej 0,015Hz, to przepełnieniem nie musisz się przejmować. Nawet dla preskalera 256 wartość dolnej częstotliwości granicznej byłaby równa ok. 0,06Hz.
    Przerwanie od przepełnienia timera będzie więc praktycznie potrzebne tylko w przypadku, gdy przewidujesz na wejściu częstotliwości niższe, niż podane powyżej. Wtedy jednak wystarczy wykrywać podwójne przepełnienie (i tak nie do przyjęcia ze względu na drastyczny wzrost czasu reakcji), a okres impulsów wejściowych można mierzyć mniej więcej tak:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    i nie trzeba się przejmować przepełnieniem. Dla zmiennych typu unsigned wyrażenie okres = ICR1 - poprzedni_stan; zwróci prawidłową liczbę odliczonych przez timer impulsów nawet w przypadku, gdy zmienna poprzedni_stan będzie większa od ICR1.
    Należy tylko pamiętać, że przy takiej zasadzie detekcji częstotliwości (wykorzystanie Input Capture) przy nagłej zmianie częstotliwości w dół potrzebny jest czas mniej więcej jednego okresu tej aktualnie ustawionej częstotliwości na reakcję na wyjściu (zapalenie/zgaśnięcie diody) np. nagła zmiana z częstotliwości powyżej granicy na 0,25Hz czas reakcji diody wyniesie ok. 4 sekundy, przy zmianie na 0,05Hz - ok. 20 sekund.
  • #14 9501142
    rozekk
    Poziom 11  
    dzięki za odpowiedź :)
    należą się 2 słowa wyjaśnienia. Aktualny preskaler i częstotliwość podawaną na wejście dobrałem tak by moje oko mogło obserwować czy częstotliwość wejścia rzeczywiście się zmienia, a w zamyśle układ będzie pracował na częstotliwościach od 0.5kHz do 6kHz. Postaram się tak dobrać preskaler żeby się nie przepełniał, ale w razie czego dopisałem ten kod, wiele mnie nie kosztował ;)

    teraz co do Twojej obsługi capture. Przepraszam ale nie rozumiem jakim cudem to może działać, skoro na początku przerwania wpisuję w poprzedni_stan 0, potem na nim operuję czyli de facto nie robię nic bo ICR1-0=ICR1, by na koniec przypisać w poprzedni_stan ICR1, a gdy wejdę w to przerwanie po raz drugi i tak nadpisać je zerem... dobrze dumam?
  • Pomocny post
    #15 9501162
    Andrzej__S
    Poziom 28  
    Zmienne static są inicjowane przypisaną wartością tylko raz, na początku programu. Kolejne wejście do procedury obsługi przerwania nie powoduje ponownego przypisania im wartości; mają wartość taką, jak po zakończeniu poprzedniej obsługi przerwania.
  • #16 9501246
    rozekk
    Poziom 11  
    ok, kumam. i nawet działa :D kolega dondu miał rację że w ten sposób jest dużo dużo prościej i klarowniej.

    dzięki wielkie Panowie!

    temat zamknę, tylko tytuł poprawię na bardziej czytelny bo komuś się jeszcze może przydać.
REKLAMA