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

atMega16 - Nierówne wyświetlanie temperatury z ADC na LED

paweciu 13 Maj 2016 19:39 3807 63
  • #1 15671118
    paweciu
    Poziom 10  
    Witam,
    Mam problem z wyświetleniem poprawnej temp z ADC.
    Problem polega na tym że po kręceniu potencjometrem i przejściu przez wartość z np.: 29 na 30 to na wyświetlaczu LED pojawia mi się

    28
    29
    20
    30
    31

    tal jakby pierwszy z wyświetlaczy później niż drugi reagował na zmianę z ADC.

    Poniżej część kodu z ADC oraz z wyświetlania temp.


    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Ta część jest w przerwaniu
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • #2 15671987
    Pokey
    Poziom 17  
    Jest to typowa "usterka" kiedy używasz w przerwaniu zmiennej ktora jest modyfikowana w programie głównym w sposob "nie atomowy" :-)
    Pomyśl co sie stanie jesli przerwanie wystapi po obliczeniu cyfry dziesiatki a przed obliczeniem cyfry jedności, wtedy kiedy ma nastapic zmiana z 29 na 30 (bo ma takie prawo w Twoim programie). Jak tego uniknąć?
  • #4 15679373
    Pokey
    Poziom 17  
    Zmodyfikowałeś procedurę przerwania, ale to nie zmienia faktu, że ono może wystąpić w dowolnym momencie.

    Spójrz na program główny i na następujące linie:

    Cytat:

    0.
    1.dziesiatki = m / 10;
    2.jednosci = m % 10;// dodano |12 aby wyświetlić przecinek na drukim znaku


    Przeanalizujmy taki przypadek:Na wyświetlaczu masz wynik 30. Zmienna dziesiatki=3, jednosci=0. Nastąpiła zmiana w dół i w lini numer 0 m=29. Linia numer 1 wyliczy dziesiatki=2 i po tej linii wystepuje przerwanie. Jakie wartosci beda miec te zmienne w przerwaniu? A no dziesiatki=2 a jednosci=0 - bo nie zostały jeszcze zaktualizowane! Linia numer 2 wykona sie dopiero jak przerwanie sie skonczy.
  • #5 15679475
    Konto nie istnieje
    Konto nie istnieje  
  • #6 15681354
    Pokey
    Poziom 17  
    No dokładnie. Ale chciałem żebyś zrozumiał dlaczego tak się dzieje i sam znalazł rozwiązanie. Typowym sposobem jest zablokowanie tego przerwania na czas modyfikacji zmiennych. Jeśli dane przerwane jest krytyczne czasowo i wykonuje jeszcze inne rzeczy mozesz użyć jakiegoś bitu znacznika i ustawiać przed modyfikacją a zerować zaraz po. W przerwaniu sprawdzasz w ten sposób czy masz aktualne obie zmienne. Jesli nie to nie wyświetlasz.
  • #7 15683575
    paweciu
    Poziom 10  
    Witam ponownie i dziękuję za zainteresowanie tematem.

    Dziękuję za rady ale niestety albo coś źle robię albo wyłączenie przerwań na czas wyliczenia zmiennej nie działa.

    Próbowałem na różne sposoby:
    - wyłączać zaraz po wejściu w tą funkcję,
    - wyłączać po wejściu w pętlę while(y),
    - włączać w różnych miejscach

    Niestety efekt jest zawsze taki sam.

    Poniżej kod całej funkcji oraz całego przerwania.

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • #8 15683610
    grko
    Poziom 33  
    Powinieneś wyłączać przerwania a programie głównym przy odczycie zmiennych a nie w przerwaniu. W sumie to nie jest do końca jesne dla mnie co jest w przerwaniu. (F_UST_TEMP_POT ?)
  • #9 15683630
    paweciu
    Poziom 10  
    Dzięki za odpowiedź.

    F_UST_TEMP_POT - to funkcja wywoływana z pętli głównej i nie jest ona w przerwaniu. Oblicza tylko zmienne wykorzystywane w przerwaniu.

    Czyli wyłączyć powinienem przed wejściem w tą funkcję i załączyć po powrocie z niej. Czy tak?

    Czy lepiej załączyć przerwanie w funkcji?
  • #10 15683640
    Konto nie istnieje
    Konto nie istnieje  
  • #12 15683731
    szelus
    Poziom 34  
    Ach, tak nie można robić. Nie dość, że zmienne współdzielone pomiędzy przerwaniem i programem głównym nie są modyfikowane atomowo, to jeszcze są modyfikowane i w programie głównym i w przerwaniu. Musisz się zdecydować.

    Po drugie, ta kolejność zmiany, która podałeś to występuje w programie rzeczywistym, czy na symulacji? A jeżeli na normalym procku, to jaką masz częstotliwość odświeżania wyświetlacza (przerwania)?

    I jeżeli chcesz kompletnie uniknąć nawet chwilowego (mrugnięcia) niewłaściwą cyfrą, to powinieneś mieć drugi bufor na cyfry do wyświetlania, do którego w przerwaniu przepiszesz wszystkie cyfry (zakładając, że program główny modyfikuje je atomowo) synchronicznie z odświeżaniem całego wyświetlacza (czyli kiedy się "przekręca" licznik wyświetlania).
  • #13 15683730
    Konto nie istnieje
    Konto nie istnieje  
  • #14 15683794
    paweciu
    Poziom 10  
    Ok to tak dla wyjaśnienia mojego błądzenia w okół "atomówek"
    Możesz mi wyjaśnić o co chodzi z modyfikacją atomową - jak to się ma na konkretny kod.
    Rozumiem że samo volatile nie wystarcza więc jak wygląda kod "atomowy"

    Co do mojego problemu.
    Cytat:
    Ach, tak nie można robić. Nie dość, że zmienne współdzielone pomiędzy przerwaniem i programem głównym nie są modyfikowane atomowo, to jeszcze są modyfikowane i w programie głównym i w przerwaniu. Musisz się zdecydować.


    Warunek if (!tr_wysw) - bo chyba to opisujesz mówiąc że mam się zdecydować jest potrzebny do wyświetlania ale w innym przypadku. W tym tj. w wyświetleniu wartości z ADC jest pomijany. - tr_wysw = 1;//Ustawienie flagi dla trybu wyswietlacza


    Cytat:
    Po drugie, ta kolejność zmiany, która podałeś to występuje w programie rzeczywistym, czy na symulacji? A jeżeli na normalym procku, to jaką masz częstotliwość odświeżania wyświetlacza (przerwania)?


    Załączam kod z ustawieniem timera oraz ADC. Zegar 8MH

    Problem występuje na procku tj. na zmontowanym układzie na płytce stykowej

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • #15 15683918
    BlueDraco
    Specjalista - Mikrokontrolery
    Zieeew... Na moje oko brak magicznego słowa na v w deklaracjach zmiennych dziesiatki i jednosci - to przyczyna opóźnienia w zmianie zawartości. Sama obsługa wyświetlacza też błędna - duchy. A styl programowania - straszny.
  • #16 15683942
    paweciu
    Poziom 10  
    Cytat:
    Zieeew... Na moje oko brak magicznego słowa na v w deklaracjach zmiennych dziesiatki i jednosci - to przyczyna opóźnienia w zmianie zawartości. Sama obsługa wyświetlacza też błędna - duchy. A styl programowania - straszny.

    Jak nie masz nic istotnego do powiedzenia o problemie to lepiej nie pisz w ogóle.

    Dopiero uczę się programowania i taki styl jest dla mnie najbardziej zrozumiały.

    a co do volatile - o ile o tym mówisz

    volatile uint8_t dziesiatki;
    volatile uint8_t jednosci;

    to deklaracja obu zmiennych wygląda tak. Wszystkie zmienne obsługiwane w przerwaniu są tak deklarowane
  • #17 15684017
    BlueDraco
    Specjalista - Mikrokontrolery
    Cieszymy się, że już w dwudziestym poście w wątku pokazałeś deklarację zmiennych. Gdybyś od razu pokazał całego knota, jakiego stworzyłeś, być może dałoby się znaleźć nie tylko błędy duchów wyświetlacza, których na razie nie widzisz, ale również ten gruby, który Ty zauważasz.

    Próbka "stylu programowania": zmienna licznik przyjmuje dwie wartości. Po co więc po sprawdzeniu, że nie przyjęła jednej z tych dwóch sprawdzasz, czy jest równa tej drugiej?

    Ok, błąd, którego skutki zauważasz, jest tu (brawo "styl formatowania");

    while(ADCSRA & (1<<ADSC)){; //czeka na zakończenie konwersji

    WIDZISZ?

    Swoją drogą - strasznie fajny ten błąd, pierwszy raz taki widzę.
  • #18 15684095
    Konto nie istnieje
    Konto nie istnieje  
  • #19 15684131
    idepopizze
    Poziom 33  
    @BlueDraco

    while(ADCSRA & (1<<ADSC)) {} ;


    tak ?
  • #20 15684147
    BlueDraco
    Specjalista - Mikrokontrolery
    Wystarczy

    while (ADCSRA & 1 << ADSC);
  • #21 15684159
    idepopizze
    Poziom 33  
    mam podobny problem ale na LCD dlatego poobserwuję rozwój sytuacji
  • #22 15684160
    Konto nie istnieje
    Konto nie istnieje  
  • #23 15684173
    BlueDraco
    Specjalista - Mikrokontrolery
    Błąd polegał na tym, że był o jeden nawias za dużo. ;)
  • #24 15684186
    Konto nie istnieje
    Konto nie istnieje  
  • #25 15684209
    BlueDraco
    Specjalista - Mikrokontrolery
    No cóż, gdyby Szanowny Autor wątku nie zakablował mnie do moderatora za obrazę majestatu, pokazałbym, jak to samo zrobić bez błędów wyświetlania, bez zbędnego oczekiwania na gotowość ADC i 4 razy krócej. No ale skoro taki honorowy, niech szuka dalszych błędów sam.
  • #26 15684243
    Konto nie istnieje
    Konto nie istnieje  
  • #27 15684557
    Pokey
    Poziom 17  
    Zobacz, że w pierwszym poście miałeś w programie dobrze z tym oczekiwaniem na konwersję.
    Wystarczyło tam dodać CLI() a potem SEI() wokół modyfikacji zmiennych używanych w przerwaniu i było by fajnie.
    Jak zacząłeś na wszystkie sposoby kombinować to Ci się tam klamra "rozwinęła" za tym while.....


    P.S. Bo chyba nikt Ci tu w końcu nie napisał co to są te "atomówki" ;-) a pytałeś o to.
    To od nazwy atomu. Kiedyś uznali, że jest to coś nierozerwalnego, czego nie można już podzielić (co się okazało nieprawdą ;-) ). Operacje atomowe nazywa się takie, których nie może nic rozdzielić (nawet przerwanie). Np. dla procesorów 8-bitowych pojedynczy wpis do zmiennej typu byte jest operacją atomową, ponieważ procesor zrobi to jednym rozkazem maszynowym, ale wpis do zmiennej int to już co najmniej 2 instrukcje więc można to "rozerwać" przerwaniem (dla 16 bitowego procesora to jest operacja atomowa). Twój przypadek jest bardzo nieatomowy ;-) bo już na poziomie języka C były to dwie instrukcje przypisania, więc jedyny sposób to uniemożliwić ich rozerwanie przez zablokowanie przerwań (wystarczy zablokować to przerwanie, które ich używa)
  • #28 15684669
    BlueDraco
    Specjalista - Mikrokontrolery
    Tyle, że to nie o to chodziło, więc blokowanie przerwań nic to nie zmieni. Problemem był wielokrotny odczyt rejestru ADC w czasie, gdy zmieniał się wynik konwersji - a to z powodu błędnego oczekiwania. Gdyby ADC był czytany raz - błąd zostałby skutecznie zamaskowany.
  • #30 15684850
    Konto nie istnieje
    Konto nie istnieje  
REKLAMA