Czasy w Arduino- jak sobie poradzić?

Masz problem z Arduino? Tutaj możesz szukać pomocy.
stiven
Złota rączka
Posty: 1581
Rejestracja: 13 maja 2014, 08:47
Lokalizacja: Zielona Góra

Re: Czasy w Arduino- jak sobie poradzić?

Post autor: stiven » 5 lip 2016, 10:59

Żeby zrobić obsługę przycisków, to nie możesz używać delayów, bo nie będzie to działać dobrze. Delay(100) już może spowodować, że nie każde wciśnięcie będzie wykryte, a delay(2000) spowoduje, że żadne normalne wciśnięcie przycisku nie będzie wykryte, no chyba że będziesz przyciskał przycisk na te 2 sekundy. 1-2 delaye na 20-50 ms są potrzebne do zlikwidowania efektu drgania styków przycisków, chociaż są też na to inne, lepsze metody. Odczyt temperatury co 2 sekundy też można zrobić inaczej niż przez delay(2000), a w tym przypadku trzeba. Właściwie jak masz 2 delaye po 2 sekundy, to odczyt z danego czujnika jest tak naprawdę co 4 sekundy a nie co dwie.

Co miesiąc do wygrania nagrody o wartości ponad 1600 zł!


Awatar użytkownika
viciu
Młodszy majsterkowicz
Posty: 4
Rejestracja: 11 gru 2015, 13:29
Kontakt:

Re: Czasy w Arduino- jak sobie poradzić?

Post autor: viciu » 5 lip 2016, 11:51

Jeśli chodzi o operacje cykliczne, to polecam bibliotekę w rodzaju Timers, upraszcza znacznie operacje i czyni kod czytelniejszym.

Nie mogę wklejać linków więc podam tylko hasło do googla :) Nettigo Biblioteka Timers Arduino

Np ustawiasz czas odpalenia na 2 sekundy przez ledBlinkTimer.begin(SECS(2));

Potem wystarczy prosty if (ledBlinkTimer.available()) {/*kod do wykonania*/}

Technicznie to dokładnie operacje na millis i mierzenie czasu od startu ale po prostu jest czytelny interfejs. Znacznie to ułatwia zrozumienie własnego kodu gdy wracasz do niego po kilku miesiącach :)
stiven
Złota rączka
Posty: 1581
Rejestracja: 13 maja 2014, 08:47
Lokalizacja: Zielona Góra

Re: Czasy w Arduino- jak sobie poradzić?

Post autor: stiven » 5 lip 2016, 12:17

http://starter-kit.nettigo.pl/2016/04/b ... rs-16-4-0/
bezpośredni link do biblioteki https://github.com/nettigo/Timers

Rzeczywiście ta biblioteka jest zrobiona na funkcji millis(), a nie na timerze i można sobie w programie zrobić takich timerów programowych więcej niż 1. Jest bardzo mała i dla kogoś początkującego może się przydać.
Awatar użytkownika
ethanak
Złota rączka
Posty: 761
Rejestracja: 21 lis 2015, 14:41
Kontakt:

Re: Czasy w Arduino- jak sobie poradzić?

Post autor: ethanak » 5 lip 2016, 13:41

...A przede wszystkim należy uważnie czytać wypowiedzi :)
Pisałem, że funkcja odczytu temperatury ma sama stwierdzić czy już przyszedł czas na pomiar. Skąd więc pytanie, czy zmniejszenie wartości w delay() spowoduje częstszy odczyt?
Zajmijmy się więc najpierw temperaturą.

Zakładamy od początku, że funkcja loop jest wywoływana co 20 msec. W związku z tym funkcja musi wiedzieć, czy można już dokonać następnego odczytu. Mało tego - ponieważ odczyt nie jest natychmiastowy, a my nie chcielibyśmy tracić responsywności programu - będziemy dokonywać odczytu najwyżej jedego termometru w każdym cyklu.

Załóżmy również dla uproszczenia, że istnieje funkcja float read_termo(int n) zwracająca odczytaną temperaturę dla n-tego termometru (wilgotność na razie pomijam).

Zadeklarujmy sobie najpierw wszystkie potrzebne stałe i zmienne globalne:

Kod: Zaznacz cały

// ilość termometrów
#define NTERM 2

// opóźnienie pętli loop
#define LOOPDELAY 20

// tabela wartości odczytanych
float temps[NTERM];
Przejdźmy teraz do samej funkcji. W pierwszej wersji będziemy liczyć ilość wywołań funkcji, i po osiągnięciu pewnej ilości wykonamy pomiar. Sposób ten jest mało dokładny i polega na w miarę stałych odstępach czasowych między wywołaniami - tym niemniej chętnie stosowany w rozwiązaniach nie wymagających specjalnej precyzji.

Kod: Zaznacz cały


void readTemps()
{
    static int tempTimer = 0;
    static int tempNr = 0;
    
    // czy już czas na przetwarzanie?
    if (--tempTimer > 0) return;
    
    // ustalamy czas następnego przetwarzania
    // w cyklach pętli loop
    tempTimer = 2000 / (NTEMP * LOOPDELAY);
    
    
    // następny termometr
    tempNr = (tempNr + 1) % NTERM;
    
    // i w tablicy temps mamy zawsze aktualną temperaturę
    // dla wszystkich termometrów
    temps[tempNr] = read_termo(tempNr);
}

Możemy zrobić to również korzystając z millis() - ten sposób jest co prawda dokładniejszy i nie wymaga zachowania stałych odstępów czasowych, jednak dla początkujących może sprawiać pewne problemy z uwagi na specyfikę arytmetyki na liczbach bez znaku.

Kod: Zaznacz cały

void readTemps()
{
    static unsigned long lastMeasure = 0;
    static int tempNr = 0;
    
    // czy już czas na przetwarzanie?
    if (millis() - lastMeasure < 2000 / NTEMP) return;

    // zapamiętujemy bieżący czas przetwarzania
    lastMeasure = millis();
    
    // następny termometr
    tempNr = (tempNr + 1) % NTERM;
    
    // i w tablicy temps mamy zawsze aktualną temperaturę
    // dla wszystkich termometrów
    temps[tempNr] = read_termo(tempNr);
}
Jako ćwiczenie proponuję napisanie funkcji w oparciu o bibliotekę Timers.
Należy pamiętać, że w odróżnieniu od powyższych sposobów ten wymaga
inicjalizacji timerów (np. w setup()).

Przejdźmy teraz do dalszej funkcji - podjęcia decyzji i wyświetlaniu. Decyzja będzie podjęta według tego co pisałem wcześniej (przełączenie po wciśnięciu klawisza, powrót do pierwszego termometru po upływie określonego czasu).

Oczywiście najpierw zdeklarujemy sobie stałe i zmienne globalne:

Kod: Zaznacz cały


// czas po którym wyświetlanie zostanie automatycznie przełączone
// w milisekundach
#define DISPLAYTIME 5000

// którą temperaturę wyświetlamy
int whichDisplay;

//i jaka jest jej wartość
float tempDisplay;
Stwórzmy teraz funkcję decydującą o numerze wyświetlanego termometru. Parametrem funkcji niech będzie informacja o wciśnięciu klawisza (o tym pisałem wcześniej):

Kod: Zaznacz cały


void selectDisplay(int command)
{
    static int displayTimer=0;

    // jeśli wciśnięto przycisk
    if (command) {
    
        // przełączamy wyświetlany termometr
        whichDisplay = (whichDisplay + 1) % NTERM;
        
        // i zapamiętujemy czas przełączenia
        displayTimer = msec();
    }
    // jeśli natomiast przycisk nie jest wciśnięcy
    else if (
        // wyświetlany termometr to nie jest termometr o numerze zero
        whichDisplay 
        // oraz minął już czas przewidywany na wyświetlenie wartości
        && millis() - displayTimer >= DISPLAYTIME) {
        
        // wracamy do wyświetlania pierwszego termometru
        whichDisplay = 0;
    }
    
    // Na koniec podstawiamy właściwą wartość do zmiennej, aby funkcja
    // wyświetlania nie musiała szukać tej wartości po całym programie
    tempDisplay=temps[whichDisplay];
}
Przy takim sposobie pisania funkcje realizujące konkretne czynności będą odseparowane od reszty, a funkcja loop będzie w efekcie wyglądać bardzo prosto:

Kod: Zaznacz cały

void loop()
{
    readTemps();
    int command = readKey();
    selectDisplay();
    displayTemps();
    delay(LOOPDELAY);
}
Napisanie reszty funkcji pozostawiam koledze zgłąszającemu problem (żeby nie było że gotowca dałem) :)
Pisze człowiek ambitnie, a tu przychodzi prostak i wszystko rozumie.
To jest ewidentna bezczelność!
pinelesss
Młodszy majsterkowicz
Posty: 2
Rejestracja: 6 lut 2016, 21:42

Re: Czasy w Arduino- jak sobie poradzić?

Post autor: pinelesss » 23 lip 2017, 19:02

Witam serdecznie.
Próbuję od kilku dni poradzić sobie z wywołaniem pętli "if" określoną liczbę razy. Próbowałem dodawać po wykonaniu pętli wartość +1 do parametru, który był zawarty w warunku pętli głównej "if", jednak bez pozytywnych rezultatów. Oto część mojego programu:

Kod: Zaznacz cały

  if ((frequency>200)&(frequency<2000))
  {
    if (millis()<(czas+DelayOn))
    {
      digitalWrite(Out, HIGH);
    }
    if (millis()>(czas+DelayOn)&(millis()<(czas+DelayOff)))
    {
      digitalWrite(Out, LOW);
    }
    if (millis()>(czas+DelayOn+DelayOff)) czas=millis();
  } 
  else
  {
    digitalWrite(DeszczOut, LOW);
    czasDeszcz=millis();
  }
  delay(500);
}
Potrzebuję wykonać daną pętlę np. 5 razy i zakończyć.

Z góry dziękuję za pomoc.
Awatar użytkownika
ethanak
Złota rączka
Posty: 761
Rejestracja: 21 lis 2015, 14:41
Kontakt:

Re: Czasy w Arduino- jak sobie poradzić?

Post autor: ethanak » 23 lip 2017, 19:35

Kod: Zaznacz cały

for (int dupa = 0; dupa < 5; dupa++) {
   zrob_cos_sensownego();
}
O to chodziło? Bo niespecjalnie się da zrozumieć...
Pisze człowiek ambitnie, a tu przychodzi prostak i wszystko rozumie.
To jest ewidentna bezczelność!
pinelesss
Młodszy majsterkowicz
Posty: 2
Rejestracja: 6 lut 2016, 21:42

Re: Czasy w Arduino- jak sobie poradzić?

Post autor: pinelesss » 23 lip 2017, 20:11

ethanak pisze:

Kod: Zaznacz cały

for (int dupa = 0; dupa < 5; dupa++) {
   zrob_cos_sensownego();
}
O to chodziło? Bo niespecjalnie się da zrozumieć...
OK, zatem od początku, bo faktycznie niewiele da się z tego zrozumieć.

Jestem na etapie budowy zintegrowanego czujnika opadów deszczu/śniegu, światła i temperatury zewnętrznej pod system alarmowy. Kwestia pomiarów światła i temperatury oraz wysyłka danych do centrali alarmowej w postaci danych dzień/noc oraz temperatura dodatnia/ujemna nie stanowi większego problemu. Do budowy czujnika opadów deszczu wykorzystałem LC7555, który pod wpływem zmiany rezystancji wejściowej generuje częstotliwość na wyjściu. Temat związany z poprawnym odczytem tej częstotliwości przez Arduino oraz wysyłki danych deszcz/sucho do centrali alarmowej, również nie stanowił większego problemu. Problemem jest jednak kwestia sterowania grzałką w zależności od 3 różnych scenariuszy, zależnych od częstotliwości zmierzonej na wejściu Arduino.

Najlepszym rozwiązaniem dla mnie byłoby aby program sprawdzał częstotliwość na wejściu i na jej podstawie sterował grzałką, i tak:
- jeśli częstotliwość jest mniejsza niż 200 Hz, program nie włącza grzałki
- jeśli częstotliwość jest w granicach 200 -15000 Hz wtedy program włącza grzałkę według scenariuszy 1-3. W każdym ze scenariuszy można przyjąć na stałe, iż grzałka jest 5-krotnie włączana na okres czas1 (docelowo na 5 min) po czym wyłączana na okres czas2 (docelowo 10 min) i jeśli w tym czasie nie zmniejszy się częstotliwość o próg niżej (np. z 3->2) czeka godzinę i ponownie uruchamia 5-krotne powtórzenie włącz/wyłącz. Natomiast jeśli w międzyczasie spadnie do progu niżej, program wchodzi do założeń niższego progu i dalej 5-krotnie wykonuje włączenie i wyłączenie grzałki... itd.

Do tej pory udało mi się napisać coś takiego dla jednego scenariusza:

Kod: Zaznacz cały

if ((frequency>200)&&(frequency<2000)&&(k1<6))
  {
    if (millis()<(czas+czas1))
    {
      digitalWrite(Out, HIGH);
      k1++;
    }
    if (millis()>(czas+czas1)&&(millis()<(czas+czas2)))
    {
      digitalWrite(Out, LOW);
    }
    if (millis()>(czas+czas1+czas2)) czas=millis();
  }
  else
  {
    digitalWrite(Out, LOW);
    czas=millis();
  }

Jednak zdaję sobie sprawę, iż nie jest to dobra droga.
Jeśli ma ktoś jakieś sugestie w jaki sposób zbudować poszczególne pętle, będę wdzięczny za sugestie
wojt3k
Młodszy majsterkowicz
Posty: 2
Rejestracja: 18 paź 2015, 22:39

Re: Czasy w Arduino- jak sobie poradzić?

Post autor: wojt3k » 26 lis 2018, 00:01

witam może to właściwy tema bo chciał bym zmienić mój programik w millis ponieważ w z delay nie można wstawić przerwania w trakcie działania programu tylko nie wiem jak do końca ugryźć
Program ma działać tak(ten działa ale na delay)
posiadam dwa przyciski
jedno wyjście(po testach na Ledzie będzie zamontowany tranzystor)
po przyciśnięciu przycisku1 muszę go przytrzymać 3 sekundy by program się uruchomił
na wyjsciu sygnał wysoki(15s) niski(3s) wysoki niski wysoki niski do kolejnego przyciśnięcia
jak przycisnę przycisk2 też muszę przytrzymać 3 sekundy by uruchomić
wyjście wysoki(15s) niski ciągle do kolejnego uruchomienia
i tu zaczynają sie moje schody bo chciał bym mieć możliwość przerwania(resetu) przez kolejny przycisk w obojętnie jakim fazie cyklu programu


Kod: Zaznacz cały

//Stałe z numerami pinów
#define wyjscie 12
#define przycisk1 6
#define przycisk2 7
#define czas 3000   //Czas do aktywacji
#define przerwa 250
//Zmienna na zliczanie "czasu" kliknięcia guzików
int licznik1 = 0;
int licznik2 = 0;

 
//Funkcja ustawiająca, która konfiguruje ustawienia pinów
void setup() {
  pinMode(wyjscie, OUTPUT);
  pinMode(przycisk1, INPUT);
  pinMode(przycisk2, INPUT);


}
 
//Pętla główna programu
void loop() {
  //Sprawdzanie przyciśniętego przycisku 1 lub 2 i zliczanie czasu
  //Dodatkowo zeruję drugi licznik żeby nie wystąpił przypadek, że odpalą się oba tryby
  if(digitalRead(przycisk1) == HIGH) {
    licznik1 += przerwa;
    
  } else if(digitalRead(przycisk2) == HIGH) {
    licznik2 += przerwa;
    
  } else {
    licznik1=0;
    licznik2=0;
  }
 
  //Jeżeli 1 przycisk będzie przytrzymany ustalony czas
  if(licznik1 > czas) {
    //Wykonaj zadaną sekwencję podnieś, opuść, podnieś...
    digitalWrite(wyjscie, HIGH);
    delay(15000);
    digitalWrite(wyjscie, LOW);
    delay(3000);
    digitalWrite(wyjscie, HIGH);
    delay(15000);
    digitalWrite(wyjscie, LOW);
    delay(3000);
    digitalWrite(wyjscie, HIGH);
    delay(15000);
    digitalWrite(wyjscie, LOW);
    licznik1=0;
    licznik2=0;
     }
  //To samo z przyciskiem 2
  if  (licznik2 > czas){
    digitalWrite(wyjscie, HIGH);
    delay(15000);
    digitalWrite(wyjscie, LOW);
    licznik2==0;
    licznik1==0;
   
  }
  else{
     digitalWrite(wyjscie, LOW);}
  //Na samym końcu opuść zasilanie na pinie
 
  delay(250);
}
dzieki za pomoc
ODPOWIEDZ

Strony partnerskie: