{"id":316,"date":"2015-12-14T09:23:02","date_gmt":"2015-12-14T08:23:02","guid":{"rendered":"http:\/\/www.daniel.slask.pl\/?p=316"},"modified":"2021-02-12T21:27:21","modified_gmt":"2021-02-12T20:27:21","slug":"moj-pomysl-na-obsluge-enkodera-biblioteka","status":"publish","type":"post","link":"https:\/\/daniel.slask.pl\/m\/moj-pomysl-na-obsluge-enkodera-biblioteka\/","title":{"rendered":"M\u00f3j pomys\u0142 na obs\u0142ug\u0119 enkodera &#8211; biblioteka"},"content":{"rendered":"\n<p>Tym razem chcia\u0142bym przedstawi\u0107 m\u00f3j pomys\u0142 na obs\u0142ug\u0119 enkodera.<\/p>\n\n\n\n<p>Biblioteka kt\u00f3r\u0105 prezentuj\u0119 zosta\u0142a wykonana w celu przypomnienia pewnych zagadnie\u0144 zwi\u0105zanych z programowaniem mikrokontroler\u00f3w, a tak\u017ce w celu zrozumienia dzia\u0142ania enkodera. Nie twierdz\u0119 wi\u0119c, \u017ce moja bibliotek jest najlepsza. W sieci na pewno znajduj\u0105 si\u0119 rozwi\u0105zania bardziej optymalne pod wzgl\u0119dem obj\u0119to\u015bci, oraz szybko\u015bci wykonywania. Przyk\u0142adem takim jest rozwi\u0105zanie przedstawione pod tym adresem: http:\/\/www.mikrocontroller.net\/articles\/Drehgeber Polecam zapoznanie si\u0119 z sposobem kt\u00f3ry zosta\u0142 tam przedstawiony &#8211; jest on bardzo ciekawy.<\/p>\n\n\n\n<p>A teraz zapraszam do zapoznania si\u0119 z moj\u0105 koncepcj\u0105 dot. enkodera.<\/p>\n\n\n\n<p>Identyfikacja obrot\u00f3w enkodera odbywa si\u0119 przy udziale dw\u00f3ch kana\u0142\u00f3w A i B. Odpowiednie prze\u0142\u0105czanie kana\u0142\u00f3w determinuje kierunek obrotu naszego enkodera. Przedstawia si\u0119 to w niniejszy spos\u00f3b:<\/p>\n\n\n\n<figure class=\"wp-block-image alignnone\"><a href=\"http:\/\/www.daniel.slask.pl\/m\/wp-content\/uploads\/2015\/12\/Enkoder-przebiegi2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"150\" height=\"150\" src=\"http:\/\/www.daniel.slask.pl\/m\/wp-content\/uploads\/2015\/12\/Enkoder-przebiegi2-150x150.png\" alt=\"Enkoder - przebiegi dla kana\u0142u A i B\" class=\"wp-image-319\"\/><\/a><figcaption>Przebieg kana\u0142u A i B<\/figcaption><\/figure>\n\n\n\n<p>Poni\u017cej przebieg\u00f3w zosta\u0142y umieszczone warto\u015bci binarne jakie uzyskuje kana\u0142 A i B podczas obrotu. Wyst\u0119puje regularno\u015b\u0107 podczas obrotu enkodera &#8211; ta regularno\u015b\u0107 ma swoj\u0105 nazw\u0119 &#8211; Kod Greya.W swojej bibliotece wykorzysta\u0142em te zale\u017cno\u015bci i powo\u0142a\u0142em do \u017cycia zmienn\u0105 <em>stan, <\/em>kt\u00f3ra przechowuje etap zmiany tych warto\u015bci. Zosta\u0142o to przedstawione na poni\u017cszym rysunku:<\/p>\n\n\n\n<figure class=\"wp-block-image alignnone\"><a href=\"http:\/\/www.daniel.slask.pl\/m\/wp-content\/uploads\/2015\/12\/Enkoder-prezentacja-zmiennej-stan.png\"><img loading=\"lazy\" decoding=\"async\" width=\"150\" height=\"150\" src=\"http:\/\/www.daniel.slask.pl\/m\/wp-content\/uploads\/2015\/12\/Enkoder-prezentacja-zmiennej-stan-150x150.png\" alt=\"Zmienna stan - prezentacja etap\u00f3w\" class=\"wp-image-320\"\/><\/a><figcaption>Zmienna stan &#8211; prezentacja etap\u00f3w<\/figcaption><\/figure>\n\n\n\n<p>Stan 1 oznacza wy\u0142\u0105czenie kana\u0142u (stan wysokiej impedancji &#8211; kana\u0142 nie pod\u0142\u0105czony do masy), natomiast 0 oznacza w\u0142\u0105czenie kana\u0142u (podci\u0105gni\u0119cie do masy &#8211; jak to ma miejsce przy przyciskach).<\/p>\n\n\n\n<p>Cztery najmniej znacz\u0105ce bity okre\u015blaj\u0105 kierunek obrot\u00f3w zgodny z ruchem wskaz\u00f3wek zegara (CW), natomiast 4 najbardziej znacz\u0105ce kierunek przeciwny do ruchu wskaz\u00f3wek zegara (CCW). Poni\u017cej w dw\u00f3ch kolumnach znajduje si\u0119 reprezentacja stanu kana\u0142\u00f3w A i B podczas obrotu w danym kierunku. Nale\u017cy zaznaczy\u0107, \u017ce poziom logiczny 0 oznacza, \u017ce kana\u0142 jest aktywny (tak jak to ma miejsce podczas obs\u0142ugi przycisk\u00f3w). Podczas ka\u017cdej zmiany stanu kana\u0142u w odpowiedniej 4-ce bit\u00f3w zmiennej <em>stan<\/em> ustawiane s\u0105 w 1 bitowe (odpowiednim miejscu) w zale\u017cno\u015bci kt\u00f3ry etap zosta\u0142 wykryty (etapy zmiany stan\u00f3w okre\u015blone s\u0105 jako A B C D).<br>Je\u017celi zosta\u0142 wykryty etap A dla kierunku zgodnego z ruchem wskaz\u00f3wek zegara, kolejne etapy b\u0119d\u0105 wykrywane tylko dla tego obrotu &#8211; dzi\u0119ki czemu podczas zako\u0144czenia ruchu zmienna <em>stan<\/em> osi\u0105gnie warto\u015b\u0107 0xF0 (dla CCW) b\u0105d\u017a 0x0F (dla CW). Po wykonanym obrocie mo\u017cna sprawdzi\u0107 warto\u015b\u0107 tej zmiennej i okre\u015bli\u0107 czy obr\u00f3t zosta\u0142 dobrze zinterpretowany przez program czy nie.<\/p>\n\n\n\n<p>Dodatkowo podczas rozpoczynania obrotu zostaje uruchomiony licznik (taki m\u00f3j watchdog), kt\u00f3ry ko\u0144cz\u0105c odliczanie w przypadku nieprawid\u0142owego odczytu danych z enkodera powoduje zresetowanie odpowiednich zmiennych, kt\u00f3re s\u0105 u\u017cyte do dzia\u0142ania programu. Mo\u017cliwa jest taka sytuacja, gdzie enkoder znajdzie si\u0119 w innym po\u0142o\u017ceniu ni\u017c referencyjne i obr\u00f3t spowoduje nieprawid\u0142owe odczytanie danych z kana\u0142u. Licznik ten zapewnia poprawno\u015b\u0107 odczytu w takich sytuacjach.<\/p>\n\n\n\n<p>Aby rozja\u015bni\u0107 jak dzia\u0142a moja biblioteka przedstawiam algorytm:<\/p>\n\n\n\n<figure class=\"wp-block-image alignnone\"><a href=\"http:\/\/www.daniel.slask.pl\/m\/wp-content\/uploads\/2015\/12\/Enkoder-algorytm3.png\"><img loading=\"lazy\" decoding=\"async\" width=\"150\" height=\"150\" src=\"http:\/\/www.daniel.slask.pl\/m\/wp-content\/uploads\/2015\/12\/Enkoder-algorytm3-150x150.png\" alt=\"Algorytm dzia\u0142ania biblioteki dla enkodera\" class=\"wp-image-324\"\/><\/a><figcaption>Algorytm dzia\u0142ania biblioteki dla enkodera<\/figcaption><\/figure>\n\n\n\n<p>Dzia\u0142anie programu oparte jest na zdarzeniach. U\u017cytkownik dostaje do dyspozycji nast\u0119puj\u0105ce funkcje:<\/p>\n\n\n\n<pre class=\"wp-block-code height-set:true lang:c decode:true\"><code lang=\"c\" class=\"language-c line-numbers\">void ENCODER_EVENT(void);\nvoid register_encoder_event( void(*encoder_callback)(void) );   \/\/rejestracja funkcji obs\u0142ugi dowolnego obrotu, u\u017cytkownik ma do dyspozycji ca\u0142\u0105 struktur\u0119\nvoid register_encoder_switch_event( void(*encoder_callback)(void) );    \/\/rejestracja funkcji obs\u0142ugi przycisku\nvoid encoder_init(void);        \/\/inicjalizacja enkodera\nvoid encoder_proccedure(void);          \/\/procedura pracy enkodera - musi znale\u017a\u0107 si\u0119 w przerwania wykonywanym co 1ms\nvoid encoder_clear_value(void);         \/\/wyzerowanie wszystkich wartosci w zmiennej enkoder_status<\/code><\/pre>\n\n\n\n<p>Pierwsze trzy funkcje dotycz\u0105 bezpo\u015brednio zdarze\u0144 (2 i 3 funkcja powoduje rejestracj\u0119 odpowiednio zdarzenia dla obrotu i przycisku, natomiast pierwsza wykonuje zdarzenie dla enkodera). Kolejne dotycz\u0105 odpowiednio inicjalizacji enkodera (nie nast\u0119puje konfiguracja przerwa\u0144), wykonania programu obs\u0142ugi enkodera, oraz wyzerowania wszystkich warto\u015bci w zmiennej enkoder_status, kt\u00f3ra jest r\u00f3wnie\u017c do dyspozycji u\u017cytkownika i ma posta\u0107 struktury:<\/p>\n\n\n\n<pre class=\"wp-block-code height-set:true lang:c decode:true\"><code lang=\"c\" class=\"language-c line-numbers\">typedef struct{\n           uint8_t switch_active;                                          \/\/czy przycisk aktywny\n            uint8_t switch_toggle;                                          \/\/zmienna toggle dla przycisku\n            uint16_t rotation_clockwise;                            \/\/ilosc impuls\u00f3w zgodnie z ruchem wskaz\u00f3wek\n            uint16_t rotation_counter_clockwise;            \/\/ilosc impuls\u00f3w przeciwnie do ruchu wskaz\u00f3wek\n            uint8_t full_rotation_cw;                                       \/\/liczba pe\u0142nych obrot\u00f3w zgodnych do ruchu wskaz\u00f3wek zegara (clockwise)\n            uint8_t full_rotation_ccw;                                      \/\/liczba pe\u0142nych obrot\u00f3w przeciwnych do ruchu wskaz\u00f3wek zegara(counter clockwise)\n    }DS_ENCODER;<\/code><\/pre>\n\n\n\n<p>Biblioteka sk\u0142ada si\u0119 z 3 plik\u00f3w, kt\u00f3re przedstawiaj\u0105 si\u0119 nast\u0119puj\u0105co:<\/p>\n\n\n\n<pre class=\"wp-block-code height-set:true height:300 lang:c decode:true\"><code lang=\"c\" class=\"language-c line-numbers\">    \/*\n     * ds_enc.h\n     *\n     *  Created on: 04-12-2015\n     *      Author: Daniel\n     *\n     *\n     *      UWAGA!!!\n     *\n     *      W tym pliku nie nale\u017cy niczego zmienia\u0107\n     *\n     *\/\n     \n    #ifndef DS_ENC_H_\n    #define DS_ENC_H_\n     \n    #include \"ds_enc_conf.h\"\n     \n    \/\/definicje\n     \n    \/\/ Makra upraszczaj\u0105ce dost\u0119p do port\u00f3w\n    \/\/ *** PORT\n    #define PORT(x) SPORT(x)\n    #define SPORT(x) (PORT##x)\n    \/\/ *** PIN\n    #define PIN(x) SPIN(x)\n    #define SPIN(x) (PIN##x)\n    \/\/ *** DDR\n    #define DDR(x) SDDR(x)\n    #define SDDR(x) (DDR##x)\n     \n    #define ENC_SW_A        !(PIN(ENC_PORT_A) &amp; (1&lt;&lt;ENC_PIN_A))\n    #define ENC_SW_B        !(PIN(ENC_PORT_B) &amp; (1&lt;&lt;ENC_PIN_B))\n     \n    #if ENC_SWITCH_ENABLE\n            #define SW_E    !(PIN(ENC_PORT_E) &amp; (1&lt;&lt;ENC_PIN_E))\n    #endif\n     \n    typedef struct{\n            uint8_t switch_active;                                          \/\/czy przycisk aktywny\n            uint8_t switch_toggle;                                          \/\/zmienna toggle dla przycisku\n            uint16_t rotation_clockwise;                            \/\/ilosc impuls\u00f3w zgodnie z ruchem wskaz\u00f3wek\n            uint16_t rotation_counter_clockwise;            \/\/ilosc impuls\u00f3w przeciwnie do ruchu wskaz\u00f3wek\n            uint8_t full_rotation_cw;                                       \/\/liczba pe\u0142nych obrot\u00f3w zgodnych do ruchu wskaz\u00f3wek zegara (clockwise)\n            uint8_t full_rotation_ccw;                                      \/\/liczba pe\u0142nych obrot\u00f3w przeciwnych do ruchu wskaz\u00f3wek zegara(counter clockwise)\n    }DS_ENCODER;\n     \n    extern volatile DS_ENCODER enkoder_status;\n     \n    \/\/**********    funkcje\n    \/\/********      zewn\u0119trzne\n     \n    void encoder_init(void);                        \/\/inicjalizacja enkodera\n    void encoder_proccedure(void);          \/\/procedura pracy enkodera - musi znale\u017a\u0107 si\u0119 w przerwania wykonywanym co 1ms\n    void encoder_clear_value(void);         \/\/wyzerowanie wszystkich wartosci w zmiennej enkoder_status\n     \n    \/\/***** zdarzenia\n    void ENCODER_EVENT(void);\n     \n    void register_encoder_event( void(*encoder_callback)(void) );   \/\/rejestracja funkcji obs\u0142ugi dowolnego obrotu, u\u017cytkownik ma do dyspozycji ca\u0142\u0105 struktur\u0119\n     \n    #if ENC_SWITCH_ENABLE\n            void register_encoder_switch_event( void(*encoder_callback)(void) );    \/\/rejestracja funkcji obs\u0142ugi przycisku\n    #endif\n     \n    #endif \/* DS_ENC_H_ *\/\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code height-set:true height:300 lang:c decode:true\"><code lang=\"c\" class=\"language-c line-numbers\">    \/*\n     * ds_enc.c\n     *\n     *  Created on: 04-12-2015\n     *       Edited on: 11-12-2015\n     *      Author: Daniel\n     *\n     *      All rights reserved.\n     *\n     *      Przy pomocy ksi\u0105\u017cki:\n     *                                                      Mikrokontrolery AVR\n     *                                                      J\u0119zyk C - podstawy programowania\n     *\n     *                                                                                                                      Autor:\n     *                                                                                                                              Miros\u0142aw Karda\u015b\n     *                                                      J\u0119zyk C.\n     *                                                      Pasja programowania mikrokontroler\u00f3w 8-bitowych\n     *\n     *                                                                                                                      Autor:\n     *                                                                                                                              Miros\u0142aw Karda\u015b\n     *\n     *      Do u\u017cytkownika dost\u0119pne s\u0105 nast\u0119puj\u0105ce elementy:\n     *\n     *      -       struktura DS_ENCODER, oraz zmienna enkoder_status, kt\u00f3ra przechowuje wszystkie aktualne stany dotycz\u0105ce enkkodera.U\u017cytkownika mo\u017ce dowolnie je zmienia\u0107\n     *      -       funkcjia do inicjalizacji enkodera encoder_init\n     *      -       funkcja obs\u0142ugi enkodera encoder_proccedure, kt\u00f3ra powinna znale\u017a\u0107 si\u0119 w przerwania od timera kt\u00f3ry wykonuje si\u0119 co 1ms (jest to warunek konieczny do prawid\u0142owego dzia\u0142ania biblioteki)\n     *      -       funkcja zeruj\u0105ce wszystkie zmienna w strukturze enkoder_status: encoder_clear_value\n     *      -       zdarzenie od enkodera ENCODER_EVENT, kt\u00f3re nale\u017cy umiescic w p\u0119tli g\u0142\u00f3wnej programu\n     *      -       rejestracj\u0119 funkcji obs\u0142ugi enkodera\n     *      -       rejestracj\u0119 funkcji obs\u0142ugi przycisku z enkodera\n     *\n     *      Konfiguracj\u0119 biblioteki nale\u017cy dokona\u0107 w pliku ds_enc_conf.h\n     *\n     *\n     *      W strukturze dost\u0119pnej dla u\u017cytkownika znajduje si\u0119 nast\u0119puj\u0105ce dane:\n     *       - switch_active                                        -       czy przycisk aktywny\n     *       - switch_toggle;                                       -       zmienna toggle dla przycisku\n     *       - rotation_clockwise;                          -       ilosc impuls\u00f3w zgodnie z ruchem wskaz\u00f3wek\n     *       - rotation_counter_clockwise;          -       ilosc impuls\u00f3w przeciwnie do ruchu wskaz\u00f3wek\n     *       - full_rotation_cw;                            -       liczba pe\u0142nych obrot\u00f3w zgodnych do ruchu wskaz\u00f3wek zegara (clockwise)\n     *       - full_rotation_ccw;                           -       liczba pe\u0142nych obrot\u00f3w przeciwnych do ruchu wskaz\u00f3wek zegara(counter clockwise)\n     *\n     *\n     *      UWAGA!!!\n     *\n     *      W tym pliku nie nale\u017cy niczego zmienia\u0107\n     *      Do prawid\u0142owego dzia\u0142ania biblioteki, konieczne jest umieszczenie funkci encoder_proccedure w przerwaniu od timera, kt\u00f3re wykonuje si\u0119 do 1ms\n     *\n     *\n     *\n     *\/\n     \n     \n    #include &lt;avr\/io.h>\n    #include &lt;avr\/interrupt.h>\n    #include &lt;avr\/pgmspace.h>\n    #include &lt;util\/delay.h>\n     \n    #include \"ds_enc.h\"\n    #include \"ds_enc_conf.h\"\n     \n     \n     \n    \/\/**********    zmienne\n     \n    volatile DS_ENCODER enkoder_status;\n     \n    \/\/**********    funkcje\n     \n    \/\/przerwanie\n     \n    void encoder_proccedure(void){\n     \n            static volatile uint8_t licznik;                        \/\/odmierza czas do wyzerowania zmiennych pomocniczych w przypadku z\u0142ego odczytu z enkodera\n            static volatile uint8_t stan=0x00;                      \/\/przechowuje informacj\u0119 o tym, w jakim po\u0142o\u017ceniu jest enkoder (podczas prze\u0142\u0105czania)\n            static volatile uint8_t enkoder=0;                      \/\/stan kana\u0142u A i B\n            static volatile uint8_t tmp_enkoder=0x03;       \/\/pami\u0119ta ostatnio stan kana\u0142u A i B\n     \n            if(licznik){\n                    \/\/odmierza czas do wyzerowania zmiennych pomocniczych\n                    --licznik;\n            }\n     \n     \n            enkoder = (!ENC_SW_A&lt;&lt;1) | !ENC_SW_B;   \/\/odczytuje obecny stan kana\u0142u A i B\n     \n            \/\/reakcja dla licznika gdy odliczy swoj czas\n            if(!licznik &amp;&amp; (stan != 0x00) &amp;&amp; (enkoder == 0x03) ){\n                    stan = 0x00;            \/\/wyzeruje dany stan po\u0142o\u017cenia enkodera\n                    tmp_enkoder = 0x03;     \/\/przywr\u00f3c pocz\u0105tkowe wartosci odczytanych wczesniej kana\u0142\u00f3w\n            }\n     \n            if( (stan==0x00)){\n                    \/\/gdy jest stan pocz\u0105tkowy\n     \n                    if(enkoder == 0x01){\n                            \/\/Kana\u0142 A czyli zgodnie z ruchem wskaz\u00f3wek\n     \n                            stan = 0x01;    \/\/4 najmniej znacz\u0105ce bity odnosz\u0105 si\u0119 do obrotu zgodnie z ruchem wskaz\u00f3wek - pierwsza zmiana\n                            licznik=ENC_TIME_COUNTER;\n     \n                    }else if(enkoder == 0x02){\n                            \/\/Kana\u0142 B czyli przeciwnie z ruchem wskaz\u00f3wek\n     \n                            stan = 0x10;    \/\/4 najbardzej znacz\u0105ce bity odnosz\u0105 si\u0119 do obrotu przeciwnie z ruchem wskaz\u00f3wek - pierwsza zmiana\n                            licznik=ENC_TIME_COUNTER;\n     \n                    }\n                    tmp_enkoder = enkoder;\n     \n            \/\/0x00\n            }else if(stan &amp; 0x01){\n                    \/\/obroty zgodnie z ruchem wskaz\u00f3wek zegara\n     \n                    if( (enkoder == 0x00) &amp;&amp; (tmp_enkoder == 0x01) ){\n     \n                            stan |= 0x02;           \/\/druga zmiana\n                            tmp_enkoder = enkoder;\/\/zapami\u0119tanie stanu kana\u0142u A i B\n     \n                    }else if( (enkoder == 0x02) &amp;&amp; (tmp_enkoder == 0x00) ){\n     \n                            stan |= 0x04;           \/\/trzecia zmiana\n                            tmp_enkoder = enkoder;\/\/zapami\u0119tanie stanu kana\u0142u A i B\n     \n                    }else if( (enkoder==0x03) &amp;&amp; (tmp_enkoder==0x02) ){\n     \n                            stan |= 0x08;           \/\/czwarta zmiana\n                            tmp_enkoder = enkoder;\/\/zapami\u0119tanie stanu kana\u0142u A i B\n     \n                            if(stan == 0x0F){\n                                    stan = 0x00;    \/\/wyzerowanie\n                                    enkoder_status.rotation_clockwise++;\n                                    licznik = 0;\n                            }\n     \n                    }\n                    \/\/0x01\n            }else if(stan &amp; 0x10){\n                    \/\/obroty przeciwne z ruchem wskaz\u00f3wek zegara\n     \n                    if( (enkoder == 0x00) &amp;&amp; (tmp_enkoder == 0x02) ){\n                            stan |= 0x20;           \/\/druga zmiana\n                            tmp_enkoder = enkoder;\/\/zapami\u0119tanie stanu kana\u0142u A i B\n     \n                    }else if( (enkoder == 0x01) &amp;&amp; (tmp_enkoder == 0x00) ){\n                            stan |= 0x40;           \/\/trzecia zmiana\n                            tmp_enkoder = enkoder;\/\/zapami\u0119tanie stanu kana\u0142u A i B\n     \n                    }else if( (enkoder==0x03) &amp;&amp; (tmp_enkoder==0x01) ){\n                            stan |= 0x80;           \/\/czwarta zmiana\n                            tmp_enkoder = enkoder;  \/\/zapami\u0119tanie stanu kana\u0142u A i B\n     \n                            if(stan == 0xF0){\n                                    stan = 0x00;\n                                    enkoder_status.rotation_counter_clockwise++;\n                                    licznik = 0;\n                            }\n     \n                    }\n            }\/\/0x10\n     \n            \/\/odczyt stanu przycisku w enkoderze\n    #if ENC_SWITCH_ENABLE\n            if(SW_E &amp;&amp; !enkoder_status.switch_active){\n                    enkoder_status.switch_active = 1;\n                    enkoder_status.switch_toggle ^= 1;\n     \n            }else if(!SW_E &amp;&amp; enkoder_status.switch_active){\n                    enkoder_status.switch_active = 0;\n            }\n    #endif\n     \n    }\/\/encoder_procedure\n     \n    \/\/********      wewn\u0119trzne\n    void encoder_full_rotation(void){\n     \n            static uint16_t tmp_cw;\n            static uint16_t tmp_ccw;\n     \n            if( enkoder_status.rotation_clockwise &amp;&amp; !(enkoder_status.rotation_clockwise % ENC_MAX_IMP) &amp;&amp; (enkoder_status.rotation_clockwise != tmp_cw) ){\n     \n                    enkoder_status.full_rotation_cw++;              \/\/zwi\u0119ksz licznik pe\u0142nych obrot\u00f3w\n                    tmp_cw = enkoder_status.rotation_clockwise;     \/\/zapami\u0119tanie obecnej wartosci impuls\u00f3w\n            }\n     \n            if( enkoder_status.rotation_counter_clockwise &amp;&amp; !(enkoder_status.rotation_counter_clockwise % ENC_MAX_IMP) &amp;&amp; (enkoder_status.rotation_counter_clockwise != tmp_ccw) ){\n     \n                    enkoder_status.full_rotation_ccw++;     \/\/zwi\u0119ksz licznik pe\u0142nych obrot\u00f3w\n                    tmp_ccw = enkoder_status.rotation_counter_clockwise;    \/\/zapami\u0119tanie obecnej wartosci impuls\u00f3w\n            }\n    }\n     \n    \/\/********      zewn\u0119trzne\n     \n    void encoder_init(void){\n            \/\/inicjalizacja port\u00f3w\n     \n            \/\/kierunek - wej\u015bcie\n            DDR(ENC_PORT_A) &amp;= ~(1&lt;&lt;ENC_PIN_A);     \/\/A\n            DDR(ENC_PORT_B) &amp;= ~(1&lt;&lt;ENC_PIN_B);     \/\/B\n     \n    #if ENC_SWITCH_ENABLE\n            DDR(ENC_PORT_E) &amp;= ~(1&lt;&lt;ENC_PIN_E);     \/\/switch\n    #endif\n     \n            \/\/podci\u0105gni\u0119cie do VCC\n            PORT(ENC_PORT_A) |= (1&lt;&lt;ENC_PIN_A);     \/\/A\n            PORT(ENC_PORT_B) |= (1&lt;&lt;ENC_PIN_B);     \/\/B\n     \n    #if ENC_SWITCH_ENABLE\n            PORT(ENC_PORT_E) |= (1&lt;&lt;ENC_PIN_E);     \/\/switch\n    #endif\n     \n    }\n     \n    void encoder_clear_value(void){\n            \/\/wyzerowanie wszystkich licznik\u00f3w enkodera\n            enkoder_status.full_rotation_ccw=0;\n            enkoder_status.full_rotation_ccw=0;\n            enkoder_status.rotation_clockwise=0;\n            enkoder_status.rotation_counter_clockwise=0;\n            enkoder_status.switch_active=0;\n            enkoder_status.switch_toggle=0;\n    }\n     \n     \n    \/\/***** zdarzenia\n    static void (*encoder_event_callback)(void);            \/\/zmienna przechowuj\u0105ca wska\u017anik do funkcji\n     \n    #if ENC_SWITCH_ENABLE\n            static void (*encoder_switch_event_callback)(void);\n    #endif\n     \n    void ENCODER_EVENT(void){\n     \n            encoder_full_rotation();        \/\/sprawdzenie czy odby\u0142 si\u0119 pe\u0142ny obr\u00f3t\n     \n            if( encoder_event_callback )\n                    if ( enkoder_status.rotation_clockwise || enkoder_status.rotation_counter_clockwise )           \/\/sprawd\u017a czy funkcja zosta\u0142a zarejestrowana\n                            (*encoder_event_callback)();            \/\/je\u017celi tak to j\u0105 wywo\u0142aj\n     \n    #if ENC_SWITCH_ENABLE\n            if( encoder_switch_event_callback )\n                    if( enkoder_status.switch_active )\n                            (*encoder_switch_event_callback)();\n    #endif\n     \n    }\n     \n    void register_encoder_event( void(*encoder_callback)(void) ){   \/\/rejestracja funkcji obs\u0142ugi przycisk\u00f3w\n     \n            encoder_event_callback = encoder_callback;\n    }\n     \n    #if ENC_SWITCH_ENABLE\n            void register_encoder_switch_event( void(*encoder_callback)(void) ){\n                    \/\/rejestracja funkcji obs\u0142ugi przycisku\n     \n                    encoder_switch_event_callback = encoder_callback;\n            }\n     \n    #endif\n<\/code><\/pre>\n\n\n\n<p>oraz plik konfiguracyjny bibliotek\u0119. U\u017cytkownik w tym pliku mo\u017ce dokonywa\u0107 zmian. W poprzednich dw\u00f3ch nie mo\u017ce.<\/p>\n\n\n\n<pre class=\"wp-block-code height-set:true height:300 lang:c decode:true\"><code lang=\"c\" class=\"language-c line-numbers\">    \/*\n     * ds_enc_conf.h\n     *\n     *  Created on: 04-12-2015\n     *      Author: Daniel\n     *\n     *      Plik konfiguracyjny dla u\u017cytkownika.\n     *\n     *      U\u017cytkownik biblioteki mo\u017ce dokonywa\u0107 zmian parametr\u00f3w.\n     *\n     *\/\n     \n    #ifndef DS_ENC_CONF_H_\n    #define DS_ENC_CONF_H_\n     \n     \n    \/\/ustalenie portu do kt\u00f3rego pod\u0142\u0105czone s\u0105 kana\u0142y A,B enkodera oraz przycisk E\n    #define ENC_PORT_A      B\n    #define ENC_PORT_B      B\n    #define ENC_PORT_E      B\n     \n    \/\/ustalenie pinu do kt\u00f3rego pod\u0142\u0105czone s\u0105 kana\u0142y A,B enkodera oraz przycisk E\n    #define ENC_PIN_A       0\n    #define ENC_PIN_B       1\n    #define ENC_PIN_E       2\n     \n    \/\/ustalenie wartosci licznik czuwaj\u0105cego nad poprawn\u0105 prac\u0105 enkodera (*1ms) -maks 255\n    #define ENC_TIME_COUNTER 150\n     \n    \/\/ustalenie ile impuls\u00f3w posiada enkoder na pe\u0142ny obr\u00f3t\n    #define ENC_MAX_IMP     20\n     \n    \/\/***** wyb\u00f3r w\u0142aciwosci bilioteki\n    #define ENC_SWITCH_ENABLE       1               \/\/czy jest dost\u0119pny przycisk w enkoderze       - je\u017celi tak to jest tak\u017ce dost\u0119pne zdarzenie od przycisku\n     \n    #endif \/* DS_ENC_CONF_H_ *\/\n<\/code><\/pre>\n\n\n\n<p>U\u017cytkownik mo\u017ce pod\u0142\u0105czy\u0107 enkoder do dowolnych pin\u00f3w mikrokontrolera &#8211; nie musz\u0105 by\u0107 to piny na tym samym porcie. Dodatkowo mo\u017ce ustali\u0107 czy biblioteka ma obs\u0142ugiwa\u0107 przycisk w enkoderze czy te\u017c nie. Z racji tej, \u017ce enkodery posiadaj\u0105 r\u00f3\u017cn\u0105 ilo\u015b\u0107 impuls\u00f3w na pe\u0142ny obr\u00f3t mo\u017cliwe jest r\u00f3wnie\u017c ustawienie ilo\u015bci impuls\u00f3w jak\u0105 posiada nasz enkoder. W pliku konfiguracyjnym mo\u017cna r\u00f3wnie\u017c ustawi\u0107 czas licznika (taki watchdog) kt\u00f3ry czuwa na poprawno\u015bci\u0105 dzia\u0142ania programu.<\/p>\n\n\n\n<p>Aby biblioteka dzia\u0142a\u0142a prawid\u0142owo, konieczne jest:<\/p>\n\n\n\n<p>&#8211; zastosowanie kwarcu 8MHz b\u0105d\u017a wi\u0119kszego;<\/p>\n\n\n\n<p>&#8211; odpowiednie skonfigurowanie pliku ds_enc_conf.h;<\/p>\n\n\n\n<p>&#8211; aktywowanie dowolnego timera na przerwanie wykonywane co 1 ms i umieszczenie w obs\u0142udze tego przerwania funkcji <em>encoder_proccedure();<\/em><\/p>\n\n\n\n<p>&#8211; wykonanie funkcji <em>encoder_init()<\/em> na samym pocz\u0105tku programu;<\/p>\n\n\n\n<p>&#8211; zarejestrowanie funkcjie obs\u0142uguj\u0105cych obroty i\/lub przycisk (wykorzystuj\u0105c funkcje rejestruj\u0105ce przedstawione wcze\u015bniej);<\/p>\n\n\n\n<p>&#8211; umieszczenie w p\u0119tli g\u0142\u00f3wnej funkcji zdarzenia <em>ENCODER_EVENT();<\/em><\/p>\n\n\n\n<p>&#8211; w funkcji odczytu obrotu nale\u017cy umie\u015bci\u0107 polecenie zeruj\u0105c\u0105 dwie zmienne:<\/p>\n\n\n\n<pre class=\"wp-block-code height-set:true lang:c decode:true\"><code lang=\"c\" class=\"language-c line-numbers\">\/\/wyzerowanie zmiennych w enkoder_status\nenkoder_status.rotation_clockwise = 0;\nenkoder_status.rotation_counter_clockwise = 0;<\/code><\/pre>\n\n\n\n<p>Nale\u017cy zwr\u00f3ci\u0107 uwag\u0119, \u017ce po wyzerowaniu tych zmiennych informacje o ilo\u015bci pe\u0142nych obrot\u00f3w nie s\u0105 poprawnie uzupe\u0142niane.<\/p>\n\n\n\n<p>Poni\u017cej przedstawiam przyk\u0142ad zastosowania tej biblioteki:<\/p>\n\n\n\n<pre class=\"wp-block-code height-set:true height:300 lang:c decode:true\"><code lang=\"c\" class=\"language-c line-numbers\">    #include &lt;avr\/io.h>\n    #include &lt;avr\/interrupt.h>\n     \n    #include \"DS_ENC\/ds_enc.h\"\n    #include \"DS_ENC\/ds_enc_conf.h\"\n     \n     \n    \/\/zmienne pomocnicze\n    uint8_t time_ls=0;\n    volatile uint8_t time_10ms = 0;\n    volatile uint8_t time_1ms = 0;\n    volatile uint8_t time_1s = 0;\n     \n    ISR(TIMER0_COMP_vect){\n     \n            if(++time_1ms>9){\n                    time_1ms=0;\n     \n                    if(++time_10ms>99){\n                            time_10ms=0;\n     \n                            if(++time_1s>59) time_1s=0;\n                    }\n            }\n     \n            encoder_proccedure();\n     \n    }\/\/ \/isr\n     \n    void obsluga_enkodera(void){\n    \/\/kod obs\u0142ugi dla enkodera\n     \n    \/\/wyzerowanie zmiennych w enkoder_status\n    enkoder_status.rotation_clockwise = 0;\n    enkoder_status.rotation_counter_clockwise = 0;\n    };\n     \n    void obsluga_przycisk(void){\n    \/\/kod obs\u0142ugi dla przycisku\n    };\n     \n    int main(void){\n           \n    \/\/inicjalizacja I\/O\n            DDRC |= (1&lt;&lt;PC0);               \/\/debuger kontrolny miga co 1 s\n     \n    \/\/*******************************inicjalizacja I\/O enkodera\n            encoder_init();\n    \/\/*******************************\n           \n    \/\/inicjalizacja przerwa\u0144\n            \/\/timer0 w trybie ctc\n            TCCR0 |= (1&lt;&lt;WGM01);                                    \/\/tryb CTC\n            TCCR0 |= (1&lt;&lt;CS01) | (1&lt;&lt;CS00);                 \/\/preskaler 64\n            OCR0 = 249;                                                     \/\/co 1ms dla preskalera 64 bity i kwarcu 16MHz\n            TIMSK |= (1&lt;&lt;OCIE0);                                    \/\/aktywacja przerwania\n     \n    \/\/aktywacja przerwa\u0144\n            sei();\n           \n    \/\/*******************************inicjalizacja zdarze\u0144 enkodera\n     \n    register_encoder_event(obsluga_enkodera);\n    register_encoder_switch_event(obsluga_przycisk);\n     \n    \/\/*******************************\n           \n            while(1){\n     \n                    \/\/wywo\u0142anie zdarzenia od enkodera\n                    ENCODER_EVENT();\n     \n                    \/\/puls programu;)\n                    if( time_1s != time_ls ){\n                            time_ls = time_1s;\n                            PORTC ^= (1&lt;&lt;PC0);\n                    }\n                   \n    \/\/..reszta kodu\n     \n            }\n     \n    }\n<\/code><\/pre>\n\n\n\n<p>Podsumowuj\u0105c &#8211; biblioteka umo\u017cliwia odpowiedni\u0105 interpretacj\u0119 obrot\u00f3w enkodera oraz przycisku, kt\u00f3ry znajduje si\u0119 w enkoderze. Dzi\u0119ki zastosowaniu licznika (programowego watchdoga), oraz przerwania wykonywanego co 1ms mo\u017cliwe by\u0142o zniwelowanie b\u0142\u0119dnych odczyt\u00f3w spowodowanych drganiami styk\u00f3w. Do dyspozycji u\u017cytkownika biblioteki zosta\u0142y oddane funkcje obs\u0142uguj\u0105ce zdarzenia oraz sam enkoder, a tak\u017ce zmienn\u0105 w kt\u00f3rej strukturze znajduj\u0105 si\u0119 wszystkie informacje dotycz\u0105ce obrot\u00f3w enkodera.<\/p>\n\n\n\n<p>Na chwil\u0119 obecn\u0105 biblioteka obs\u0142uguje tylko enkodery z tzw. <span style=\"text-decoration: underline;\">pe\u0142nym krokiem<\/span>. W przysz\u0142o\u015bci zostanie ona uzupe\u0142niona o obs\u0142ug\u0119 kolejnych typ\u00f3w enkoder\u00f3w.<\/p>\n\n\n\n<p>Poni\u017cej umieszczam ca\u0142\u0105 bibliotek\u0119:<\/p>\n\n\n\n<a  data-e-Disable-Page-Transition=\"true\" class=\"download-link download-button aligncenter\" title=\"Wersja v1.0\" href=\"https:\/\/daniel.slask.pl\/m\/download\/325\/?tmstv=1788163884\" rel=\"nofollow\" id=\"download-link-325\" >\n\t<p>\n\t\tPobierz &ldquo;DS_ENC.zip&rdquo;\t<\/p>\n\t<small>DS_ENC.zip\t\t&ndash; Pobrano 627 razy\t\t&ndash; 3,96 KB<\/small>\n<\/a>\n\n\n\n<p>Zach\u0119cam do pobierania.<\/p>\n\n\n\n<p>W razie jakichkolwiek pyta\u0144 prosz\u0119 pisa\u0107 wykorzystuj\u0105c formularz kontaktowy.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Tym razem chcia\u0142bym przedstawi\u0107 m\u00f3j pomys\u0142 na obs\u0142ug\u0119 enkodera. Biblioteka kt\u00f3r\u0105 prezentuj\u0119 zosta\u0142a wykonana w celu przypomnienia pewnych zagadnie\u0144 zwi\u0105zanych z programowaniem mikrokontroler\u00f3w, a tak\u017ce w celu zrozumienia dzia\u0142ania enkodera. Nie twierdz\u0119 wi\u0119c, \u017ce moja bibliotek jest najlepsza. W sieci&#8230;<br \/><a class=\"read-more-button\" href=\"https:\/\/daniel.slask.pl\/m\/moj-pomysl-na-obsluge-enkodera-biblioteka\/\">Wi\u0119cej<\/a><\/p>\n","protected":false},"author":1,"featured_media":333,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_exactmetrics_skip_tracking":false,"_exactmetrics_sitenote_active":false,"_exactmetrics_sitenote_note":"","_exactmetrics_sitenote_category":0,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[7],"tags":[12,32,33,34],"views":2352,"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/daniel.slask.pl\/m\/wp-content\/uploads\/2015\/12\/network-65923_19201.jpg","jetpack_shortlink":"https:\/\/wp.me\/pcPgV3-56","_links":{"self":[{"href":"https:\/\/daniel.slask.pl\/m\/wp-json\/wp\/v2\/posts\/316"}],"collection":[{"href":"https:\/\/daniel.slask.pl\/m\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/daniel.slask.pl\/m\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/daniel.slask.pl\/m\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/daniel.slask.pl\/m\/wp-json\/wp\/v2\/comments?post=316"}],"version-history":[{"count":7,"href":"https:\/\/daniel.slask.pl\/m\/wp-json\/wp\/v2\/posts\/316\/revisions"}],"predecessor-version":[{"id":1653,"href":"https:\/\/daniel.slask.pl\/m\/wp-json\/wp\/v2\/posts\/316\/revisions\/1653"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/daniel.slask.pl\/m\/wp-json\/wp\/v2\/media\/333"}],"wp:attachment":[{"href":"https:\/\/daniel.slask.pl\/m\/wp-json\/wp\/v2\/media?parent=316"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/daniel.slask.pl\/m\/wp-json\/wp\/v2\/categories?post=316"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/daniel.slask.pl\/m\/wp-json\/wp\/v2\/tags?post=316"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}