Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

Stroboskop z regulowanymi parametrami błysków na Atmega16

AdamZad 21 Sty 2013 15:14 1755 12
  • #1 21 Sty 2013 15:14
    AdamZad
    Poziom 12  

    Witam

    Skonstruowałem stroboskop (schemat, płytka, obudowa).
    Niebawem zrobię i wrzucę całość, nie działa mi poprawnie jednak program.
    Czy ktoś mógłby rzucić okiem ?


    Programuję w Codevision Atmege16.
    Ten prosty kod powinien dawać rezultat taki że po nadejściu sygnału z portu czeka około 7ms i wysterowuje na 1ms wyjście mikrokontrolera PORTB.0 (taktowanego 1Mhz wewnętrznym generatorem) po czym odczekuje
    około 63 ms.
    Listing :

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Jak tylko uporam się z programem zamieszczę tutaj całą tą konstrukcję.
    Pomocy!!!

    0 12
  • #2 21 Sty 2013 15:34
    AdamZad
    Poziom 12  

    Uważam że nie , ponieważ aby wyzwolić pojedynczy błysk potrzeba jest w odpowiednim momencie podać impuls wyzwalający na palnik (odpowiedni moment względem napięcia sinusoidalnego).

    0
  • #4 21 Sty 2013 16:09
    BlueDraco
    Specjalista - Mikrokontrolery

    Tak użyte przerwanie nie ma sensu. Albo rób wszystko w przerwaniu timera, albo zrezygnuj z przerwania i testuj znacznik przepełnienia timera w pętli głównej. To, co masz w tej chwili, to w zasadzie przykład, do czego ie należy używać przerwań.

    Ponadto typ char (zmienna y) może być zaimplementowany jako bez znaku lub ze znakiem. W tym drugim przypadku wyrażenie (y < 250) będzie zawsze prawdziwe. Powinieneś użyć typu uint8_t.

    0
  • #5 21 Sty 2013 17:34
    AdamZad
    Poziom 12  

    Dlaczego nie ma sensu? W procedurze obsługi przerwania inkrementuję tylko zmienną y. W pętli głównej sprawdzam instrukcją "while" kiedy y narośnie do odpowiedniej wartości.

    Uwagę ostatnią tj. "Ponadto typ char (zmienna y) może być zaimplementowany jako bez znaku lub ze znakiem. W tym drugim przypadku wyrażenie (y < 250) będzie zawsze prawdziwe. Powinieneś użyć typu uint8_t."

    Rozumiem w 100%. Proszę jednak o wyjaśnienie dot.pytania 1.

    0
  • #7 21 Sty 2013 17:59
    AdamZad
    Poziom 12  

    Tak jak napisałem u góry : Codevision AVR

    0
  • #8 21 Sty 2013 18:24
    piotrva
    Moderator na urlopie...

    Ok, nie zauważyłem, umieść ten kod w znacznikach syntax.
    Zapisuj te konfiguracje z wykorzystaniem przesunięć bitowych.
    Nich timer jeśli już zmniejsza wartość licznika ustawionego na ustawiony odcinek czasu.
    Wykorzystaj tryb CTC do odmierzania czasu.
    Do wykrywania impulsu wykorzystaj przerwanie zewnętrzne.

    0
  • #9 21 Sty 2013 19:02
    BlueDraco
    Specjalista - Mikrokontrolery

    Skoro kręcisz się w pętli testowania stanu y, to równie dobrze można również w głównym programie inkrementować tę zmienną na podstawie ustawienia znacznika przepełnienia timera w pętli głównej - kod będzie w ten sposób krótszy o obsługę przerwania.
    Ponieważ całe urządzenie nie robi praktycznie nic poza czekaniem na upłynięcie czasu, a to, co robi po upłynięciu tego czasu, jest trywialne i b. szybkie, a więc należy to zrobić w przerwaniu. Pętli głównej mogłoby nie być, czyli mogłaby ona tylko usypiać procesor, ew. może ona uruchamiać timer po wykryciu wyzwolenia (ale to równie dobrze da się zrobić w przerwaniu zmiany stanu portu).

    Tak , jak jest teraz, procesor zasuwa w kółko w głupich pętelkach w pętli głównej i dodatkowo męczy się z przerwaniami timera, których użyteczne akcje zajmują ułamek czasu potrzebnego na wywołanie i powrót z procedury obsługi przerwania. Oczywiście to działa, ale jaki jest cel takiej dziwacznej dekompozycji, poza pochwaleniem się tym, że się umie programować z niewielkim użyciem przerwań?

    Przerwań w tego typu projektach używa się w dwóch celach:
    - Zrobienia czegoś "w tle", gdy mamy pracochłonny główny wątek - tutaj to nie zachodzi.
    - Zaoszczędzenia energii przez uśpienie procesora, gdy z rzadka mamy coś do zrobienia - to jest właśnie ten przypadek.

    Na współczesnych procesorach robi się to bez żadnej "pętli głównej", bo procesor usypia na dobre jedną instrukcją, z której już nie wychodzi - program główny kończy się uśpieniem. W starszych procesorach umieszczamy instrukcję uśpienia w pętli, w której nic poza tym nie ma. Są też przypadki, gdy w pętli coś jeszcze jest poza uśpieniem, ale to dość specyficzna technika, stosowana głównie w procesorach z jednopoziomowym systemem przerwań (czyli np. AVR).

    0
  • #10 22 Sty 2013 00:48
    AdamZad
    Poziom 12  

    Bardzo dziękuję Panowie.
    Postanowiłem że zrobię ten program zgodnie z sugestiami piotrva.
    I spróbuję zastosować wszystkie wskazówki.

    Proszę o sprawdzenie poprawności samodokumentującego się pierwszego wycinka kodu (szczególnie zależy mi na info czy dobrze wykorzystuję operatory bitowe):

    Kod: c
    Zaloguj się, aby zobaczyć kod

    DDRB&=~(1<<DDRB.2); // inaczej : ustawia DDRB=0xfb czyli ustawienie PB0 jako wyjścia oraz PB2 jako wejścia cyfrowego
    PORTB&=~(1<<PORTB.2); // inaczej : ustawia PORTB=0xfb czyli wylaczenie rezystora podciagajacego wewnetrznego (jest montowany hardware'owo)
    Kod: html4strict
    Zaloguj się, aby zobaczyć kod
    [/syntax]

    Proszę o sprawdzenie poprawności kolejnego drugiego wycinka kodu oraz odpowiedź w jaki sposób najlepiej likwidować błąd typu brak dostępu do konkretnego bitu - zaznaczam że dotyczy to tylko rejestru TIMSK, wszystko resztę kompilator łyka.
    Gdy zaś robię TIMSK|=(1<<OCIE0) to także widzi błąd bo nie wie co to OCIE0. :

    Kod: c
    Zaloguj się, aby zobaczyć kod

    TIMSK|=(1<<TIMSK.1) // inaczej TIMSK = 0x01 czyli inicjalizacja przerwania wywołanego przepełnieniem timera0 (bit OCIE0=1)
    TCCR0|=(1<<TCCR0.0) // czyli TCCR0 = 0x01 czyli bity CS00=1 CS01=0 CS02=0 czyli bez preskalera z wewnetrznego 1MHz
    TCCR0|=(1<<TCCR0.3) // czyli ustawia tylko WGM01=1 czyli mamy WGM01=1 WGM00=0 zatem tryb CTC
    OCR0|=(1<<OCR0.6)|(1<<OCR0.5)|(1<<OCR0.2)|(1<<OCR0.1)|(1<<OCR0.0) // czyli OCR=0x47 czyli ustawienie OCR0 na wartość 199
    Kod: html4strict
    Zaloguj się, aby zobaczyć kod


    Kolejne pytanie : czy w trybie Compare Mode jeśli licznik dojdzie do wartości z rejestru OCR0 to czy po wykonaniu obsługi przerwania skasuje się na zero, czy powinienem to zrobić właśnie w obsłudzie przerwania ?
    Oczywiście odpowiedzi prosiłbym umotywować - zapamiętam i nie będę drugi raz pytać (pewnie pisze w datasheet ale to sporo szukania, zrozumcie proszę)

    Piotrva nie zrealizowałem jedynie ostatniego zalecenia "Do wykrywania impulsu wykorzystaj przerwanie zewnętrzne"
    Powód : mam już płytkę pod to zrobioną (w ogóle już mi to kiedyś migało :D) i jak na pin trzeci portu B czyli PINB.2 przychodzi impuls wtedy ma rozpocząć odmierzanie czasu....
    Pojawia się pytanie : czy można to zrobić z użyciem powyższego pinu czy tylko z użyciem INT0 na nóżce PORT'u D PD.2 ?
    Jeśli się da proszę w skrócie opisać jak.
    Jeśli się nie da to czy dobrym rozwiązaniem byłoby teraz już sprawdzać to instrukcją warunkową :





    Kod: c
    Zaloguj się, aby zobaczyć kod

    if (PINB.2==0)
    Kod: html4strict
    Zaloguj się, aby zobaczyć kod


    Będę naprawdę wdzięczny za pomoc.

    A tutaj cały listing nieco już zmodyfikowanego programu:

    Kod: c
    Zaloguj się, aby zobaczyć kod

    // I/O register definitions for ATMEGA16
    #include <mega16.h>

    volatile unsigned char y;

    void init_timer(void) //Inicjalizacja timera
    {
    TIMSK|=(1<<TIMSK.1) // inaczej TIMSK = 0x01 czyli inicjalizacja przerwania wywołanego przepełnieniem timera0 (bit OCIE0=1)
    TCCR0|=(1<<TCCR0.0) // czyli TCCR0 = 0x01 czyli bity CS00=1 CS01=0 CS02=0 czyli bez preskalera z wewnetrznego 1MHz
    TCCR0|=(1<<TCCR0.3) // czyli ustawia tylko WGM01=1 czyli mamy WGM01=1 WGM00=0 zatem tryb CTC
    OCR0|=(1<<OCR0.6)|(1<<OCR0.5)|(1<<OCR0.2)|(1<<OCR0.1)|(1<<OCR0.0) // czyli OCR=0x47 czyli ustawienie OCR0 na wartość 199
    }

    interrupt [TIM0_COMP] void timer0_compa_isr(void)
    {
    // flaga od comparatora kasuje się tutaj sama (datasheet) w momencie wejścia w procedurę obsługi przerwania
    y--;
    }


    void main(void)
    {

    DDRB&=~(1<<DDRB.2); // inaczej : ustawia DDRB=0xfb czyli ustawienie PB0 jako wyjścia oraz PB2 jako wejścia cyfrowego
    PORTB&=~(1<<PORTB.2); // inaczej : ustawia PORTB=0xfb czyli wylaczenie rezystora podciagajacego wewnetrznego (jest montowany hardware'owo)

    init_timer();
    #asm("sei");

    while(1)
    {
    if (PINB.2==0)
    {

    // czekanie 8ms
    y=40; while (y);

    PORTB.0=1; //impuls

    // czekanie 1ms
    y=5; while (y);

    PORTB.0=0; //koniec impulsu

    // czekanie 100ms
    y=250; while (y);
    y=250; while (y);
    }
    }

    #asm("cli");
    }

    0
  • #11 22 Sty 2013 01:04
    piotrva
    Moderator na urlopie...

    1. Z tymi przesunieciami robisz to źle - podejrzyj sobie jakiś normalny kod w C. O to chodzi żeby widzieć od razu za co są odpowiedzialne ustawiane bity, a nie które to bity. Chodzi o coś typu TCCR0A = (1<<CS01); - od razu widzę że włączam cs01, a nie muszę wiedzieć co to za bit...
    2. Licznik TCNT sam o siebie zadba w trybie CTC.
    3. Zezwalasz na złe przerwanie
    4. Musisz zacząć porządnie od podstaw naukę, pomyśl nad zmianą kompilatora na coś klasycznego, czyli avr-gcc i do tego dobre ide.

    0
  • #12 22 Sty 2013 01:28
    AdamZad
    Poziom 12  

    Zgadza się, zmienię ten kompilator niebawem.
    Oprogramowanie Codevision otrzymałem razem z programatorem.

    Nie wiem czy mój programator będzie współpracował z tego typu kompilatorem który proponujesz... :/ Ale zapytam sprzedawcę co i jak.

    W związku z operatorami bitowymi to chyba już rozumiem w czym rzecz.
    Jeśli będę miał możliwość pracy w innym środowisku gdzie kompilator akceptuje tego typu wyrażenia to napewno skorzystam z tego (mniej komentarzy będzie).

    Czy takie ustawienia i takie zastosowanie operatorów bitowych jest poprawne i Waszym zdaniem optymalne dla "oka" ?

    Kod: c
    Zaloguj się, aby zobaczyć kod

    0
  • #13 22 Sty 2013 08:33
    BlueDraco
    Specjalista - Mikrokontrolery

    Nadużywasz |= - tak, jak byś nie wiedział, co jest w rejestrze po resecie i bałbyś się to zmienić. Po prostu zapisz do rejestru to, ca ma tam być, np.

    TCCR0 = 1 << WGM01 | 1 << CS0;

    W ten sposób oglądając kod za pół roku będziesz wiedział dokładnie, co jest w rejestrze i nie będziesz szukał po całym programie, w którym miejscu wpisałeś który bit.

    Co lepsze modele procesorów mają coś takiego, jak "port change interrupt" - wtedy możesz zgłosić przerwanie od zmiany stanu dowolnego wejścia.

    Przyzwoicie zrobiony program wyglądałby tak:

    main - zainicjowanie timera bez włączania go, zainicjowanie przerwania od portu, pusta pętla z usypianiem.

    Przerwanie portu - włączenie timera i wyłączenie przerwania portu

    Przerwanie timera - kilka stanów, przejście do następnego stanu po zliczeniu określonej liczby przerwań. Przy zmianie stanu zmiana wyjścia, przy ostatniej zmianie stanu - zatrzymanie timera i włączenie przerwania portu.

    No i dlaczego ATmega16 a nie ATtiny13? - i nóg i pamięci wystarczy, cena i rozmiary - sporo mniejsze, no i ma przerwanie od zmiany stanu dowolnej linii portu.

    0