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.

obsługa wyświetlacza LED przerwaniami i wyświetlanie zmiennej z pętli głównej

Gorylov 05 Paź 2013 14:08 2415 16
  • #1 05 Paź 2013 14:08
    Gorylov
    Poziom 6  

    Witam, napisałem bardzo prosty kod sterujący przez przerwanie wyświetlaczami LED i zwiększający o 1 zmienną z co sekundę

    Kod: c
    Zaloguj się, aby zobaczyć kod




    Niestety nie mogę dojść do tego, jak wykonywać operacje na zmiennej z w pętli głównej (np zwiększać ją o 1), gdy spróbuję zrobić coś takiego
    Kod: c
    Zaloguj się, aby zobaczyć kod

    To wyświetlana jest wartość początkowa z czyli 0, a więc nic nie ulega zmianie.


    Natomiast gdy napisałbym coś takiego
    Kod: c
    Zaloguj się, aby zobaczyć kod

    to operacja z++ wykona się jeden raz a na wyświetlaczu pojawi się jedynka, czyli wszystko przebiega prawidłowo, uznałem więc, że gdy wykonuję operacje na zmiennej w pętli nieskończonej, nie jest ona odczytywana w przerwaniu i tym samym wyświetlana.

    1. W jaki sposób zmusić uC, żeby wyświetlał aktualny stan zmiennej?

    2. Jak odmierzać czas w "normalny" sposób? (tak, żeby funkcja odmierzania czasu nie była realizowana w przerwaniu obsługującym wyświetlacze)

    3. Jakie inne błędy ma mój program i czy jest w miarę czytelny?

    Z góry dziękuję za pomoc

    0 16
  • Pomocny post
    #2 05 Paź 2013 14:33
    Marek_Skalski
    Moderator Projektowanie

    A co jest złego w odmierzaniu czasu i obsłudze wyświetlaczy w jednym przerwaniu? O ile nie masz typowego zegara RTC (na kwarcu 32768Hz), to właśnie ten sposób jest najlepszy.
    Zmiany wymaga natomiast Twoje podejście do tej kwestii. Program powyżej jest lekko chaotyczny. Nie rozumiem dlaczego sekundy potrafisz liczyć w 1 zmiennej, a do minut już potrzebujesz dwie. Przekombinowane.
    Moja propozycja:
    Sensowny czas zgłaszania przerwań przez licznik (np. 1ms jeżeli masz 4 cyfry na wyświetlaczu)
    Na samym początku przerwania inkrementujesz licznik programowy i zliczasz jednostki poniżej sekundy, czyli 1000 dla okresu licznika 1ms.
    Przepełnienie licznika (np.1000) -> time_s++ i time_ms =0
    Przepełnienie sekund (=60) -> time_m++ i time_s=0
    Przepełnienie minut (=60) -> time_h++ i time_m=0
    Jak już masz nowy czas, to rozbijasz go na cyfry dla wyświetlaczy (tylko jeden raz na sekundę).
    A w każdym przerwaniu aktualizujesz dane dla segmentów i przełączasz się na kolejny wyświetlacz.
    Wracając do kwestii okresu przerwań i czasu wyświetlania, postaraj się tak dobrać parametry, aby każdy wyświetlacz świecił przez 3-5ms i przełączaj się na następny bez zbędnych opóźnień. I zamiast tego if'a użyj case.

    0
  • #3 05 Paź 2013 17:34
    BlueDraco
    Specjalista - Mikrokontrolery

    Zamiast if lub case użyj tablicy, bez jakichkolwiek rozgałęzień przy wyświetlaniu cyfr. ;)

    0
  • Pomocny post
    #4 08 Paź 2013 10:28
    PJS
    Poziom 15  

    Jeżeli odwołujesz się do zmiennej w pętli głównej i w przerwaniu zadeklaruj ją jako volatile.

    0
  • #5 11 Paź 2013 11:34
    Kociejsko
    Poziom 14  

    Kolego ...
    Tutaj masz specjalnie przygotowany kod dla Ciebie.
    Srodowisko WinAVR dla atmega8 8MHz
    W srodku jest driver do display który będzie wyswietlał czas ze struktury ( MIN , SEC )
    W pliku aplikacji jest funkcja do inkrementacji czasu i dekrementacji.

    Kompiluje się na pewno, co do działania bez zadnej ppoprawki to glowy nie dam uciąć bo nie mam na czym przetestowac.

    W razie ptyan pisz.

    Kilka zasad jeśli chodzi o dynamiczne wyswietlanie :

    1. Dane zawsze przygotowujemy z kontekstu Main a nie w przerwaniu.
    W przerwaniu gotowe przekonwertowane dane wrzucamy na display.

    DISP_DATA_PORT = dispTab[currDisp];

    2. w przerwaniu przy zmianie aktywnego displeja najpierw wyłaczamy wszystko, potem zapisujemy nową wartość na port i następnie włączamy display.
    Przerwanie ma max krótko trwać.

    DISPLAYS_OFF();
    DISP_DATA_PORT = dispTab[currDisp];
    DISP_ON( currDisp );

    3. Inkrementacja minut i sekund nie jest sprawą drivera ( driver ma tylko za zadanie wyswietlac podrzuconą mu wartość )

    4. Wszelkiego rodzaju statyczne tablice umieszczamy w kodzie PROGRAMU anie w ramie ( bo jak w ramie to i tak kod programu jest zżerany dodatkowo )
    PROGMEM i pgm_read_byte

    0
  • #6 11 Paź 2013 17:29
    BlueDraco
    Specjalista - Mikrokontrolery

    Kociejsko napisał:

    Kilka zasad jeśli chodzi o dynamiczne wyswietlanie :

    1. Dane zawsze przygotowujemy z kontekstu Main a nie w przerwaniu.


    A to niby dlaczego? Jeśli ktoś upiera się przy pętli zdarzeń, to pewnie tak, ale większość prostych programów (w tym wszelkie budziki i mierniki) daje się napisać bez zbędnej pętli zdarzeń w funkcji main().

    Kociejsko napisał:

    4. Wszelkiego rodzaju statyczne tablice umieszczamy w kodzie PROGRAMU anie w ramie ( bo jak w ramie to i tak kod programu jest zżerany dodatkowo )
    PROGMEM i pgm_read_byte


    Nie "statyczne" a "stałe". Statyczne tablice o zmiennej zawartości raczej nie mogą być umieszczone w pamięci ROM.

    0
  • #7 11 Paź 2013 17:58
    Kociejsko
    Poziom 14  

    Są programy ktore całe sie da napisac w przerwaniu. Oby się tylko wyrobiło wykonywanie kodu zanim przyjdzie następne.
    Ale przy bardziej rozbudowanych programach nie stosuje się takiej polityki.
    Przerwanie przerwanie służy tylko do flagowania czegoś ew przekonfigurowania, lub przepisania a obsługę zdarzeń nie krytycznych czasowo się robi w Main.

    W tym przypadku krytyczne czasowo jest jedynie multiplexowanie displeji.
    Więc ten tylko kod został umieszczony w przerwaniu systemowego ticka 1ms

    A co do tablic statycznych czy stałych, no to zgadzam się ... chodziło mi o stałe które nigdzie w kodzie nie są modyfikowane

    0
  • #8 11 Paź 2013 18:31
    BlueDraco
    Specjalista - Mikrokontrolery

    Nie. Nie ma sensu robić pętli zdarzeń, gdy parametry czasowe gwarantują, że nadążymy z obsługą przerwań. jeśli mamy tylko jedno przerwanie, to i tak musimy nadążyć, więc pętla jest bez sensu. Jeśli mamy więcej niż jedno przerwanie, to obecność pętli, w której coś się robi, praktycznie uniemożliwia usypianie procesora w celu oszczędzania energii, co jest poważnym mankamentem przy zasilaniu bateryjnym.

    Z kolei użycie przerwań tylko do ustawiani znaczników jest kompletnie bezsensowne, bo samo przerwanie jest spowodowane wcześniejszym sprzętowym ustawieniem jakiegoś znacznika, więc po co ten znacznik powielać jeszcze w oprogramowaniu?

    Przy bardziej rozbudowanych programach stosuje się RTOS.

    0
  • #9 11 Paź 2013 21:26
    Kociejsko
    Poziom 14  

    A nazywaj sobie jak to chcesz... pętla zdarzeń czy jakies cos innego.
    Dla mnie to jest normalny main w którym ma sie wykonywać kod programu.
    Przerwanie służy mi sygnalizowania ze jakis czas juz upłynął i pętla main moze sobie na to jakos reagować i cos robić. Ewentualnie w przerwaniu jakies niewielkie kawałki kodu.

    Cytat:
    Nie. Nie ma sensu robić pętli zdarzeń, gdy parametry czasowe gwarantują, że nadążymy z obsługą przerwań.


    A kto zagwarantuje ze się wyrobimy ... Raz sie wyrobimy a drugi raz sie nie wyrobimy i stracimy ticka i precyzje odmierzonego czasu.
    Wystarczy ze wtym zawiłym kodzie który bedzie wprzerwaniu wykona sie jakis grubszy IF i pozamiatane. Stracimy ticka i ktoś sie pozniej glowi ze odmirza sekunde i tak naprawde w szczególnych przypadkach trwa 1,1 s
    Na pierwszy rzut tego nie widać i ktos powie ze super zrobione, ale jak to juz pozostaje kwestią dyskusyjną.


    Cytat:
    Z kolei użycie przerwań tylko do ustawiani znaczników jest kompletnie bezsensowne, bo samo przerwanie jest spowodowane wcześniejszym sprzętowym ustawieniem jakiegoś znacznika, więc po co ten znacznik powielać jeszcze w oprogramowaniu?


    A po to ze z kontekstu przerwania mogę sobie dekrementować softwerowe timery z różnymi dzielnikami. i ich moze byc kilka. Jeden np do taktowania co 15 sec jakiejs długiej procedury w main, a drugi innej procedury kompletnie nie zależnej.
    I dla tego flaga mi nie wystarczy wspolna dla wysztkiego.

    A co do usypiania to upychając kod w przerwaniu nic to wiele nie pomoze.

    Robi się albo moduł kolejkowanych timerów jak w RTOS i wtedy w całym kodzie sie z nich korzysta i ten moduł wie kiedy jest nastawiony najszybszy timer i moze isc spac na ten czas.

    Albo synchronizuje sie całą petle main do jednej milisekundy i jezeli sie wykona szybciej to mozna sobie pospac do konca danej milisekundy.


    Nie chce mi się zbytnio wdawać w dyskusje na ten temat.
    Bo to jakby sie rozwodzić nad wyższością swiąt bożego narodzenia nad wielkanocnymi.

    Powiem tylko ze pracuje w zawodzie programisty ok 7 lat i mam juz sporo doświadczenia, i RTOS i maszyny stanów bez Rtos jak kto chce.

    A koledze "autorowi postu" napisałem ten kod cały od nowa bo nie widze sensu :

    1. non stop w przerwaniu na nowo przeliczać wszystkie wartości za każdym razem nawet kiedy sie nie zmieniają.

    2. a co by bylo jakby bylo 20 wyswietlaczy ? 20 ifów by bylo w przerwaniu ?
    lepiej to było stablicować i jedną instrukcją załatwić.
    Mniej kodu , mniej czasochłonności dla procesora

    0
  • #10 11 Paź 2013 22:41
    BlueDraco
    Specjalista - Mikrokontrolery

    Nie ma znaczenia, czy zgubisz zdarzenie w zbyt wolnej obsłudze przerwania, czy w pętli głównej. W każdym przypadku zgubisz.

    Od dość dawna nie piszę żadnych pętli w main, ale jak byłem początkującym (czyli przez jakieś 7 lat), to takie knoty pisałem, więc poniekąd rozumiem Twój punkt widzenia, chociaż poprawny to on nie jest.

    0
  • #11 11 Paź 2013 22:58
    Kociejsko
    Poziom 14  

    Knoty to jak wszysko w przerwaniu sie wykonuje i kazdy o tym wie.
    Transmisje i długotrwałe obliczenia to tez w przerwaniu ?

    Ticka nie zgubisz w przerwaniu w moim sposobie... najwyżej spóźnisz się z obsługą zdarzenia jak Main przymuli ... ale nie rozwalisz podstawy czasu innym modułom, które być moze potrzebują przecyzyjnego czasu.

    Małe kawałki kodu które nie mogą mieć żadnego opóźnienia to fakt ... do przerwania z nimi.

    Ale jak przerwanie zawalisz całym kodem programu to niestety juz nic nie bedzie mialo wiarygodnej podstawy czasowej.

    Skonczmy te dyskusje ... bo widze ze kolega ma tu status "pirani elektrodowej" i rzuca sie na wszystko co sie ruszy na forum.

    Niestety ja nie zawsze mam na to czas bo sie zajmuje programowaniem.

    A 7 lat to zawodowo , + do tego 10 lat szkoły o profilu elektronicznym.
    I wlasnie w ciągu tych 17 lat to przez pierwsze 2 lata upychałem cały kod w przerwaniu i sie zastanawiałem a co wtedy kiedy przerwanie goni przerwanie bez przerwy bo kod jest dłuższy niz 1ms ... proste ... przerwanie wykonuje sie nie co 1ms tylko tyle ile trwa kod w nim zawarty

    0
  • #12 11 Paź 2013 23:25
    Gorylov
    Poziom 6  

    Kolego Kociejsko; niestety mało zrozumiałem z Twojego kodu, gdyż C uczę się "na poważnie" ledwo trzeci miesiąc i używam znacznie prostszej składni i dużo więcej komentarzy niż wtajemniczeni w mikrokontrolery forumowicze :)

    0
  • #13 12 Paź 2013 00:00
    Kociejsko
    Poziom 14  

    Ok .. może dla początkującego to może to być troche niezrozumiałe.
    Wskaźniki , struktury zazwyczaj sprawiają problemy na początku.

    W tym przypadku można by faktycznie umieścić cały kod w przerwaniu, bo jest go tak niewiele że przerwania na pewno się nie nałożą.

    Mam nadzieje że sam plik display.c jest przynajmniej trochę zrozumiały.

    Jeżeli chcesz wytłumaczenia to mogę jakoś się postarać coś wytłumaczyć.

    Ale najpierw trzeba by przestudiować na googlach "wskaźnki" i "struktury"

    0
  • #14 12 Paź 2013 12:33
    Gorylov
    Poziom 6  

    Chciałbym po prostu wytłumaczenia "na chłopski rozum", co można poprawić moim kodzie, na co to zamienić i dlaczego tak jest :)

    0
  • #16 13 Paź 2013 13:10
    Gorylov
    Poziom 6  

    No i o coś takiego mi chodziło, wszystko ładnie wytłumaczone, dzięki bardzo :D

    Niestety, gdy załaduję program do uC, na wyświetlaczu nic się nie pojawia
    Zauważyłem, że makefile był robiony na atmege8, więc zmieniłem na 162,
    może jakiś rejestr w atmega8 ma inną nazwę niż na atmega162?

    0
  • #17 14 Paź 2013 17:56
    Kociejsko
    Poziom 14  

    juz wiem co moglo byc ze sie display nie zapala

    Kod: c
    Zaloguj się, aby zobaczyć kod


    po dodaniu

    DISP_SW_PORT_AS_OUTPUT();
    DISP_DATA_PORT_AS_OUTPUT();

    przy starcie powinno byc ok...

    po prostu sie zapomniało

    0