Skocz do zawartości

Pomiar odstępów czasowych pomiędzy impulsami przerwania


kellyq

Pomocna odpowiedź

Hej, może ktoś sprawdzić czy mój kod będzie poprawnie zliczasł czas pomiędzy impulsami? Mam obiekcje, czy można w funkcji przerwania mierzyć czas?

#define hallPin 2
unsigned long start_time = millis();
unsigned long event_time = 0; 
boolean flaga = 0;
float obwod_kola = 2.09;           //obwód w metrach
byte ilosc_imp = 6;                //impulsy na obrót
float wycinek_kola = obwod_kola/ilosc_imp; 


void setup() {
  pinMode(hallPin, INPUT);
  attachInterrupt(0, Przerwanie, FALLING);
  Serial.begin(9600);

}

void loop() {
float predkosc;

if(flaga == 1){
  predkosc = (wycinek_kola/(event_time-start_time)*1000)*3.6; 
  start_time = millis();
  flaga = 0;
  Serial.print(predkosc);
  Serial.println("km/h");
  }
}
void Przerwanie(){
event_time = millis();
flaga = 1; 
}

 

Link do komentarza
Share on other sites

17 minut temu, kellyq napisał:

czy można w funkcji przerwania mierzyć czas?

Mierzyć to nie, ale odczytywać wynik pomiaru (podobnie jak Ty to robisz) można. Ale...

Poczytaj sobie o tajemniczym słówku volatile, a jeśli coś więcej będziesz robić za pomocą przerwań o operacjach atomowych.

  • Lubię! 1
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

A ja bym właśnie nie próbował manipulować blokowaniem/odblokowywaniem przerwań, tylko lepiej wykorzystał flagę jako semafor. Niech to jego stan kontroluje możlwość zapisu/odczytu nadzorowanej zmiennej. Proices główny (odczyt) jest OK: odczytuje wartość czasu tylko gdy flaga jest ustawiona i ją zeruje. Teraz kolej na zapis: powinien robić odwrotnie: zapisywać tylko wtedy gdy flaga jest wyzerowana:

void Przerwanie(){
  if (flaga == 0) {
	event_time = millis();
	flaga = 1;
  }
  else
    overrun = 1;
}

Część po else nie jest konieczna, ale może informować proces odczytu, że pewnego razu nie zdążył i że event_time jest nieważny. W każdym razie teraz zmienna event_time może być dowolnie długa (w sensie "atomowości" operacji procesora) a procesy i tak poprawnie zabezpieczają sobie do niej dostęp i nie psują jej w asynchronicznych momentach bez uciekania się do "ręcznego sterowania gospodarką" przerwaniami.

Acha, i jeszcze jedno,. W pętli głównej robisz pewnien delikatny "poślizg", który może prowadzić do błędów. Jak rozumiem, urządzenie działa tak, że chcesz mierzyć czas między kolejnymi zboczami sygnału z czujnika. Dlaczego więc, nie zrobisz tego tak:

if(flaga == 1){
  predkosc = (wycinek_kola/(event_time-start_time)*1000)*3.6; 
  start_time = event_time;
  flaga = 0;
  Serial.print(predkosc);
  Serial.println("km/h");
  }
}

Po co drugi raz w swojej wersji wołasz millis()? Przecież event_time własnie wyznacza czas ostatniego zdarzenia. To od niego masz mierzyć czas do następnego, a nie od kolejnego odczytu czasu systemowego, który może być lekko opóźniony (nie wiesz ile kosztują obliczenia floating point).

No i jeszcze: o jakich okresach czasu mówimy? Bo w pętli pomiarowej masz print na port szeregowy a to może być dowolnie wolne. Znacznik overrun powinien być w takiej sytuacji obowiązkowy.

Edytowano przez marek1707
  • Lubię! 1
Link do komentarza
Share on other sites

Czas pomiędzy zboczami opadającymi to od ok. 10ms do ok. 2000ms. W drugim przypadku trochę się chyba zamotałem z tym czasem. Wieczorem  sprawdzę to zaproponowane rozwiązanie. Odczyt tej prędkości na serial jest tylko do testów. Finalnie odczytuję wartość prędkości na lcd co 250ms w pętli if

if(millis()-oldTime > 250){

  lcd.bla bla bla
  bla bla bla
  
    
  oldTime = millis();
}

 

Link do komentarza
Share on other sites

11 minut temu, marek1707 napisał:

A ja bym właśnie nie próbował manipulować blokowaniem/odblokowywaniem przerwań, tylko lepiej wykorzystał flagę jako semafor.

Możesz wyjaśnić skąd taki dziwny pomysł i dlaczego zablokowanie przerwań na czas odczytu zmiennej jest złe? Zablokowanie/odblokowanie przerwań to jedna instrukcja procesora, a ATOMIC_BLOCK właśnie po to został wymyślony żeby ułatwić życie programistom. Poleganie na własnych flagach do synchronizacji wątków to nie jest dobry pomysł. To będzie działać w jednym programie, w drugim nie będzie.

14 minut temu, marek1707 napisał:

wykorzystał flagę jako semafor

Ale wiesz o tym, że operacje na semaforach również muszą być atomowe?

Link do komentarza
Share on other sites

@ethanakNie twierdzę, że blokowanie przerwań jest złe. To taki mechanizm "siłowy" i czasem konieczny jeśli nie ma innych pomysłów a czasem najprostszy, to fakt. Moim zdaniem jest taki trochę.. nielegancki a przede wszystkim tutaj zupełnie zbędny. Opisałem przecież jak jak bym to zrobił. W tej konkretnej aplikacji "semafor" jest nazwą trochę na wyrost, bo choć flaga działa jak on - synchronizuje dostęp do zasobu, to jednak jej obsługa nie musi być atomowa, w tym całe piękno tego rozwiązania. Masz rację, typowe, uniwersalne semafory muszą mieć zaimplementowane nieprzerywalne operacje typu "sprawdź i podnieś: lub "sprawdź i opuść" co w dużych systemach, gdzie jest wielu ubiegających się o o zasób petentów pracujących równocześnie ma sens i jest konieczne. Tutaj - nie. Popatrz na spokojnie: tylko jeden proces jest producentem danych i tylko jeden jest konsumentem. Tylko jeden z nich ustawia flagę po wpisaniu danych i tylko jeden ją opuszcza po wykorzystaniu. Na pewno nikt inny nie będzie robił tego samego równolegle więc nigdy nie zajdzie przypadek w którym np. pętla główna sprawdzi w if-ie ustawienie flagi i zanim wykorzysta dane ktoś inny w tym czasie także to sprawdzi, odczyta i skasuje flagę. Tak nigdy się nie stanie, prawda? Także Twoje obawy są zatem niesłuszne. Oczywiście, w skomplikowanym środowisku obowiązuje zasada niepodzielności tego typu operacji i zwykle zapewnia to jakiś system albo nasze własne fukcje (np. blokując przerwania), ale nie tutaj. 

Ze stwierdzeniem, że pewne rozwiązania będą działać w jednym programie a w drugim nie - zgadzam się w zupełności. I czy to znaczy, że te mniej uniwersalne są złe z założenia?

@kellyq Zobacz: funkcja obsługująca przerwanie zapisuje do zmiennej event_time dokładny czas zdarzenia. To powinien być koniec pomiaru poprzedniego odcinka i jednocześnie start następnego. Niemusisz więc po raz drugi odpytywać funkcję millis() by poznać start_time. Przecież już go masz - siedzi w najnowszym event_time. Policzyłes długość odcinka a teraz koniec ostatniego staje się startem następnego. Wydaje się intuicyjne, prawda?

Edytowano przez marek1707
Link do komentarza
Share on other sites

2 minuty temu, marek1707 napisał:

Moim zdaniem jest taki trochę.. nielegancki a przede wszystkim tutaj zupełnie zbędny

Tyle, że ów mechanizm jest całkowicie naturalny, użycie ATOMIC_BLOCK jest IMHO całkiem eleganckie (cokolwiek by to "eleganckie" znaczyło), a zbytnie komplikowanie programu nikomu na zdrowie nie wyszło. Zauważ: wprowadzasz dodatkowego cosia co się nazywa "semafor", i uzależniasz działanie kodu od owego semafora w dwóch miejscach - a to o jedno miejsce za dużo. Zakładając, że czas przetwarzania danych jest dużo krótszy niż odstęp czasowy między impulsami (sądząc po parametrach wygląda mi to na jakiś prędkościomierz do jednośladu) spokojnie można użyć flagi jako informacji "masz nowe dane", i nawet blokowanie przerwań nie jest w tym konkretnym przypadku konieczne. A jeśli czas przetwarzania jest zbyt długi - robi się to w ogóle inaczej (liczy się ilość impulsów w jednostce czasu). W żadnym przypadku nie są potrzebne żadne dodatkowe overruny czy inne byty rodem z cirthu.

I jeszcze jedno: ten program jest najprawdopodobniej testową wersją prędkościomierza. W docelowym pewnie będzie jakaś obsługa wyświetlacza, może coś jeszcze, jakaś klawiatura, jakaś obsługa dodatkowych urządzeń (choćby olejarka łańcucha w motocyklu), i ten pomiar prędkości nie jest jedyną czynnością, której Arduino musi się poświęcić. A jeśli np. dodasz do tego jakiś licznik przejechanej drogi, w ogóle nie ma mowy o jakimkolwiek uzależnieniu zliczania impulsów od jakichś flag (a szczególnie od tego, czy główny program raczył ustosunkować się do naszego impulsu czy może zajęty jest innymi równie ważnymi rzeczami).

 

 

Link do komentarza
Share on other sites

Trafiony zatopiony prawie 😉 nie jednoślad tylko ciągnik.

Już wyjaśniam. Program "czyta" stan 11 przycisków oraz wylicza prędkość z czujnika halotronowego, następnie przesyła paczkę z danymi drogą radiową na drugi sterownik. Pobiera z kolei z tego drugiego sterownika dane z 2 czujników: ciśnienia i przepływomierza. Na LCD wyświetla aktualną prędkość, ciśnienie i przepływ cieczy. Więc walczę żeby to działało jak najlepiej.

Sterownik z lcd mam już fizycznie gotowy, tylko ten drugi wykonawczy to prowizorka, używam na razie arduino UNO.

 

No to teraz pytanie co taka komunikacja radiowa na nrf24l01 powie na przerwania w trakcie wysyłania? Wszystko niby działa ale mam takie odczucie że delikatnie opóźnia się reakcja na drugim odbiorniku. Jakieś ja wiem 100ms poślizgu.

Link do komentarza
Share on other sites

Dokładnie tak, pisałem o tym konkretnym przypadku. A gdyby okazało się, że overrun jest nieakceptowalny (bo może tak być, ale tego nie wiemy), to pomyślałbym o liczeniu odcinków czasu i ich liczby bezpośrednio w przerwaniu i dopiero wtedy ustawianiu flagi "nowe"dane". Raczej żadnego liczenia na floatach bym tam wciskał, ale wyniki w postaci uint32_t - zarówno dla czasu liczonego w ms jak i licznika drogi by się sprawdziły. Wtedy proces odczytu mógłby bezkarnie gubić kolejne wpisy gdyby był czymś zajęty na dłużej, a i tak zawsze mógł by się dosynchronizować do pomiarów np. przez skasowanie flagi i poczekanie na jej ustawienie. Także choć trudno mi rozpatrywać wszystkie przyszłe przypadki, to jeden producent i jeden konsument nie wymagają blokowania przerwań tj. wprowadzania bloków operacji atomowych ani przy manipulacji na semaforze ani podczas zapisów/odczytów danych - i tylko na to chciałem zwrócić uwagę.

Nie rozumiem tego o wprowadzaniu nowego cosia. Przecież flaga już była a blokowanie zapisów przed odczytem poprzednich danych bardzo ładnie synchronizuje obu "chętnych" w dostępie do zasobu. Odczytujący powinien miec szansę się dowiedzieć, że nie zdążył odczytać więc overrun także wydaje się naturalne, choć jak napisałem, nie jest konieczne. To raczej zależy od preferencji piszącego i jego wyczucia zjawisk. Przyjęta w tym konkretnym przypadku metoda liczenia czasu od ostatniego zdarzenia właśnie przez duże obciążenie głównej pętli może w bardzo łatwy sposób zgłupieć i zacząć w dobrej wierze liczyć złe rzeczy tylko daltego, że nie będzie świadoma pominięcia jakichś wyników. Prosta flaga tego nie sygnalizuje i stąd dodatkowa overrun.

Czy możemy uznać, że znamy i rozumiemy swoje stanowiska? Ja się wypisuję a Tobie zostawiam zaszczytną rolę niosącego kaganek oświaty w tym wątku. Powodzenia 🙂

BTW: Z resztą widzę, że kolega @kellyq szybko łapie więc - o ile nie będą przeszkadzać różni "mądrale" - większych problemów nie przewiduję 🙂 

Link do komentarza
Share on other sites

@marek1707 Marku, zdaje się że umknęła Ci jedna dość ważna rzecz: kolega nie pytał o to, w jaki sposób należy liczyć prędkość obrotową czy ilość obrotów koła, ale o konkretne zagadnienia z działu "obsługa przerwań". I na takie pytania odpowiedzi udzieliłem chyba wyczerpująco. Co do samej metody pomiaru: zgodzę się z tym że jest do niczego, nie zgodzę się z tym że to można w jakiś sposób poprawić (o ile napisania tego od początku nie uznamy za poprawianie). Tyle, że to nie ten wątek... a tu wolałbym się ograniczyć do właściwego tematu (bo przyznasz sam: na pytanie "jak się tego używa" odpowiedź w stylu "a ja bym tego nie używał" jest jakby poza tematem).

Sam zadałem ostatnio pytanie o konkretny prosty układ elektroniczny. Gdyby ktoś mi odpowiedział, że zamiast ESP32 on by użył STM, a dodatkowo wsadził tam szesnastobitowy przetwornik ADC to mógłby się paru nowych słów nauczyć...

Pozostańmy więc może przy odpowiadaniu na zadane pytania 🙂

A kolega @kellyq jak sobie nie da rady z prędkościami to na 100% zapyta, ale w tym celu założy nowy wątek.

 

Link do komentarza
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!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   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...

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.