Skocz do zawartości
Komentator

Kurs Arduino II - #9 - wielozadaniowość, opóźnienia z millis()

Pomocna odpowiedź

Kurcze a w mich czasach robiło się to na TIMERze 🙂 i liczniku. Wielozadaniowość bez jawnych przerwań. Wykonalne, ale pełne pułapek. 😉.

Udostępnij ten post


Link to post
Share on other sites

Witam,
Właśnie borykam się z problemem millis() i (prawie) 50 dni. Bo jeżeli chcę obliczyć różnicę: różnica = czasAktualny - czasZapamiętany to przy zmiennych unsigned long po czasie (2^32)-1 czas aktualny robi się mniejszy od zapamiętanego i różnica jest ujemna. Ogólnie dobrym sposobem na to jest podzielenie różnicy czasu modulo (2^32) czyli:

różnica = (czasAktualny - czasZapamiętany) % (2^32)

W excelu to działa bez zarzutu i zawsze pokazuje prawidłową różnicę czasu (funkcja excela MOD(różnica, 2^32)). Symulacja wygląda następująco:

Ale Arduino daje jakieś dziwne wyniki. Sprawdzałem z innymi rodzajami zmiennych (double, signed long) ale nie wylicza prawidłowej różnicy czasu. Korzystanie z funkcji abs() także nie pomaga. Czy przykład podany w artykule (tzn. zwykła różnica bez kombinowania) na pewno zadziała, bo nie chce mi się czekać 50 dni żeby sprawdzić 🙂 BTW jest jakiś sposób na zasymulowanie końcowych wartości funkcji millis(), tak żeby to przetestować?

Udostępnij ten post


Link to post
Share on other sites
BTW jest jakiś sposób na zasymulowanie końcowych wartości funkcji millis(), tak żeby to przetestować?

Ciężko będzie nadpisać wartości zwracane przez tę funkcję. Najszybciej byłoby pewnie przygotować funkcję millisTest(), która będzie zwracała jakieś inne, większe wartości. Wewnątrz tej funkcji możesz mieć przykładowo coś w rodzaju return 4294960000 + milis(); Wtedy zmienna przepełni się po około 7 sekundach 😉

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Po pierwsze: różnica dwóch liczb unsigned będzie również unsigned, czyli z założenia nie może być ujemna.

Po drugie: nie ma konieczności jakichkolwiek modulo - liczba unsigned long jest 32-bitowa i raczej większa niż 2^32 nie będzie.

Po trzecie: Excel to nie Arduino.

Ergo: zwykłą różnica bez kombinowania wystarczy. Jeśli nie wierzysz, poodejmuj sobie parę większych liczb.

Udostępnij ten post


Link to post
Share on other sites

A ja mam takie pytanie zmieniłem swój kod tzn wyrzuciłem gdzie się dało delay a wstawiłem millis. program ładnie działa tylko ze tylko raz (za sterowanie diodami odpowiada czujni ruchu ) jak dalej nim ruszam to nie załączają się diody jak w ostatnim przykładzie tylko wszystko miga. może ktoś pomógłby mi pomóc

#include <OneWire.h>                           //dodaj biblitekę LiquidCrystal.h
#include <DallasTemperature.h>                 //dodaj biblitekę LiquidCrystal.h
#include <LiquidCrystal.h>                     //dodaj biblitekę LiquidCrystal.h

#define one_wire  2                              //Transmisja 1-Wire na pinie 10
#define  temp  12                                //pin 12 jako led wyjscie

#define ruchLED   13                           //wyjscie czujnika wibracji led
#define ruchINPUT  3                           //wejscie czujnika wibracji  
unsigned char state = 0; 
int stantemp = LOW;
int stanruchLed = LOW;

unsigned long zapamientanyczastemp = 0;
unsigned long aktualnyCzas = 0; 
unsigned long zapamientanyczasled = 0;                       


OneWire oneWire(one_wire);                     //wywołujemy transmisję 1-Wire na pinie 10
DallasTemperature sensors(&oneWire);           //informujemy Arduino, ze przy pomocy 1-Wire
                                              //chcemy komunikowac sie z czujnikiem                          

void setup(void){
 pinMode(ruchLED, OUTPUT);                    //ustawian ruchled na wyjsciu danych
 pinMode(ruchINPUT, INPUT);                   //ustawian ruchinput jako wejscie danych
 //Trigger the blink function when the falling edge is detected
 attachInterrupt(0, blink, FALLING);

 Serial.begin(9600);                          //odczyt danych
 pinMode(temp,OUTPUT);                        //ustawiamy led od temp jako wyjscie
 sensors.begin();                             //rozpocznij odczyt z czujnika
}

void loop(void){ 
  //Pobierz liczbe milisekund od startu
 aktualnyCzas = millis();  


 sensors.requestTemperatures(); 
 while (state != 0 && sensors.getTempCByIndex(0)  >= 20 && sensors.getTempCByIndex(0) <= 30 ) {
     state = 0;

                  zapamientanyczastemp = aktualnyCzas;
                  stantemp = !stantemp;
                  digitalWrite(temp, stantemp);   
                 digitalWrite(ruchLED, HIGH); //jesli jest ruch i miesci sie w przedziale załącza led

 }

 sensors.requestTemperatures(); 
 if (state != 0 && sensors.getTempCByIndex(0) <= 18) {    // jezeli jest temp dobre wszytskie led włączone zminilem na 18
     state = 0;
                  digitalWrite(ruchLED, HIGH); //jesli jest ruch załacz diody  spełniony warunek
                  digitalWrite(temp, HIGH);    

 }


 else  if  (aktualnyCzas - zapamientanyczasled >= 8000UL) {            // w przeciwnym wypadku (brak ruchu) zgaś wszystko

                 digitalWrite(ruchLED, LOW);  
                 digitalWrite(temp, LOW);     

 }

}

void blink() {
   state++;

}

Udostępnij ten post


Link to post
Share on other sites

OG, witam na forum!

Ciężko do razu znaleźć przyczynę Twojego problemu, jeśli jednocześnie zmieniłeś kilka rzeczy w programie. Jeśli kod działa raz poprawnie, to pewnie w złym miejscu przypisujesz dane do zmiennych odpowiadających za zliczanie czasu i nie są później aktualizowane. Spróbuj wyświetlać sobie np. przez UART wartości aktualnyCzas oraz zapamientanyczasled i zobacz co się z nimi dzieje 😉

Udostępnij ten post


Link to post
Share on other sites

no kurde dzięki kolego, problem rozwiązany- pomyliłem zmienne, przez pomyłkę albo z nie wiedzy wpisałem zmienną która nigdzie nie ma odniesienia.

Teraz jeszcze jedno pytanie jak za pomocą mills przyspieszyć miganie led chciałbym żeby 3 s świeciła a 1 nie.

Proszę o podpowiedz tylko co muszę zmienić .

Udostępnij ten post


Link to post
Share on other sites

OG, możesz np. napisać program w taki sposób, aby zwiększał wartość jakiejś zmiennej licznikowej co sekundę. Wtedy w pętli głównej wystarczy zrobić coś w stylu: jeśli wartość licznika równa się 1, 2 lub 3 to dioda ma być włączona, a przy wartości wynoszącej 4 wyłączamy diodą i zerujemy zmienną licznikową 🙂

Udostępnij ten post


Link to post
Share on other sites

// pierwszy wpis. Cześć! (nie bijcie)

Jestem bardzo początkujący, ale wydaje mi się (i mój excel to potwierdza), że po 50 dniach, kiedy millis() zacznie naliczać od zera-  szkic z kursu przestaje działać poprawnie. Tabela w załączniku.

arduino excel.png

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

@KarolKarol, witam na forum 😉 Widzę, że to Twoje pierwsze kroki na Forbocie, oto najważniejsze informacje na start:

  • Chcesz przywitać się z innymi członkami naszej społeczności? Skorzystaj z tematu powitania użytkowników.
  • Opis najciekawszych funkcji, które ułatwiają korzystanie z forum znajdziesz w temacie instrukcja korzystania z forum - co warto wiedzieć?
  • Poszczególne posty możesz oceniać (pozytywnie i negatywnie) za pomocą reakcji - ikona serca w prawym dolnym rogu każdej wiadomości.
17 godzin temu, KarolKarol napisał:

Jestem bardzo początkujący, ale wydaje mi się (i mój excel to potwierdza), że po 50 dniach, kiedy millis() zacznie naliczać od zera-  szkic z kursu przestaje działać poprawnie. Tabela w załączniku.

Super, że nie wierzysz na słowo i próbujesz to dokładniej sprawdzić. Nie analizowałem dokładnie Twojego Excela, ale na początek rzuca mi się w oczy od raz pewien błąd. W kolumnie "różnica czasu" masz od pewnego momentu ujemne wartości. W programie byłoby to niemożliwe. Zwróć uwagę, że zmienna ta jest zadeklarowana jako "unsigned long", czyli zmienna bez znaku. Oznacza to, że nie może ona przyjąć wartości ujemnej. Nawet jeśli taką wartość do niej wpiszesz to w wyniku otrzymasz wartość dodatnią. Jaką? Sprawdź najlepiej prostym programem, w którym przypiszesz taką wartość "na sztywno" do zmiennej, a później wyświetl ją sobie np. w monitorze portu szeregowego.

Udostępnij ten post


Link to post
Share on other sites
(edytowany)

Pięknie dziękuję za odpowiedź.

Czyli będzie wartość bezwzględna z różnicy. Wtedy kiedy millis() dochodzi do maxa i się zeruje - wynik różnicy jest ogromny,  dużo większy niż nasze 1000UL - wtedy program zrobi "skok w czasie"  i wyświetli napis wcześniej. Czy rozwiązaniem jest tylko zewnętrzny zegar czasu rzeczywistego?

arduino2.png

Edytowano przez KarolKarol

Udostępnij ten post


Link to post
Share on other sites

Używasz niewłaściwego narzędzia. Arytmetyka liczb bez znaku o określonej długości to nie jest ta sama arytmetyka której używa Excel i której uczyliśmy się w podstawówce. Piszesz że wynik jest ogromny... owszem, ale zbyt ogromny by zmieścić się w 32 bitach - i po obcięciu otrzymamy prawidłowy wynik.

W skrócie - wyniki wszystkich operacji musisz potraktować modulo 2^32.

  • Lubię! 2

Udostępnij ten post


Link to post
Share on other sites
14 godzin temu, KarolKarol napisał:

Czyli będzie wartość bezwzględna z różnicy.

Nie. Tak jak pisałem:

16 godzin temu, Treker napisał:

Nawet jeśli taką wartość do niej wpiszesz to w wyniku otrzymasz wartość dodatnią. Jaką? Sprawdź najlepiej prostym programem, w którym przypiszesz taką wartość "na sztywno" do zmiennej, a później wyświetl ją sobie np. w monitorze portu szeregowego.

Naprawdę warto sprawdzać takie rzeczy w praktyce i nie zakładać, że coś musi być tak jak nam się wydaje 😉

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Witam Wszystkich,

Dzięki za objaśnienie tematu wielozadaniowości, dzięki temu udało mi się zrobić kolejny krok do przodu. Obecnie próbuję zrobić własną funkcje do migania diodami. Funkcja 1 działa tak jak tego oczekuje dla jednej diody. Jednak kiedy próbuję ją zastosować do kolejnej diody to już nie działa. Jest to dla mnie zrozumiałe (jest tam stan diody który nie może być taki sam dla różnych diod oraz zapamiętany czas który też będzie się różnił dla każdej następnej diody. Poradziłem sobie z tym pisząc drugą funkcje która różni się tymi parametrami i wszystko działa. Nie chcę jednak tworzyć nowych funkcji dla każdej następnej diody. Próbuję zastosować tablice ale nie bardzo mi to wychodzi i już trochę zwątpiłem czy idę w odpowiednim kierunku.

Mój kod z dwoma funkcjami:

unsigned long prv_T_1 = 0;
unsigned long prv_T_2 = 0;
unsigned long curren_T = 0;

int stanLED_1 = LOW;
int stanLED_2 = LOW;

void setup() {}

void loop() {
  curren_T = millis();
  LED_BLINK_1 (300, 30, 2); //czas LED OFF / czas LED ON / pin
  LED_BLINK_2 (100, 500, 3); //czas LED OFF / czas LED ON / pin
}

//------------------  FUNKCJA 1  -----------------------
void LED_BLINK_1(unsigned long interval, unsigned long blink_T, int pin) {
  pinMode(pin, OUTPUT);

  if (stanLED_1 == LOW) {
    if (curren_T - prv_T_1 >= interval) {
      stanLED_1 = HIGH;
      digitalWrite(pin, stanLED_1);
      prv_T_1 = curren_T;
    }
  }
  else {
    if (curren_T - prv_T_1 >= blink_T) {
      stanLED_1 = LOW;
      digitalWrite(pin, stanLED_1);
      prv_T_1 = curren_T;
    }
  }
}

//------------------  FUNKCJA 2  -----------------------
void LED_BLINK_2(unsigned long interval, unsigned long blink_T, int pin) {
  pinMode(pin, OUTPUT);

  if (stanLED_2 == LOW) {
    if (curren_T - prv_T_2 >= interval) {
      stanLED_2 = HIGH;
      digitalWrite(pin, stanLED_2);
      prv_T_2 = curren_T;
    }
  }
  else {
    if (curren_T - prv_T_2 >= blink_T) {
      stanLED_2 = LOW;
      digitalWrite(pin, stanLED_2);
      prv_T_2 = curren_T;
    }
  }
}

 

Udostępnij ten post


Link to post
Share on other sites
(edytowany)
#define kontaktron 4
#define pir 7
#define led_r 5
#define led_g 6
#define led_b 9
#define foto_rez A0
#define potencjometr A5

unsigned long aktualnyCzas = 0;       //Zmiene pomocnicze do obslugiwania f. miilis()
unsigned long zapamietanyCzas = 0;
unsigned long timerLED = 10000;

boolean czyCiemno = false;            //zmienna infomujaca o oswietleniu
boolean czyWlaczyc = false;           //zmienna pomocnicza do f. wlacznik()
boolean zapamietanyTryb = czyWlaczyc;

int progJasnosci = 0;           //zmienne do obslugiwania progu jasnosci
int wartoscPotencjometru = 0;
int jakJasno = 0;

void setup() {
    Serial.begin(9600);
    pinMode(kontaktron, INPUT_PULLUP);   //Ustawienie pinow
    pinMode(pir, INPUT);
    pinMode(led_r, OUTPUT);
    pinMode(led_g, OUTPUT);
    pinMode(led_b, OUTPUT);
}

void loop() {
    aktualnyCzas = millis();      //pobranie czasu ktory uplynal od wlaczenia plytki
    progJasnosci = analogRead(potencjometr);
    jakJasno = map(analogRead(foto_rez), 0, 1023, 200, 1100);
    
    if(jakJasno < progJasnosci){        //sprawdzenie poziomu jasnosci
      czyCiemno = true;                 //jesli ciemno, to zmien watosc zmiennej na pawde
    }
    else{
      czyCiemno = false;         //jesli jasno, zmien wartosc funkci na true
      czyWlaczyc = false;
    }
    
    if(czyCiemno){                       //jesli jest ciemno
      if(digitalRead(kontaktron) == HIGH){
        czyWlaczyc = false;
      }
      else if(digitalRead(pir) == HIGH){      //jesli wykryto takze ruch
        zapamietanyCzas = aktualnyCzas;  //zapamietaj czas w ktorym ostatnio wykryles ruch
        
        czyWlaczyc = true;
      }
    }
    else{                       
      czyWlaczyc = false;        //jesli jest jasno to wylacz swiatlo
    }
    
    if(aktualnyCzas - zapamietanyCzas >= timerLED){        //jesli minie wyznaczony czas
      czyWlaczyc = false;                                  //wylacz swiatlo
    }
    
    if(zapamietanyTryb == !czyWlaczyc){                //jesli zmieni sie tryb swiecenia
      zapamietanyTryb = czyWlaczyc;                    //zapamietaj aktualny tryb
      wlacznik(czyWlaczyc);                            //pzekaz aktualny tryb do f. wlacznik
    }
}
  
void wlacznik(boolean tryb){       //Funkcja obslugujaca wlaczanie/wylaczanie swiatla
  if(tryb){
    digitalWrite(led_r, HIGH);
    digitalWrite(led_g, HIGH);
    digitalWrite(led_b, HIGH);
  }
  else{
    digitalWrite(led_r, LOW);
    digitalWrite(led_g, LOW);
    digitalWrite(led_b, LOW);
  }
}

Program rozbudowany o fotorezystor i  potencjometr to ustawienia maksymalnej wartości natężenia światła. Funkcja map do zneutralizowania problemu ze zbyt małą wartością napięcia na fotorezystorze przy zgaszonym świetle. Uwagi mile widziane, może uda się coś skrócić.  @Treker, ogromne podziękowania i ukłony w Twoją stronę. Świetnie się bawiłem już od pierwszego poziomu kursu.

Edytowano przez D3binski

Udostępnij ten post


Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Gość
Napisz odpowiedź...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.


×
×
  • Utwórz nowe...