Příklady pro použití rotačního enkoderu s Arduinem.
Rotační enkodér je typ snímače polohy, který převádí úhlovou polohu (otáčení) knoflíku na výstupní signál, který se používá k určení směru otáčení knoflíku.
Díky své robustnosti a jemnému digitálnímu ovládání se používají v mnoha aplikacích včetně robotiky, CNC strojů a tiskáren.
Existují dva typy rotačních enkodérů - absolutní a přírůstkové. Absolutní enkodér nám udává přesnou polohu knoflíku ve stupních, zatímco inkrementální enkodér hlásí, o kolik přírůstků se hřídel posunula.
Rotační enkodér použitý v tomto článku je přírůstkového typu.
Rotační enkodéry vs. potenciometry
Rotační enkodéry jsou moderním digitálním ekvivalentem potenciometru a jsou univerzálnější než potenciometr.
Mohou se plně otáčet bez koncových dorazů, zatímco potenciometr se může otáčet pouze asi o 3/4 kruhu.
Potenciometry jsou nejlepší v situacích, kdy potřebujete znát přesnou polohu knoflíku. Rotační enkodéry jsou však nejlepší v situacích, kdy potřebujete znát změnu polohy místo přesné polohy.
Jak rotační enkodéry fungují
Uvnitř kodéru je drážkovaný disk připojený ke společnému uzemňovacímu pinu C a dvěma kontaktním pinům A a B, jak je znázorněno níže.

Když otočíte knoflíkem, A a B dostanou se do kontaktu se společným uzemňovacím kolíkem C v určitém pořadí podle směru, kterým knoflíkem otáčíte.
Když přijdou do styku se společnou zemí, vydávají signály. Tyto signály jsou navzájem posunuty o 90° mimo fázi, protože jeden kolík přichází do kontaktu před druhým kolíkem. Toto se nazývá kvadraturní kódování.

Když otočíte knoflíkem ve směru hodinových ručiček, nejprve se připojí pin A a poté pin B. Když otočíte knoflíkem proti směru hodinových ručiček, nejprve se připojí pin B a poté pin A.
Sledováním, kdy se každý kolík připojuje a odpojuje od země, můžeme pomocí těchto změn signálu určit, ve kterém směru se knoflík otáčí. Můžete to udělat jednoduchým pozorováním stavu B, když A změní stav.
Když A změní stav:
Pokud B! = A, pak byl knoflík otočen ve směru hodinových ručiček.

Pokud B = A, pak byl knoflík otočen proti směru hodinových ručiček.

Piny rotačního enkodéru
Vývody rotačního kodéru jsou následující:

GND je zem.
VCC je kladné napájecí napětí, obvykle 3,3 nebo 5 voltů.
SW je aktivní výstup spínače s nízkým tlačítkem. Když stisknete knoflík, napětí klesne na NÍZKÉ.
DT (výstup B) je stejný jako výstup CLK, ale zpožďuje CLK o 90° fázový posun. Tento výstup lze použít k určení směru otáčení.
CLK (výstup A) je primární výstupní impuls pro určení velikosti otáčení. Pokaždé, když se knoflík otočí o jednu zarážku (kliknutí) v libovolném směru, výstup „CLK“ projde jedním cyklem přechodu HIGH a poté LOW.
Výše uvedený popis pinů je dostupný ve speciální verzi enkodéru, které jsou součástí kompletního obvodu s pžadovanými rezistory. Rotační enkodér, ale lze zakoupit i v "holém stavu" a potom se musí celý obvod zapojit podle následujícího schématu:
Zapojení rotačního kodéru k Arduinu
Nyní, když víme vše o rotačním kodéru, je čas jej použít!
Pojďme připojit rotační kodér k Arduinu. Připojení je poměrně jednoduché. Začněte připojením pinu +V na 5V na pinu Arduino a GND k zemi.
Nyní připojte piny CLK a DT k digitálnímu pinu #2 a #3. Nakonec připojte SW pin k digitálnímu pinu #4.
Následující obrázek ukazuje zapojení.
Arduino kód - čtení rotačních enkodéru
Nyní, když máte připojený enkodér, budete potřebovat program, aby vše fungovalo.
Následující program detekuje, kdy se enkodér otáčí, určuje, kterým směrem se otáčí, a zda se stiskne tlačítko.
// Rotary Encoder Inputs
#define CLK 2
#define DT 3
#define SW 4
int counter = 0;
int currentStateCLK;
int lastStateCLK;
String currentDir ="";
unsigned long lastButtonPress = 0;
void setup() {
// Set encoder pins as inputs
pinMode(CLK,INPUT);
pinMode(DT,INPUT);
pinMode(SW, INPUT_PULLUP);
// Setup Serial Monitor
Serial.begin(9600);
// Read the initial state of CLK
lastStateCLK = digitalRead(CLK);
}
void loop() {
// Read the current state of CLK
currentStateCLK = digitalRead(CLK);
// If last and current state of CLK are different, then pulse occurred
// React to only 1 state change to avoid double count
if (currentStateCLK != lastStateCLK && currentStateCLK == 1){
// If the DT state is different than the CLK state then
// the encoder is rotating CCW so decrement
if (digitalRead(DT) != currentStateCLK) {
counter --;
currentDir ="CCW";
} else {
// Encoder is rotating CW so increment
counter ++;
currentDir ="CW";
}
Serial.print("Direction: ");
Serial.print(currentDir);
Serial.print(" | Counter: ");
Serial.println(counter);
}
// Remember last CLK state
lastStateCLK = currentStateCLK;
// Read the button state
int btnState = digitalRead(SW);
//If we detect LOW signal, button is pressed
if (btnState == LOW) {
//if 50ms have passed since last LOW pulse, it means that the
//button has been pressed, released and pressed again
if (millis() - lastButtonPress > 50) {
Serial.println("Button pressed!");
}
// Remember last button press event
lastButtonPress = millis();
}
// Put in a slight delay to help debounce the reading
delay(1);
}
Pokud je vše v pořádku, měli byste vidět níže výstup na sériovém monitoru.

Arduino kód - pomocí přerušení
Aby rotační enkodér fungoval, musíme nepřetržitě sledovat změny signálů DT a CLK.
Abychom zjistili, kdy k takovým změnám dochází, můžeme je průběžně dotazovat (jako jsme to dělali v našem předchozím příkladu). Z níže uvedených důvodů to však není nejlepší řešení.
- Musíme neustále provádět kontrolu, abychom zjistili, zda se hodnota změnila. Pokud se úroveň signálu nezmění, bude docházet ke ztrátě cyklů.
- Od doby, kdy se událost stane, do doby, kdy to zkontrolujeme, bude existovat latence. Pokud potřebujeme okamžitě reagovat, budeme s touto latencí zdrženi.
- Je-li doba změny krátká, je možné zcela vynechat změnu signálu.
Široce přijímaným řešením je použití přerušení.
// Rotary Encoder Inputs
#define CLK 2
#define DT 3
int counter = 0;
int currentStateCLK;
int lastStateCLK;
String currentDir ="";
void setup() {
// Set encoder pins as inputs
pinMode(CLK,INPUT);
pinMode(DT,INPUT);
// Setup Serial Monitor
Serial.begin(9600);
// Read the initial state of CLK
lastStateCLK = digitalRead(CLK);
// Call updateEncoder() when any high/low changed seen
// on interrupt 0 (pin 2), or interrupt 1 (pin 3)
attachInterrupt(0, updateEncoder, CHANGE);
attachInterrupt(1, updateEncoder, CHANGE);
}
void loop() {
//Do some useful stuff here
}
void updateEncoder(){
// Read the current state of CLK
currentStateCLK = digitalRead(CLK);
// If last and current state of CLK are different, then pulse occurred
// React to only 1 state change to avoid double count
if (currentStateCLK != lastStateCLK && currentStateCLK == 1){
// If the DT state is different than the CLK state then
// the encoder is rotating CCW so decrement
if (digitalRead(DT) != currentStateCLK) {
counter --;
currentDir ="CCW";
} else {
// Encoder is rotating CW so increment
counter ++;
currentDir ="CW";
}
Serial.print("Direction: ");
Serial.print(currentDir);
Serial.print(" | Counter: ");
Serial.println(counter);
}
// Remember last CLK state
lastStateCLK = currentStateCLK;
}
Všimněte si, že hlavní smyčka tohoto programu je prázdná, takže Arduino nebude ničím zaneprázdněno.
Tento program mezitím sleduje digitální pin 2 (odpovídá přerušení 0) a digitální pin 3 (odpovídá přerušení 1) pro změnu hodnoty. Jinými slovy, hledá změnu napětí z HIGH na LOW nebo LOW na HIGH, což se stane, když otočíte knoflíkem.
Když k tomu dojde, zavolá se funkce
(často nazývaná rutina služby přerušení nebo jen ISR ). Spustí se kód v rámci této funkce a pak se program vrátí zpět k tomu, co dělal předtím.updateEncoder
Za tím vším jsou zodpovědné dva řádky. Tato funkce se jmenuje
, která říká, jaký pin monitorovat, který ISR provést, pokud je aktivováno přerušení, a jaký typ spouště hledat.attachInterrupt()
attachInterrupt(0, updateEncoder, CHANGE);
attachInterrupt(1, updateEncoder, CHANGE);
Ovládání servomotor pomocí rotačního enkodéru
Pro náš další projekt použijeme rotační enkodér k ovládání polohy servomotoru.
Tento projekt může být velmi užitečný v mnoha situacích, například když chcete ovládat rameno robota, protože vám umožní přesné umístění paže a jejího úchopu.
V případě, že nejste obeznámeni se servomotorem, zvažte přečtení (alespoň skimming) níže uvedeného tutoriálu.
Schéma zapojení
Jak ukazuje schéma zapojení, budete potřebovat servomotor. Připojte červený vodič servomotoru k externímu napájení 5V, černý/hnědý vodič k zemi a oranžový/žlutý vodič ke kolíku 9 povoleného PWM.
Samozřejmě můžete použít výstup Arduino 5V, ale mějte na paměti, že servo může indukovat elektrický šum na 5V linku, kterou Arduino používá, což nemusí být to, co chcete.
Proto doporučujeme použít externí napájecí zdroj.

Arduino kód
Zde je kód pro přesné ovládání servomotoru pomocí rotačního kodéru. Při každém otočení knoflíku o jednu zarážku (kliknutí) se poloha ramene serva změní o jeden stupeň.
// Include the Servo Library
#include <Servo.h>
// Rotary Encoder Inputs
#define CLK 2
#define DT 3
Servo servo;
int counter = 0;
int currentStateCLK;
int lastStateCLK;
void setup() {
// Set encoder pins as inputs
pinMode(CLK,INPUT);
pinMode(DT,INPUT);
// Setup Serial Monitor
Serial.begin(9600);
// Attach servo on pin 9 to the servo object
servo.attach(9);
servo.write(counter);
// Read the initial state of CLK
lastStateCLK = digitalRead(CLK);
}
void loop() {
// Read the current state of CLK
currentStateCLK = digitalRead(CLK);
// If last and current state of CLK are different, then pulse occurred
// React to only 1 state change to avoid double count
if (currentStateCLK != lastStateCLK && currentStateCLK == 1){
// If the DT state is different than the CLK state then
// the encoder is rotating CCW so decrement
if (digitalRead(DT) != currentStateCLK) {
counter --;
if (counter<0)
counter=0;
} else {
// Encoder is rotating CW so increment
counter ++;
if (counter>179)
counter=179;
}
// Move the servo
servo.write(counter);
Serial.print("Position: ");
Serial.println(counter);
}
// Remember last CLK state
lastStateCLK = currentStateCLK;
}
Jak je to v ESP32-S3
V ESP32-S3 od společnosti Laska je zapojení rotačního enkoderu stejné, akorát se musí zvolit odpovídající piny.
Programový kód pak bude vypadat:
#include <Arduino.h> // Vložení Arduino knihovny
// Definice pinů pro rotační enkodér
#define CLK 8 // Pin pro hodinový signál enkodéru
#define DT 6 // Pin pro datový signál enkodéru
#define SW 5 // Pin pro tlačítko enkodéru
int counter = 0; // Počítadlo pro sledování pozice enkodéru
int currentStateCLK; // Aktuální stav hodinového signálu
int lastStateCLK; // Předchozí stav hodinového signálu
String currentDir =""; // Aktuální směr otáčení (CW nebo CCW)
unsigned long lastButtonPress = 0;
void setup() {
Serial.begin(115200); // Inicializace sériové komunikace rychlostí 115200 baudů
pinMode(CLK,INPUT); // Nastavení pinu CLK jako vstup
pinMode(DT,INPUT); // Nastavení pinu DT jako vstup
pinMode(SW, INPUT_PULLUP); // Nastavení pinu SW jako vstup s pull-up rezistorem
lastStateCLK = digitalRead(CLK); // Uložení počátečního stavu hodinového signálu
}
void loop() {
currentStateCLK = digitalRead(CLK); // Čtení aktuálního stavu hodinového signálu
if (currentStateCLK != lastStateCLK && currentStateCLK == 1){ // Detekce změny stavu a náběžné hrany
if (digitalRead(DT) != currentStateCLK) { // Kontrola směru otáčení pomocí porovnání DT a CLK
counter --; // Dekrementace počítadla při otáčení proti směru hodinových ručiček
currentDir ="CCW"; // Nastavení aktuálního směru na CCW (proti směru hodinových ručiček)
} else {
counter ++; // Inkrementace počítadla při otáčení ve směru hodinových ručiček
currentDir ="CW"; // Nastavení aktuálního směru na CW (ve směru hodinových ručiček)
}
Serial.print("Direction: ");
Serial.print(currentDir);
Serial.print(" | Counter: ");
Serial.println(counter);
}
lastStateCLK = currentStateCLK; // Aktualizace předchozího stavu CLK pro příští iteraci
int btnState = digitalRead(SW); // Čtení stavu tlačítka enkodéru
if (btnState == LOW) { // Kontrola, zda je tlačítko stisknuto (LOW při stisku díky INPUT_PULLUP)
if (millis() - lastButtonPress > 50) { // Ošetření zákmitů tlačítka (debouncing) - 50ms prodleva
Serial.println("Button pressed!");
}
lastButtonPress = millis(); // Aktualizace časové značky posledního stisknutí tlačítka
}
delay(1);
}
Zdroj: https://lastminuteengineers.com/rotary-encoder-arduino-tutorial/