Skocz do zawartości

Arduino- enkoder obrotowy - jakim cudem działa ten kod?


mareq

Pomocna odpowiedź

Chodzi mi o kod dostępny jako przykład dla enkodera od WaveShare:

#define PinA 2  
#define PinB 3  

unsigned long time = 0; 
long count = 0; 
long num = 0;


void setup()
{
 Serial.begin(9600);

 pinMode(PinA,INPUT); 
 pinMode(PinB,INPUT); 

 attachInterrupt(0, blinkA, LOW);  
 attachInterrupt(1, blinkB, LOW);  

 time = millis(); 
}

void loop()
{
 while (num != count)
 {
   num = count;
   Serial.println(num);
 }
}

void blinkA()
{
 if ((millis() - time) > 3)
       count ++; 
 time = millis();
}

void blinkB()
{
 if ((millis() - time) > 3)  
       count --;
 time = millis();
}

Ten kod w realu działa, spawdzone.

Po pierwsze: dlaczego przerwanie aktywowane jest stanem a nie zboczem. Toż to te przerwania powinny się wykonywać niemal na okrągło.

Po drugie: n=mamy funkcję "blinkA", która zwiększa licznik oraz "blinkB", która zmniejsza. Przecież kręcąc osią enkodera w jedną stronę stan niski pojawia się sekwencyjnie na obu wejściach a tymczasem licznik zlicza właściwie a na chłopski rozum nie powinien. Gdzie robię błąd w rozumowaniu?

Link do komentarza
Share on other sites

Zaciekawił mnie ten przypadek, szczególnie że masz rację pisząc - to nie ma szans działać 🙂

Ten kod jest świetnym przykładem, który potwierdza dwie rzeczy: kody dostępne w internecie bywają bardzo różnej jakości (czytaj bardzo kiepskiej). Druga - to że coś działa nie znaczy, że to dobry program.

Zacznijmy od początku. Sprawdziłem - faktycznie program działa, wyniki wyglądają bardzo ładnie, tylko program jakby mniej.

Patrząc na program podejrzewałem, że tajemnica kryje się w 3 ms filtrowaniu. Jeśli impulsy mają mą szerokość <= 3ms to program powinien działać poprawnie. Podłączyłem analizator i wyszło coś takiego:

Od razu widać, że impulsy są szersze niż 3ms, więc jak to działa? Spróbowałem trochę pozmieniać program - na początek wyrzuciłem przerwanie od pinu 3, czyli został tylko blinkA. Program nie wykrywa już kierunku ruchu, ale poza tym działa idelanie... Magia?

Już planowałem poszukiwać księgi czarnej magii, gdy przyszło mi do głowy coś innego - a co będzie jak wyrzucimy ten 3ms filtr? Okazuje się że wtedy faktycznie przychodzi dużo przerwań. Zmieniłem więc trochę program i wyświetlam nie tylko odczyt enkodera, ale również liczbę zgłoszonych przerwań:

#define PinA 2 
#define PinB 3 

unsigned long time = 0;
long count = 0;
long num = 0;
unsigned long repeats = 0;

void setup()
{
 Serial.begin(9600);
 Serial.println("Hello world!");

 pinMode(PinA,INPUT);
 pinMode(PinB,INPUT);

 attachInterrupt(0, blinkA, LOW); 
 //attachInterrupt(1, blinkB, LOW); 

 time = millis();
}

void loop()
{
 while (num != count)
 {
   num = count;
   Serial.print(num);
   Serial.print(" (");
   Serial.print(repeats);
   Serial.println(")");
   repeats = 0;
 }
}

void blinkA()
{
 repeats++;
 if ((millis() - time) > 3)
   count ++;
 time = millis();
}

void blinkB()
{
 if ((millis() - time) > 3) 
   count --;
 time = millis();
}

Rezultaty wyglądają mniej więcej jak:

Hello world!

1 (1065)

2 (4292)

3 (6058)

4 (1057)

5 (1049)

6 (1039)

7 (1069)

8 (1054)

9 (1054)

10 (1763)

11 (496)

12 (654)

13 (1018)

14 (1673)

15 (1668)

16 (1264)

17 (1775)

18 (725)

19 (576)

20 (751)

21 (881)

22 (983)

23 (1703)

24 (1695)

25 (1689)

26 (1691)

27 (1349)

28 (1706)

29 (1694)

30 (1698)

31 (1707)

Teraz już lepiej widać jak działa magia tego programu. Faktycznie przerwanie zgłaszane jest przy stanie niskim. Co więcej zgłaszane jest tysiące razy na każdy impuls. Jak więc działa filtracja?

Otóż procesor nie ma czasu na nic poza obsługą przerwania - czyli wchodzi do procedury, sprawdza czy minęły 3ms, jeśli nie to kończy obsługę. Ale od razu wykrywa że przerwanie oczekuje, więc wraca i jeszcze raz obsługuje przerwanie... I tak w kółko aż stan pinu zmieni się na wysoki.

Ponieważ to przerwanie zajmuje cały czas procesora, nie są obsługiwane inne przerwania, w tym przerwanie timera... Dlatego dla procesora nie mijają nawet 3ms chociaż w rzeczywistości to powiedzmy 20ms.

To chyba wyjaśnia jak działa ten koszmarny program. Należałoby go oprawić w ramki i z daleka pokazywać studentom: Uwaga tak nigdy nie należy robić! A że to działa to już zupełnie inna sprawa....

Link do komentarza
Share on other sites

Jeden drobiazg: po powrocie z przerwania procesor (w przypadku avr) zawsze wykonuje następną instrukcję przed wejściem w następne przerwanie. I tu się pewnie kryje tajemnica pozornie prawidłowego działania...

Link do komentarza
Share on other sites

Wykonywanie jednej instrukcji nie tłumaczy dlaczego licznik count daje poprawne wyniki - ale jeśli milis() przestaje działać, mamy gotowe wytłumaczenie "fenomenu" tego programu. Nie jestem ekspertem od przerwań AVR, ale domyślam się, że mamy jakiś prosty układ priorytetów, więc jeśli przerwanie od pinu ma wyższy priorytet niż przerwanie zegara, milis() ma prawo przestać działać. A głowny program możliwe że wykonuje się po jednej instrukcji na przerwanie - wierzę na słowo.

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

O ile wiem, to przerwania w AVR są kolejkowane - czyli przerwanie zegarowe kiedyś tam się wykona...

Tak przy okazji - nie rozumiem idei przerwania wywoływanego stanem a nie zdarzeniem... Ale to już inna bajka 🙂

Link do komentarza
Share on other sites

O ile wiem to nie do końca tak - jeśli więcej niż jedno przerwanie jest zgłoszone w momencie gdy mikrokontroler rozpoczyna obsługę przerwania, zawsze wykona to o najwyższym priorytecie. Nie ma więc kolejkowania, chociaż zgłoszenie przerwania jest niejako pamiętane - więc kiedyś się wykowna, ale dopiero po obsłudze wszystkich z wyższym priorytetem.

Dlatego jeśli przerwanie o wyższym priorytecie jest stale zgłaszane może "zagłodzić" pozostałe przerwania. To prowadzi do różnych błędów, np. złego zliczania czasu... I raczej nie pownno być traktowane jako przykład dobrego programu 😃

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.