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.

Program zliczający impulsy enkodera

miccaldo 25 Gru 2012 21:48 3474 20
  • #1 25 Gru 2012 21:48
    miccaldo
    Poziom 12  

    Witam, mam za zadanie zrobić program zliczający impulsy enkodera, enkoder ma 100 działek/obrót.

    Chcę, aby po zliczeniu 100 impulsów zaświeciła się jedna część wyświetlacza(wspólna anoda), program wygląda następująco:


    Kod: c
    Zaloguj się, aby zobaczyć kod


    Enkoder nie zlicza impulsów, dioda 'A' pali się od razu, co tutaj zmienić aby za każdym razem kiedy na pinie do którego podciągnięty jest kanał enkodera będzie stan wysoki, program dodawał +1 do licznika?

    0 20
  • #2 25 Gru 2012 22:06
    Raphaw
    Poziom 20  

    Cytat:
    if(licznik = 100)
    {
    A;
    }

    licznik == 100 :) dlatego dioda się pali. Co za uc? schemat?

    0
  • #3 26 Gru 2012 10:46
    tmf
    Moderator Mikrokontrolery Projektowanie

    Musisz wykrywać zbocza. Pętla jest przmeiatana zapewne tysiące razy na sekundę, stąd też sekwencja if(PINB & 0x02) przy ustawionym pinie jest z łatwością wykonywana 100x i stąd natychmiast zapala się dioda. Zbocze wykryjesz xorując poprzedni stan pinu z obecnym. BTW, obsługa enkodera jest nieco bardziej skomplikowana.

    0
  • #4 26 Gru 2012 11:45
    miccaldo
    Poziom 12  

    No tak faktycznie te 100x zrobi się w mgnieniu oka... Więc program musi dodać +1 przy zmianie na stan wysoki i koniec, jednokrotnie. Może zastosować przerwania? W momencie kiedy wystąpi stan wysoki, następuje przerwanie które zlicza +1 do licznika. Czy to miało by sens? uC to attiny 2313.

    0
  • #5 26 Gru 2012 12:05
    tmf
    Moderator Mikrokontrolery Projektowanie

    Niespecjalnie, chyba, że to będzie przerwanie wyzwalane zboczem, a nie poziomem. Niemniej gorąco cię zniechęcam do robienia obsługi enkodera na przerwaniach wyzwalanych zboczami z pinów IO. Lepszym pomysłem jest przerwanie timera co określony czas i w nim badanie stanu pinów IO.
    Niemniej gotowca znajdziesz w darmowych przykładach do mojej książki (do pobrania z ftp - patrz moja stopka) - masz tam przykład enkodera, przeanalizuj jak działa.

    0
  • #6 26 Gru 2012 15:03
    miccaldo
    Poziom 12  

    Przyznam że niezbyt zrozumiały jest dla mnie ten program z obsługą enkodera, ciężko mi to ogarnąć wszystko po prostu. O co chodzi tak w skrócie z tym przerwaniem timera?

    Dodano po 3 [minuty]:

    Czytam teraz ciekawy artykuł o timerze 0, pisze że jego zadaniem jest zliczanie przychodzących do niego impulsów, jeśli to tak działa, to myślę że idealne rozwiązanie.

    Dodano po 57 [minuty]:

    Ok rozumiem już przerwania w timerze. Tylko mam jedno pytanie: zliczane impulsy są w rejestrze TCNT0(timer0), tylko jest to zliczanie w odstępie czasu, uzależnionego od preskalera i taktowania uC, w artykule co czytałem jest napisane co trzeba zrobić aby uzyskać przepełnienie w jedną sekundę itd. jednak niezupełnie o to mi chodzi, ja potrzebuję aby timer zliczał mi impuls zależnie od stanu jakiegoś bitu do którego podłączony mam enkoder. Czy jeśli podłączę enkoder powiedzmy do pinu OCA0, będzie możliwość zliczania tych stanów? Nie wiem czy idę w dobrą stronę, jeśli nie proszę mnie poprawić. Sądzę że jednak ta sprawa z przerwaniami timera nie jest jakoś strasznie trudna.

    Dodano po 1 [godziny] 4 [minuty]:

    Moje domysły o OCA0 z pewnością były błędne.

    Napisałem coś takiego:


    Kod: c
    Zaloguj się, aby zobaczyć kod

    Zamiast stanu wysokiego na pinie enkodera, sądzę że lepsza by była zmiana stanu tego pinu, jednak dioda pali się od razu, podobnie jak w pierwszym przypadku.

    0
  • #7 26 Gru 2012 16:12
    tmf
    Moderator Mikrokontrolery Projektowanie

    A dlaczego miałaby się nie palić skoro robisz to samo, tylko w bardziej dziwaczny sposób? Zajrzałeś do przykładów o których mówiłem?
    W przerwaniu timera masz badać stan wyjść A i B enkodera i dekodować ten stan z kodu Graya. Dodatkowo ciągle musisz zrobić wykrywanie zbocza. Co do timera to dopiero XMEGA ma sprzętowy dekoder kwadraturowy, zwykłe AVRy go nie mają i tą funkcjonalność trzeba zaimplementować programowo.

    0
  • #8 26 Gru 2012 16:12
    JarekC
    Poziom 27  

    Już w pierwszym poście miałeś informacje o błędzie w kodzie i dalej go powielasz:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    JarekC

    0
  • #9 26 Gru 2012 21:58
    miccaldo
    Poziom 12  

    Owszem zajrzałem do tych przykładów z enkoderem, ale nie rozumiem większości z tych programów. Nie wiem czy dam radę to zaprogramować, bo widzę że jednak może być ciężej niż przypuszczałem. Ale spróbuję, a w związku z tym mam pytania:

    1. Jak na razie mam podłączony tylko kanał A. Czytałem trochę o kodzie Graya, ale żadnych praktycznych sposobów jego wykorzystania nie znalazłem, czy ten kod jest konieczny? Co on daje? Nie można na normalnym binarnym?

    2. Wykrywanie zbocza, czyli zmiana stanu z niskiego na wysoki bądź na odwrót, w jakim sensie ciągle mam to sprawdzać? W przerwaniu timera tak?

    0
  • #10 26 Gru 2012 22:16
    tmf
    Moderator Mikrokontrolery Projektowanie

    Ad 1. Chyba wszystkie enkodery dają wynik w kodzie Graya. Więc, żeby z enkoderem zacząć zabawę musisz go przekonwertować na zwykły kod binarny + znacznik lewo/prawo
    ad 2. Tak, w przerwaniu timera masz sprawdzać, czy od ostatniego przerwania zaszła jakaś zmiana. Jeśli zdekodujesz stan enkodera (zamienisz z Graya na binarny) to wyjdzie to automatycznie.

    BTW, akurat w moich przykłądach masz zupełnie gotowy kod, który to robi. Zamiast kombinować spróbuj go przeanalizować.

    0
  • #11 26 Gru 2012 23:10
    miccaldo
    Poziom 12  

    Ok, spróbujmy na tym kodzie, jednak sam niezbyt dam radę go przeanalizować, może wkleję go i byłbym bardzo wdzięczny za pomoc w rozszyfrowaniu tego kodu.

    Kod: cpp
    Zaloguj się, aby zobaczyć kod

    Jeżeli jakieś pytanie wymaga może dłuższej odpowiedzi, proszę napisać a ja znajdę odpowiedź w internecie.

    1.
    Kod: cpp
    Zaloguj się, aby zobaczyć kod


    a) Co to jest? Coś jak podprogram? Ale dlaczego zakończony średnikiem?
    2.
    Kod: cpp
    Zaloguj się, aby zobaczyć kod


    a) Ok, zmienna 'static' dlaczego tak a nie np. int czy char? Co daje static?
    b) Ta tablica oznacza ledy wyświetlacza żeby dawały jakąś cyfrę tak?
    c)Jeśli tak, to czy mogę usunąć tą tablicę? a zastąpić to makrodefinicjami do wyświetlacza jakie ustawiłem na początku programu?

    3.
    Kod: cpp
    Zaloguj się, aby zobaczyć kod

    a) Jakiś skomplikowany podprogram, jeszcze trochę czasu minie zanim zacznę w taki sposób programować. Podprogram ma włączać wyświetlacz? Mogę go zastąpić czymś prostszym, żeby np. pokazywał część wyświetlacza "A"?
    4.
    Kod: cpp
    Zaloguj się, aby zobaczyć kod

    a) pierwsza linijka, znów coś z cyframi wyświetlacza, o co chodzi?
    b)coś z timerem0, ale znów niezbyt rozumiem co tam się dzieje.

    Jak na razie tyle... Niestety w tym programie są dołączone biblioteki o których nie słyszałem i pewnie dlatego nie wiem do czego służą poszczególne funkcje. Musiał bym wybrać z tego programu wszystko co niezbędne i napisać na podstawach, jedyne wyjście bo to póki co nie mój poziom programowania.

    0
  • #12 27 Gru 2012 01:55
    McMonster
    Poziom 32  

    miccaldo napisał:

    1.
    Code:
    void ReadEncoder();
    

    #define GLUE(a, b)     a##b

    #define LEDPORT1(s)   GLUE(PORT,s)
    #define LEDPORT LEDPORT1(LED)
    #define LEDDDR1(s)   GLUE(DDR,s)
    #define LEDDDR   LEDDDR1(LED)


    a) Co to jest? Coś jak podprogram? Ale dlaczego zakończony średnikiem?


    Pierwsza linia to deklaracja funkcji, dalsze linie nie są z nią bezpośrednio związane. Dalej w programie jest definicja tej funkcji, czyli nagłówek i kod w klamrach. Deklaracja jest po to, żeby dana funkcja była widoczna nie tylko od miejsca definicji, ale w całym pliku.

    Cytat:
    2.
    Code:
    static uint8_t PROGMEM DIGITS[11]={0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0xBF};


    a) Ok, zmienna 'static' dlaczego tak a nie np. int czy char? Co daje static?
    b) Ta tablica oznacza ledy wyświetlacza żeby dawały jakąś cyfrę tak?
    c)Jeśli tak, to czy mogę usunąć tą tablicę? a zastąpić to makrodefinicjami do wyświetlacza jakie ustawiłem na początku programu?


    a) Mylisz typ zmiennej ze słowem kluczowym modyfikującym jej zachowanie. Typem jest tutaj uint8_t, czyli 8-bitowa liczba bez znaku, a static w tym miejscu mówi kompilatorowi, że ta zmienna dostępna jest tylko i wyłącznie w tym pliku. Taka ochrona przed przypadkowym użyciem jej w zupełnie innym miejscu w programie.
    b) Ta tablica to "szablony" dla kolejnych cyfr. Cyfra na wyświetlaczu 7-segmentowym składa się z segmentów, każdy segment to jeden bit i pin procesora. Jak zamienisz każdy element tablicy na liczbę binarną, to będziesz miał ciąg kolejnych segmentów, które mają być zapalone i zgaszone.
    c) Jeśli bardzo chcesz, to możesz. Musisz tylko pamiętać o prawidłowym zastąpieniu tej metody we wszystkich miejscach w programie.

    Cytat:
    3.
    Code:
    static inline void ShowOnLED(uint8_t val)
    
    {
       uint8_t tmp=0xFF;
       if((val & 0x7F)<11) tmp=pgm_read_byte(&DIGITS[val & 0x7F]);
       if((val & DP)==1) tmp&=~(DP);
       LEDPORT=tmp;
    }

    a) Jakiś skomplikowany podprogram, jeszcze trochę czasu minie zanim zacznę w taki sposób programować. Podprogram ma włączać wyświetlacz? Mogę go zastąpić czymś prostszym, żeby np. pokazywał część wyświetlacza "A"?


    Ta funkcja wyświetla cyfrę na wyświetlaczu. Wartość tymczasowa ma wszystkie segmenty zgaszone, potem sprawdzanie zakresu w pierwszym ifie i odczyt wzorca dla odpowiedniej cyfry. Drugi if sprawdza, czy ma być zapalona kropka i modyfikuje zmienną tymczasową. Na koniec jest przenoszona na port, na którym jest wyświetlacz. Polecam szczegółową analizę, można się sporo dowiedzieć przy wyszukiwaniu informacji, co, jak i dlaczego.





    Cytat:
    4.
    Code:

    volatile uint8_t LEDDIGITS[LEDDISPNO];

    ISR(TIMER0_OVF_vect)
    {
       static uint8_t LEDNO;

       PORTB|=0x0F;      //Wyłącz wszystkie wyświetlacze
       LEDNO=(LEDNO+1)%LEDDISPNO;
       ShowOnLED(LEDDIGITS[LEDNO]);
       PORTB=(PORTB & 0xF0) | (~(1<<LEDNO) & 0x0F);   //Wybierz kolejny wyświetlacz
       ReadEncoder();
    }

    a) pierwsza linijka, znów coś z cyframi wyświetlacza, o co chodzi?
    b)coś z timerem0, ale znów niezbyt rozumiem co tam się dzieje.

    Jak na razie tyle... Niestety w tym programie są dołączone biblioteki o których nie słyszałem i pewnie dlatego nie wiem do czego służą poszczególne funkcje. Musiał bym wybrać z tego programu wszystko co niezbędne i napisać na podstawach, jedyne wyjście bo to póki co nie mój poziom programowania.


    a) Słowo volatile na razie zignoruj, ma tam być, a wyjaśnienie łatwo znajdziesz w sieci. Ta linia to deklaracja tablicy dla czterech wyświetlaczy (cyfr).
    b) To jest procedura obsługi przerwania, wywołuje się automatycznie za każdym razem, gdy timer się przepełni, tj. co określony czas zależny od częstotliwości zegara timera i wartości, po której ma się przepełniać. Po więcej informacji o timerach i przerwaniach zapraszam do jednego z wielu kursów, artykułów, poradników, książek i tak dalej.

    Żadnych dziwnych bibliotek tam nie ma, obsługa przerwań (interrupt), obsługa zmiennych w pamięci Flash (pgmspace), którą nie musisz się przejmować i delay do zwykłych opóźnień. Ich użycie w samym kodzie jest minimalne. Ale trzeba niestety się podszkolić C i działania AVRów.

    0
  • #13 27 Gru 2012 11:56
    BlueDraco
    Specjalista - Mikrokontrolery

    To nie działa! :)

    Kod: c
    Zaloguj się, aby zobaczyć kod

    Powinno być:
    Kod: c
    Zaloguj się, aby zobaczyć kod


    No i wypadałoby, żeby te funkcyjki, co mają po dwie linie kodu były zadeklarowane jako static inline.

    0
  • #14 27 Gru 2012 12:22
    tmf
    Moderator Mikrokontrolery Projektowanie

    BlueDraco napisał:

    No i wypadałoby, żeby te funkcyjki, co mają po dwie linie kodu były zadeklarowane jako static inline.


    Z czym znowu możnaby dyskutować :)
    Oczywiście to w niczym raczej nie zaszkodzi, ale też nie jest potrzebne - gcc automatycznie traktuje jako inline funkcje, których kod wynikowy jest nie większy niż zadana wartość, którą można zmieniać stosowaną opcją kompilatora. Domyślna jest ok w większości przypadków. Warto też pamiętać, że inline jest tylko sugestą, którą kompilator może zignorować (w przeciwieństwie do atrybutu __always_inline__), więc na końcu i tak wychodzi, że czy to inline damy, czy nie, to kompilator zrobi po swojemu :) W kontekście klonowania funkcji to static też ma ograniczone zastosowanie, po włączeniu whole-optimization, kompilator sam stwierdzi, czy należy robić kopię funkcji, czy nie.
    Ze względu na zaawansowaną heurystykę kompilatora raczej bym właśnie odradzał ręczne stosowanie static inline i inline - zazwyczaj kompilator wybierze lepsze rozwiązanie. Programista może też mieć różne priorytety - raz jest nim prędkość kodu (tu inline zazwyczaj pomaga), a raz jest nim wielkość kodu (tu inline zazwyczaj przeszkadza), zazwyczaj kod wynikowy go nie obchodzi :) Jak coś wymusimy to tak naprawdę raczej ograniczymy możliwości kompilatora, co globaln ie wcale nie musi doprowadzić do powstania lepszego kodu.
    Oczywiście sprawa jest mocno dyskusyjna, ale tu znowu wracamy do kwestii podstawowej - jeśli chcemy otrzymać optymalny kod to dokładnie musimy znać "wnętrze" kompilatora. Z drugiej strony nie po to się używa c, żeby otrzymać optymalny kod (acz można).

    0
  • #15 27 Gru 2012 12:42
    BlueDraco
    Specjalista - Mikrokontrolery

    Ok, inline to sugestia, ale brak static oznacza, że kompilator musi wyemitować funkcję dostępną na zewnątrz, a wtedy polegamy już tylko na miłosierdziu konsolidatora. Moim zdaniem zdrowiej jest nie polegać, tym bardziej, że tych funkcji na zewnątrz nie potrzebujemy.

    0
  • #16 27 Gru 2012 12:57
    tmf
    Moderator Mikrokontrolery Projektowanie

    Nie do końca. Kompilator musi wygenerować ciało funkcji tylko jeśli program pobiera jej adres. W sytuacji, w której program składa się z oddzielnych modułów kompilacji oczywiście tego stwierdzić nie może i dopiero wtedy linker może ten kod usunąć (--gc-sections). Z drugiej strony linker sam od siebie spisze się dobrze i zbędny kod usunie bez dodatkowych opcji.
    Z drugiej strony to czy te funkcje są niepotrzebne nie jest takie oczywiste - ze względu na ich naturę mogą być wykorzystywane w innych fragmentach kodu. Czyli trzebaby zrobić static inline i umieścić je w nagłówku. Z trzeciej strony gcc od wersji 4.4 robi klony funkcji, więc nawet static inline nie pomoże, trzebaby sięgnąć do atrybutów np. __no_clone__. Niestety współczesne kompilatory to naprawdę sprytne bestie i nic nie jest oczywiste :)
    BTW, ponieważ autor wątku najwyrażniej dopiero zaczyna, IMHO skupianie się na optymalizacji jest największym błędem jaki można zrobić. Otrzyma po miesiącu 10 linii optymalnego kodu, tyle, że projekt ciągle nie będzie skończony :)

    0
  • #17 27 Gru 2012 16:33
    miccaldo
    Poziom 12  

    Ok, to może już zacznę coś pisać.

    Skonfigurowałem swoje podłączenie segmentów wyświetlacza, ale dalej to ciężko się zaczyna robić...

    Kod: cpp
    Zaloguj się, aby zobaczyć kod


    Ok, ale no cóż muszę teraz jakoś po kolei to zrozumieć.

    Kod: cpp
    Zaloguj się, aby zobaczyć kod


    typ static ok już wiem o co chodzi, ale chciałbym jakoś uprościć tą funkcję. Ta zmienna val, dlaczego jest w nawiasie? co oznacza kod 0x7F? Jak zapisać tą funkcję jakoś bez wyrazów 'read_byte', '(&DIGITS' itd. I do jakich pinów podpięte są kanały enkodera? Jeszcze na początku są niezrozumiałe makrodefinicje, myślałem że wpierw pisze się nazwę definicji, później do jakich pinów jest coś podciągnięte, a tutaj jest jakieś '(s)', 'GLUE', a##b? Ja tylko bym chciał aby program liczył działki enkodera i wyświetlacz to sygnalizował, a mam wrażenie że ten kod robi znacznie więcej:/ coś mi się zdaje że zwiększyłem sobie poziom o kilka półek wzwyż.
    Kod: cpp
    Zaloguj się, aby zobaczyć kod

    0
  • #18 27 Gru 2012 16:45
    BlueDraco
    Specjalista - Mikrokontrolery

    W nawiasie jest nie zmienna, a wyrażenie, z powodu priorytetów operatorów w języku C. pgm_read_byte jest potrzebne do czytanai staych zapisanych w pamięci programu, bo to AVR, a nie normalny procesor z jedną przestrzenią adresową. iloczyn logiczny z 0x7F eliminuje znacznik kropki dziesiętnej z bajtu zawierającego wartość cyfry. Drugi if w kodzie funkcji wyświetlania jest błędny, o czym pisałem wyżej. Służy on do doklejenia kropki dziesiętnej do wyświetlanej cyfry.
    Tak, jak zdefiniowałeś DIGITS, robi się to dla wyświetlacza ze wspólną katodą.

    0
  • #19 27 Gru 2012 18:32
    miccaldo
    Poziom 12  

    Tak myślę że może by wybrać z tego kodu wszystkie niezbędne funkcje i wtedy coś działać, no chyba że wszystko jest ze sobą powiązane to odpada. Np. ta funkcja z if'ami co eliminuje/dokleja kropkę nie jest jakoś bardzo potrzebna.
    Ten program widzę że też jest na dwa kanały - A i B, a w związku z tym trzeba zaprogramować kierunek w lewo/prawo bo kanały są przesunięte w fazie, ale ja mam podłączony tylko kanał A, więc w prawo i w lewo są impulsy takie same, także tu sprawa się ułatwia.

    0
  • #20 27 Gru 2012 19:30
    tmf
    Moderator Mikrokontrolery Projektowanie

    Enkoder ma wyjście w kodzie Graya, nie jest tak że A to prawo, a B to lewo. Aby zdekodować obrót enkodera potrzebujesz wyjścia A i B. Chyba, że kierunek obrotu jest bez znaczenia, i czy kręcisz w lewo, czy w prawo ma zliczać impulsy - wtedy wystarczy A lub B.
    Z drugiej strony poucz się c jeśli chcesz z tego korzystać, bo wklejanie i wycinanie losowych fragmentów kodu zazwyczaj nie prowadzi do powstania sensownego programu.

    0
  • #21 27 Gru 2012 23:08
    miccaldo
    Poziom 12  

    No tak, za enkoder wezmę się jak umiejętności i wiedza pozwolą. Ale ogólnie miał być to taki projekt do szkoły - licznik obrotów silnika, że zmienne miały liczyć impulsy i obroty enkodera, a potencjometrem za pomocą PWM'a zwiększanie/zmniejszanie prędkości a wszystko wyświetlane na wyświetlaczu. Czy jest coś co było by alternatywą dla enkodera do zliczania obrotów silnika? Coś łatwiejszego.

    I jeszcze jedno pytanie: co polecanie żeby zwiększyć umiejętności programowania w C? Przestudiowałem sporą część kursu z forbota, parę innych kursów, jednak chciałbym nauczyć się programować na miarę tego programu z enkoderem. Czy może zakupić książkę z której pochodzi przykład z enkoderem? Jakieś godne polecenia artykuły, poradniki w internecie?

    0