eGospodarka.pl
eGospodarka.pl poleca

eGospodarka.plGrupypl.misc.elektronikaprogramowanie w C - bardzo ogólne pytanie o filozofię. Arduino w roli programatora pralki › Re: programowanie w C - bardzo ogólne pytanie o filozofię. Arduino w roli programatora pralki
  • Data: 2017-11-01 03:37:36
    Temat: Re: programowanie w C - bardzo ogólne pytanie o filozofię. Arduino w roli programatora pralki
    Od: Jacek Radzikowski <j...@s...die.die.die.piranet.org> szukaj wiadomości tego autora
    [ pokaż wszystkie nagłówki ]

    On 10/23/17 17:07, ToMasz wrote:
    > witam
    > bardzo bardzo hobbisticznie zajmuję się arduino. w wolnych chwilach,
    > żeby synowi zaszczepić.... Nie. Żeby się nie bał elektroniki. Wpadłem na
    > pomysł aby pralkę w której umarła elektronika, naprawić za pomocą
    > arduino. Tyle ze ja pokolenie temu programowałem zx spectrum, więc
    > jestem skażony na całe życ[...]

    Ja bym zaczął od porządnego zdefiniowania problemu: Każde z zadań składa
    się z serii kroków, a każdy krok ma określony zestaw wyjść, warunek jaki
    musi być spełniony żeby przejść do kolejnego kroku, oraz numer
    następnego kroku. Te wszystkie informacje da się opisać jako w miarę
    prostą strukturę:

    typedef WarunekWyjścia bool (*f)();
    struct {
    int nastepnyKrok;
    int wyjscia[liczbaWyjsc];
    int czasTrwania;
    WarunekWyjscia funkcjaTestujaca;
    } KrokOperacji;

    Co oznaczają pola w strukturze:
    * nastepnyKrok - to jest indeks w tabeli opisującej zestaw kroków dla
    danej operacji, do którego kroku przejeść po spełnieniu warunku wyjścia
    dla danego kroku.
    * wyjścia - Tabela opisująca stan wyjść dla danego kroku. W najprostszym
    przypadku to mogą być wartości jakie należy wysłać na piny
    mikrokontrolera: np. pierwszej pozycji w tabeli odpowiada pin sterujący
    wolnymi obrotami w lewo. Druga pozycja ro wolne obroty w prawo. Trzecia
    to włączenie grzałki, itd. "Wyjścia" mogą też uruchamiać funkcje
    wykonujące bardziej skomplikowane operacje. Wszystko zależy od
    implementacji.
    * czasTrwania - Jeśli ustawione jest na wartość większą od zera, to mówi
    przez jak długo ma trwać dany krok. Jeśli ostawione jest na zero, to
    wywoływana jest funkcja testująca warunek stopu.
    * funkcjaTestująca - funkcja zwracająca true jeśli pralka osiągnęła stan
    kiedy można przejść do kolejnego kroku, albo false, jeśli trzeba czekać
    dalej (np. sprawdzanie czy temperatura wody osiągnęła wymaganą
    temperaturę, albo czy pralka jest napełniona wodą.)


    Opis każdej operacji będzie zawarty w tabeli takich struktur,
    składającej się w tylu wierszy, ile jest kroków:

    struct KrokOperacji operacjaPranie[] = {
    {1, {0, 0, 0, 1, .... }, 0, czekajNaNapełnienieBębna}, // napełnij
    beben pralki wodą
    {2, {1, 0, 0, 0, .... }, 10, NULL}, //kręć powoli silnikiem w lewo

    ...
    };

    struct KrokOperacji operacjaWirowanie[] = {
    .....
    };
    itd.


    Na pierwszy rzut oka to może wyglądać jak niepotrzebne komplikowanie
    sobie życia, ale w rzeczywistości to przekłada się na olbrzymie
    uproszczenie programu sterującego, który nie zależy od tego jak są
    zdefiniowane operacje. Zmiana w opisie operacji pociągnie za sobą
    jedynie zmianę tabeli ja opisującej, bez potrzeby modyfikowania kodu.


    Główna pętla programu sterującego będzie wyglądać mniej-więcej w taki
    sposób:

    void loop() {
    switch(operacja) {
    case 0: // czekaj na wybranie programu
    operacja = wybórOperacji();
    krok = 0;
    break;

    case 1: // pranie
    //Funkcja zwróci numer kroku do którego należy przejść. Jeśli
    krok = nastepnyKrok(operacjaPranie, krok)

    // krok < 0 oznacza koniec operacji i wracamy do wyboru
    programu
    if(krok < 0)
    operacja = 0
    break;
    case 2: // pranie
    krok = nastepnyKrok(operacjaWirowanie, krok)
    if(krok < 0)
    operacja = 0
    break;
    ... // pozostałe operacje
    default:
    operacja = 0; // kiedy operacja nie jest zdefiniowana,
    powróć do wyboru programu
    }
    }

    //Funkcja sprawdzająca czy użytkownik che uruchomić jakąś operację, i
    zwracająca jej indeks.
    int wybórOperacji() {
    if(wybranePranie)
    operacja = 1;
    else if (wybraneWirowanie)
    operacja = 2;
    else ....
    ....

    else
    operacja = 0; //w pozostałych przypadkach zawsze wróć do wyboru
    operacji
    return operacja
    }



    Tak implementacja głównej pętli jest bardzo rozwlekła, ale zależało mi
    na pokazaniu jak ręcznie obsłużyć każdy z programów. Kosztem
    niewielkiego zwiększenia skomplikowania tabeli opisującej programy, i
    (pozornego) zmniejszenia czytelności głównej pętli, można ją mocno skrócić.

    I teraz chyba najważniejsza funkcja programu, wyliczająca następny krok
    programu. Jeśli nie nadszedł czas na przejście do następnego, to zwraca
    numer obecnego kroku

    int nastepnyKrok(struct KrokOperacji *opis, int krok)
    {
    // jeśli przerywamy program, to wróć do wyboru operacji
    if(przerwanieProgramu)
    {
    zatrzymajPralkę();
    return -1;
    }

    opisKroku = opis[krok]; //opis wykonywanego kroku


    //Ustaw wyjścia dla aktualnego kroku.
    //Ustawianie wyjść tutaj ma tę zaletę, ze pożądany stan wyjśc będzie
    ustawiany przy każdym obiegu pętli. Wadą jest to że ustawianie wyjść
    jest wywoływane przy każdym obiegu pętli, co w wyjątkowych sytuacjach
    może mieć niepożądane następstwa.
    ustawWyjścia(opisKroku.wyjścia);

    //jeśli obecna operacja ma trwać określony czas, to sprawdź czy
    jeszcze odlicza timer.
    if(timerAktywny)
    {
    if(timer > 0) // czas nie upłynął. Zwróć numer aktualnego kroku
    return krok

    //Czas upłynął. Zatrzymaj timer i zwróć indeks kolejnego kroku
    zatrzymajTimer();
    return opisKroku.nastepnyKrok;
    }

    //Jeśli timer nie jest aktywny, to albo trzeba zbadać warunek
    wyjście, albo ustawić timer dla kolejnego kroku.
    if(opisKroku.czasTrwania > 0) // krok trwa określony czas)
    {
    uruchomTimer(opisKroku.czasTrwania);
    return krok;
    }

    //sprawdź warunek wyjścia
    if(opisKroku.funkcjaTestujaca())
    //funkcja zwróciła true, przechodzimy do kolejnego kroku
    return opisKroku.nastepnyKrok

    //nic się nie dzieje, pozostajemy dalej w tym samym kroku
    return krok
    }


    Z doprowadzeniem kodu do postaci kompilowalnej i działającej,
    zaimplementowaniem obsługi timera odmierzającego czas i funkcji
    pomocniczych testujących warunki i ustawiających wyjścia powinieneś
    poradzić sobie sam :)

    Jacek.

Podziel się

Poleć ten post znajomemu poleć

Wydrukuj ten post drukuj


Następne wpisy z tego wątku

Najnowsze wątki z tej grupy


Najnowsze wątki

Szukaj w grupach

Eksperci egospodarka.pl

1 1 1

Wpisz nazwę miasta, dla którego chcesz znaleźć jednostkę ZUS.

Wzory dokumentów

Bezpłatne wzory dokumentów i formularzy.
Wyszukaj i pobierz za darmo: