Een rotary encoder uitlezen met een PIC
Een tijdje geleden ben ik gaan kijken hoe ik een rotary encoder kan gebruiken. Een rotary encoder, ook wel rotary decoder of draaipulsgever genoemd, ziet er een beetje uit als een potmeter. Het verschil is dat een potmeter een begin en eindpunt heeft, terwijl je een rotary encoder oneindig kan ronddraaien in beide richting. Alleen verandert hierbij geen weerstandswaarde, maar hij stuurt pulsjes over 2 lijnen (A en B). Door de kijken welke puls eerst komt kan je zien of er linksom en rechtsom gedraaid wordt. In de code van de PIC, AVR, Arduino of iets anders kan je dan bepalen waar er met dat gegeven gedaan moet worden. Dat is simpel gezegd wat ik hier zal bespreken.
De aansluitingen
Ik ben aan de slag gegaan met de goedkoopste rotary encoder met ingebouwde schakelaar (behalve draaien kan de knop ook ingedrukt worden als drukknop) die ik zo snel kon vinden. Het is de hier links afgebeelde encoder geworden, gekocht bij Voti.nl.
De 3 pootjes aan de voorkant is waar het om gaat. In principe is de middelste altijd de common, deze leg je normaalgesproken aan massa. Links en rechts ervan zijn de contacten A en B. Deze mag je verwisselen, technisch gezien zijn ze gelijk. Je zal dan
alleen de andere kant op moeten draaien, maar dit is ook in de software makkelijk om te draaien uiteraard. De 2 pinnen aan de overzijde zijn van de maak schakelaar. Deze ga ik vandaag niet gebruiken.
Verder is het nog belangrijk om aan de lijnen A en B twee pull-up weerstanden van 10k (dit komt niet precies) te plaatsen om te zorgen dat de lijnen hoog worden als de inwendige schakelaars die de pulsen verzorgen open staan. Er zijn overigens ook rotary encoders/draai puls gevers die optische sensors hebben ipv mechanische contacten. Deze zijn over het algemeen wel duurder, en hebben een 4e aansluiting voor een voedingsspanning. Verder zijn er nog verschillen in 'detent' en normaal. Detent wil zeggen dat je deukjes voelt tijdens het draaien om de stapjes voelbaar te maken. De normale voelt tijdens het draaien hetzelfde als wanneer je een potmeter verdraait.
Het signaal
Wat komt er nu eigenlijk uit zo'n rotary encoder? Zowel pin A als pin B zal een blokgolf geven als er gedraaid wordt. Door te kijken welke van de pinnen als eerst hoog wordt kan je zien of de encoder linksom of rechtsom gedraaid wordt.
Laten we eens naar onderstaande afbeelding kijken. Tijdens het draaien herhaald het steeds 4 stappen. Mijn decoder is detent zoals gezegd. Stap 1 is daarbij altijd een rustpositie, waarbij zowel A als B allebei 1 zijn. Draai ik nu met de klok mee dan kom ik bij stap 2. Ik zie dan dat A=0 en B=1. Had ik nu tegen de klok in gedraaid dan kan je in de tekening aflezen dat A=1 en B=0.
Nu de software
De pinnen A en B verbind in aan 2 ingangen van de PIC. Om de decoder betrouwbaar uit te lezen. Zorg er voor dat bij 1 van de pinnen ook een IOC (Interrupt On Change) aanwezig is. In mijn geval heb ik voor pin A gekozen, ik had ook B kunnen kiezen, dat maakt niet uit. Voor de rest van dit verhaal blijven we even van pin A uitgaan. Laten we eerst de pinnen definiëren met meer bruikbare namen en definieer een variabele die we nodig hebben.
1 2 3 4 56
//************* PINS ************************* sbit RotA at RA4_bit; sbit RotB at RA3_bit; //************* VARIABLES ********************unsigned short OldRotA;
Vergeet ook niet tijdens de init te zorgen dat IOC ingeschakeld is en voor de juiste ingang van de PIC.
7 8 9 GIE_bit = 1; // Enable interrups RABIE_bit = 1; // IOC enabled IOCA4_bit = 1; // IOC at RA4 (RotA)
Oké, we weten nu dat in rust A=1 en B=1. Om de code simpel te houden kijk ik alleen wanneer wanneer A omlaag gaat. Je kan in de tekening zien dat vanuit rust A op 2 momenten naar 0 gaat. Dat is in stap 2 bij clockwise, of in stap 3 bij anti-clockwise. Het verschil is dat in stap 2, B nog hoog is, maar in stap 3 is hij laag.
Kort gezegd, als A=0 en B=0 dan is er tegen de klok in gedraaid (anti clockwise).
Maar als A=0 en B=1 dan is er met de klok mee gedraaid (clockwise).
Dit is vrij eenvoudig in code om te zetten. In mijn geval gebruik ik de decoder om de waarde van de variable Voltage de verhogen of te verlagen. Om overflow te voorkomen kan Voltage niet lager worden dan 0 en niet hoger dan 720. In de MikroC code ziet dit er zo uit:
10 11 12 13 1415 16 17 18 1920 21 22 //******************** FUNCTIONS ************* void Interrupt(){ if (RABIF_bit){ // Only do something after falling edge if(OldRotA && RotA==0) { if(RotB && Voltage > 0) Voltage--; else if (!RotB && Voltage < 720) Voltage++; } OldRotA = RotA; RABIF_bit=0; } } //*******************************************
Misschien dat if(OldRotA && RotA==0) nog wat uitleg nodig heeft. Een IOC reageert op iedere verandering van de pin. Dus als ingang RotA van 1 naar 0, of van 0 naar 1 gaat zal er een interrupt komen. Ik ben alleen maar geinteresseerd in de overgang van 1 naar 0. Vandaar dat ik kijk of RotA eerst 1 was (OldRotA==1) en nu 0 is (RotA==0). Is dat het geval dan kijk ik naar RotB en of het minimum of maximum nog niet bereikt is. Aan de hand van het resultaat daarvan wordt Voltage verlaagt of verhoogt.
Tenslotte wordt de status van RotA bewaard in OldRotA voor de volgende keer.
Zo simpel kan het zijn. Ik gebruik deze code al tijden en het werkt perfect. Een volgende keer zal ik een iets uitgebreidere versie van de code bespreken. Hierbij is de functie meer universeel geworden en kan voor verschillende variabele gebruikt worden met verschillende maximale en minimale waardes. Maar dit een andere keer 🙂