www.leniwiec.org

Tym wpisem chcę zacząć krótką serię artykułów opisujących kolejne perypetie procesu projektowania, podejmowania decyzji oraz budowania zasilacza „cyfrowego”. Słowo cyfrowego zostało celowo napisane w cudzysłowie, gdyż nie będzie to zasilacz impulsowy (przez niektórych nazywany cyfrowym), nie będzie to też do końca zasilacz liniowy ze sterowaniem cyfrowym (np. pwm lub DAC). Będzie to natomiast zasilacz liniowy z tym, że pętla histerezy (dla niewtajemniczonych pętla stabilizująca napięcie, prąd, ustalająca limitery dla prądu i napięcia itd.) zostanie przeniesiona do programu. Koncepcja taka jest przez wielu elektroników bardzo mocno krytykowana, jako że taki zasilacz nie nadaje się do uruchamiania układów, gdyż jest zbyt wolny w stosunku do rozwiązań opartych na wzmacniaczach operacyjnych. Jak to będzie w naszym przypadku, to się okaże, w ostatnim odcinku obiecuję dokonać stosownych pomiarów gotowego układu. Ja natomiast zdecydowałem się, aby oprzeć ten projekt właśnie na takiej koncepcji z kilku względów. Po pierwsze, jestem bardziej software-guy, a więc przeniesienie ile się da do programu jest mi na rękę, poza tym daje to spore możliwości modyfikacji i zmiany funkcjonalności całego układu poprzez wymianę oprogramowania. Po drugie, takie rozwiązanie minimalizuje liczbę elementów elektronicznych oraz ich połączenia, co sprawia, że układ od strony elektronicznej staje się prostszy i łatwiejszy do wykonania przez młodych adeptów elektroniki.

Zaczynamy

Zanim zaczniemy cokolwiek projektować, musimy przygotować sobie odpowiednie narzędzia – w naszym przypadku będą to odpowiednie biblioteki programistyczne / komponenty, których później będziemy używać. Zacznijmy na początku od sterowania. Pierwsze, co przychodzi do głowy jako wejście danych do naszego programu/układu, to klawiatura i wszelkie jej odmiany: klawiatura up/down/ok, klawiatura numeryczna 0…9, joystick itp. itd. Kolejna opcja to sterowanie poprzez port szeregowy bezpośrednio z komputera. Pomysł ok, ale bardziej jako ficzers niż jako główne sterowanie (który nota bene zostanie przez nas dodany w następnych odcinkach) – nie wyobrażam sobie sytuacji, kiedy uruchamiam jakiś nowy układ i za każdym razem, kiedy chcę zmienić wartość napięcia albo ustawić ogranicznik prądu sięgam po laptopa.
To, co wydaje się, że byłoby najlepsze to mieć dużą wygodną gałkę, najlepiej potencjometr wieloobrotowy… zostaje tylko pytanie jak go podłączyć do procesora ? Otóż nie podłączać go wcale! W naszym projekcie użyjemy enkodera obrotowego…

Enkoder obrotowy? Co to takiego?!

Zdjęcie enkodera

Enkoder obrotowy to takie małe pokrętełko (foto powyżej), a właściwie sprytny przełącznik, który zmienia swoje stany w zależności od tego, czy kręcimy go w prawo, czy w lewo. Schemat enkodera typu ENC-11 znajduje się poniżej.

Schemat enkodera ENC-11

Dlaczego akurat ten typ? Bo akurat jest dostępny na pewnym znanym portalu aukcyjnym i jest naprawdę tani. Koszt takiego enkodera to tylko kilka eurocentów, co dodatkowo czyni go jak najbardziej odpowiednim dla nas. Model, który udało mi się zakupić miał dodatkowo wbudowany przełącznik, który zamykał styki po naciśnięciu gałki – fajnie! Będziemy mogli zrobić takie fajne sterowanie menu jak np. w komputerku pokładowym w BMW E39 ;-)

Zanim zaczniemy podłączać go do kontrolera i programować, przyjrzyjmy się, co jest napisane w dokumentacji.

Na powyższym rysunku zaczerpniętym z noty katalogowej widzimy, jak wygląda sygnał wyjściowy z naszego enkoderka w momencie, gdy go kręcimy w lewo i w prawo. Jak już pewnie zauważyłeś, w zależności od tego, w którą stronę go kręcimy, sygnał na końcówce B jest przesunięty w fazie, tak że mamy albo zbocze opadające albo narastające w stosunku do sygnału wyzwalającego A. Tę właśnie właściwość wykorzystamy w programie. Sygnał z końcówki A będzie nam generował „przerwanie”, natomiast sygnał z końcówki B w zależności od zbocza będzie nas informował o kierunku, w jakim się on obraca.

Podłączamy!

W celu zaprogramowania oraz testowania układu, zbudujmy sobie (najlepiej na płytce stykowej) następujący układ:

Schemat układu testowego

Schemat układu testowego

Jak widać na schemacie, będzie nam potrzebny kontroler (ja użyłem Atmega8, może to być jednak dowolny inny rodziny Atmega8..32), wyświetlacz oparty na układzie HD447780, no i nasz enkoder.

Do obsługi samego enkodera wykorzystamy dwa porty procesora ustawione jako wejścia + dodatkowe jedno wejście dla wbudowanego przełącznika.

Pora na kod…

Zanim zacząłem pisać ten artykuł, zrobiłem małe rozeznanie w sieci, jak robią to inni ludzie, jednak ich metody mi się nie podobały. A to dlatego, że wszystkie rozwiązania, które widziałem były oparte o przerwania, co w naszym zastosowaniu nie jest najszczęśliwsze, gdyż kontroler będzie się zajmował również stabilizacją napięcia wyjściowego zasilacza. Zatem ewentualne opóźnienie, które mogłoby wprowadzić takie rozwiązanie obsługi enkodera, mogłoby skutkować niestabilną pracą zasilacza, a co za tym idzie – skokami napięcia wyjściowego.

Nasz program (biblioteka?) będzie się składał z dwóch procedur. Pierwsza z nich będzie uruchamiana na początku programu i będzie się w niej znajdował kod odpowiedzialny za inicjalizację i konfigurację portów – nazwijmy ją init_encoder(). Druga z kolei będzie uruchamiana w głównej pętli programu, a jej zadaniem będzie uaktualnianie zmiennej globalnej tackt, która będzie nas informowała o aktualnym stanie enkodera: LEFT, RIGHT, CLICK – nazwijmy ją read_encoder().

Konfiguracja

Poniższy kod przedstawia ciało procedury init_encoder()

#define ENC_PIN_1_PIN       PINC
#define ENC_PIN_1_REG       DDRC        //first encoder pin register
#define ENC_PIN_1_PORT      PORTC       //first encoder pin port
#define ENC_PIN_1           PC4         //first encoder pin

#define ENC_PIN_2_PIN       PINC
#define ENC_PIN_2_REG       DDRC        //second encoder pin register
#define ENC_PIN_2_PORT      PORTC       //second encoder pin port
#define ENC_PIN_2           PC5         //second encoder pin

#define ENC_BTN             PD7         //encoder button pin
#define ENC_BTN_PORT        PORTD       //encoder button port
#define ENC_BTN_REG         DDRD        //encoder button register
#define ENC_BTN_PIN         PIND

#define LEFT    1
#define RIGHT   2
#define CLICK   3
#define NEUTRAL 0

uint8_t tackt;

void init_encoder(void )
{
    ENC_PIN_1_REG &=~ (1 << ENC_PIN_1);        /* Pin 1 input              */
    ENC_PIN_1_PORT |= (1 << ENC_PIN_1);        /* Pin 1 pull-up enabled    */

    ENC_PIN_2_REG &=~ (1 << ENC_PIN_2);        /* Pin 2 input              */
    ENC_PIN_2_PORT |= (1 << ENC_PIN_2);        /* Pin 2 pull-up enabled    */

    //click button
    ENC_BTN_REG &=~ (1 << ENC_BTN);        /* Pin button input              */
    ENC_BTN_PORT |= (1 << ENC_BTN);        /* Pin button pull-up enabled    */
}//end of init_encoder()

Jak widać, nie ma tutaj wielkiej filozofii, po prostu ustawiamy 3 wybrane przez nas porty kontrolera jako wejścia oraz aktywujemy dla nich pull-up`y (podciągnięcia wewnętrznym rezystorem do plusa zasilania). Oprócz tego w globalnej przestrzeni programu definiujemy dwie zmienne globalne: 8mio bitową zmienną tackt, która będzie nas informowała o aktualnym stanie enkodera, oraz zmienną pomocniczą last.
Zdefiniowałem oprócz tego jeszcze kilka stałych LEFT, RIGHT, CLICK, NEUTRAL – te 4 stale są listą enumeratywnych wartości stanów enkodera, ENC_PIN_*, w których definiujemy, do których portów kontrolera jest podłączony enkoder.

Czytanie stanu enkodera

Poniższy fragment kodu przedstawia ciało procedury, która służy do odczytu aktualnego stanu enkodera.

void read_encoder(void )
{
    uint8_t current = 0;
    unsigned int i = 0;

    if(bit_is_clear(ENC_BTN_PIN, ENC_BTN))
    {
        for(i=0;i<65535;i++)
        {
            if(!bit_is_clear(ENC_BTN_PIN, ENC_BTN))
                return ;
        }

        tackt = CLICK;
        return ;
    }

    if(bit_is_clear(ENC_PIN_1_PIN, ENC_PIN_1))
        current = 1;
    else
        current = 0;

    if(last != current)
    {
        if(bit_is_clear(ENC_PIN_1_PIN,ENC_PIN_1))
        {
            if(bit_is_clear(ENC_PIN_2_PIN,ENC_PIN_2))
                tackt = LEFT;
            else
                tackt = RIGHT;
        }
        else
        {
            if(bit_is_clear(ENC_PIN_1_PIN,ENC_PIN_2))
                tackt = RIGHT;
            else
                tackt = LEFT;
        }

        last = current;
    }
}//end of read_encoder()

W pierwszej kolejności sprawdzamy, czy został przyciśnięty przycisk wbudowany w pokrętło enkodera.

    if(bit_is_clear(ENC_BTN_PIN, ENC_BTN))
    {
        for(i=0;i<65535;i++)
        {
            if(!bit_is_clear(ENC_BTN_PIN, ENC_BTN))
                return ;
        }

        tackt = CLICK;
        return ;
    }

Jeśli na pinie ustawionym jako wejście przycisku enkodera pojawi się stan niski, wówczas do zmiennej globalnej tackt przypisujemy wartość CLICK i kończymy wykonywanie procedury.
Dodatkowo znajduje się tam pętla, która sprawdza, czy przez 65535 cyklów, cały czas przycisk jest non-stop wciśnięty – jest to bardzo prymitywny kod debouncujący. Co to znaczy? Podczas przełączania każdego elektromechanicznego przełącznika, zanim znajdzie się on w stabilnym stanie (włączonym bądź wyłączonym), to w czasie przełączania występuje zjawisko iskrzenia i „podskakiwania styków”, wejście kontrolera jest natomiast na tyle czułe, że wyłapuje te „migotania” i interpretuje je jako zmiany stanu portu. Natomiast dzięki takiej pętli debouncującej upewniamy się, że przycisk jest już wciśnięty „na stałe”.

Dalej zaczyna się właściwy kod odpowiedzialny za wykrywanie aktualnego stanu enkodera

    if(bit_is_clear(ENC_PIN_1_PIN, ENC_PIN_1))
        current = 1;
    else
        current = 0;

Na początku sprawdzamy, czy na pinie podłączonym do wyjścia A enkodera (nasze wyjście wyzwalające), został ustawiony stan niski, jeśli tak, to zapisujemy w zmiennej current jedynkę, jeśli natomiast nie, zapisujemy w niej wartość zero. W ten sposób w zmiennej current mamy aktualny stan wejścia wyzwalającego.

Dalej mamy warunek, w którym sprawdzamy, czy aktualnie odczytana wartość wejścia wyzwalającego jest różna od ostatnio odczytanej wartości, jeśli tak, oznacza to, że pokrętło zostało poruszone, nadal jednak nie wiemy, czy w lewo, czy w prawo. Aby to określić, mamy dalej odpowiednie warunki, w których sprawdzamy, czy ruch nastąpił w lewo, czy w prawo. Poniekąd można powiedzieć, że do tej pory kod, który został wykonany, jest swoistym substytutem przerwania, które mogłoby być wygenerowane podczas zmiany stanu enkodera.
Mamy zatem następujący kod:

    if(last != current)
    {
        if(bit_is_clear(ENC_PIN_1_PIN,ENC_PIN_1))
        {
            if(bit_is_clear(ENC_PIN_2_PIN,ENC_PIN_2))
                tackt = LEFT;
            else
                tackt = RIGHT;
        }
        else
        {
            if(bit_is_clear(ENC_PIN_1_PIN,ENC_PIN_2))
                tackt = RIGHT;
            else
                tackt = LEFT;
        }

        last = current;
    }


Najpierw sprawdzamy, czy na nóżce A enkodera jest aktualnie stan niski, jeśli tak, to sprawdzamy, czy na nóżce B enkodera jest również stan niski, jeśli tak, oznacza to sytuację z górnej części rysunku z datasheetu, czyli wiemy, że gałka enkodera została przesunięta w kierunku ruchu wskazówek zegara (clockwise). Jeżeli natomiast na nóżce B enkodera stan jest wysoki, oznacza to sytuację z dolnej części tego rysunku, czyli gałka została przekręcona w kierunku odwrotnym do ruchu wskazówek zegara (counter-clockwise). Po tych warunkach przypisujemy odpowiednią wartość do zmiennej globalnej tackt. Jeśli natomiast pierwszy warunek nie zostanie spełniony (na nóżce A enkodera jest stan wysoki), dokonujemy analogicznego testu. Jedynie tylko należy uwzględnić, że wartości z rysunku (datasheet) są przesunięte o pół okresu.

Na końcu przypisujemy aktualną wartość wejścia wyzwalającego (nóżka A enkodera) do zmiennej last. I na tym kończy się procedura.

Złóżmy to do kupy…

Poniżej cały złożony razem kod.

define ENC_PIN_1_PIN       PINC
#define ENC_PIN_1_REG       DDRC        //first encoder pin register
#define ENC_PIN_1_PORT      PORTC       //first encoder pin port
#define ENC_PIN_1           PC4         //first encoder pin

#define ENC_PIN_2_PIN       PINC
#define ENC_PIN_2_REG       DDRC        //second encoder pin register
#define ENC_PIN_2_PORT      PORTC       //second encoder pin port
#define ENC_PIN_2           PC5         //second encoder pin

#define ENC_BTN             PD7         //encoder button pin
#define ENC_BTN_PORT        PORTD       //encoder button port
#define ENC_BTN_REG         DDRD        //encoder button register
#define ENC_BTN_PIN         PIND

#define LEFT    1
#define RIGHT   2
#define CLICK   3
#define NEUTRAL 0

uint8_t tackt = NEUTRAL;
uint8_t last = 0;

void init_encoder(void )
{
    ENC_PIN_1_REG &=~ (1 << ENC_PIN_1);        /* Pin 1 input              */
    ENC_PIN_1_PORT |= (1 << ENC_PIN_1);        /* Pin 1 pull-up enabled    */

    ENC_PIN_2_REG &=~ (1 << ENC_PIN_2);        /* Pin 2 input              */
    ENC_PIN_2_PORT |= (1 << ENC_PIN_2);        /* Pin 2 pull-up enabled    */

    //click button
    ENC_BTN_REG &=~ (1 << ENC_BTN);        /* Pin button input              */
    ENC_BTN_PORT |= (1 << ENC_BTN);        /* Pin button pull-up enabled    */
}//end of init_encoder()

void read_encoder(void )
{
    uint8_t current = 0;
    unsigned int i = 0;

    if(bit_is_clear(ENC_BTN_PIN, ENC_BTN))
    {
        for(i=0;i<65535;i++)
        {
            if(!bit_is_clear(ENC_BTN_PIN, ENC_BTN))
                return ;
        }

        tackt = CLICK;
        return ;
    }

    if(bit_is_clear(ENC_PIN_1_PIN, ENC_PIN_1))
        current = 1;
    else
        current = 0;

    if(last != current)
    {
        if(bit_is_clear(ENC_PIN_1_PIN,ENC_PIN_1))
        {
            if(bit_is_clear(ENC_PIN_2_PIN,ENC_PIN_2))
                tackt = LEFT;
            else
                tackt = RIGHT;
        }
        else
        {
            if(bit_is_clear(ENC_PIN_1_PIN,ENC_PIN_2))
                tackt = RIGHT;
            else
                tackt = LEFT;
        }

        last = current;
    }
}//end of read_encoder()

int main()
{
  init_encoder();
  init_lcd();
  
  while(1)
  {
    read_encoder();
    if(tackt != NEUTRAL)
    {
      lcd_clrscr();
      
      if(tackt == LEFT)
        lcd_puts(PSTR("Left"));
      else if(tackt == RIGHT)
        lcd_puts(PSTR("Right"));
      else if(tackt == CLICK)
        lcd_puts("Click");
        
      tackt = NEUTRAL;
    } 
  }
}//end of main()

Zobaczmy po kolei, co się dzieje. Program wchodzi w funkcję main, najpierw inicjalizuje porty kontrolera odpowiedzialne za obsługę kontrolera oraz bibliotekę obsługi wyświetlacza.
Dalej program przechodzi do głównej pętli, w której za każdym „przelotem” wywoływana jest procedura read_encoder(), która odczytuje aktualny stan enkodera. Następnie sprawdzamy, czy stan jest różny od NEUTRAL, czyli czy cokolwiek się dzieje z enkoderem, jeśli tak sprawdzamy jego stan i wyświetlamy odpowiedni komunikat na lcd. I na samym końcu czyścimy zmienną globalną tackt poprzez przypisanie jej wartości NEUTRAL. Jest to niezbędne, aby przy następnym wywołaniu procedury read_encoder() wykonała się ona poprawnie.

Co dalej ?

Wypadałoby przenieść nasz kod do osobnych plików, ustawić zmienną tackt jako extender oraz wszystko ładnie sformatować. Poniżej gotowy sformatowany kod:

encoder.h:

#ifndef ENCODER_H
#define ENCODER_H

#define ENC_PIN_1_PIN       PINC
#define ENC_PIN_1_REG       DDRC        //first encoder pin register
#define ENC_PIN_1_PORT      PORTC       //first encoder pin port
#define ENC_PIN_1           PC4         //first encoder pin

#define ENC_PIN_2_PIN       PINC
#define ENC_PIN_2_REG       DDRC        //second encoder pin register
#define ENC_PIN_2_PORT      PORTC       //second encoder pin port
#define ENC_PIN_2           PC5         //second encoder pin

#define ENC_BTN             PD7         //encoder button pin
#define ENC_BTN_PORT        PORTD       //encoder button port
#define ENC_BTN_REG         DDRD        //encoder button register
#define ENC_BTN_PIN         PIND

#define LEFT    1
#define RIGHT   2
#define CLICK   3
#define NEUTRAL 0

extern uint8_t tackt;

void init_encoder(void );
void read_encoder(void );

#endif

encoder.c:

#include <stdlib.h>
#include <avr/io.h>
#include <util/delay.h>

#include "encoder.h"

uint8_t tackt = NEUTRAL;
uint8_t last = 0;

void init_encoder(void )
{
    ENC_PIN_1_REG &=~ (1 << ENC_PIN_1);        /* Pin 1 input              */
    ENC_PIN_1_PORT |= (1 << ENC_PIN_1);        /* Pin 1 pull-up enabled    */

    ENC_PIN_2_REG &=~ (1 << ENC_PIN_2);        /* Pin 2 input              */
    ENC_PIN_2_PORT |= (1 << ENC_PIN_2);        /* Pin 2 pull-up enabled    */

    //click button
    ENC_BTN_REG &=~ (1 << ENC_BTN);        /* Pin button input              */
    ENC_BTN_PORT |= (1 << ENC_BTN);        /* Pin button pull-up enabled    */
}//end of init_encoder()

void read_encoder(void )
{
    uint8_t current = 0;
    unsigned int i = 0;

    if(bit_is_clear(ENC_BTN_PIN, ENC_BTN))
    {
        for(i=0;i<65535;i++)
        {
            if(!bit_is_clear(ENC_BTN_PIN, ENC_BTN))
                return ;
        }

        tackt = CLICK;
        return ;
    }

    if(bit_is_clear(ENC_PIN_1_PIN, ENC_PIN_1))
        current = 1;
    else
        current = 0;

    if(last != current)
    {
        if(bit_is_clear(ENC_PIN_1_PIN,ENC_PIN_1))
        {
            if(bit_is_clear(ENC_PIN_2_PIN,ENC_PIN_2))
                tackt = LEFT;
            else
                tackt = RIGHT;
        }
        else
        {
            if(bit_is_clear(ENC_PIN_1_PIN,ENC_PIN_2))
                tackt = RIGHT;
            else
                tackt = LEFT;
        }

        last = current;
    }
}//end of read_encoder()

Podsumowanie

Polecam samemu poeksperymentować z enkoderem. Imo jest on bardzo wygodnym sposobem obsługi menu i generalnie dobrym interfejsem użytkownik->sprzęt. W następnym odcinku zajmiemy się kolejnym ważnym elementem, jakim jest przetwornik DAC. Będziemy również podejmować pierwsze poważne decyzje projektowe, które będą rzutować na dalszy przebieg prac i ostateczny produkt! Stay tuned!

Zapraszam do nowego artykułu na którym jest opisany sposób PRAWIDŁOWEJ obsługi enkodera obrotowego – Enkoder obrotowy w praktyce – prawidłowe podłączenie do mikrokontrolera AVR

19 KOMENTARZY
sprae
10 grudzień 2011
ad

Świetny wpis. Już nie mogę się doczekać kolejnego odcinka ;-) .

airborn
10 grudzień 2011
ad

Z jednej strony mówisz, że przerwanie wprowadza jakieś opóźnienia (tak na prawdę to jakie?), a z drugiej robisz 65k razową pętlę. Sam nie wiem co gorsze :P

leniwiec
10 grudzień 2011
ad

airborn: zapewne chodzi Ci o tę pseudo-debouncową pętle dla przycisku… Zauważ że nie jest ona wykonywana w przerwaniu. Idea jest taka aby kod który będzie wykonywany w przerwaniu – wyniki z przetwornika ADC miał najwyższy priorytet, pętla główna w sumie nie ma znaczenia… myślę że docelowo będzie można w niej nawet umieścić _delay(xxx);

airborn
10 grudzień 2011
ad

No więc właśnie, nie lepiej było by zrobić całość w przerwaniu i filtrować drgania styku kondziorkiem równolegle do masy?

leniwiec
10 grudzień 2011
ad

Ale po co? Po co angażować przerwanie do tak nisko-priorytetowego zadania jakim jest obsługa enkodera? W przerwaniu zostanie zaimplementowana histereza stabilizatora napięcia i tam będzie się liczył każdy pojedyńczy cykl zegara. Gdybyśmy zrobili obsługę enkodera na przerwaniu, to mogło by dojść do sytuacji w której „histereza” musi czekać aż zostanie zakończona obsługa enkodera, a w międzyczasie zasilany układ już zaczął by się dymić.

airborn
10 grudzień 2011
ad

Rozumiem intencje, ale piszesz trochę tak, jak gdyby obsługa tego przerwania miała zajmować kilka minut. Jeżeli boisz się, że te parę taktów okaże się zabójczych dla układu to trzeba albo pomyśleć o obniżeniu krańcowego punktu ‘histerezy’ albo o ustawianiu priorytetu przerwań (nie jestem pewien jak wygląda sprawa priorytetyzowania przerwań w AVRach.

leniwiec
10 grudzień 2011
ad

Hmm, ja nie rozumie po co miałbym stosować przerwanie tam gdzie ono nie jest potrzebne? Skoro mogę to zrobić bardzo łatwo bez niego, to po co mi one?

airborn
10 grudzień 2011
ad

Dlatego, że w chwili obecnej możesz przegapić event od encodera

leniwiec
10 grudzień 2011
ad

Teoretycznie, w praktyce przy zegarze 16mhz nie jest to możliwe, mimo usilnych prób nie byłem w stanie przegapić takiego eventu… Poza tym nie popadajmy w skrajności, nawet jeśli został by event przegapiony (co jak wspomniałem nie zdarza się w praktyce), to jest to na tyle mało istotne że tak naprawdę nic się nie stanie ;)

airborn
10 grudzień 2011
ad

Zwróć tylko uwagę na jeden drobny szczegół: w chwili obecnej Twój kod nie wiele robi. Docelowo kod poza odczytem enkodera będzie robił ciut więcej, więc i częstotliwość odczytu znacznie się zmniejszy. Jak sam wspomniałeś, masz 16MHz taktowanie, więc szanse, że tak krótka obsługa przerwania z enkodera (przy założeniu sprzętowego debouncowania) będzie Ci w czymkolwiek przeszkadzać są jeszcze mniejsze.

leniwiec
10 grudzień 2011
ad

Znów praktyka się kłania, w chwili pisania artykułu, projekt jest już praktycznie zakończony, jedynie wprowadzane są modyfikację. I tak jak poprzednio pisałem, nie ma problemu że coś jest tracone. Nie rozumie dlaczego kolega się tak uczepił aby wykonać obsługę tej gałki przy pomocy przerwania, jeśli moje rozwiązanie ma jakiś feler chciałbym wiedzieć jaki.
Wydaje mi się że to o czym piszesz, bo rozumie że powodem użycia przerwania ma być każdorazowość zadziałania kodu jego obsługi, zakłada że gałką kręci z jakąś gigantyczną prędkością superman ;)

leniwiec
10 grudzień 2011
ad

Tak, tylko z obserwacji wynika, że jeden okres na wyjściu enkodera nawet przy bardzo szybkim kręceniu trwa… bardzo długo w stosunku do prędkości procka, stąd też taka sytuacja w praktyce jest niemożliwa, bo styki enkodera po prostu nie otworzą się i nie zamkną na tyle szybko żeby się wpakować z sygnałem w akurat tak że procek jest zajęty przerwaniem.

marwis95
18 kwiecień 2014
ad

Witam.
Mam problem z niestabilną pracą enkodera.
Obracam enkoderem w prawo to pokazuje sie right, ale czasami wskakuje left, to samo gdy kręcę w lewo – jest napisane left ale czsami wskakuje right. Click działa dobrze. W czym może byc problem?

leniwiec
18 kwiecień 2014
ad

@marwis95 coś się dzieje zbyt wolno naj wyraźniej. Jeśli sam enkoder jest ok, to możesz spróbować z przeniesieniem tego wszystkiego do przerwania. Wówczas problem powinien zniknąć ;)

marwis95
18 kwiecień 2014
ad

No enkoder jest nowy wiec powinien działać, podłaczyłem go tak jak na schemacie

marwis95
18 kwiecień 2014
ad

for(i=0;i<65535;i++) {
if(!bit_is_clear(ENC_BTN_PIN, ENC_BTN))
return ;
}
Pozbyłem się tej pętli i jest lepiej ;) Nie jeste idealnie, ale wystarczy :)

leniwiec
18 kwiecień 2014
ad

Bo ona wprowadza opóźnienia, jakim zegarem masz taktowany procek ? Ten przykład który podałem w artykule jest specyficzny… na dniach będę pisał nowy artykulik o enkoderach ;)

marwis95
25 kwiecień 2014
ad

Taktuje 8MHz, ale próbowałem też inne częstotliwości i przy każdej jest tak samo. Używam Atmega32. Czekam na ten nowy artykuł :)

leniwiec
25 kwiecień 2014
ad

art się właśnie pisze ;) Chce też zrobić taką prostą lib-ke do obsługi enkodera którą każdy będzie se mógł podłączyć do swojego kodu… do wieczora mam czas, zobaczymy co mi z tego wyjdzie ;)

Wyślij