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

Czy można "zapętlać" pętlę główną

Krejzol_3000 25 Lut 2011 02:59 2538 25
REKLAMA
  • #1 9200662
    Krejzol_3000
    Poziom 12  
    Witam,

    mam pytanie do doświadczonych programistów, czy dobrą praktyką jest umieszczanie pewnych funkcji bezpośrednio w pętli głównej? Z tego co mi wiadomo to należy unikać takich sytuacji, gdyż niepotrzebnie uC ciągle wykonuje coś co zdarza się raz na jakiś czas. Wiem, że powinienem starać się robić większość rzeczy w przerwaniach, ale z pewnymi rzeczami jest ciężko.

    Tak więc stąd moje pytanie, czy umieszczanie pewnych funkcji/warunków w pętli głównej jest błędem.

    Pozdrawiam
  • REKLAMA
  • #2 9200710
    august_a
    Poziom 21  
    Umieszczanie pewnych funkcji i warunków w pętli głównej jest zupełnie normalne.
  • Pomocny post
    #3 9200743
    Lucky Tom
    Poziom 10  
    Krejzol_3000 napisał:
    Wiem, że powinienem starać się robić większość rzeczy w przerwaniach, ale z pewnymi rzeczami jest ciężko.


    Witam,
    według mnie w przerwaniach należy zrobić tylko to co jest najważniejsze np. wpisanie danych do tabeli. Natomiast w pętli głównej (lub funkcji wywoływanej z pętli głównej) robi się analizę tych danych. Oczywiście nie można powiedzieć, że tylko tak jest dobrze, ale przerwanie nie może trwać za długo.

    Pozdrawiam, Tomek.
  • Pomocny post
    #4 9200759
    krru
    Poziom 33  
    Krejzol_3000 napisał:
    Witam,

    mam pytanie do doświadczonych programistów, czy dobrą praktyką jest umieszczanie pewnych funkcji bezpośrednio w pętli głównej? Z tego co mi wiadomo to należy unikać takich sytuacji, gdyż niepotrzebnie uC ciągle wykonuje coś co zdarza się raz na jakiś czas.


    Takie zastrzeżenie ma wielki sens w systemach wielozadaniowych, gdzie wolne moce przerobowe wykorzysta coś innego. W uC problemem z tym związanym może być pobór prądu.

    Krejzol_3000 napisał:

    Wiem, że powinienem starać się robić większość rzeczy w przerwaniach, ale z pewnymi rzeczami jest ciężko.


    To dość oczywiste, że nie wszystkie urządzenia daje się podpiąć pod przerwania. Należy jednak zastanowić się, czy nie można by ograniczyć częstotliwości sprawdzania różnych warunków i w pętli głównej dać jakiś sleep/wait. Jak pisałem wyżej jedyny problem to pobór prądu - uC zasuwający na pustej pętli i tak nie ma nic innego do roboty.
  • Pomocny post
    #5 9200976
    mirekk36
    Poziom 42  
    Krejzol_3000 napisał:

    Tak więc stąd moje pytanie, czy umieszczanie pewnych funkcji/warunków w pętli głównej jest błędem.


    Dlaczego miałoby to być błędem ? skądże znowu ? ....albo ktoś coś nie tak gdzieś napisał albo coś źle zrozumiałeś.

    To całkowicie normalne i praktyczne, że wszystko rozpoczyna się od pętli głównej w głównym pliku programu np main.c

    Warto jedynie pamiętać o tym aby nie pisać CAŁEGO swojego programu w tej pętli i w jednym pliku. Ale nie ze względu że tak nie można, czy ze względu że to obciąża lub nie procesor, czy też z innych jakichś paranormalnych przyczyn ;)

    Powód jest prosty - dobra praktyka programowania i wspaniałe możliwości języka C w tym zakresie, które pozwalają na logiczny podział całości projektu na oddzielne pliki i przeniesienie do nich pewnych fragmentów programu czyli jakiegoś bloku funkcji. Bo inaczej to zamieni się wszystko w jeden wielki plik z tysiącami linii kodu, którego przeglądanie będzie okropne.

    Np jeśli miałbym zrobić sprawdzanie czy obsługę odbioru danych z USART to nie pisałbym tego w pętli głównej bo to wręcz nieporozumienie, tylko przygotował oddzielny plik z funkcjami potrzebnymi do tego wszystkiego, a następnie takie funkcje używał już w pętli głównej np:

    USART_init(); // inicjalizacja USART -jedna z fnkcji omawianych wyżej
    
        // pętla główna programu
        while(1) {
    
            USART_EVENT();  // tu kolejna funkcja/zdarzenie do obsługi USARTA
    
            //......
    
            //.... tu własne inne instrukcje w pętli głównej - czemu nie ? ;)
    
            //......
    
        }


    Dodano po 2 [minuty]:

    a wyobrażasz sobie teraz jak by wyglądał kod gdyby zamiast funkcyjki USART_EVENT(); był tutaj cały kod , który w niej się znajduje ??? no można byłoby i tak - ale dodałbyś jeszcze kilka takich rzeczy i KLOPS ;) Nie mówiąc już, że nie zawsze tą drogą da się zrealizować bardziej skomplikowane zadania. Po to są funkcje. A nawet ich gromadzenie gdzieś poniżej main() też zaśmieczałoby główny plik programu. Dlatego warto DZIELIĆ ;)

    Dodano po 3 [minuty]:

    NO !!! .... chyba że tobie chodziło o jeszcze coś innego - bo tak czytam temat : "Zapętlać pętlę główną" ;) i aż się uśmiecham - co to może oznaczać ? .... tak sobie myślę, może chcesz zrealizować jakiś projekt wielozadaniowy i nie za bardzo jeszcze wiesz jak się za to wziąć i stąd takie pomysły ??? - daj znać jak coś - co miałeś dokładniej na myśli w razie czego.
  • REKLAMA
  • #6 9202018
    Krejzol_3000
    Poziom 12  
    Dziękuję za zainteresowanie tematem.

    Pytam, gdyż pokazałem kod koledze którego uważam za dość dobrego programistę i stwierdził, że "program niepotrzebnie młóci non stop pewne warunki, wszystko powinno być zrealizowane na przerwaniach i pętla główna powinna wyglądać w ten sposób".

    bez żadnych instrukcji w niej wykonywanych.

    Więc dostosowałem się trochę do jego rad, ale teraz mam problem ze zrealizowaniem klawiatury matrycowej i menu(na wyświetlaczy 7LED) poza pętlą główną. Jednak teraz mnie uspokoiliście trochę wypowiedziami i będę się starał zrobić to w pętli.

    mirekk36: Oczywiście projekt jest podzielony na wiele plików dla zwiększenia czytelności.

    Pozdrawiam
  • REKLAMA
  • Pomocny post
    #7 9202129
    Balu
    Poziom 38  
    Odnośnie klawiatury ją się akurat robi na przerwaniach prawie zawsze - przerwanie od timera;)
    Wyświetlacz tak samo jeśli nie chcesz migania;)
    W głównej możesz sobie dane parseować ale wysyłać trzeba w stałych odstępach czasu;)
  • #8 9202225
    Krejzol_3000
    Poziom 12  
    No tak, wyświetlacz jak najbardziej jest w przerwaniach.

    A możesz rozwinąć dokładniej co masz na myśli obsługi klawiatury od timera... Bo wiem mniej więcej jak to się robi w przypadku "normalnej" klawiatury, nie wiem jak tego użyć do klawiatury matrycowej. Klawiatura jest w zestawie uruchomieniowym
    ZL3AVR.
  • #9 9202246
    Balu
    Poziom 38  
    Timer, co x ms, w obsłudze przerwania sprawdzasz czy ktoś wcisnął na biężaco przemiatanej linii coś, jak nie zmieniasz aktywną linię matrycy na kolejną i wychodzisz z przerwania w kolejnym od nowa;)
  • #10 9202314
    mirekk36
    Poziom 42  
    Krejzol_3000 napisał:
    Dziękuję za zainteresowanie tematem.

    Pytam, gdyż pokazałem kod koledze którego uważam za dość dobrego programistę i stwierdził, że "program niepotrzebnie młóci non stop pewne warunki, wszystko powinno być zrealizowane na przerwaniach i pętla główna powinna wyglądać w ten sposób".


    Nie, no nie będę oceniał twojego kolegi bo może jednak sam coś nie do końca zrozumiałeś z jego podpowiedzi, ale żeby tak miała wyglądać pętla główna w programie i żeby do tego dążyć - to jest niedorzeczność.
  • #11 9202413
    hotdog
    Poziom 26  
    Główna pętla while raczej nie powinna tak wyglądać. Nie widziałem chyba żadnego złożonego programu w którym pętla tak wygląda.

    Obsługa LCD też powinna być umieszczona w pętli while. Oczywiście to zależy od LCD, często można sobie wrzucić jego obsługę sprzętową w przerwania czy DMA, ale dane do wyświetlenia na nim itak najczęściej powinny być przygotowane w pętli głównej.

    Nawet jak klawiaturę obsłużysz sobie w przerwaniach od timera, czy czego kol wiek, to w pętli głównej powinieneś sobie odczytać jej stan.

    Reasumując. Jeżeli Twój kolega powiedział "pętla while musi wyglądać tak: "while(1);" bo inaczej jest do dupy", to poszukaj sobie nowych autorytetów :)
    Peace
  • #12 9202439
    Krejzol_3000
    Poziom 12  
    Nie, na pewno dobrze go zrozumiałem. No ale nie dyskutujmy na temat jego umiejętności, chociaż wydawałoby się, że ma pojęcie.

    Zauważyłem rozbieżny tok jego rozumowania względem prezentowanych projektów na elektrodzie i w książce Mirka. Więc zadałem tu pytanie aby rozwiać swoje wątpliwości. Teraz już bez oporów wrzucę obsługę menu w pętlę główną bez głowienia się w jaki inne sposób można to zrealizować.

    Pozdrawiam
  • #13 9202451
    Balu
    Poziom 38  
    Może kolega kolegi pisze tylko zegarki?:x (Sorry za złośliwość).
    U mnie zawsze jest coś w main loopie;)
    Kilka razy mi się zdarzyło że wyglądała tak:
    while(1)
    {
    Func1();
    Func2();
    Func3();
    Func4();
    }

    Ale przeważnie pętla główna i tak ma 1 / kilka(i więcej) stron (:
    Sorry pewnych rzeczy nie da się skrócić:)

    W przerwaniach powinno się robić rzeczy które MUSZĄ być obsłużone natychmiast (Interfejs użytkownika - klawiatura czy panel dotykowy, porty szeregowe etc).
    Powinno się dążyć do skrócenia do maksimum przerwań (ponoć :x) u mnie bywają długie i z tym żyję spokojnie, ale ja wiem co robię (już wiem).
    A w pętli głównej powinno się parse'ować wszelakie zebrane w przerwaniach dane.
    Hawk!
  • REKLAMA
  • #14 9202552
    Krejzol_3000
    Poziom 12  
    Z tym kolegą to taki trochę offtopic, ale jednak uważam go za człowieka, który ma trochę pojęcia. Jak widać może nie robi wszystkiego do końca wg dobrych praktyk, ale na pewno jest dużo bardziej zaawansowany ode mnie. Z tego co wiem ostatnio zrobił obsługę urządzeń przez Internet na ARMie.

    Teraz już będę wrzucał spokojnie funkcje do while. Ja niestety mam tą przypadłość, że zanim czegoś użyję muszę dokładnie wiedzieć jak to działa, więc ze zrealizowaniem jakichś nowych rzeczy długo mi się schodzi.
  • #15 9202627
    dondu
    Moderator na urlopie...
    Krejzol_3000 napisał:
    Teraz już będę wrzucał spokojnie funkcje do while. Ja niestety mam tą przypadłość, że zanim czegoś użyję muszę dokładnie wiedzieć jak to działa, więc ze zrealizowaniem jakichś nowych rzeczy długo mi się schodzi.

    Nie czytałem całości, ale proponuję, abyś pokazał jak to robisz na konkretnym przykładzie. Wtedy łatwiej jest pokazać zalety, wady, możliwości lub ograniczenia danej metody lub podpowiedzieć inne rozwiązanie. W ten sposób unikniesz:
    Krejzol_3000 napisał:
    Więc dostosowałem się trochę do jego rad, ale teraz mam problem ze zrealizowaniem klawiatury matrycowej i menu(na wyświetlaczy 7LED) poza pętlą główną.
  • Pomocny post
    #16 9204070
    hotdog
    Poziom 26  
    Z programowaniem to już jest tak że ja np sam jak piszę coś i ma to być z zalożenia coś nie skomplikowanego, to sobie wszystko zaplanuje tak, że mam wszystko fanie w modułach, różnych plikach itd itp.

    Najgorzej jest później jak nagle klient, chce mieć coś tam więcej, albo inaczej. W tedy często się robi coś na szybko i już to często nie jest best practice, ponieważ np trzeba by było zmodyfikować 1/2 całego kodu.

    Zmierzam do tego że zasadę, że przerwania "muszą" być krótkie często można naciągnąć. Wiadomo, że trzeba wiedzieć co się robi i być pewnym że to czy przerwanie potrwa 1ms czy 1us nie wpłynie negatywnie na program.

    Osobiście kiedyś, na początku mojej przygody, zrobiłem sterownik który całe menu na LCD obsługiwał w przerwaniu od INT. Urządzenie działa do dzisiaj i żaden z was by tego nie zauważył nie patrząc w kod. Dopiero z czasem człowiek sam doszedł do tego co się powinno robić, czego nie powinno.

    Krejzol_3000 napisał:
    Z tego co wiem ostatnio zrobił obsługę urządzeń przez Internet na ARMie.

    Nie chcę się tutaj czepiać ani Ciebie ani Twojego kolegi, ale uwierz że to wcale nie jest trudne. Wykorzystując zewnętrzne moduły musisz obsłużyć tylko UART. Implementacja stosu TCP/IP posiadając przykład też nie jest niczym specjalnym. Osobiście uważam, że "na ARMie" jest to zrobić tak samo łatwo jak na AVR. Może nawet i łatwiej bo przynajmniej można je fajnie, łatwo i wygodnie debugować.

    A odnoście Twojego kodu, jak napisał kolega pokaż Twój kod, to się tutaj dowiesz, co zrobiłeś bardzo źle, a co tylko źle, a co może być :)

    Z programowaniem już tak jest, że człowiek uczy się cały czas. Prawie zawsze robiąc coś większego na końcu zdajemy sobie sprawę że jakbyśmy pisali go jeszcze raz, od początku to zrobili byś to i tamto inaczej.
  • #17 9205122
    Krejzol_3000
    Poziom 12  
    	while(1) {
    
    		SuperDebounce(&PIND, KL1, 20, 500, kl1_press, kl1_rep );
    
    		/* ****** ZDARZENIE Z UKŁADU RTC ********** */
    		if ( int0_flag ) {
    			//odczyt 4 bajtów do bufora od adresu 0x01 z pamięci RAM naszego RTC
    			TWI_read_buf( PCF8583_ADDR, 0x01, 4, bufor );
    
    			if(!tcl) DS18X20_start_meas( DS18X20_POWER_EXTERN, NULL );
    
    			if(++tcl>1) tcl=0;
    
    			int0_flag=0;
    		}
    
    		switch(menu) {
    		case 0:
    			HELLO();	//wyświetl napis "HELLO"
    			break;
    		case 1:
    			display_time();		//wyświetl czas
    			break;
    		case 2:
    			if( DS18X20_OK == DS18X20_read_meas(gSensorIDs[0], &subzero, &cel, &cel_fract_bits) )
    			display_temp(0);	//wyświetl temperaturę
    			break;
    		case 3:
    			display_date();		// wyświetl datę
    			break;
    		}
    	}
    


    W tym momencie tak wygląda moja pętla główna. Wciśnięcie klawisza inkrementuje wartość 'menu', co powoduje zmianę wyświetlanych informacji. Po osiągnięciu przez 'menu' wartości 3 menu=0.
    Czekam więc na opinie odnośnie poprawności zapisu. Dodam tylko, że działa:)
  • #18 9205357
    mirekk36
    Poziom 42  
    No ale przecież pokazałeś kod w którym masz wszystko zrobione w pętli głównej while(1); a nie jak wcześniej pisałeś w przerwaniach ;) więc o co tutaj chodzi ? Jest to napisane jak najbardziej prawidłowo a w świetle tego co pisałeś na samym początku miało być ponoć wszystko prawie poprzenoszone do obsługi przerwań.

    Tak coś mi się zaczyna wydawać, że chyba jednak kolega coś wcześniej źle zrozumiał z tych podpowiedzi swojego kolegi jednak. Bo jeśli w tym listingu uważasz, że np:

    TWI_read_buf( PCF8583_ADDR, 0x01, 4, bufor ); 
    

    Pzeniesione jest do przerwania to jesteś w błędzie. To nie jest przeniesione do przerwania, tylko odczyt czasu z RTC i wysłanie rozpoczęcia pomiaru temperatury przeniesione jest do hmm można by tak powiedzieć obsługi zdarzenia z RTC w pętli głównej:

    if ( int0_flag ) { 
    


    a to jest ogromna różnica. Bo ja myślałem, że ty takie funkcje poprzenosiłeś na prawdę do przerwań, czyli tam gdzie są makra typu:

    ISR( ....... _vect ) {


    Bo to są procedury obsługi przerwań. Jeśli więc ten twój kolega podpowiada ci tak jak to było zrobione wyżej to nie ma w tym nic zdrożnego ;) .... sposób programowania w pętli głównej można robić już na milion różnych sposobów i zależy to od programisty.

    Niestety w tym rozwiązaniu jest jeden błąd jeśli chodzi o takie działanie z czujnikami 1wire :( ..... bo owszem, wprawdzie rozkaz pomiaru do czujników jest ładnie wysyłany co sekundę to jednak już rozkaz odczytu po wyborze z tego menu może często trafić w moment gdy jeszcze trwa konwersja w czujniku (czas 750ms dla DS18B20 w trybie 12-bitowym). Wprawdzie nie spowoduje to wyświetlenia błędnej temperatury bo te funkcje by sobie na to nie pozwoliły ;) są dobrze napisane, ale za to po prostu po niektórym naciśnięciu nie zawsze odświeży się bieżąca wartość temperatury bo warunek:

    if( DS18X20_OK == .....


    nie zostanie spełniony. Ale masz "tam" przykłady jak można sobie z tym radzić. Tylko chyba właśnie twój kolega uznał , że rozdysponowywanie zadań w sposób karuzelowy (w czasie) jest czymś złym, co niepotrzebnie wkółko działa w pętli głównej. Myślę jednak że to wyniknęło tylko z niezrozumienia tego zagadnienia lub sposobu w który ja to przedstawiłem. Jest to jedna z metod realizacji algorytmu karuzelowego "Round Robin" i to chyba twój kolega uznał za coś "nieteges" w kodzie.
  • #19 9205970
    Krejzol_3000
    Poziom 12  
    Ten kod który zamieściłem wcześniej do sprawdzenia był już po uwzględnieniu poprawek z tego wątku. Wcześniej wg rad kolegi miałem to zrobione w ten sposób.
    // procedura obsługi przerwania INT 0
    ISR( INT0_vect ) {
    	obsluga_INT0();
    }
    
    
    void obsluga_INT0(void) {
    
    	sei();
    
    	static int tcl=0;
    	//odczyt 4 bajtów do bufora od adresu 0x01 z pamięci RAM naszego RTC
    	TWI_read_buf( PCF8583_ADDR, 0x01, 4, bufor );
    
    	display_time();
    
    	if(!tcl) DS18X20_start_meas( DS18X20_POWER_EXTERN, NULL );
    
    	if(tcl) {
    		if( DS18X20_read_meas(gSensorIDs[0], &subzero, &cel, &cel_fract_bits) ==DS18X20_OK ) display_temp(0);
    
    	}
    
    	if(++tcl>1) tcl=0;
    
    }
    


    należy zauważyć, że w przerwaniu musiały zostać odblokowane przerwania poprzez dodanie

    gdyż inaczej wyświetlacze migały, co było zapewne spowodowane zbyt długim wykonywaniem się odczytów.

    Pozdrawiam
  • Pomocny post
    #20 9206033
    tadzik85
    Poziom 38  
    zapoznaj się z hasłem noblock a propo przerwań
  • #21 9209239
    mirekk36
    Poziom 42  
    Krejzol_3000 --> no dokładnie, czyli jak widać na żywym przykładzie jednak przenoszenie wszystkiego do przerwań to totalnie nieporozumienie. Myślę że teraz to już sobie uświadomiłeś i fajnie. Życzę powodzenia.
  • #22 9209748
    Krejzol_3000
    Poziom 12  
    tadzik85: dzięki, nie spotkałem się wcześniej z tym makrem. Pewnie będę go używał jeżeli nabiorę trochę wprawy, a teraz już wiem, że przerwanie powinno trwać jak najkrócej, tak żeby nie było potrzeby zagnieżdzania przerwań.

    Dziękuję wszystkim za udział w dyskusji oraz rozwianie moich wątpliwości odnośnie pętli głównej. Jak widać nie dla wszystkich rzeczy oczywiste są oczywiste.

    Pozdrawiam
  • #23 9210122
    tmf
    VIP Zasłużony dla elektroda
    Z noblock trzeba bardzo uważać, szczególnie jeśli w ten sposób jako nieblokujące deklarujesz przerwania od zdarzeń zewnętrznych, np. INT. noblock praktycznie wymusza napisanie procedury obsługi przerwania w sposób reentrant, dla przykładu twoja taka nie jest. Pomyśl co się stanie jeśli w trakcie obsługi przerwania INT zostanie zgłoszone kolejne przerwanie INT. Druga rzecz to problem ze stosem - strasznie trudno w takim przypadku ocenić zapotrzebowanie na stos. Stąd też IMHO noblock to wyższa szkoła jazdy i przynajmniej na początku bym z tego zrezygnował. Ponieważ obsługa twojego przerwania trwa długo, nawet bardzo długo, więc zastosowałbym raczej strategię odwrotną - wywaliłbym to do pętli main, czyli tak jak miałeś na początku.
    Zauważ także, że wykorzystanie funkcji obsługi I2C i 1-wire w przerwaniu uniemożliwia ich wykorzystanie w innych miejscach kodu - zapewne funkcje te ani nie są reentrant, ani nie zostały stworzone do czegoś w rodzaju multitaskingu.
    IMHO nie należy sobie utrudniać życia, a problemy rozwiązywać tak prosto jak się tylko da, ale nie prościej :) Jak widzisz w twoim przypadku przeniesienie części kodu do przerwania ma bardzo poważne implikacje - czy warto sobie tak komplikować życie, tylko dlatego, żeby zadowolić kolegę?
  • #24 9212278
    Krejzol_3000
    Poziom 12  
    Ile mądrych słów poznałem dzięki temu wątkowi.

    Za dyskusje w tym stylu cenię sobie forum elektroda(wędka - nie ryba).

    tmf: masz rację, należy kierować się mądrościami słynnych ludzi. W tym przypadku Alberta Einsteina.
  • #25 9212634
    dondu
    Moderator na urlopie...
    tmf napisał:
    IMHO nie należy sobie utrudniać życia, a problemy rozwiązywać tak prosto jak się tylko da, ale nie prościej :) Jak widzisz w twoim przypadku przeniesienie części kodu do przerwania ma bardzo poważne implikacje - czy warto sobie tak komplikować życie, tylko dlatego, żeby zadowolić kolegę?

    I choć zgadzam się tym co piszą MIREKK36 i TMF, to jednak zachęcam (w przypadku małych projektów) do utrudniania sobie życia i korzystania z przerwań nawet przy I2C, dla własnej wiedzy i doświadczenia, bo gdy przyjdzie Ci realizować w przyszłości duży projekt, to będzie Ci łatwiej poruszać się po możliwościach sprzętowych jakie dają procesory.

    Oczywiście dozuj stopień trudności.
  • #26 9212758
    Krejzol_3000
    Poziom 12  
    dondu napisał:
    ... to jednak zachęcam (w przypadku małych projektów) do utrudniania sobie życia i korzystania z przerwań nawet przy I2C, dla własnej wiedzy i doświadczenia, bo gdy przyjdzie Ci realizować w przyszłości duży projekt, to będzie Ci łatwiej poruszać się po możliwościach sprzętowych jakie dają procesory.

    Oczywiście dozuj stopień trudności.



    Napisałeś coś o czym sam chciałem wspomnieć, że czasem warto pokombinować trochę dla wprawy i własnej satysfakcji.
REKLAMA