2013-04-30 11 views
5

Jestem względnie nowy dla c i Raspberry Pi i próbuję prostych programów. Chciałbym, aby po naciśnięciu przycisku wydrukował jeden raz i nie drukuje ponownie, dopóki przycisk nie zostanie naciśnięty ponownie, nawet jeśli przycisk jest wciśnięty (rodzaj zatrzasku). Pomyślałem, że może dodanie drugiej pętli while naprawi to, ale czasami nie wykryje naciśnięcia przycisku.C zaprogramuj przycisk, aby wykonać zadanie raz po naciśnięciu (zatrzaśnięcie)

#include <bcm2835.h> 
#include <stdio.h> 
#define PIN RPI_GPIO_P1_11 

int main() 
{ 
    if(!bcm2835_init()) 
     return 1; 

    bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_INPT); 

    while(1) 
    { 
     if(bcm2835_gpio_lev(PIN)) 
     { 
      printf("The button has been pressed\n"); 
     } 

     while(bcm2835_gpio_lev(PIN)){} 
    } 

    bcm2835_close(); 
    return 0; 
} 
+3

Być może pomocne okaże się wyszukiwanie w google w celu "rozwinięcia przycisku". –

+2

Chodzi o to, aby wiedzieć, co to jest termin techniczny, abym mógł go wyszukać. Dziękuję – Marmstrong

+0

Z przyjemnością pomogłem - nie sądziłem, że to uzasadnia odpowiedź, ale czasem znalezienie właściwego terminu na pomoc naprawdę pomaga! –

Odpowiedz

1

Dla prostego programu takiego jak to, korzystanie z pętli zajęty, jak zrobiłeś, jest w porządku. Sugerowałbym jednak wyjście z przyzwyczajenia, ponieważ często jest to niedopuszczalne w przypadku czegoś więcej niż projekt zabawkowy.

Istnieje wiele sposobów na odrzucenie przycisku, ponieważ ludzie piszą kod. Wykonanie tego w sprzęcie może być w niektórych przypadkach rozwiązaniem, ale nie jest pozbawione wad. W każdym razie, ponieważ jest to strona programowania, załóżmy, że nie możesz (lub nie chcesz) zmienić sprzętu.

Szybką i brudną modyfikacją jest okresowe sprawdzanie przycisku w pętli głównej i działanie tylko w przypadku zmiany.Ponieważ jesteś początkującym programistą C i programowaniem wbudowanym, uniknę liczników czasu i przerwań, ale wiem, że możesz uczynić kod bardziej zrozumiałym i łatwym w utrzymaniu, gdy się o nich dowiesz.

#include <bcm2835.h> 
#include <stdio.h> 
#define PIN RPI_GPIO_P1_11 

// A decent value for the number of checks varies with how "clean" your button is, how 
// responsive you need the system to be, and how often you call the helper function. That 
// last one depends on how fast your CPU is and how much other stuff is going on in your 
// loop. Don't pick a value above UINT_MAX (in limits.h) 
#define BUTTON_DEBOUNCE_CHECKS 100 

int ButtonPress() 
{ 
    static unsigned int buttonState = 0; 
    static char buttonPressEnabled = 1; 

    if(bcm2835_gpio_lev(PIN)) 
    { 
     if(buttonState < BUTTON_DEBOUNCE_CHECKS) 
     { 
      buttonState++; 
     } 
     else if(buttonPressEnabled) 
     { 
      buttonPressEnabled = 0; 
      return 1; 
     } 
    } 
    else if(buttonState > 0) 
    { 
     buttonState--; 
     // alternatively you can set buttonState to 0 here, but I prefer this way 
    } 
    else 
    { 
     buttonPressEnabled = 1; 
    } 

    return 0; 
} 

int main() 
{ 
    if(!bcm2835_init()) 
     return 1; 

    bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_INPT); 

    while(1) 
    { 
     if(ButtonPress()) 
     { 
      printf("The button has been pressed\n"); 
     } 

     // the rest of your main loop code 
    } 

    bcm2835_close(); 
    return 0; 
} 
+0

Próbowałem zaimplementować ten fragment kodu. Wciąż było trochę odbicia, więc zmieniłem "BUTTON_DEBOUNCE_CHECKS". Znalazłem dobrą wartość około 300 (mój przycisk prawdopodobnie nie jest taki "czysty"). Częściej niż nie zwalniał przycisku, który spowodował odbicie. (Wypróbowano też "BUTTON_DEBOUNCE_CHECKS" = 5000 dla zabawy, zauważalne opóźnienie, ale bez bounce). Czy rozwiązania sprzętowe są bardziej niezawodne? Dzięki za pomoc! – Marmstrong

+0

Problem z tym rozwiązaniem polega na tym, że wartość wymagana dla "BUTTON_DEBOUNCE_CHECKS" zależy nie tylko od charakterystyki przełącznika, ale także od szybkości procesora i czasu wykonania "reszty kodu głównego". Jest również "zajęty-czeka" - bez żadnego blokowania w głównej pętli, zużycie procesora osiągnie 100%, a inne procesy spowodują spowolnienie systemu. Jeśli chcesz mieć możliwość wykonania innej pracy podczas monitorowania naciśnięcia przycisku, lepiej użyć osobnego wątku z poleceniem 'usleep()', aby ograniczyć obciążenie procesora. – Clifford

+0

Absolutnie zgadzam się co do delikatnej natury wartości "BUTTON_DEBOUNCE_CHECK" i wyrażonej zarówno w tekście, jak iw kodzie. Jednak czułem, że lepiej jest unikać zbyt wielu dyskusji na temat timerów, przerwań i harmonogramowania dla kogoś, kto dopiero uczy się o debouncing. Nie zgadzam się z tym, że kod jest zajęty oczekiwaniem. Nic nie stoi na przeszkodzie, by "reszta kodu" uległa lub spała. Jak napisano, zasysa wszystkie cykle procesora ** tylko wtedy, gdy ** jest czysto kooperatywnym wielozadaniowym systemem operacyjnym lub nie ma innych zadań. Nie jestem dostatecznie zaznajomiony z Raspberry Pi, aby wiedzieć, jakiego RTOS używa (jeśli jest). – jerry

2

Twoja logika jest poprawna i działałoby, gdyby przyciski były idealne. Ale nie są. Musisz odbić sygnał przycisku. Dwa sposoby osiągnięcia tego (najlepiej działa w połączeniu):

I. Dodaj kondensator między dwoma pinami przycisku (lub spróbuj an even more sophisticated button debouncer circuitry) i/lub

II. korzystać z oprogramowania debouncing (pseudo-C):

while (1) { 
    while (!button_pressed) 
     ; 

    printf("Button pressed!\n"); 


    while (elapsed_time < offset) 
     ; 
} 

itp

Edycja: jak @jerry wskazał, wyżej nie działa "poprawnie", gdy przycisk zostanie przytrzymany. Oto kilka z more professional code snippets, których możesz użyć, aby spełnić wszystkie wymagania.

+0

Jeśli przycisk jest przytrzymany, ten kod będzie powtarzać printf w nieskończoność – jerry

+0

@jerry To prawda. Dodano to, a także link do lepszych alternatyw. –

+0

Jeśli zmieniłeś 'while (elapsed_time jerry

1

Następująca funkcja sonduje przycisk w odstępach nominalnie 1 milisekundę i wymaga, aby stan pozostał "zwolniony" do 20 kolejnych ankiet. Zazwyczaj wystarcza to, aby porzucić większość przełączników, zachowując jednocześnie czas reakcji.

Zamień swoją pętlę while(bcm2835_gpio_lev(PIN)){} dzwoniąc pod numer waitButtonRelease().

#include <unistd.h> 
#define DEBOUNCE_MILLISEC 20 

void waitButtonRelease() 
{ 
    int debounce = 0 ; 

    while(debounce < DEBOUNCE_MILLISEC) 
    { 
     usleep(1000) ; 

     if(bcm2835_gpio_lev(PIN)) 
     { 
      debounce = 0 ; 
     } 
     else 
     { 
      debounce++ ; 
     } 
    } 
} 

Może okazać się, że konieczne będzie również odklejenie naciśnięć przycisków i wydań. Odbywa się to w ten sam sposób, ale licząc odwrotny Stan:

void waitButtonPress() 
{ 
    int debounce = 0 ; 

    while(debounce < DEBOUNCE_MILLISEC) 
    { 
     usleep(1000) ; 

     if(!bcm2835_gpio_lev(PIN)) 
     { 
      debounce = 0 ; 
     } 
     else 
     { 
      debounce++ ; 
     } 
    } 
} 

czy może jedną funkcję do nieczułości albo STAN:

#include <stdbool.h> 

void waitButton(bool state) 
{ 
    int debounce = 0 ; 

    while(debounce < DEBOUNCE_MILLISEC) 
    { 
     usleep(1000) ; 

     if(bcm2835_gpio_lev(PIN) == state) 
     { 
      debounce++ ; 
     } 
     else 
     { 
      debounce = 0 ; 
     } 
    } 
} 

W tej ostatniej funkcji, głównym pętli while może wyglądać :

while(1) 
    { 
     waitButton(true) 
     printf("The button has been pressed\n"); 

     waitButton(false) ; 
    } 

Jeśli masz dostęp do cyfrowego oscyloskopu, można badać sygnał przełączający bezpośrednio zobaczyć dokładnie to, co bounce przełącznik wygląda. Może pomóc w zrozumieniu problemu, a także w dostosowaniu się do cech konkretnego przełącznika.

+0

Wywołanie 'usleep (1000)' powinno znajdować się wewnątrz pętli while. W przeciwnym razie nie rozwiążesz wielu problemów. – jerry

+0

@Jerry: Dobre miejsce - dzięki. – Clifford

Powiązane problemy