Skocz do zawartości

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


Komentator

Pomocna odpowiedź

11 minut temu, farmaceuta napisał:

uzywam liczb tylko dodatnich

Jeśli już to nieujemnych. A w rzeczywistości nie "nieujemnych" tylko "liczb bez znaku o ustalonej długości w bitach". A arytmetyka unsigned rządzi się swoimi prawami (nieco innymi niż szkolna matematyka) - w skrócie: nie ma pojęcia przepełnienia, jeśli coś chciałoby się znaleźć poza ustaloną długością jest po prostu ignorowane. W przypadku liczb które podałem bit przeniesienia powstały w wyniku odejmowania (również dodawania) najstarszych bitów nie jest brany pod uwagę, bo nie ma już dla niego miejsca.

Jako ćwiczenie możesz po prostu spróbować na kartce (coś jak słupki w początkowych klasach podstawówki) przeprowadzić odejmowanie liczb które podałem wcześniej (najlepiej szesnastkowo albo dwójkowo).

 

  • Pomogłeś! 1
Link do komentarza
Share on other sites

witam Pańtwa

mam pewien dylemat testując kwestię millis, a mianowicie wygląda to tak:

1. mam dwie diody

2. obie załączają się w tym samym czasie, poczym jedna z nich ma zgasnąć po 2 sekundach, druga po 4 sekundach - i w takiej konfiguracji działa mi to świetnie 🙂

3. problem pojawia się wtedy kiedy chcę dodać do całej układanki czas opóźnienia po wyłączeniu ostatnie z diód - w moim przypadku jest to "maszyna1_oproznianie_d = 5; po czym obie diody mają się znowu załączyć i sekwencja się powtarza.

i tu jest problem - zaświecają się razem, ale potem to już każda idzie w inna stronę, ta co ma się wczesniej wyłączyć to sobie miga, a ta co ma czas na 4 sekundy to świecie czasem w nieskończoność, bardzo was proszę byście pokazali mi gdzie robię błąd bo chyba wykorzystałem już wszystkie opcje a efekt ciągle ten sam. z góry dziękuje za pomoc - poniżej kod tego programu

boolean maszyna1 = 1;
boolean maszyna2 = 1;
boolean maszyna3 = 1;
boolean maszyna4 = 1;

#define maszyna1_proznia 0
boolean smaszyna1_proznia = LOW;
#define maszyna2_proznia 3
boolean smaszyna2_proznia = LOW;

#define maszyna1_zawor 1
boolean smaszyna1_zawor = LOW;
#define maszyna2_zawor 4
boolean smaszyna2_zawor = LOW;

unsigned long maszyna1_proznia_d = 4;
unsigned long maszyna2_proznia_d = 2;

unsigned long maszyna1_zawor_d = 2;
unsigned long maszyna2_zawor_d = 2;

unsigned long maszyna1_oproznianie_d = 5;
unsigned long maszyna2_oproznianie_d = 2;

unsigned long maszyna1_proznia_t = 0;
unsigned long maszyna2_proznia_t = 0;

unsigned long maszyna1_zawor_t = 0;
unsigned long maszyna2_zawor_t = 0;

unsigned long maszyna1_oproznianie_t = 0;
unsigned long maszyna2_oproznianie_t = 0;

int kolejnosc = 1;
int maszyna1_switch = 0;
void setup() {

pinMode(maszyna1_proznia, OUTPUT);
pinMode(maszyna2_proznia, OUTPUT);
pinMode(maszyna1_zawor, OUTPUT);
pinMode(maszyna2_zawor, OUTPUT);

digitalWrite(maszyna1_proznia, smaszyna1_proznia);
digitalWrite(maszyna2_proznia, smaszyna2_proznia);
digitalWrite(maszyna1_zawor, smaszyna1_zawor);
digitalWrite(maszyna2_zawor, smaszyna2_zawor);
}

void loop() {

//petla dla maszyny 1
if(maszyna1_switch == 0 && maszyna1 == 1 && kolejnosc == 1 && millis() - maszyna1_oproznianie_t >= maszyna1_oproznianie_d*1000UL){
   digitalWrite(maszyna1_proznia, !smaszyna1_proznia);
   digitalWrite(maszyna1_zawor, !smaszyna1_zawor);
   maszyna1_proznia_t = millis();
   maszyna1_zawor_t = millis();
   maszyna1_switch = 1; 
}
if(maszyna1_switch == 1 && millis()- maszyna1_proznia_t >= maszyna1_proznia_d*1000UL){
  digitalWrite(maszyna1_proznia, smaszyna1_proznia);
}
if(maszyna1_switch == 1 && millis()- maszyna1_zawor_t >= maszyna1_zawor_d*1000UL){
  digitalWrite(maszyna1_zawor, smaszyna1_zawor);
}
if(maszyna1_switch == 1 && millis()- maszyna1_proznia_t >= maszyna1_proznia_d*1000UL && millis()- maszyna1_zawor_t >= maszyna1_zawor_d*1000UL){
  maszyna1_oproznianie_t = millis();
  maszyna1_switch = 0;
}
}
//petla dla maszyny 2
......

 

Link do komentarza
Share on other sites

24 minuty temu, Dantey napisał:

masz na myśli że chcesz jedna ma się palić 2s druga 4s. Jak ta druga zgaśnie jest odliczane 5s i od nowa? 

tak, dokładnie - według mojej poniżej podstawowej wiedzy w kwestii programowania bo dopiero się tego uczę wszystko jest ok, ale najwidoczniej coś tu nie gra i to tak grubo

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

@merinum Szczerze, strasznie to skomplikowałeś od ilości nazw bardzo podobnych aż sie w głowie może zakręcić. Warto byłoby uporządkować ten kod. Nawet nie wiem w jakim celu tyle zmiennych Ci jest potrzebne.

Ja poszedłem na lenia i napisałem Ci przykład, nie wiem jak bardzo wartościowy ale na pewno krótszy 😉 
Sprawdziłem online i zapala/gasi według Twoich wymagań ( Moje Piny to 7 i 8 - zmień jak Tobie pasują) 
 

#define LED1 7
#define LED2 8

uint32_t DelayTime = 5000;      // 5s
uint32_t LED1_Time = 4000;      // 4s
uint32_t LED2_Time = 2000;      // 2s

unsigned long ActualTime = 0;
bool Delay = false;

void setup()
{
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);

  digitalWrite(LED1, HIGH);
  digitalWrite(LED2, HIGH);
}

void loop()
{
  if (Delay && millis() - ActualTime > DelayTime) 
  {
    digitalWrite(LED1, HIGH);
    digitalWrite(LED2, HIGH);
    ActualTime = millis();
    Delay = false;
  }
  else if (digitalRead(LED1) || digitalRead(LED2)) // jedna lub druga dioda musi być HIGH zeby weszło tu w ogóle
  {
    if (millis() - ActualTime > LED2_Time)
    {
      digitalWrite(LED2, LOW);

      if (millis() - ActualTime > LED1_Time)
      {
        digitalWrite(LED1, LOW);
        Delay = true;             // w tym miejscu sygnalizujemy że ostatnia dioda zgasła i powinien wykonać delay 5s
        ActualTime = millis();   
      }
    }
  }
}

Nie wiem do końca jak chcesz rozwijać swój program, ale na pewno wystarczy logicznie rozwiązać to tak:

1.Zapalamy obie diody
2. Gasimy jedną po 2s
3. Drugą po 4
4. Druga zgaszona dioda to sygnał warunku który mówi że ma coś zrobić -> w tym wypadku odczekać 5s.
5. Po 5s się zapętla.

To można fajnie napisać na maszynie stanów, mi sie nie chciało 😛 Pisania byłoby dużo więcej ale kod byłby bardzo łatwo rozszerzalny o bardzo dużą ilość ledów 😉

Edytowano przez Dantey
Link do komentarza
Share on other sites

8 minut temu, Dantey napisał:

To można fajnie napisać na maszynie stanów, mi sie nie chciało 😛 Pisania byłoby dużo więcej ale kod byłby bardzo łatwo rozszerzalny o bardzo dużą ilość ledów 😉

nie no, rewelacja, działa idelanie - wielkie dzięki za wsparcie, co do powyższej twojej wypowiedzi to dużo jeszcze wody musi upłynąć zanim wejdę w temat "maszyny stanów", poki co to raczkuję w tym temacie.

co do projektu to sprawa jest taka, że będzie sie to składało z 12 takich zestawów, które będą miały rózne czasy i jak skończy się jeden to rozpocznie drugi - przez to miałem tam w kodzie zmienna "kolejność" co miało mi gwarantować że pójdzie to jeden po drugim, zmienna zas maszyna 1, maszyna2 itak dalej była po to bym mógł sobie wyłączyć któryśz zestawów, więc jest to trochę pokombinowane dodatkowo jest jeszcze kwestia taka, że jak cały ten zestaw 12 sztuk przejdzie i samo w sobie podczas uruchomienia poszczególnych zestawów czas przerwy sam minie to gdy przyjdzie kolej na pierwszy zestaw to załączy się on bez odczekania tych 5 sekund bo miną one w czasie kiedy będą się uruchamiały inne zestawy, chodzi tylko o to by od zgaśnięcia drugiej diody minął zdefiniowany czas przed kolejnym  załączeniem dwóch, zagmatwane to. póki co to jeszcze raz bardzo dziękuję za pomoc

Link do komentarza
Share on other sites

@merinum No to co piszesz wygląda na maszyne stanów. Nie jest to nic trudnego, wywodzi się to z automatyki. Jakbyś np programował światła na skrzyżowaniu, to masz konkretne przejścia miedzy kolejnymi układami. Zawsze jest Czerwone->czerwone+ Zółte>Zielone->Zółte -> czerwone. Chodzi o kierowaniem przejściem stanów. Piszesz funkcje, która sprawdza to jakie warunki powinny teraz zajść i jeśli zachodzą przenosisz się np z Maszyna1 do maszyna2. Pisanie czegoś takiego na dodatek mogłoby fajnie sprawdzić się napisanie struktury. Struktura Twojej maszyny wyglądałaby mniej wiecej tak:

 

struct Machine
{
uint32_t LED_Time;
uint8_t Pin;

}M1, M2, M3; // itd

Tworzysz wtedy obiekty M1, M2, M3 odpowiadające Twoim Maszynom i każdy z nich ma swoj "zestaw" zmiennych czyli czaś swiecenia  i numerr pinu.
W przypadku robienia 12 Maszyn Twoja ilość zmiennych byłaby ogromna i byłby bałagan 😉 Struktury załatwią ten problem. 

Link do komentarza
Share on other sites

brzmi to interesująco i jest to to co z pewnością jest mi potrzebne, poszperam w siecie w kwestii doedukowania się w tym temacie.

jeszcze raz dziękuję za naprowadzenie mnie na drogę rozwiązania tego tematu

Link do komentarza
Share on other sites

20 minut temu, Dantey napisał:

Tworzysz wtedy obiekty M1, M2, M3 odpowiadające Twoim Maszynom i każdy z nich ma swoj "zestaw" zmiennych czyli czaś swiecenia  i numerr pinu.
W przypadku robienia 12 Maszyn Twoja ilość zmiennych byłaby ogromna i byłby bałagan 😉 Struktury załatwią ten problem. 

a naprowadziłbyś mnie jeszcze jak do przykładu, który podałeś dołożyć drugi taki zestaw zakładając z takimi samymi czasami - wtedy czas pierwszego oczekiwania utopi się podczas świecenia drugiego zestawu i gdy przyjdzie kolej na pierwszy to nie będzie on już czekał bo te 5 sekund minie w trakcie drugiego świecenia, no chyba że czas świecenia zestawu drugiego będzie krótszy niż 5 sekund to odczeka tylko tą różnicę, która pozostanie, w sumie jest to już czarna magia żeby to opisać a co dopiero oprogramować .... eh, jeszcze długa droga przede mną zanim zacznę jakoś w tym swobodnie się poruszać 🙂

Link do komentarza
Share on other sites

W teorii możesz dodać 3 zmienna, która zabezpieczy przed tym, gdyby jakiś kolejny stan sie zakończył wcześniej i wrócił. Jednak w praktyce i tak skoro to ma iść do przodu to nie ma co mierzyć. Popatrz sobie na kanał Inżynier Domu na YT. On opisał tam maszyne stanów. 

Link do komentarza
Share on other sites

(edytowany)

Wariacja na temat millis(). Może niekoniecznie do użycia w całości w projekcie (do prostszych całkowicie wystarczy rozwiązanie z artykułu, do bardziej skomplikowanych są sprawdzone biblioteki). Może jednak komuś się przyda, albo nauczymy się czegoś z dyskusji 🙂

#if __has_include(<functional>)
#include <functional>
#endif
void printString(String str, int i) {
  Serial1.println(str);
  Serial1.println(i);
}

void test() {
  Serial1.println("test");
}

bool intervalFn(int &something) {
  Serial1.print(millis());
  Serial1.print("ms ");
  Serial1.println(--something);
  if (something > 0)
    return 1; else return 0;
}

#if !__has_include(<functional>)
template <typename Function, typename Parameter>
#endif
class OneTimeTimer
{
  public:
#if __has_include(<functional>)
    template <typename Function, typename... Parameters>
    void setTask(unsigned int interval, Function _fn, Parameters&&... _par) {
#else
    void setTask(unsigned int interval, Function _fn, Parameter _par) {
#endif
      nextRun = millis() + interval;
#if __has_include(<functional>)
      callback = [_fn, _par...]() {
        _fn(_par...);
      };
      //callback = std::bind(_fn, std::forward<Parameters>(_par)...);
#else
      callback = _fn;
      par = _par;
#endif
    };
    void run() {
      if (callback && static_cast<long>(millis() - nextRun) >= 0) {
#if __has_include(<functional>)
        callback();
#else
        callback(par);
#endif
        callback = NULL;
      }
    };
    unsigned long nextRun = 0;
#if __has_include(<functional>)
    std::function<void()> callback;
#else
    Function callback;
    Parameter par;
#endif
};


#if !__has_include(<functional>)
template <typename Function, typename Parameter>
#endif
class IntervalTimer
{
  public:
#if __has_include(<functional>)
    template <typename Function, typename... Parameters>
    void setTask(unsigned int _interval, Function _fn, Parameters&&... _par) {
#else
    void setTask(unsigned int _interval, Function _fn, Parameter _par) {
#endif
      nextRun = millis() + _interval;
      interval = _interval;
#if __has_include(<functional>)
      callback = std::bind(_fn, std::forward<Parameters>(_par)...);
#else
      callback = _fn;
      par = _par;
#endif
    };
    void run() {
      if (callback && static_cast<long>(millis() - nextRun) >= 0) {
        bool result;
#if __has_include(<functional>)
        result = callback();
#else
        result = callback(par);
#endif
        if (result) nextRun += interval;
        else callback = NULL;
      }
    };
    unsigned long nextRun = 0;
    unsigned long interval = 0;
#if __has_include(<functional>)
    std::function<bool()> callback;
#else
    Function callback;
    Parameter par;
#endif
};

OneTimeTimer
#if !__has_include(<functional>)
<decltype(&printString), String>
#endif
timer;

IntervalTimer
#if !__has_include(<functional>)
<decltype(&intervalFn), int>
#endif
interval;

void setup() {
  Serial1.begin(115200);
  timer.setTask(1000, printString, "aaa", 5);
  int sm = 3;
  interval.setTask(1500, intervalFn, sm);
  //timer.setTask(1000, test);
}

void loop() {
  timer.run();
  interval.run();
}

image.thumb.png.e71eb801dffe4ba234c739282c614bc9.png

Edytowano przez rziomber
Link do komentarza
Share on other sites

Witam!

Mam problem z funkcją millis:

Kiedy wgrywam jakikolwiek program z tą funkcją na arduino (oryginalne) to nie da się wgrać innego programu na arduino. Reset arduino nie pomaga. Pomaga tylko restart komputera.
Linux ubuntu 23.10
Wersja arduino ide (pobrane z centrum programów (dostarczane przez ubuntu)): latest/stable 1.8.19

Proszę o jak najszybszą pomoc.

Link do komentarza
Share on other sites

@sp2013 to bardzo dziwny objaw i szczerze mówiąc nie pamiętam, aby ktokolwiek wcześniej zgłaszał tutaj taki program. Warto byłoby zrobić test na Windowsie, aby wykluczyć jakieś kwestie Linuksowe. Sam program wgrywany na Arduino nie powinien sprawiać, że wymagany jest restart komputera - szczerze mówiąc na pierwszy "rzut" oka nie widzę między tym żadnego powiązania.

Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.