Verwendung eines inkrementellen Drehgebers mit einem Arduino Micro
(EN google-translate)
(PL google-translate)
Hardware
|

|
|
Ordnen Sie die Kabel auch bei der Klemmverbindung in dieser Reihenfolge an.
Verwenden Sie für die Weiterleitung möglichst die gleichen Kabelfarben.
|

Bild 0-1: Anordnung NPN-Transistor mit Pullup für Signal A und phasenverschogenes Signal B.
Test
Zum einfachen Testen wird der Drehgeber mit Strom versorgt und dessen Ausgänge werden mit digitalen Eingängen beim Arduino verbunden, bei denen die Pullup-Widerstände aktiviert sind.
|
Es handelt sich um die gleichen Anschlüsse, wie bei I2C, weshalb beispielsweise der MPU6050 so nicht gleichzeitig betrieben werden kann.
|
Bevor nicht die Schaltung fertig aufgebaut und das Programm übertragen wurde, sollte der Drehgeber nicht mit Spannung versorgt werden (ROT gekappt).

Bild 0-2: Testaufbau.

Bild 0-3: Stromlaufplan des Testaufbaus.
void setup() { DDRD&=~((1<<PD0)|(1<<PD1)); //PD0 und PD1 Input PORTD|=((1<<PD0)|(1<<PD1)); //PD0 und PD1 interne Pullup-Widerstände aktiviert. DDRD|=((1<<PD6)|(1<<PD7)); //PD6==DIO12...A anzeigen, PD7==DIO6...B anzeigen PORTD&=~((1<<PD6)|(1<<PD7));//beide aus } void loop() { //Wenn A LOW => PD6 HIGH, sonst PD6 LOW if((PIND&(1<<PD0))==0)//DOPPELTE KLAMMERUNG NOTWENDIG!!!! { PORTD|=(1<<PD6); } else { PORTD&=~(1<<PD6); } //Wenn B LOW => PD7 HIGH, sonst PD7 LOW if((PIND&(1<<PD1))==0)//DOPPELTE KLAMMERUNG NOTWENDIG!!!! { PORTD|=(1<<PD7); } else { PORTD&=~(1<<PD7); } }
Code 0-1: Drehgeber001: Testansteuerung mit normalen Digitalen Eingängen.
Beobachtung bei Draufsicht auf die Achse: A...PD0...GRÜN B...PD1...ROT Drehen gegen Uhrzeigersinn 1==LED AN, PD0/1 LOW, 0==LED AUS, PD0/1 HIGH: AB 00 01 11 10 Drehen im Uhrzeigersinn: AB 00 10 11 01
Code 0-2: Protokoll Testlauf.
Nun soll die Phaseninformation benutzt werden, um Drehrichtungen erfassen zu können. Man kann sich die dazu nötigen Bedingungen für einen Zähler Z zunächst verbal formulieren:
|
Der Zählerstand wird an über die serielle Schnittstelle an den PC gesendet, so, dass man sich ihn im Seriellen Monitor der Arduino IDE ansehen kann:
bool AB_LOW=false; int Z=0; int ZALT=0; unsigned char IN=0; void setup() { DDRD&=~((1<<PD0)|(1<<PD1)); //PD0 und PD1 Input PORTD|=((1<<PD0)|(1<<PD1)); //PD0 und PD1 interne Pullup-Widerstände aktiviert. DDRD|=((1<<PD6)|(1<<PD7)); //PD6==DIO12...A anzeigen, PD7==DIO6...B anzeigen PORTD&=~((1<<PD6)|(1<<PD7));//beide aus Serial.begin(115200);//ACHTUNG: Höhere Baudrate als Default! } void loop() { IN=PIND&0b00000011; if( IN==0 ) { AB_LOW=true; } else if( IN==1 && AB_LOW) //A HIGH { AB_LOW=false; Z--; } else if( IN==2 && AB_LOW) //B HIGH { AB_LOW=false; Z++; } if(Z!=ZALT) { Serial.println(Z); ZALT=Z; } //Wenn A LOW => PD6 HIGH, sonst PD6 LOW if((IN&0b00000001)==0)//DOPPELTE KLAMMERUNG NOTWENDIG!!!! { PORTD|=(1<<PD6); } else { PORTD&=~(1<<PD6); } //Wenn B LOW => PD7 HIGH, sonst PD7 LOW if((IN&0b00000010)==0)//DOPPELTE KLAMMERUNG NOTWENDIG!!!! { PORTD|=(1<<PD7); } else { PORTD&=~(1<<PD7); } }
Code 0-3: Drehgeber002: Um einen Zähler erweitertes Programm.
Verwendung mit Interrupts
In der jetzigen Form des Programms kann nicht viel mehr als die Drehgebererfassung mit dem Mikrocontroller umgesetzt werden.
Mit jeder Codezeile mehr, die in dem Loop-Bereich hinzu kommt, muss man befürchten, dass ein Durchlauf so langsam wird, dass eine Pegeländerung bei A und B "verpasst" wird und die Drehgebererfassung nicht mehr richtig arbeitet.
Um einerseits die Ressourcen des loop-Bereichs wieder frei zu bekommen und andererseits sicher zu gehen, dass kein Pegelwechsel bei A oder B verpasst wird, können die Anschlüsse bei PD0 und PD1 so konfiguriert werden, dass sie als externe Interrupt-Eingänge funktionieren und jeweils die Ausführung einer Interrupt-Service-Routine ausgelöst wird.
Eine Interrupt-Service-Routine ist eine kleine Funktion im Quelltext, die genau dann abgearbeitet wird, wenn ein definierter Pegelwechsel auf einem definierten Eingang stattfindet. Der Hauptloop wird für die Dauer der Ausführung unterbrochen und unmittelbar danach wieder fortgesetzt. Dazu werden die Arbeitsregister und der Programmzeiger gemerkt und bei Fortsetzen von loop() wieder eingelesen, so, dass das Programm genau an der Stelle fortgesetzt wird, wo es unterbrochen wurde.
Interrupts mit Arduino-Befehlen
|
|
Als Möglichkeiten für den Typ der Änderung stehen zur Verfügung: LOW, CHANGE, RISING, FALLING, HIGH.

bool AB_LOW=false; int Z=0; int ZALT=0; unsigned char IN=0; void fallendeFlankeBeiA() { if((PIND&0b00000011)>0) Z--; } void fallendeFlankeBeiB() { if((PIND&0b00000011)>0) Z++; } void setup() { DDRD&=~((1<<PD0)|(1<<PD1)); //PD0 und PD1 Input PORTD|=((1<<PD0)|(1<<PD1)); //PD0 und PD1 interne Pullup-Widerstände aktiviert. DDRD|=((1<<PD6)|(1<<PD7)); //PD6==DIO12...A anzeigen, PD7==DIO6...B anzeigen PORTD&=~((1<<PD6)|(1<<PD7));//beide aus Serial.begin(115200); cli();//Verbiete zunächst Interrupts attachInterrupt(0, fallendeFlankeBeiA, FALLING); attachInterrupt(1, fallendeFlankeBeiB, FALLING); sei();//Erlaube Interrupts } void loop() { IN=PIND&0b00000011; if(Z!=ZALT) { Serial.println(Z); ZALT=Z; } //Wenn A LOW => PD6 HIGH, sonst PD6 LOW if((IN&0b00000001)==0) { PORTD|=(1<<PD6); } else { PORTD&=~(1<<PD6); } //Wenn B LOW => PD7 HIGH, sonst PD7 LOW if((IN&0b00000010)==0) { PORTD|=(1<<PD7); } else { PORTD&=~(1<<PD7); } }
Code 0-4: Drehgeber003: Verwendung von Interrupts mittels Arduinio-Befehlen.
Nun kann die loop-Methode wieder für andere Aufgaben verwendet werden.
Interrupts mit Register-Befehlen
Das vorangehende Programm soll nun so modifiziert werden, dass die Interrupt-Konfiguration mittels Register-Befehlen läuft und die nativen ISR (Interrupt-Service-Routinen) verwendet werden:
#include <avr/interrupt.h> bool AB_LOW=false; int Z=0; int ZALT=0; unsigned char IN=0; ISR(INT0_vect) { if((PIND&0b00000011)>0) Z--; } ISR(INT1_vect) { if((PIND&0b00000011)>0) Z++; } void setup() { DDRD&=~((1<<PD0)|(1<<PD1)); //PD0 und PD1 Input PORTD|=((1<<PD0)|(1<<PD1)); //PD0 und PD1 interne Pullup-Widerstände aktiviert. DDRD|=((1<<PD6)|(1<<PD7)); //PD6==DIO12...A anzeigen, PD7==DIO6...B anzeigen PORTD&=~((1<<PD6)|(1<<PD7));//beide aus Serial.begin(115200); cli();//Verbiete zunächst Interrupts //Falling Edge => Interrupt: 10 bei ISCn1 und ISCn0, siehe Datenblatt S. 89: EICRA=( (1<<ISC31)|(0<<ISC30)|(1<<ISC21)|(0<<ISC20)|(1<<ISC11)|(0<<ISC10)|(1<<ISC01)|(0<<ISC00) ); //External Interrupt Request 6, 3 - 0 Enable, S.90: EIMSK=( (1<<INT1)|(1<<INT0) ); sei();//Erlaube Interrupts } void loop() { IN=PIND&0b00000011; if(Z!=ZALT) { Serial.println(Z); ZALT=Z; } //Wenn A LOW => PD6 HIGH, sonst PD6 LOW if((IN&0b00000001)==0) { PORTD|=(1<<PD6); } else { PORTD&=~(1<<PD6); } //Wenn B LOW => PD7 HIGH, sonst PD7 LOW if((IN&0b00000010)==0)//DOPPELTE KLAMMERUNG NOTWENDIG!!!! { PORTD|=(1<<PD7); } else { PORTD&=~(1<<PD7); } }
Code 0-5: Drehgeber004: Interrupts mittels Register-Befehlen aktivieren und verwenden.

ÜBUNGEN
Aufgabe 1
|
Aufgabe 2
|
Aufgabe 3
|