Mikrokontrolery - Jak zacząć?

... czyli zbiór praktycznej wiedzy dot. mikrokontrolerów.

piątek, 11 lutego 2011

O drganiach styków bez bajek (przykład dla STM32)

Autor: BlueDraco
Redakcja: Dondu


Jednym z powszechnych problemów, z jakimi styka się projektant urządzeń cyfrowych, są drgania styków zawartych w przyciskach, klawiszach, impulsatorach, przełącznikach i przekaźnikach. Samo zjawisko jest bardzo proste, jednak narosło wokół niego wiele mitów, z których najdziwniejszy głosi, że zjawisko to w ogóle nie występuje lub występuje bardzo rzadko.

Niestety, drgania styków występują w praktycznie każdym łączniku mechanicznym. Przy każdym zwieraniu i rozwieraniu styku mechanicznego następują sprężyste odbicia, które skutkują tam, że podczas jednej logicznej zmiany stanu styku w rzeczywistości następuje takich zmian kilka.

Jeżeli styk jest rozwarty i następuje jego zwarcie, w rzeczywistości w bardzo krótkim czasie nastąpi seria zwarć i rozwarć, po której nastąpi trwałe zwarcie. Jeżeli przez taki przycisk zasilimy np. żarówkę lub diodę świecącą, to nie zauważymy żadnych niespodziewanych efektów, gdyż czas drgań styku jest znacznie krótszy zarówno od czasu zaświecania włókna żarówki jak i od czasu bezwładności ludzkiego oka, które nie jest w stanie zauważyć tak krótkich rozbłysków pozbawionej praktycznie bezwładności diody świecącej.


Drgania styków ujawniają się wtedy, gdy stan styku oddziałuje na układ elektroniczny, którego czas reakcji wyraża się w nanosekundach lub mikrosekundach, a nie w dziesiątkach milisekund, jak ma to miejsce w przypadku zmysłu wzroku.

Pojedyncze naciśnięcie przycisku jest w takim przypadku rejestrowane jako seria kolejnych naciśnięć i zwolnień.

Jeżeli więc urządzenie zlicza pochodzące od przycisku impulsy – pojedyncze naciśnięcie zostanie odebrane jako seria impulsów.

Najprostszym przykładem uwidaczniającym ten efekt jest wyłącznik światła reagujący na naciśnięcia pojedynczego przycisku chwilowego. Jedno naciśnięcie ma powodować zaświecenie światła, następne – jego zgaszenie.

Jeżeli nie uwzględnimy zjawiska drgań zestyków – działanie przycisku będzie dość przypadkowe, gdyż podczas naciskania i zwalniania przycisk będzie generował kilka impulsów, a wpływ naciskania przycisku na końcowy efekt w postaci zaświecenia lub zgaszenia światła będzie zależał od tego, czy liczba odbić styku podczas łączenia i rozłączania była nieparzysta, czy parzysta.


Bajki zaczynają się na "be", i takie są ...


Spróbujmy przyjrzeć się praktycznie temu problemowi, korzystając z niedrogiej i wygodnej w użyciu płytki demonstracyjnej STM32F0DISCOVERY, wyposażonej w mikrokontroler serii STM32F051, dwie diody świecące i jeden przycisk podłączony do portu wejściowego mikrokontrolera.

Skorzystamy z dostępnego na jednej z polskich witryn internetowych poświęconych mikrokontrolerom programu, który ma demonstrować brak lub nieistotność efektu drgań styków. Program, stworzony oryginalnie dla mikrokontrolerów rodziny AVR, zaadaptujemy dla STM32F0. Zamiast zaświecać i gasić jedną diodę, program będzie sterował obiema diodami na płytce, naprzemiennie zaświecając je i gasząc.

Poniżej zamieszczam treść programu, zawartego w projekcie o nazwie Drgania1. Oba projekty demonstracyjne zostały przygotowane w darmowej wersji środowiska Keil i są zawarte w dostępnym poniżej pliku drgania.zip.


/*
 STM32F0DISCOVERY
 Drgania stykow - wersja 1 - bledna
 BlueDraco 02'2013
*/

#include "stm32f0xx.h"
//========================================================================
// STM32F05x
#define GPIO_MODER_OUT 1
//========================================================================
// STM32F0DISCOVERY
#define LED_PORT  GPIOC
#define BLUE_LED_BIT 8
#define GREEN_LED_BIT 9

#define BUTTON_PORT  GPIOA
#define BUTTON_BIT  0
//========================================================================
void SystemInit(void)
{
}
//========================================================================
int main(void)
{
    uint8_t key_lock = 0;

    // konfiguracja portow
    RCC->AHBENR = RCC_AHBENR_GPIOCEN |  RCC_AHBENR_GPIOAEN; // GPIOC, GPIOA
    LED_PORT->MODER = GPIO_MODER_OUT << (GREEN_LED_BIT << 1)
                      | GPIO_MODER_OUT << (BLUE_LED_BIT << 1); // LED
    LED_PORT->ODR = 1 << GREEN_LED_BIT; // green LED on

    while (1)
    {
        if ( !key_lock && (BUTTON_PORT->IDR >> BUTTON_BIT & 1))
        {
            key_lock=1;
            // reakcja na wcisniecie przycisku
            LED_PORT->ODR ^= 1 << GREEN_LED_BIT | 1 << BLUE_LED_BIT;
        }
        else if( key_lock && !(BUTTON_PORT->IDR >> BUTTON_BIT & 1))
            key_lock = 0;
    }
}


Do pobrania oba programy: drgania.zip


Po skompilowaniu programu i zaprogramowaniu mikrokontrolera możemy rozpocząć próby.

Program według intencji jego Autora miał zmieniać stan diody na przeciwny po każdym naciśnięciu przycisku. Możemy jednak zaobserwować, że niekiedy program podczas naciskania przycisku nie zmienia stanu diod; zdarza się również, że stan diod zmienia się podczas zwalniania przycisku.

Dlaczego?

To proste – zarówno podczas naciskania jak i zwalniania przycisku następuje kilka zwarć i rozwarć, które są wykrywane przez program. Program nie jest w stanie rozróżnić, czy połączenie jest wynikiem naciśnięcia przycisku, czy odbicia styku.

Okres drgań jest wielokrotnie większy od czasu obiegu pętli, stąd bierze się losowe zachowanie programu, całkowicie zgodne z tym, czego można się spodziewać po błędnie napisanym kodzie – zarówno podczas naciskania jak i zwalniania przycisku program kilkukrotnie wykonuje obie instrukcje warunkowe, po jednym razie dla każdej zarejestrowanej zmiany stanu przycisku.

Jak więc pozbyć się tego efektu?

Każde poprawne rozwiązanie problemu ignorowania drgań musi być oparte o odmierzanie czasu (opóźnienia).


Z kolei poprawne odliczanie czasu wymaga w praktyce zawsze użycia sprzętowego timera, gdyż działanie opóźnień programowych zależy od tak wielu czynników, że nie można polegać na odliczonym w ten sposób czasie.

Wymieńmy tylko niektóre z tych czynników:
  • częstotliwość procesora, 
  • czas oczekiwania na dostęp do pamięci, 
  • atrybuty użyte w deklaracjach danych, 
  • wersja kompilatora, 
  • wybrany stopień optymalizacji, 
  • liczba zmiennych używanych w danym fragmencie programu, 
  • bieżący stan warunków dla instrukcji warunkowych użytych w programie.


Delay() ... brr!

Jedynym sposobem na wygenerowanie deterministycznego opóźnienia przez samo oprogramowanie jest użycie procedury opóźniającej (takiej jak np. delay) o znanym rozwinięciu asemblerowym, wykonywanej przy zablokowanych przerwaniach mikrokontrolera.

W praktyce trudno jest wskazać zastosowanie mikrokontrolera, które nie wymagałoby użycia przerwań, więc rozwiązanie z opóźnieniem programowym może być używane wyłącznie w krótkich programach o charakterze dydaktycznym i demonstracyjnym, a nie w realnych zastosowaniach.


Pętle zdarzeń

Rzadko również mamy do czynienia z sytuacją, w której możemy sobie pozwolić na tracenie czasu w procedurze opóźnienia – nawet w programach demonstracyjnych bez przerwań, których budowa opiera się na tzw. pętli zdarzeń.

Na ogół nie możemy zatrzymać pętli zdarzeń, gdyż mogłoby nastąpić „zgubienie” zdarzenia.

Nie możemy również odliczać czasu obiegami pętli zdarzeń, gdyż z definicji musi ona zawierać instrukcje warunkowe, a więc czas jej obiegu zależy od wartości logicznych warunków tych instrukcji.

Jeżeli spróbujemy odmierzać czas obiegami pętli zdarzeń, to dla skutecznego ignorowania drgań musimy przyjąć do obliczenia niezbędnej liczby obiegów najkrótszy możliwy czas obiegu pętli. Problem w tym, że w realnym programie, który robi coś użytecznego, pętla zdarzeń zawiera wiele złożonych bloków warunkowych w postaci instrukcji if, zapewniających reakcje na zdarzenia.

Czas wykonania tych bloków jest wielokrotnie dłuższy od czasu obiegu pętli głównej w przypadku niewystąpienia żadnego ze zdarzeń. Może się więc zdarzyć, że „pusty” obieg pętli głównej będzie trwał np. jedną setną czasu potrzebnego na obieg z obsługą zdarzeń. Jeśli okres ignorowania drgań zostanie poprawnie wyznaczony dla obiegu pustego, to przy obiegach niepustych czas opóźnienia reakcji na naciśnięcie przycisku może sięgnąć nawet kilku sekund.

Typowe przykłady takich czasochłonnych akcji programu, to:
  • odczyt temperatury z czujników, 
  • współpraca z wyświetlaczem LCD
  • transmisja szeregowa ciągu znaków z aktywnym oczekiwaniem,
a więc coś, co występuje w większości programów z pętlą zdarzeń.

Opóźnienie uzyskane poprzez zliczanie obiegów pętli zależy od tylu czynników, że trudno jest podać prawdziwą jego wartość, a każda modyfikacja programu powoduje zmianę czasu opóźnienia.

Proces określania liczby obiegów pętli gwarantującej poprawną reakcję na stan przycisku musiałby być prowadzony metodą prób i błędów, co zdecydowanie nie jest właściwym podejściem inżynierskim.


Sprzętowe opóźnienie, to jest to!

Jak widać, w prawdziwym projekcie oprogramowania, które miałoby robić cokolwiek poza zapalaniem i gaszeniem światła zachowując przy tym determinizm czasowy, nie da się wyeliminować drgań przycisków bez sprzętowego odmierzania czasu.

Tymczasem sprzętowe odmierzanie czasu, którego unika się w wielu poradnikach dla konstruktorów, nie kosztuje nic, gdyż praktycznie w każdym projekcie systemu mikroprocesorowego używamy (a przynajmniej powinniśmy używać) timera, zaprogramowanego na stałą częstotliwość, najczęściej mieszczącą się w zakresie od kilkudziesięciu Hz do kilku kHz. Mamy więc do dyspozycji wiarygodną i deterministyczną bazę czasu - musimy tylko jej użyć.

Do programowego ignorowania drgań styków potrzebne jest więc przerwanie timera. Podstawowy odcinek czasu używany przy eliminacji drgań zależy od okresu drgań styku. Dla większości współczesnych niewielkich elementów stykowych okres ten nie przekracza 20..30 ms.

Najprostsza technika ignorowania drgań polega na testowaniu stanu styku z okresem przekraczającym czas drgań. Uogólniając, oprogramowanie powinno reagować na dwa lub więcej kolejno próbkowanych stanów zestyku, przy czym długość przedziału czasowego analizowanych stanów musi być większa od maksymalnego czasu drgań.

Przyjmując maksymalny czas drgań nie przekraczający 30 ms, powinniśmy monitorować stan styku przez min. 30 ms, próbkując w tym czasie stan styku nie mniej niż dwukrotnie.

Przykładowe rozwiązanie (jedno z wielu możliwych), to analiza czterech stanów w odstępach 10 ms, czyli próbkowanych z częstotliwością 100 Hz. Odstęp czasowy pomiędzy pierwszą i ostatnią z czterech próbek wynosi w tym przypadku 30 ms.

Jeżeli przerwanie timera zgłaszane jest z częstotliwością 100 Hz – notujemy stan zestyku w każdym przerwaniu. Jeśli częstotliwość przerwań jest większa – w procedurze obsługi przerwania odliczamy przerwania, wywołując fragment kodu sprawdzający stan zestyku co pewną liczbę przerwań.

Program demonstrujący tę technikę przedstawiamy poniżej. Należy zwrócić uwagę, że tak napisany program nie jest w stanie ignorować zakłóceń, powodujących błędny odczyt stanu zestyku, może on więc być stosowany tylko wtedy, gdy sam odczyt stanu przez mikrokontroler nie jest zakłócany przez czynniki zewnętrzne. Technika ignorowania zakłóceń wpływających na błędny odczyt stanu jest nieco (ale tylko nieco) bardziej złożona.

Jak działa nasz program?

Timer systemowy SysTick został zaprogramowany na częstotliwość 100 Hz. W każdym przerwaniu timera notowany jest stan przycisku, a zmienna bstate przechowuje cztery ostatnie stany. Program reaguje na naciśnięcie przycisku, gdy po trzykrotnym stwierdzeniu zwolnienia przycisku nastąpi wykrycie jego naciśnięcia.

Dzięki temu program podczas naciskania przycisku zareaguje tylko na pierwsze zetknięcie styków, ignorując ich ewentualne odbicia, o ile skończą się one w czasie trzech kolejnych okresów próbkowania (nasze opóźnienie).

Program nie zareaguje również na odbicia podczas zwalniania przycisku, bo ewentualne zwarcie styków po ich rozwarciu podczas zwalniania przycisku nie będzie poprzedzone trzema okresami rozwarcia.


Przykład 2 (w kompilatorze)
/*
 STM32F0DISCOVERY
 Eliminacja drgan stykow - wersja 1
 BlueDraco 02'2013
*/

#include "stm32f0xx.h"
//========================================================================
#define GPIO_MODER_OUT 1
//========================================================================
// STM32F0DISCOVERY
#define LED_PORT  GPIOC
#define BLUE_LED_BIT 8
#define GREEN_LED_BIT 9

#define BUTTON_PORT  GPIOA
#define BUTTON_BIT  0
//========================================================================
// Parametry czasowe
#define SYSCLK_FREQ  HSI_VALUE
#define SYSTICK_FREQ 100 // 100 Hz -> 10 ms
//========================================================================
void SystemInit(void)
{
}
//========================================================================
int main(void)
{
    // konfiguracja portow
    RCC->AHBENR = RCC_AHBENR_GPIOCEN |  RCC_AHBENR_GPIOAEN; // GPIOC, GPIOA
    LED_PORT->MODER = GPIO_MODER_OUT << (GREEN_LED_BIT << 1)
                      | GPIO_MODER_OUT << (BLUE_LED_BIT << 1); // LED
    LED_PORT->ODR = 1 << GREEN_LED_BIT; // green LED on

    // kofiguracja timera SysTick
    SysTick->LOAD = SYSCLK_FREQ / SYSTICK_FREQ - 1;
    SysTick->VAL = 0;
    SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk
                    | SysTick_CTRL_ENABLE_Msk;
    SCB->SCR = SCB_SCR_SLEEPONEXIT_Msk;
    __WFI();
}
//========================================================================
void SysTick_Handler(void)
{
    static uint8_t bstate = 0; //ostatnie stany przycisku

    if ((bstate = (bstate << 1 & 0xf) | 
        (BUTTON_PORT->IDR >> BUTTON_BIT & 1)) == 1)
        
         // byl zwolniony, teraz jest wcisniety - zmiana stanu LED
        LED_PORT->ODR ^= 1 << GREEN_LED_BIT | 1 << BLUE_LED_BIT;
}


Jak widać, sam kod testujący przycisk jest krótszy, niż w poprzednim, błędnym przykładzie. Oczywiście musimy zaprogramować timer i oprogramować jego przerwanie, ale i tak robimy to w każdym projekcie.

Stawiając pytanie o koszt takiej techniki obsługi warto wziąć pod uwagę nie liczbę linii kodu programu, a liczbę instrukcji związanych z obsługą przycisku wykonywanych w jednostce czasu, np. w ciągu sekundy.

Jak widać rozwiązanie z przerwaniem timera  wykonuje kilka linii kodu 100 razy na sekundę; przez pozostały czas procesor może być uśpiony lub wykonywać inne zadania. Błędne rozwiązanie z testowaniem stanu przycisku w pętli zużywa cały czas procesora na testowanie stanu przycisku, drenując przy tym energię ze źródła zasilania, co często jest bardzo istotnym elementem projektu w dobie miniaturyzacji.

Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

22 komentarze:

  1. W pierwszym przykładzie w 43 linijce zamiast:
    key_lock = 0;
    wstaw
    key_lock++;

    Dzięki temu zabiegowi będą wyeliminowane drgania styków i wszystko będzie CI chodziło :)

    OdpowiedzUsuń
  2. Będzie, pod warunkiem, że program nie będzie robił nic więcej. Niestety, jednostką czasu drgań styków nie są obiegi pęti, a milisekundy. Dość szczegółowo starałem się to opisać - jeśli pętlę zdarzeń przejdziemy "na pusto" - będzie dobrze, jeśli w pętli wejdziemy w obsługę zdarzeń - może się zdarzyć, że przez dłuższy czas (np. kilka sekund) program nie zareaguje na wciśnięcie przycisku.
    Tu trzeba zawsze odczekać określony czas, a to gwarantuje timer,a nie liczba obiegów pętli.

    Nawet bez tego efektu trick z zawijaniem zmiennej na wartości 256 nie jest raczej przyzwoitą metodą programowania (jak każde rozwiązanie abzujące na długości typu danej, która w języku C jest cechą implementacji).

    OdpowiedzUsuń
  3. Wejdziemy w zdarzenie które zajmuje kilka sekund? Jakie np zdarzenie masz na myśli?

    Ale można stworzyć system pseudowielowątkowy w którym zdarzenia wykonują się "po trochu", tak aby nigdzie nie użyć funkcji delay. Program nigdzie nie może się zatrzymać. Powprowadzać timery programowe itp.

    OdpowiedzUsuń
  4. Myślę, że wszystko wyjaśniłem w sekcji "pętle zdarzeń". Jeśli jeden obieg pętli zdarzeń zajmuje np. od 100 us (pusty) do 10 ms (z obsługą jednego ze zdarzeń), to 300 obiegów zajumje od 30 ms (w sam raz na ignorowanie drgań) do 3 s (czyli przez 3 sekundy użytkownik zastanawia się, co się zepsuło).
    Oczywiście jest wiele innych skomplikowanych metod ignorowania drgań. Jak zauważyłeś można stworzyć system wielowątkowy, powprowadzać timery programowe albo użyć Linuxa na Raspberry Pi - każde z takich rozwiązań będzie merytorycznie poprawne.
    Rozwiązanie, które pzedstawiłem, robi to też poprawnie, bez wielowątkowości i timerów programowych, w trzech liniach kodu i daje się zastosować w każdym, nawet najmniejszym mikrokontrolerze dla dowolnej iczby niezależnych przycisków. Dopisując kolejne 3 linie możemy zrobić autorepetycję klawisza.
    Po co robić coś pisząc skomplikowany i działający niedeteministycznie kod, skoro ten sam efekt można uzyskać w kilku liniach bez komplikacji i z zachowaniem determinizmu czasowego?

    OdpowiedzUsuń
  5. widzę tu konkurencję między dwoma popularnymi blogami... ale to bardzo dobrze :)

    OdpowiedzUsuń
  6. Zajrzyj proszę jeszcze raz pod "Pętle zdarzeń". Tu nie chodzi o kilkusekundową obsługę jednego zdarzenia, a o wahania czasów obiegu pętli. Jeśli najkrótszy obieg pętli trwa np. 100 us, to w celu ignorowania drgań trwających 30 ms musimy odliczyć 300 obiegów. Jeśli wskutek obsługi zdarzeń czas obiegu wzrośnie do 10 ms (całkiem realistyczna wartość), to odczekiwanie 300 obiegów zajmie 3 sekundy i przez ten czas urządzenie nie zareaguje na przycisk.
    Można oczywiście zastosować kilka wyrafinowanych technik zmniejszających niedeterminizm czasowy źle zaprojektowanego programu. Ja zaproponowałem powyżej inne rozwiązanie - krótki, deterministyczny czasowo kod poprawnie reagujący na przycisk.
    Oczywiście jestem otwarty na wszelkie sugestie rozwiązań prostszych i bardziej poprawnych ze strony p.t. Czytelników. Chętnie je tutaj zamieszczę.

    OdpowiedzUsuń
  7. No i jak zwykle się znajdą krytykanci, mistrzowie pętli głównej. ;) Bardzo mi się podobało i ładny kod pod stm32f0 do tego. Czekam na więcej.

    @Piotr Zasada: Ale o co Ci właściwie chodzi? Przecież timer jest tu właśnie wykorzystywany. Timer "programowy"? Że niby while(i<pierdyliard) i++;? ;)

    OdpowiedzUsuń
  8. @mrsajm0n: Proszę o spokojny ton i odpowiednie słownictwo, by nie drżał mi palec nad przyciskiem OPUBLIKUJ.
    :-)

    OdpowiedzUsuń
  9. @mrsajm0n: ja nie krytykuje rozwiązania kolegi BlueDraco. Muszę przyznać, że bardzo fajne i ciekawe rozwiązanie! Bardzo fajne rozwiązanie przedstawił. Jak będę miał chwilkę na pewno je przetestuję :)
    @mrsajm0n: No i jak zwykle się znajdą krytykanci, mistrzowie pętli głównej. ;)
    Nie wiem dlaczego mnie tak nazywasz?

    Zgadzam się z kolegą BlueDraco, że obiegi pętli głównej mają różny czas. Ale wydaje mi się, że rozstrzał jaki podał (30ms - 10ms) jest dosyć duży! Wówczas przy wejściu w to najdłuższe zdarzenie można by zerować od razu zmienną key_lock - ale zgodzę się, że było by to nie eleganckie.

    Praktycznie wszystkie swoje programy piszę, używając bardzo podobnego sposobu jaki został podany w pierwszym listingu i nigdy nie spotkałem się z problemem, że musiałem trzymać przycisk 3 sekundy aby zadziałał. Doskonale rozumiem argumentację kolegi BlueDraco, że jeżeli rozstrzał pomiędzy najkrótszym obiegiem a najdłuższym obiegiem pętli głównej jest ogromny, to ta metoda by się nie sprawdziła lub musiała by być delikatnie korygowana:)

    Zdaję sobie sprawę, czas nie jest odmierzany za pomocą Timera sprzętowego powiązanego z przerwaniem w którym stan wejścia, jest próbkowaniem co określony czas (stały czas próbkowania - na pewno jest to niewątpliwa zaleta tego rozwiązania!). Moim zdaniem wadą tego rozwiązania jest "strata" Timera sprzętowego! Jeżeli będzie kilka przycisków to czas wykonywania przerwania się również wydłuża (mała wada, bo za dużo tych linijek nie dojdzie). Czasami Timer może się przydać do innch rzeczy, wówczas ... ? Można zastosować metodę numer 1 ;)

    Moje wnioski (Główne wady):
    Metoda 1:
    Brak dokładnego odmierzania czasu, potrzebnego na eliminację drgań styków.
    Metoda 2:
    Wykorzystanie Timera.
    ps.
    @mrsajm0n: Timer "programowy"? Że niby while(i<pierdyliard) i++;? ;)

    "Timer programowy" jest to timer, sterowany za pomocą Timera sprzętowego. Nie jest on tak dokładny jak sprzętowy ale do wielu zadań spokojnie wystarczy. Wówczas możemy mieć "dużo" timerów.

    OdpowiedzUsuń
  10. W miarę możliwości staram się w projektach używać jednego timera. To nie jest żadna "strata" timera. Jak napisałem powyżej, trudno sobie wyobrazić realny projekt bez zgłaszanego za stałą częstotliwością przerwania timera, które służy do wielu różnych celów. Nie pamiętam, był kiedykolwiek napisał oprogramowanie mikrokontrolera bez takiego przerwania timera, a trochę projektów w życiu zrobiłem. Obsługa przycisków - to jest tylko kwestia dopisania do istniejącej procedury timera po 2..3 linijek dla każdego przycisku. Myślę, że taniej, prościej i wydajniej trudno byłoby to zrobić - patrz ostatni czerwony akapit artykułu.
    Wbrew popularnym zabobonom procedura obsługi przerwania nie ma być "jak najkrótsza", tylko nie ma się wykonywać ZBYT długo. Nieważne jak długie będzie jej wykonanie - wystarczy, że zagwarantujemy, że żadne zdarzenie nie zostanie zgubione i że reakcja na każde nastąpi w wymaganym czasie. Na ogół kilkudziesięciowierszowa procedura obsługi przerwania timera w tym nie przeszkadza, nawet w przypadku prostego mikrokontrolera z jednopoziomowym systemem przerważ, jak AVR z serii Tiny czy Mega.
    Ważnym aspektem tego podejścia jest też eliminacja pętli zdarzeń, z której stosowania zwykle wynikają głównie kłopoty.

    OdpowiedzUsuń
  11. Ja od siebie dodam tylko, że nie rozumiem pojęcia "strata timera". To znaczy, że zasoby sprzętowe są od tego, żeby były, najlepiej nieużywane? Nie, jeśli leżą odłogiem to znaczy, że źle dobraliśmy procesor (niepotrzebnie przerośnięty). Z kolei jeśli nam tego timera brakuje to tym bardziej znaczy, że źle dobraliśmy procesor i swój błąd chcemy zamaskować robiąc karykaturę programowania.
    Świat nie składa się tylko z ATMega8, nawet wśród 8-bitowych AVRów jest kilkaset modeli, z pewnością któryś ma wystarczającą liczbę timerów :)
    Przyczepiłem się tych timerów, ale to dotyczy wszystkiego. Np. robienie 1-wire przez pooling i delaye, co blokuje procka na setki ms, zamiast użycia np. USART, bo przecież szkoda USARTa, a to, że są małe procki mające ich kilka jakoś umyka.
    Także propagujmy prawidłowy styl programowania, a nie toczmy sporów czy da się coś sklecić byle jak, byle jakoś tam działało.

    OdpowiedzUsuń
  12. @tmf: Ja od siebie dodam tylko, że nie rozumiem pojęcia "strata timera". To znaczy, że zasoby sprzętowe są od tego, żeby były, najlepiej nieużywane? Nie, jeśli leżą odłogiem to znaczy, że źle dobraliśmy procesor (niepotrzebnie przerośnięty).
    Źle mnie zrozumiałeś! Uważam, że zasoby sprzętowe powinny być rozsądnie używane.

    @tmf: Także propagujmy prawidłowy styl programowania, a nie toczmy sporów czy da się coś sklecić byle jak, byle jakoś tam działało.
    Jeżeli chodzi o programowanie, to uważam, że programista powinien mieć elastyczny umysł. Nie powinno mu się narzucać sztywnych rozwiązań, typu "Jeżeli używasz przycisków, to zastosuj Timer oraz przerwanie", "Nigdy nie korzystaj z odmierzania czasu w pętli głównej programu" itd
    To są wytyczne producentów mikroprocesorów, twórców języka C, Twoje?

    Dla mnie jedno i drugie rozwiązanie jest ciekawe :)

    OdpowiedzUsuń
  13. No to ja także zabiorę głos: KOMPROMIS - to dobre słowo :-)

    Nie ma jednej, jedynie słusznej drogi, zarówno pisania programu, jak i wyboru mikrokontrolera. Zawsze mamy jakieś ograniczenia lub nadmiar jakichś zasobów. A sposób wykorzystania zależy od nas i wielu czynników, które w danym momencie na nas naciskają.

    OdpowiedzUsuń
  14. Dochodzimy to do kwestii dogmatycznych jak widzę... Uważam, że do dawania wskazówek i ustalania zasad jest uprawniony każdy, kto ma wiedzę teoretyczną i doświadczenie w jakiejś dziedzinie. Nie trzeba być producentem mikrokontrolera ani twórcą języka, żeby mówić o zasadach poprawnego programowania. Jeżeli jakieś rozwiązanie ma istotne wady funkcjonalne, to nie należy go stosować. Koniec. Można podać warunki stosowalności do każdego rozwiązania, nawet błędnego, wtedy, kiedy od błędnego funkcjonowania urządzenia nic istotnego nie zależy. Myślę, że nie chciałbyś jeździć samochodem, którego hamulce raz na tysiąc hamowań uruchomią się z opóźnieniem 3 s, bo programista uznał, że lepiej nie "tracić" timera, a akurat było co robić w pętli zdarzeń.
    Mogę uzasadnić dlaczego w ogólnym przypadku NIE należy używać w pętli zdarzeń a NIE WOLNO używać w przerwaniach opóźnień programowych typu "delay", chociaż mogę też wskazać okoliczności, w których nie spowoduje to istotnych zakłóceń w pracy urządzenia. Podręczną kolekcję dogmatów przedstawię wkrótce w oddzielnym tekście - myślę, że będzie wesoło.
    Pozdrawiam.

    OdpowiedzUsuń
  15. Bardzo fajne i w alternatywny sposób napisane narzędzie do pozbywania się drgan styków. Co prawda programuję tylko AVR-ki, ale przeanalizowałem sobie kod i chętnie go przetestuję na żywym organizmie :-). Póki co korzystam z kodu również opartego na timerze sprzętowym, w którym mam obsługę timerów programowych. Jest to soft, który powstał troszkę na bazie programów kolegów GwynbleidD oraz Mirekk36. Tutaj widzę totalnie inne podejście... u mnie tylko timery są wstawione w obsługę przerwania, a tu widzę, że reszta obsługi też siedzi w obsłudze przerwania... Tak się zastanawiam (jako wciąż laik) czy nie narobiłbym sobie kłopotu skacząc z wnętrza ISR() do callbacka, bo własnie taki mechanizm wykorzystuję. Pozdrawiam

    Antystatyczny

    OdpowiedzUsuń
  16. Anty: to, czy narobiłeś sobie kłopotu zależy od tego, ile czasu zajmuje wykonanie procedury wywoływanej z przerwania. Jeśli jest tam np. transmisja UART lub wyświetlanie na LCD, to są spore szanse na kłopoty. W takim przypadku na AVR wypadałoby w miejscu, gdzie w moim kodzie jest zaświecanie LED, ustawić znacznik naciśnięcia przycisku, który byłby testowany instrukcją if () w pętli zdarzeń. Bardzo łatwo jest również do tego dorobić autorepetycję - 2 dodatkowe linijki w przerwaniu timera. Mam kilka takich przykładów - czekają na dopracowanie, opisanie i publikację.

    OdpowiedzUsuń
  17. Witam,
    w większości komentarzy, przykładów opisane jest maksymalnie kilka przycisków i ich programowy debouncing, a ja mam pytanie co z resztą wejść, do których podłączone są np: krańcówki, czujniki indukcyjne czy optyczne? W takich nie trzeba eliminować drgań?-jak krańcówka -to zwykły styk? Często tych wejść jest dużo np 15 a niekiedy i więcej, jak radzić sobie z wszystkimi na raz?
    Dalej interesującym tematem jest eliminacja zakłóceń np w długich przewodach sygnałowych często wielożyłowych idących w niewielkim odstępie od załączających silniki rzędu kilkunastu kilowatów czy inne dużej mocy obciążenia. Chciałbym uzyskać więcej informacji dot:
    cytat
    "Program demonstrujący tę technikę przedstawiamy poniżej. Należy zwrócić uwagę, że tak napisany program nie jest w stanie ignorować zakłóceń, powodujących błędny odczyt stanu zestyku, może on więc być stosowany tylko wtedy, gdy sam odczyt stanu przez mikrokontroler nie jest zakłócany przez czynniki zewnętrzne. Technika ignorowania zakłóceń wpływających na błędny odczyt stanu jest nieco (ale tylko nieco) bardziej złożona." -jakby można to jakoś bardziej rozwinąć.

    Zastanawia mnie jak to jest rozwiązane w PLC, gdzie jakby buduje się układy sprzętowo z klocków i nie przejmuje się zakłóceniami, ich eliminacją przez dodatkowe wstawianie gasików czy diod przy cewkach a takich problemów tam nie ma. Czyżby programowo to było tam eliminowane?

    Dodam,że ja wejścia przeważnie robię tak: we->rezystor>optotransoptor>noga AVR, nie daję żadnych kondensatorów, czy może tu za mało czegoś? Oczywiście jakby ktoś opisywał przykłady to proszę z obszernymi komentarzami.
    Pozdrawiam
    PP

    OdpowiedzUsuń
  18. Da ktoś rade poradzić coś w powyższym?
    PP

    OdpowiedzUsuń
  19. Autor tu zagląda?

    OdpowiedzUsuń
  20. Ja osobisie najbardziej wole obslugiwac klawisz na przerwaniu timera. przezroczyste dla glownego kodu. Łatwe do pisania i interpretacji.

    OdpowiedzUsuń
  21. Skuteczną metodą jest implementacja filtru dolnoprzepustowego. Łatwo można regulować jego skuteczność a co z tym związane szybkość działania.
    Inna, dobra, rodem z czasów układów TTL to rejestr przesuwny + bramki AND i OR sterujące przerzutnikiem RS. Niestety, wymagany jest większy nakład pracy.
    Metody polegające na akceptowaniu dwóch takich samych odczytów w zadanym odstępie czasu są ryzykowne, jeśli odczyt wejścia ma być szybki (np enkoder). Akceptowalne minimum to 3 odczyty.

    OdpowiedzUsuń

Działy
Działy dodatkowe
Inne
O blogu




Dzisiaj
--> za darmo!!! <--
1. USBasp
2. microBOARD M8


Napisz artykuł
--> i wygraj nagrodę. <--


Co nowego na blogu?
Śledź naszego Facebook-a



Co nowego na blogu?
Śledź nas na Google+

/* 20140911 Wyłączona prawa kolumna */
  • 00

    dni

  • 00

    godzin

  • :
  • 00

    minut

  • :
  • 00

    sekund

Nie czekaj do ostatniego dnia!
Jakość opisu projektu także jest istotna (pkt 9.2 regulaminu).

Sponsorzy:

Zapamiętaj ten artykuł w moim prywatnym spisie treści.