• Witaj na Forum Arduino Polska! Zapraszamy do rejestracji!
  • Znajdziesz tutaj wiele informacji na temat hardware / software.
Witaj! Logowanie Rejestracja


Ocena wątku:
  • 0 głosów - średnia: 0
  • 1
  • 2
  • 3
  • 4
  • 5
przekaźnik czasowy
#1
Witam. Z Arduino miałem przerwę ponad 4 lata, trochę(a nawet sporo) z dyńki mi wyleciało. Postanowiłem powrócić do zabawy w zlepiacza kodów i oto jestem z pierwszym problemem. Mój kod to przekaźnik z odliczaniem wstecznym. I  problem jest taki że mój kod po uruchomieniu nie załącza stanu wysokiego na pinie 13 dopóki czas nie zejdzie poniżej jednej minuty tzn po wciśnięciu przycisku odlicza wstecz zmienna stan jest widoczna w monitorze jako 1 a stan wysoki pojawia się dopiero na minutę przed końcem. Prosiłbym o sprawdzenie kodu i wskazanie błędów. 

Kod:
int A = 1;
int minuta = A;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);

  pinMode(13, OUTPUT);
  pinMode(6, INPUT_PULLUP);



}
boolean stan = 0;
void loop()

{

  if (digitalRead(6) == LOW)
  {
    delay(20);
    stan = !stan;
    while (digitalRead(6) == LOW);
    delay(20);
  }

  if (stan == 1)
  {
    licznik();
    digitalWrite(13, HIGH);
  }
  else
  {
    digitalWrite(13, LOW);
  }
  if (minuta == -1)
  {
    stan = 0;
    digitalWrite(13, LOW);
    minuta = A;
  }

}
void licznik()
{
  int sekunda = 59;
  for (int i = 59 ; i > -1 ; i--)
  {
    Serial.println(stan);
    Serial.print(minuta);
    Serial.print("-");
    Serial.println(sekunda);

    sekunda--; 
    if (sekunda == -1)
    {
      minuta--;
    }

    delay(1000);
  }
}
 
Odpowiedź
#2
(16-01-2024, 00:24)ferdricco napisał(a): Witam. Z Arduino miałem przerwę ponad 4 lata, trochę(a nawet sporo) z dyńki mi wyleciało. Postanowiłem powrócić do zabawy w zlepiacza kodów i oto jestem z pierwszym problemem. Mój kod to przekaźnik z odliczaniem wstecznym. I  problem jest taki że mój kod po uruchomieniu nie załącza stanu wysokiego na pinie 13 dopóki czas nie zejdzie poniżej jednej minuty tzn po wciśnięciu przycisku odlicza wstecz zmienna stan jest widoczna w monitorze jako 1 a stan wysoki pojawia się dopiero na minutę przed końcem. Prosiłbym o sprawdzenie kodu i wskazanie błędów. 

Kod:
int A = 1;
int minuta = A;

...


  if (stan == 1)
  {
    licznik();
    digitalWrite(13, HIGH);
  }

Jak widać, najpierw wywołujesz funkcje licznik() - która czeka minutę, a dopiero potem ustawiasz pin.

Mam też wątpliwości co do inicjalizacji zmiennej minuta - czy można jedną zmienną inicjować inną. Jak kiedyś bawiłem się w kody startowe itp to zainicjowanie zmiennych globalnych w środowiskach procesorków to robi taki jeden "wielki" memcpy z ROM do RAM, na komputerach "dyskowych" po prostu plik z programem zawiera sekcje zmiennych globalnych i ona jest czytana z dysku prosto do zmiennych. Tylko obiekty (w C++) są inicjowane "aktywnie" przez kod - poprzez wywołanie konstruktorów.
 
Odpowiedź
#3
Działa. Dziękuję za pomoc. Tak dla jasności chciałbym się dowiedzieć czegoś. Więc wywołanie funkcji powoduje "zablokowanie" programu na czas jej trwania?
 
Odpowiedź
#4
(16-01-2024, 13:27)ferdricco napisał(a): Działa. Dziękuję za pomoc. Tak dla jasności chciałbym się dowiedzieć czegoś.  Więc wywołanie funkcji  powoduje "zablokowanie" programu na czas jej trwania?

Tak, zasadniczo program ma jeden 'punkty wykonania' i kolejno wykonuja instrukcje. Nieco na innych zasadach działają przerwania - co prawda dalej jest jedno miejsce gdzie wykonuje się program, ale może niespodziewanie wyskoczyć 'w bok' a potem wrócić.
Dopiero w większych kompach można sobie poprosić o kolejny 'punkt wykonania' (wątek). Jedną z możliwości jest wtedy tzw asynchroniczne wywolanie funkcji - program nie czeka na koniec, ale musi mieć coś 'uchwyt' gdzie może sprawdzić czy funkcja już się skończyła i z jakim wynikiem.
 
Odpowiedź
#5
W swoich programach nie przebrnąłeś przez podstawową rzecz, wyrugowanie delay z kodu.
Na pierwszej lekcji Arduino uczysz się, że by uzyskać racjonalne działanie programu możliwe do obserwacji przez powolnego człowieka zatrzymujesz program delay 1000 by rzeczy robiły się co 1s, na drugiej uczysz się, że zamiast zatrzymywać program na 1000ms, ignorujesz konieczność wykonania kolejnego kroku w programie przez 1000ms pomijając go ifem uzależnionym od zliczonych jednostek czasu, zajmując się w tym czasie innymi rzeczami, w szczególności przy miganiu ledem co prawda niczym, ale masz całą sekundę w dyspozycji.
Przecież mogłeś wstawić delay (60000), ale program by się zablokował na minutę, zamiast tego zatrzymujesz program 60x po 1000ms by coś zrobić co 1s. Możesz też zrobić delay(1) i zliczać ms, co 60000 zliczeń masz swój mierzony okres.
Albo wykorzystać wbudowany w Arduino licznik ms, zamiast zatrzymywać program na 1ms, sprawdzasz, czy od poprzedniej czynności upłynęła wybrana ilość ms.
Różnica jest taka, że używając millis() program czasowo nie będzie Ci się rozjeżdżał, jak będziesz chciał dodać miganie led i wyświetlenie tekstu przez 5s. Jak teraz będziesz chciał dodać jakieś czynności to musisz sprawdzić ile trwają, jak znacząco długo to zmniejszyć delay(1000) na np. delay(950), i tak cały czas dodając/odejmując wraz z każdą linią kodu.
Miło być decenianym https://buycoffee.to/kaczakat
 
Odpowiedź
#6
(16-01-2024, 15:12)kaczakat napisał(a): W swoich programach nie przebrnąłeś przez podstawową rzecz, wyrugowanie delay z kodu.
próbowałem niejednokrotnie ale ze skutkiem mizernym. Nie radzę sobie z millis. Ten kod który tu zmieściłem pierwotnie powstał na millis ale niestety nie odejmował sekund. W sumie to pokażę pierwokod, może uda się go uruchomić.
Kod:
int A = 1;
int minuta = A;
unsigned long previousMillis = 0;
const long interval = 1000;
void setup() {

  Serial.begin(9600);
  pinMode(13, OUTPUT);
  pinMode(6, INPUT_PULLUP);



}
boolean stan = 0;
void loop()
{
  unsigned long currentMillis = millis();

  if (digitalRead(6) == LOW)
  {
    delay(20);
    stan = !stan;
    while (digitalRead(6) == LOW);
    delay(20);
  }

  if (stan == 1)
  {
    digitalWrite(13, HIGH);
    int sekunda = 59;
    for (int i = 59 ; i > -1 ; i--)
    {

      if (currentMillis - previousMillis >= interval)
      {
        sekunda--;
        previousMillis = currentMillis;

        Serial.println(stan);
        Serial.print(minuta);
        Serial.print("-");
        Serial.println(sekunda);
      }
      if (sekunda == -1)
      {
        minuta--;
      }
    }

  }
  else
  {
    digitalWrite(13, LOW);
  }
  if (minuta == -1)
  {
    stan = 0;
    digitalWrite(13, LOW);
    minuta = A;
  }
}
 
Odpowiedź
#7
Jak tam w tym kodzie ciągle widzę jakieś delay-e   Wink 
Oczywiście to żart, malutkie delaye mogą zostać, nie ma sensu odliczać pojedynczyć ms licznikami, chyba, że nie możesz się spóźnić nawet o kilka ms.
Ale pozostała jedna nieakceptowalna rzecz - pętla oczekiwania na zwolnienie klawisza. Struktura programu ma być następująca: w funkcji loop sprawdzasz wszystkie sygnały wejściowe i czas. W odpowiednich zmiennych, niestety globalnych, odznaczas w jakim jesteś stanie i przy zmianie stanu zmieniasz sygnały wyjściowe. To jest tzw synchroniczny automat stanów i twoim zadaniem jest napisać funkcje przejść i wyjść. Konkretnie w twoim przypadku np. nie powinieneś liczyć czasu, tylko w momencie wykrycia przycisku włączyć sygnał wyjściowy i obliczyć czas (w ms) kiedy ma się wyłączyć sygnał wyjściowy. cos w stylu:
Kod:
wylaczycMillis = currentMillis + (60*1000);

A kiedy currentMillis będzie większe (lub równe) wyłączasz wyjście, i podstawiasz np. -1 do zmiennej wylaczycMillis, lub odznaczasz to w typowy sposób w zmiennej określającej stan układu. Zmienna ta powinna wyróżniać przypadku: IDLE - nic się nie dzieje, czekanie na klawisz, PRESSED - klawisz naciśniety, czekanie na puszczenie, ON - sygnał właczony, w trakcie odliczania i po ustalonym czasie wracasz do IDLE. Trzeba tylko pamiętać, że millis co jakiś czas się przepełnia i wraca do zera (chyba co ok 50 dni).
Oczywiście można ten schemat zmodyfikować, trzeba np. określić, czy w trakcie włączenia układ jest głuchy, czy kolejne naciśniecie przycisku w trakcie odliczania na skasować odliczanie, zostać zignorowane, czy wyresetować odliczanie do 60sekund od aktualnej chwili.
 
Odpowiedź
#8
Urządzenie które buduję to sterownik do.... czegoś tam... Zmienna A będzie rególowana od 5 do 20. Dane te traią na wyświetlacz aby kontrolować czas posostały do zakończenia cyklu. Wolałbym napisać kod na millis ale poległem kilkukrotnie. Poradziłem sobie z Delay i kod działa nie do końca tak jak bym chciał ale jeszcze powalczę z przerwaniami bo wywołana funkcja blokuje program i nie mogę wyłączyć
 
Odpowiedź
#9
Do małych delay wystarcza flagi i też nie będzie logować
Arduino zostało wymyślone po to, by robić dobrze jedną prostą rzecz – migać diodą. 
 
Odpowiedź
#10
Bardzo przekombinowałeś proste rzeczy. Masz przykład BlinkWithouDelay do nauki zamiany delay na millis. Loop ma się wykonywać najszybciej jak to możliwe, miliony lub chociaż tysiące razy na sekundę. Wystarczy ten przykład tak zmodyfikować, żeby oznaczyć jakoś ten jeden przebieg loop gdy jest nowa sekunda, a w każdym innym loop ją zerować, na koniec zamianę stanu led zamienić na zmienną zwiększającą swój licznik co 1s.
Kod:
uint32_t poprzednio = 0,licznikSekund;        // will store last time LED was updated
uint32_t interwal = 1000;           // interval at which to blink (milliseconds)
bool flaga1s;

void setup()
{
Serial.begin(115200);
}

void loop() {
uint32_t teraz = millis();
flaga1s=0; //zerowanie flagi, tu lub na koncu loop
  if (teraz - poprzednio >= interwal)
  {
    poprzednio = teraz; //zapamietanie tego stanu licznika jako ostatni uzyty
    flaga1s=1; //oznaczamy ten loop jako wybrany
    licznikSekund++;//i zwikszamy licznik uplynietych sekund
  }
//teraz mozna uzyc flagi

if(flaga1s) //czyli tylko wtedy, gdy jest loop z nowa sekunda, raz bo zerujemy flaga1s w kazdym innym loop
{
  Serial.println(licznikSekund);
}

//mozna tez uzyc licznika sekund i flagi
if(flaga1s and licznikSekund%10==0) //czyli gdy jest ten jeden loop z nowa sekunda, a licznik sekund dzieli sie przez 10 bez reszty, ==0
{
  Serial.println("A to co dziesiata sekunde");
}
}

Można w ten sam sposób zmniejszyć mierzone odcinki do np. 10ms, ze 100 sklejać 1s, sekundy w minuty, minuty w godziny, itd. Jak to zrozumiesz to znajdź mój post z funkcją czas().
Jeśli nie wiesz jak bez delay odczytać przycisk to są gotowe biblioteki, ale jest to proste jak zrozumiesz działanie millis. Przy intervale 10ms można raz na te 10ms sprawdzać stan pinu, jeśli nie był wciśnięty, a jest, i jest przez kolejne 3-4 wywołania, to znaczy, że jest wciśnięty i drgania minęły. A jak to zrobić, tak samo jak millis, robisz zmienną poprzedni stan przycisku, aktualny stan przycisku, czas wciśnięcia, jeśli nie jest wciśnięty to zerujesz czas, jeśli czas wciśnięcie >=4, to został wciśnięty, można zareagować, albo dodać dodatkowe funkcje i czekać na puszczenie, czyli wykonać akcję na wciśnięcie po 40ms, albo czekać na puszczenie i wykonać akcję dla krótkiego wciśnięcia przy czasie 40-500ms, albo wykonać akcję dla długiego wciśnięcia powyżej 500ms, albo zmienić na jeszcze inną akcję gdy ktoś trzyma przycisk dalej bo minęło już np. 4000ms. Oczywiście nie stoisz w tym miejscu, żaden for, while, delay, wskakujesz w ten blok programu, sprawdzasz stan, odnotowujesz parametry w zmiennych, zerujesz lub inkrementujesz i powrót do loop, trwa to kilka us.
Zrobiłem sobie funkcję czas(), bo nie ma sensu sprawdzać millis dla każdej opcji dodanej w programie, wywolanie millis też trwa parę us, tak samo jak raz funkcji czas(), a potem takie polecenie if(flaga1s) to już tylko ze 100ns, lub kilka ns w szybszym od UNO ESP32.
Miło być decenianym https://buycoffee.to/kaczakat
 
Odpowiedź
  


Skocz do:


Przeglądający: 1 gości