Pytanie:
Debouncing oprogramowania w celu wykrycia, czy przełącznik został naciśnięty przez T sekund
rrz0
2018-02-20 22:22:03 UTC
view on stackexchange narkive permalink

Muszę wykryć, czy przełącznik został naciśnięty dłużej niż przez określony czas, bez użycia jakichkolwiek rejestrów Timera w moim osadzonym kodzie.

Aby wykryć normalne naciśnięcie przełącznika, używam usuwania oprogramowania w następujący sposób.

  int buttonPress (void)
{
  static int downCount = 0; // static = value nie jest tracona między wywołaniami.

  int currentButton = ((IO0PIN & 0x00000200) == 0x00000200);

  if (currentButton) {// przycisk nie działa

    if (downCount == 5) {// przycisk nie działa od 5 zliczeń
      downCount ++; // zwiększ licznik, aby nie uruchomić następnym razem
      powrót 1;
    } else {// niektóre liczą się inne niż 5
      if (downCount < 5) // zwiększ, jeśli jest mniej niż 5
        downCount ++;
    }
  Przycisk} else {// jest włączony
    downCount = 0;
  }
  return 0;
}
 

Chciałbym jednak sprawdzić, czy ten przycisk został naciśnięty przez 5 sekund lub dłużej. Problem, z którym się zmagam, polega na tym, że nie mogę policzyć do 300 000, a jeśli to nie zostanie osiągnięte, wróć do pierwotnego downCount 5, aby oznaczyć jako normalne naciśnięcie przycisku.

(Ponieważ taktowanie mojego mikrokontrolera wynosi 60 MHz, osiągnięcie 300 000 zajmie 5 sekund).

Bawiłem się następującą logiką, jednak w przypadku pokazanym poniżej, ze względu na wprowadzenie instrukcji while , liczę do 300 000 niezależnie od tego, jak długo przycisk był wciśnięty.

  if (currentButton) {// button is down

  if (downCount == 5) {// przycisk nie działa od 5 zliczeń
    downCount ++; // zwiększ licznik, aby nie uruchomić następnym razem
    while (downCount > 5) {
      if (debounce == 300000) {
        flag_5SEC = 1;
      } else if (usuń < 300000) {
         debounce ++;
      }
    }
    powrót 1;
  } else {// niektóre liczą się inne niż 5
    if (downCount < 5) // zwiększ, jeśli jest mniej niż 5
      downCount ++;
  }
}
 

Jakieś wskazówki lub sugestie, jak wykonać to zadanie?

Note: Chcę wyraźnie wykryć dłuższe naciśnięcie przełącznika w powyższym kodzie odbijającym, ponieważ robienie czegokolwiek innego może mieć niekorzystny wpływ na resztę kodu, który ktoś wcześniej napisał.


Udało mi się ukończyć powyższe zadanie przy użyciu timerów, jednak od tego czasu używam wszystkich rejestrów timera (i dopasowuję dostępne rezystory) do ważniejszych zadań w ramach tego samego projektu.

Nie muszę wykrywać dokładnego opóźnienia, po prostu jeśli przycisk był wciśnięty dłużej niż, powiedzmy, 2-3 sekundy.

Komentarz @DiBosco wyjaśnia więcej:

Więc mówisz, że jeśli naciśniesz przełącznik dla, powiedzmy, dwudziestu zliczeń i zostanie zwolniony, który działa jak normalne naciśnięcie, które wykonuje jedną funkcję, i jeśli zostanie naciśnięty i przytrzymany przez około dwie sekundyspełnia inną funkcję ...

Wydaje mi się, że gdy spadnie do pięciu, po prostu utkniesz w pętli while.Zakładam, że wywołujesz to z głównej nieskończonej pętli?Potrzebujesz lepszego sposobu na wejście do funkcji z głównej pętli, zwiększenie licznika i wyjście, jeśli nie osiągnąłeś docelowej liczby.Jeśli jednak nie zrobisz tego z timera (możesz ustawić flagę w przerwaniu, nawet jeśli jest to przerwanie współdzielone) i wejdziesz tutaj z głównej pętli, gdy ta flaga jest ustawiona, będzie to szalenie niedokładne.
PS Kiedy odpowiadasz komuś, upewnij się, że używasz jego imienia, aby wiedział, że odpowiedziałeś.:)
@DiBosco, dziękuję za wnikliwy komentarz, tak, wzywam to z głównej nieskończonej pętli.Zgadzam się ze wszystkimi wspomnianymi oświadczeniami i próbuję znaleźć lepszy sposób na zrobienie wszystkich wymienionych rzeczy.Czy metoda, której próbuję użyć (bez timerów), byłaby dobra, jeśli chcę po prostu wykryć naciśnięcie przycisku dłuższego niż, powiedzmy, 2 sekundy, zamiast dokładnej wartości?
Przepraszam, że źle odczytałem kod ... nie jestem jednak pewien, jak to nazywasz ...
Mam instrukcję `if (buttonPress ())`, która jest prawdą tylko w przypadku zwrotu 1
@Rrz0 ya, ten rodzaj działa przy założeniu, że wywołania są wykonywane w regularnych i dostatecznie długich, ale niezbyt długich odstępach czasu.
@DiBosco, Obecnie szukam rozwiązania w kierunku aktualnych odpowiedzi (które wydają się najlepszą opcją), jednak widziałem fragment odpowiedzi, którą opublikowałeś i zastanawiałem się, czy możesz go zmodyfikować / przywrócić?
Gotowe.Ponieważ były dwie inne dobre odpowiedzi i przypadkowo kliknąłem Prześlij, zanim była gotowa, moja wydawała się niepotrzebna, ale mam nadzieję, że pomoże.
Dodano do tego trochę więcej dla większej przejrzystości (miejmy nadzieję!)
Mówisz, że nie chcesz używać timerów, ale czy na używanej platformie jest funkcja, która zwraca czas, który upłynął?(Podobnie do `millis () 'w Arduino).
@NickGammon, nie, nie ma ...
Jak zamierzasz wykryć „5 sekund lub dłużej”, jeśli nie masz pojęcia, ile czasu upłynęło?Dobrze jest powiedzieć, że będziesz liczyć instrukcje, ale jeśli dodasz coś do pętli liczącej, to się skończy.
„Ponieważ taktowanie mojego mikrokontrolera wynosi 60 MHz, osiągnięcie 300 000 zajmie 5 sekund”.Czy coś mi brakuje, czy też (1) pomyliłeś mega kilogram i (2) założyłeś, że iteracja pętli jest wykonywana w jednym cyklu zegara?
@ThomasPadron-McCarthy, dzięki za zwrócenie uwagi.Tak, to błąd z mojej strony.
Cztery odpowiedzi:
Dave Tweed
2018-02-20 22:44:07 UTC
view on stackexchange narkive permalink

Udało mi się ukończyć powyższe zadanie przy użyciu timerów, jednak od tego czasu używam wszystkich rejestrów timera (i dopasowuję dostępne rezystory) do ważniejszych zadań w ramach tego samego projektu.

W takim przypadku wszystko idzie źle. O ile absolutnie nie potrzebujesz wysokiej rozdzielczości, niskiego jittera lub innych funkcji sprzętowych timerów sprzętowych, powinieneś wykonywać synchronizację w oprogramowaniu, używając jednego z timerów sprzętowych do generowania ciągłych okresowych przerwań, które możesz policzyć w ISR.

„Przerwanie pulsu” z okresem od 100 µs do 100 ms (lub więcej) jest powszechną cechą systemów wbudowanych. Pozwala to na utworzenie dowolnej liczby programowych timerów.

Nawet jeśli już używasz wszystkich liczników sprzętowych, o ile przynajmniej jeden z nich wykonuje coś okresowego, na przykład generuje sygnał PWM lub tworzy zegar dla interfejsu szeregowego, możesz włączyć na nim przerwania i używaj ich jako podstawy czasu oprogramowania. Okres może nie być „wygodną” wartością, ale jest to prosty problem skalowania, z którym oprogramowanie może sobie poradzić.

Jak umysły :) 341 na wynos ..... rolka werbla!
Tak więc, jeśli mam przerwanie Timer z okresem 1 ms, mogę po prostu ustawić flagę, gdy przycisk został naciśnięty, a następnie, gdy flaga jest wysoka, policzyć wewnątrz przerwania.Czy to stwierdzenie jest poprawne?
Nie jestem nawet typem EE, a to była bardzo dobrze napisana, łatwa do zrozumienia odpowiedź!Jedyne, czego nie znam, to „ISR”, ale po to jest Google;)
@Rrz0: Tak, ale w rzeczywistości potrzebne są dwa liczniki: jeden do zliczania około 10 ms czasu „odbicia” w celu określenia, czy przełącznik zmienił stan, a drugi do zliczania czasu „wstrzymania” przełącznika wynoszącego 5000 ms.
Dave, właściwie potrzebujesz tylko jednego licznika i dwóch poziomów porównawczych liczników.Jeśli zegar tikowy wynosi 1 ms, liczba 10 = pozbawiona, liczba 5000 = wstrzymana, a następnie przestań odliczać.
@Trevor_G: Istnieje wiele sposobów implementacji wielu timerów w przypadku okresowego przerwania.Posiadanie osobnego licznika dla każdego jest prawdopodobnie najłatwiejsze do zrozumienia.Pojedynczy licznik z wieloma komparatorami jest ogólnie bardziej wydajny, ale trudniejszy do wyjaśnienia.Wybór zależy w dużej mierze od innych aspektów systemu.
Trevor_G
2018-02-20 22:45:12 UTC
view on stackexchange narkive permalink

Twój kod wydaje się być nieco błędny i jest trudny do zrozumienia, ponieważ nie jest jasne, gdzie znajduje się pętla while w odniesieniu do przerwania lub cokolwiek ustawia funkcję DownCount.

Wydaje się również, że zwiększasz licznik odbić w dwóch miejscach w pętli while, co po prostu wydaje się błędne.

Twoje rozwiązanie in-line jest problematyczne z wielu powodów, z których nie mniej ważny jest Twój czas, który będzie się różnił w zależności od tego, co jeszcze dzieje się w mikro, chyba że zatrzymasz się i wykonasz ścisłą pętlę podczas liczenia. Ale zatrzymanie się na pięć sekund to również zły pomysł.

Ten rodzaj funkcji jest normalnie obsługiwany w mikro za pomocą licznika tików, czyli bicia serca. Zasadniczo powszechną praktyką jest rezerwowanie jednego timera, który będzie działał jako zegar dla twojego kodu. Zegar generowałby regularne przerwanie, być może 10 razy na sekundę.

Ten program obsługi przerwań może zająć się wieloma funkcjami porządkowymi, takimi jak wyłączanie przełączników, miganie diod LED, sprawdzanie limitów czasu komunikacji itp. Może również obejmować liczenie czasu naciskania przycisku. Gdy zostanie naciśnięty przez odpowiedni czas, wspomniana funkcja obsługi wywołuje następnie odpowiednią procedurę obsługi dla tego zdarzenia.

Ta metoda umożliwia użycie jednego licznika czasu dla wielu funkcji.

Dziękuję za wskazanie błędu w moim kodzie.To rzeczywiście jest złe.
Zacząłem rozumieć, że wspomniana powyżej metoda jest rzeczywiście właściwą drogą.Czy sugerujesz, żebym zajął się pełnym debounowaniem oprogramowania wewnątrz przerwania?
@Rrz0 tak, możesz obsłużyć również odbicia w przerwaniu zegara taktującego w oparciu o flagi z przerwania przycisku, jeśli takie istnieje, lub przez odpytywanie linii IO oraz flagi i zmienne ustawione od poprzedniego stanu zegara taktowania.
@Rrz0 Powinienem również wspomnieć, ponieważ nie jest to jasne z twojego kodu, musisz usunąć zarówno naciśnięcie przycisku, jak i zwolnienie przycisku.Co zaskakujące, przełączniki również odbijają się, gdy puszczasz.
Jeroen3
2018-02-21 01:30:15 UTC
view on stackexchange narkive permalink

Ponieważ przyciski są obsługiwane przez bardzo powolnych ludzi, w stosunku do szybkości procesora, można uniknąć powolnego odpytywania. Na przykład co 10 milisekund.
Możesz łatwo utworzyć jedną funkcję do obsługi wielu przycisków, z wieloma wzorcami naciśnięć i akcjami przytrzymania.

very simple wersja tego, czego często używam, wygląda tak. Ma tylko flagę wstrzymania 5 sekund, żadne z naciśnięć nie liczy i nie potwierdza flagi (flagi zostaną ustawione natychmiast po ponownym wyczyszczeniu).
Ale przy odrobinie kreatywności możesz w ten sposób dodać wiele rzeczy. Ale będzie trochę wydłużony.
Możesz nawet dodać kanały wejściowe komputera i sprawić, by były w ten sposób usuwane.

  #define BUTTON_INTERVAL 10
# zdefiniować BUTTON_COUNT 1

/ * Definicja typu dla przycisku * /
typedef struct button_s {
    void * port; / * Używaj typu portu GPIO, niezależnie od tego, z czego korzysta Twoja platforma, może nawet za pośrednictwem bitbandingu * /
    uint32_t hi_time; / * Czas wysoki * /
    uint8_t pin; /* Kod PIN */
    flagi uint8_t; / * Flagi wyjściowe * /
} button_t;

/ * Tablica wszystkich przycisków * /
button_t przyciski [BUTTON_COUNT];

/ * Obsługa odpytywania przycisku * /
void buttonHandler (void) {
    uint8_t i;
    for (i = 0; i<NR_OF_BUTTONS; i ++) {
        button_t * b = przyciski [i];
        uint8_t state = * b->port & (1<<b->pin);
        if (state) {
            // Nasycenie 32-bitowego dodawania bez znaku
            // Twój procesor może mieć odpowiednią instrukcję, np .: arm __uqadd (a, b))
            if (b->hi_time < 0xFFFFFFFF) b->hi_time + = BUTTON_INTERVAL;
        }jeszcze{
            b->hi_time = 0;
        }
        // Jedno z wielu możliwych porównań flag:
        if (b->hi_time > = 5000) {
            b->flags | = BUTTON_HI_5S;
        }
    }
}

/ * Przykład użycia flagi * /
void waitForButton () {
    if (button [0] ->flags & BUTTON_HI_5S) {
        przycisk [0] ->flags & = ~ BUTTON_HI_5S;
// ..stuff ..
    }
}
 

Najważniejsze punkty, jeśli wybierasz się sam:
- Struktura dla wszystkich danych, jeśli chcesz, możesz nawet zmieniać mapowanie kluczy w locie.
- Nieskończone przyciski za pomocą struktur.Nigdy więcej nie pisz tego kodu!
- Zegary nasycające, nie przepełniają.
- Flagi, łatwe do wymiany na wywołania systemu operacyjnego.

DiBosco
2018-02-20 22:47:36 UTC
view on stackexchange narkive permalink

Jeśli dobrze rozumiem Twoje potrzeby, potrzebujesz czegoś takiego:

  void SwitchCheck (void)
{
    DebounceTimer = false; // Jeśli używasz flagi timera
    if (currentButton)
        {
        debounce ++;
        }
    jeszcze
        {
        if (odbij > NORMAL_OPERATION)
              {
              // Rób tutaj normalne rzeczy
              }
        debounce = 0;
        }
    if (debounce == MAX_TIMEOUT)
         {
         // Zrób swoją trzymaną tutaj rzecz
         }
}
 

Nazwij to z głównej pętli. Najlepiej wywołać to dopiero po poprawnym ustawieniu przerwania timera, aby uzyskać dokładny czas.

Przez lata robiłem wiele, wiele razy wyłączanie przełączników bez timerów (chociaż nie tak naprawdę), po prostu dzwoniąc z głównej pętli i działa. Jeśli jednak szukasz dokładności w swoim pięciosekundowym pomiarze czasu, to nie wystarczy.

Dalsza edycja:

W ISR Twojego timera:

  TimerISR ()
{
DebounceTimer = true;
// Inne rzeczy
}
 

Głównie:

  if (DebounceTimer)
    SwitchCheck ();
 

Lub po prostu wywołaj to z main bez sprawdzania flagi, czy nie używasz timera, i eksperymentuj empirycznie z MAX_TIMEOUT.

Mogę potwierdzić, że nie szukam dokładności w tym konkretnym przypadku.
@JohnSmith Nie jestem pewien, co masz na myśli.(Nie jest sarkastyczny, naprawdę nie do końca jestem pewien, do czego zmierzasz). Nie zrobi nic, dopóki nie osiągnie MAX_TIMEOUT, a następnie wykona to, co należy wykonać, i po prostu odpadnie.Ryzykuje, jeśli zostanie przytrzymany przez BARDZO długi czas, że DebounceTimer działa prawidłowo i ponownie wykonuje.Może być konieczne sprawdzenie, czy tak się nie stanie.Jednak jest to raczej przewodnik niż kompletne rozwiązanie.Mogłem jednak całkowicie przegapić coś w tym, czego szukał OP.
@DiBosco, czytając powyższe, nie, nie przegapiłeś całkowicie tego, czego szukam.Jednak chcę móc wykryć normalną prasę przełączającą, w której TIMEOUT byłby znacznie mniejszy niż MAX_TIMEOUT.Tak więc, ponieważ nic się nie dzieje, dopóki nie osiągnie MAX_TIMEOUT, należy wprowadzić pewne poprawki.
@Rrz0 Więc to, co mówisz, to to, że jeśli naciśniesz przełącznik dla, powiedzmy, dwudziestu zliczeń i zostanie zwolniony, który działa jak normalna prasa, która wykonuje jedną funkcję, a jeśli zostanie naciśnięty i przytrzymany przez dwie sekundy lub takma inną funkcję?
Dokładnie, jestem w trakcie wdrażania takiej funkcjonalności.Chociaż inne odpowiedzi mogą być lepszą praktyką, w tym konkretnym przypadku wybieram tę metodę.
@Rrz0 OK, dodano dodatkowo.Nie testowałem tego, ale * myślę *, że zrobi to, co chcesz.Musisz wypróbować różne wartości MAX_TIMEOUT i NORMAL_OPERATION, aby było to, czego chcesz.


To pytanie i odpowiedź zostało automatycznie przetłumaczone z języka angielskiego.Oryginalna treść jest dostępna na stackexchange, za co dziękujemy za licencję cc by-sa 3.0, w ramach której jest rozpowszechniana.
Loading...