Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek dla www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

[ATMEGA16][C]Pojedyncze wykrycie przycisku.

11 Wrz 2008 14:38 6005 21
  • Poziom 15  
    Witam, mam problem z pojedynczym wykryciem przycisku. Otoz mam 4 przyciski, 2 steruja wychylenie serwa( i te dzialaja jak maja dzialac) natomiast pozostale 2 steruja numerem serwa(jeden przycisk dodaje 1 do numeru, drugi natomiast odejmuje 1 od numeru serwa). Probowalem: sprawdzenie stanu ->odstep czasowy -> ponowne sprawdzenie stanu, i niestety nie dziala zbyt fajnie - czasem nie skacze do nastepnego serwa, czasem przeskakuje o kilka serw ;/ Bede bardzo wdzieczny za jakies rozwiazanie.
  • Poziom 31  
    Testuj np 3 razy w jednakowym odstepie czasu i jesli wszystkie beda 0 uznajesz ze 0, jesli 1 uznajesz ze jeden, klasyczny "debouncing".
  • Specjalista - Mikrokontrolery
    mozna tez czekac na zwolnienie przycisku, moim zdaniem tez sie to bardzo dobrze sprawdza.

    4\/3!!
  • Poziom 35  
    Nie wykrywaj stanu tylko zbocze tzn:
    Sprawdzaj pętla w koło stan przycisków i porównuj z ostatnim stanem.
    Jeśli się zmienił to wykonaj odpowiednią czynność przypisana do przycisku i zapamiętaj ten stan by go porównać w kolejnej pętli.

    Dodatkowo drgania styków możesz wyeliminować prostym układem RC i przerzutnikiem Schmitta.
  • Poziom 15  
    Freddie Chopin napisał:
    mozna tez czekac na zwolnienie przycisku, moim zdaniem tez sie to bardzo dobrze sprawdza.

    4\/3!!


    Jak to zrealizowac w kodzie?
  • VIP Zasłużony dla elektroda
    Pewnie masz za krótki odstęp czasowy. Albo go zwiększ, albo zmniejsz i wykonaj kilka testów jak radzi fantom.

    Przykładowy kod do debouncingu:
    Code:
    unsigned debounce(unsigned button_state)
    
    {
        static uint8_t prev_states = 0;
        static unsigned state = 0;

        old_states = (old_states << 1) | (!!button_state);
        switch (old_states) {
            case 0x00: state = 0; break;
            case 0xff: state = 1; break;
        }
        return state;
    }


    Pozdrawiam,
    Dr.Vee
  • Pomocny post
    Specjalista - Mikrokontrolery
    mog123 napisał:
    Jak to zrealizowac w kodzie?

    dokladnie tak jak napisal mietekn - porownujesz stan portu ze stanem z poprzedniego porownania - jesli sie zmienil (XOR) - to cos sie stalo - przycisk zostal puszczony/nacisniety. inne operacje logiczne pozwola wykryc tylko wcisniecie, albo tylko puszczenie przycisku.

    przykladowy kod, tym razem na ARMa, pracujacy nie w przerwaniu, tylko po prostu wywolywany przed switchem, umieszczonym w petli. daje on 1 w zmiennej wynik na pozycji danego przycisku, dopiero jak stan zmieni sie z 1 na 0.

    Code:

    unsigned long debounce(void)
    {
       static unsigned long last = 0;
       unsigned long port;
    unsigned long wynik;
       
       port = PORT&MASKA;
       wynik=(~poirt)&last;
       last=port;
       
       return wynik;
    }


    4\/3!!
  • Poziom 31  
    Co ciekawe kazdy pisze o tym samym ;-)
  • Specjalista - Mikrokontrolery
    tez to zauwazylem, tyle ze tego kodu Dr. Vee to ja nie kumam wcale [; pozatym tam sa dwie zmienne statyczne, fuj <:

    4\/3!!
  • VIP Zasłużony dla elektroda
    Freddie: W Twoim jest jedna zmienna statyczna, za to większych rozmiarów - na jedno wychodzi ;)

    Jak nie zmienne statyczne, to kod wywołujący procedurę musi przekazywać jej stan w parametrach. Alternatywą są zmienne globalne - czyli też statyczne, ale za to każda część kodu może je zmieniać...

    Kod jest prosty - rejestr przesuwny do zapamiętywania 8 poprzednich stanów przycisku. Podwójna negacja logiczna (!!) to sposób na zamianę niezerowej na wartość 1. Innymi słowy:
    Code:
    !!zmienna == (zmienna ? 1 : 0);

    Pozdrawiam,
    Dr.Vee
  • Poziom 20  
    Dr. Vee nie żebym się czepiał ale w case brakuje default a dodatkowo unsigned oznacza domyślnie int (na AVR ma 16 bit ale nie jest stały na różnych architekturach) i nie powinno się stosować tego typu dla takich rozwiązań tym bardziej 16 bitów które trzeba zwrócić z funkcji przez kopiowanie i pamietać bo jest statyczne. A wystarczy do tego char
  • Poziom 15  
    Nie wiem czy mam jakas blokade czy co, ale siedze nad waszym kodem i naprawde go nie czaje o0. Ani tego napisanego przez freddiego ani tego napisanego przez mietka. Wiem ocb w algorytmie, ale kodu nie potrafie skminic. Nie moge w nieskonczonej petli sprawdzac stanu przyciskow bo inaczej serwa zaczna wariowac, bo wlasnie ich obsluge mam w nieskonczonej petli. Freddie, jak uzyc twojego kodu? pod PORT&MASKA daje np PIND & 0xfe? A wtedy z wynik pobieram 1?
  • VIP Zasłużony dla elektroda
    bobbyAIR napisał:
    Dr. Vee nie żebym się czepiał ale w case brakuje default a dodatkowo unsigned oznacza domyślnie int (na AVR ma 16 bit ale nie jest stały na różnych architekturach) i nie powinno się stosować tego typu dla takich rozwiązań tym bardziej 16 bitów które trzeba zwrócić z funkcji przez kopiowanie i pamietać bo jest statyczne. A wystarczy do tego char


    Default nie jest konieczne, bo w pozostałych przypadkach nie przypisuje się nowej wartości do zmiennej state.

    Tak naprawdę zamiast unsigned powinno być bool - ale wtedy to nie będzie ANSI C, tylko C99. Z resztą się zgadzam :)

    Pozdrawiam,
    Dr.Vee
  • Specjalista - Mikrokontrolery
    moja zmienna jes 32bitowa, bo i kod jest dla 32bitowej architektury (ARM7)... co wiecej moj kod obsluzy na raz 32 przyciski, twoj jedynie 1 <: pod wzgledem zuzycia pamieci wiec wygrywam, pod wzgledem tlumienia drgan juz jest gorzej, w koncu u ciebie jest 8 porownan, u mnie tylko 2.

    jak dziala moj kod opisalem dosyc dokladnie ... (szukam) ... https://www.elektroda.pl/rtvforum/viewtopic.php?p=5513807&highlight=#5513807

    jak tego uzywac? albo w przerwaniu i wtedy algorytm ustawia jakas zmienna globalna, albo mozna po prostu tak:
    Code:

    swtch(debounce())
    {
    case BUTTON1:
    ...
    case BUTTON1:
    ...
    }


    jednak wtedy sugeruje dodac na koncu funkcji debounce (albo gdzie indziej) jakies opoznienie rzedu kilkunastu ms.

    4\/3!!
  • VIP Zasłużony dla elektroda
    Freddie Chopin napisał:
    moja zmienna jes 32bitowa, bo i kod jest dla 32bitowej architektury (ARM7)... co wiecej moj kod obsluzy na raz 32 przyciski, twoj jedynie 1 <: pod wzgledem zuzycia pamieci wiec wygrywam, pod wzgledem tlumienia drgan juz jest gorzej, w koncu u ciebie jest 8 porownan, u mnie tylko 2.

    Jak zwykle coś za coś - ale niech Ci będzie, masz większy procesor, dam Ci wygrać ;)

    Freddie Chopin napisał:

    jak tego uzywac? albo w przerwaniu i wtedy algorytm ustawia jakas zmienna globalna, albo mozna po prostu tak:
    Code:

    swtch(debounce())
    {
    case BUTTON1:
    ...
    case BUTTON2:
    ...
    }


    Zaraz, zaraz, coś mi to przypomina... "a mówiłem, żeby dwóch guzików na raz nie przyciskać?" ;)

    A na serio to powyższy kod nie zadziała, jeśli user tak właśnie zrobi.

    Pozdrawiam,
    Dr.Vee
  • Specjalista - Mikrokontrolery
    oj to tylko przyklad [;

    1. malo prawdopodobne, zeby userowi udalo sie puscic obydwa w idealnie rownym czasie...

    2. jak ktos ma odpowiednia technike puszczania, to:

    Code:

    u32_t var=debounce();
    if(var&BUTTON1)
    ...
    if(var&BUTTON2)
    ...


    i po problemie [;

    4\/3!!
  • Poziom 15  
    Freddie, uzylem twojego kodu, i dziala znacznie lepiej, ale nie doskonale, czasem nie zalapie, ale ogolem przelacza pojedynczo.

    Tak to u mnie wyglada:

    Code:

    unsigned debounce(void)
    {
       static unsigned last = 0;
       unsigned port;
       unsigned wynik;
       
       port = PIND;
       wynik=(~port)&last;
       last=port;
       
       return wynik;
    }



    if(debounce()==0xfe)
    {
       servonumber=servonumber-1;
    }
    if(debounce()==0xfd)
    {
       servonumber=servonumber+1;
    }


    EDIT: Nie dziala ;/ za pierwszym razem jak kompilowalem to dzialalo, teraz juz nic, choc w kodzie nic nie zmienialem
  • Specjalista - Mikrokontrolery
    po pierwsze typy zmiennych proponuje jednak zdefiniowac... unsigned char zapewne bedzie ok dla atmegi

    po drugie, widze, ze u ciebie przycisk wcisniety to 0, puszczony to 1 (tak jest?) - w takim wypadku zmien funkcje na:

    wynik=port&(~last);

    aby wykrywala puszczenie, a nie nacisniecie przycisku.

    jesli na tym porcie masz cos jeszcze, co jest jakos sterowane, to w istocie kod nie bedzie ci dzialal, bo np zmienia sie stan jednego portu i w wyniku nie masz 0xFE tylko np 0x7E. w takim wypadku maskowanie jest konieczne!

    pozatym funkcji nie powinienes wywolywac tak czesto jak to robisz - to wlasnie bylo przyczyna tego, ze czasem zlapalo za duzo lub za malo.

    zrob albo tak:

    switch(debounce())
    {
    ....
    }

    albo tak:

    unsigned char var;
    var=debounce;

    if (var == ...)
    ...
    if (var == ...)
    ...

    no i oczywiscie cala funkcja musi byc w petli wraz z selektorem jakichs dzialan, bo w innym wypadku nie ma ona najmniejszego sensu.

    4\/3!!
  • VIP Zasłużony dla elektroda
    mog123 napisał:
    EDIT: Nie dziala ;/ za pierwszym razem jak kompilowalem to dzialalo, teraz juz nic, choc w kodzie nic nie zmienialem

    To już jakieś czary-mary ;)

    Może sformatuj ładnie ten kod i wklej tutaj (albo w załączniku, jeśli ma > 100 linijek).

    Pozdrawiam,
    Dr.Vee
  • Poziom 15  
    nadal nie dziala, ale serwa teraz zamiast robic kroczki, normalnie plynnie chodza. Co nie zmienia faktu ze nadal nie moge ich przelaczyc ta metoda.

    Ale opracowalem chyba jak juz to zrobic. Mam zmienna timer ktora zlicza ile razy TCNT1 odliczyl 20ms. I w petli resetujacej timer dalem funkcje odpowiedzialna za odczyt z przyciskow, dziala jak nalezy:

    Code:


          if(timer==12)
          {
          if(PIND==0xfe)
          {
             servonumber=servonumber-1;
          }
          if(PIND==0xfd)
          {
             servonumber=servonumber+1;
          }
  • Pomocny post
    VIP Zasłużony dla elektroda
    Już prawie masz to, co trzeba. Jeśli zmienna timer zwiększana jest co 20 ms, to zrób:
    Code:

    if (timer >= 2) { /* albo więcej, ale 40ms to i tak dużo */
        uint8_t zmiany = debounce(); /* wersja freddiego */
        timer = 0;
        if (zmiany & _BV(PIN_MINUS))
            servonumber -= 1;
        if (zmiany & _BV(PIN_PLUS))
            servonumber += 1;
        if (servonumber >= LICZBA_SERW) /* zakładam, że servonumber jest unsigned */
            servonumber = 0;
    }

    Kod umieść w głównej pętli programu, a zmienna timer musi być volatile.

    Pozdrawiam,
    Dr.Vee
  • Poziom 15  
    Dziala :) Wszystko ladnie pieknie, ale serwa drza jakby im bylo zimno :P Dopoki nie bede mial jakiegos xtala to zostawie moj kod, wielkie dzieki :)

    EDIT: A tak wlasnie to wyglada:

    http://tiny.pl/85fz