Verständnis code zum Einlesen Drehencoder

Hallo zusammen

gerade arbeite ich an einer Schieblehren-Anzeige.
Um einen Korekturwert mittels Drehencoder einzugeben habe ich im Netz nach einem Sketch gesucht un diesen gefunden.
jetzt sind mir die Funktionen der einzelnen Programmteile nicht ganz klar.
Könntet Ihr mir eventuell beim Beschreiben einiger Befehlen (Funktionen) helfen?

Dieser Sketch kann zurzeit nur vorwärts zählen ( Impulseingang Pin 2)
abhängig vom Zustand Pin 3 sollte er rückwärts oder vorwärts zählen.

Erwähnen sollte ich vieleicht noch dass ich den Drehenkoder mittels einer Zusatzschalung entprelle und die Impulse und die Drehrichtung jeweils an einem extra Pin ausgebe und diese zwei Signale an den Ardu-Eingangen einlese.

Wie oben schon beschrieben möchte ich das Programm besser verstehen und es fürs Hoch- Runterzählen umstricken.

#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

#define InPin 2


volatile uint16_t counter;
char pbuf[20];

void setup() {
  pinMode(InPin, INPUT);
  digitalWrite(InPin, HIGH);
 
 
 
  lcd.begin(16, 2);
  lcd.print("Stueckzahl:");
  attachInterrupt(0, count, FALLING );
}

void loop() {
  uint16_t lastcount=0xffff;   [font=Verdana]Dieser Befehl sagt mir zum Beispiel nichts[/font]
  uint8_t sreg;                             [font=Verdana]Dieser auch nichts[/font]
  if (counter != lastcount) {
    sreg = SREG;                             [font=Verdana]was bedeutet Das[/font]
    cli();                                             [font=Verdana]Auch unbekannt[/font]
    lastcount = counter;
    SREG = sreg;
    sprintf(pbuf, "%5u", lastcount); 
    lcd.setCursor(11, 0);
    lcd.print(pbuf);
    delay(100);
  }
  delay(100);
}

void count() {
  counter++;
}

Vielen Dank schonmal Rudi

Hallo,

eine ziemlich gute Erklärung über das Auslesen von Drehimpulsgebern findest Du hier: http://www.mikrocontroller.net/articles/Drehgeber.

Einen fertigen Sketch, der vor- und rückwärts zählt gibt es hier: http://www.elektronik-bastelkeller.de/stec12E07.php.

Gruß, Ralf

uint16_t lastcount=0xffff;

Das einen "Befehl" zu nennen ist etwas hochtrabend. Das deklariert einen unsigned int und setzt ihn auf den Maximalwert.

sreg = SREG;

Speichert den Wert des Status Registers. Da stehen so Dinge drin wie das Carry Bit. Das Zero Bit. Oder das Sign Bit, die bei allen möglichen Operationen ausgewertet werden. Hier geht es aber um das globale Interrupt Enable Bit in Bit 7.

Siehe Datenblatt Seite 10

cli();

Deaktiviert die Interrupts indem das Interrupt Enable Bit gelöscht wird. Steht für "clear interrupts"

Das ist eine gute Idee, da die Zählvariable 16 Bit hat. Auf einem 8 Bit Prozessor hat man daher keinen atomaren Zugriff darauf. Es könnte sein, dass nachdem auslesen des einen Bytes genau an der Stelle ein Interrupt kommt und den Wert wieder inkrementiert. Dann ist es nicht mehr konsistent.

Dieses wird dann hier wieder auf den vorherigen Wert gesetzt:

SREG = sreg;

Das ist die Luxus-Variante von sei() (set interrupts), welches einfach das Interrupt Enable Bit setzt. Da man in diesem Fall eigentlich davon ausgehen kann dass das der Fall war.

Hallo Zusammen

vielen Dank für die tolle Hilfe.
Ich bin auch schon weitergekomen.
So kann ich den Wert den ich mittels Incrementalgeber verstellen will schon auf dem LCD ausgeben und dieser lässt sich auch höher und tiefer verstellen, ausserdem geht er auch ins negative.
Doch zwei Sachen fehlen Mir noch und zwar sollte :

  • Dieser Wert mit zwei Stellen hinter dem Komma dargestellt werden können
  • Die Schrittweite mit der der Wert erhöht wird ,mittels eines Tasters um den Faktor 100 erhöht werden können

alle meine Versuche das mittels anderen Variablen hinzubekommen sind gescheitert. Eventuell kann mir hier jemand helfen.

Vielen Dank im Vorraus Rudi

achso hier noch er Sketch wie ieser von mir umgestrickt woren ist.

#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

float Impuleingang = 2;
const int  Drehrichtung = 3;
int Drehrichtunstatus = 0;


volatile uint16_t counter;
char pbuf[20];

void setup() {
  pinMode(Impuleingang, INPUT);
  digitalWrite(Impuleingang, HIGH);
  
  pinMode(Drehrichtung, INPUT);
  digitalWrite(Drehrichtung, HIGH);
 
 
 
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print("Incrementalgeber");
  lcd.setCursor(0, 1);
  lcd.print("Wert:");
  attachInterrupt(0, count, FALLING );
}

void loop() 
{
  Drehrichtunstatus = digitalRead(Drehrichtung);
  
  int lastcount=0xffff;  
  uint8_t sreg;
  
  if (counter != lastcount) 
  {
    sreg = SREG;              
                               
    cli();                    
    lastcount = counter;
    SREG = sreg;              
                               
    sprintf(pbuf, "%5d", lastcount); 
    lcd.setCursor(11, 1);
    lcd.print(pbuf); 
  }
  delay(100);
}

void count() {
  if (Drehrichtunstatus == 1)
  counter++;
  
   if (Drehrichtunstatus == 0)
  counter--;
}

Rudi01: Doch zwei Sachen fehlen Mir noch und zwar sollte :

  • Dieser Wert mit zwei Stellen hinter dem Komma dargestellt werden können
  • Die Schrittweite mit der der Wert erhöht wird ,mittels eines Tasters um den Faktor 100 erhöht werden können

Mit einer Integer-Variablen kannst Du Werte von ?32768 bis 32767 darstellen.

Werte mit Nachkommastellen kannst Du durch Ganzzahldivision und Divisionsrest darstellen.

int wert,vorkommawert, nachkommawert;
wert=4711;  // diese Variable ist der Zähler
vokommawert= wert/100;
nachkommawert= abs(wert%100);

Wenn Du nun den Vorkommawert, einen Dezimalpunkt und den Nachkommawert 2-stellig (ggf. mit führender Null) auf dem LCD-Display anzeigst, dann ist es eine Anzeige mit zwei Nachkommastellen.

Wertebereich mit int dann von ?327.68 bis 327.67. Falls das nicht reicht, müßte man "long" als Variablentyp nehmen.

Den Zähler läßt Du einfach mit +100 und -100 für "ganze" Stellen laufen und +1 und -1 für "hundertstel" beim Zählen.

Drehgeber sind übrigens extra dafür konstruiert, dass man nicht nur "Impulse" damit auswerten kann, sondern dass man gezielt Rechtsimpulse und Linksimpulse auswerten kann. Eine irgendwie geartete Umschaltung über einen Bedienknopf ist dazu nicht notwendig, sondern der Anschluss der Ausgänge A und B an zwei I/O-Pins.

Hallo jurs

danke für die Hilfe. Die Funktion -> Schrittweite mit der der Wert erhöht wird ,mittels eines Tasters um den Faktor 100 erhöhen , funktioniert.

Das Problem mit der Darstellung der Nachkommastellen funktioniert leider nicht .Seit Stunden sitze ich schon dran und komme einfach nicht weiter. Mein Problem ist dass die Ausgabe nicht mittels einer Variablen funktioniert sondern mittels eines CharArrays -->

char pbuf[20]; 

hier steht natürlih noch anderer Code


sprintf(pbuf, "%5d", lastcount); 
    lcd.setCursor(7, 1);
    lcd.print(pbuf);

Die Zeilen funktionieren mit dem von Dir vorgeschlagenen Programmteilen leider nicht. gibt es eventuell noch eine andere Möglichkeit. Ich sollte den Wert des pbuf auch noch weiterverwenden Dieser muß von einem anderen Wert abgezogen werden.

Grüße Rudi

sprintf() ist nur dazu da die Zahl auf eine konstante Länge zu formatieren. Die Funktion funktioniert auf dem Arduino nicht mit floats, aber mit dem Code oben kannst du genauso eine Ausgabe auf zwei Nachkommastellen formatieren, wenn du die Ziffern vor und nach dem Komma getrennt behandelst.

Oder die verwendest gleich dtostrf(): http://www.mikrocontroller.net/topic/86391 http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#ga060c998e77fb5fc0d3168b3ce8771d42 Damit kann man Floats auf eine feste Breite formatieren. Wie in dem ersten Link beschrieben, brauchst du als Parameter die Länge der gesamten Zahl (inklusive Vorzeichen und Punkt) und die Anzahl der Nachkommastellen.

Rudi01: Die Zeilen funktionieren mit dem von Dir vorgeschlagenen Programmteilen leider nicht. gibt es eventuell noch eine andere Möglichkeit. Ich sollte den Wert des pbuf auch noch weiterverwenden Dieser muß von einem anderen Wert abgezogen werden.

Die von mir vorgeschlagene Formatierung mit Vor- und Nachkommastellen funktioniert so, allerdings ist der Code nur für positive Zahlen gut brauchbar:

void formatLong(unsigned long zahl) // Vorsicht, nur für positive Zahlen!
{
  char buf[20];
  snprintf(buf,sizeof(buf),"%6ld.%02ld",zahl/100,zahl%100);
  Serial.println(buf);
}

Um negative Zahlen auf dieselbe Art korrekt zu formatieren, ist doch etwas mehr Aufwand nötig, als ich zunächst dachte (das eine "abs" reicht leider nicht). Deshalb verwendest Du für negative und positive Zahlen am besten die Methode von Serenifly.

Den long-Wert per Division durch 100.0 in einen Gleitkommawert umwandeln und als Gleitkommazahl mit dtostrf formatieren, das funktioniert dann auch für negative Zahlen problemlos (bis 7-stellig brauchbar, also nicht für den gesamten long-Bereich):

void formatLong(long zahl) // Für positive und negative Zahlen, 
{  // Vorsicht:  eingeschränkter long-Wertebereich!
  char buf[20];
  dtostrf (zahl/100.0, 10, 2, buf);
  Serial.println(buf);
}

Nachtrag: Ich sehe bei Dir oben Dinge wie: "Erwähnen sollte ich vieleicht noch dass ich den Drehenkoder mittels einer Zusatzschalung entprelle" und:

 attachInterrupt(0, count, FALLING );

So macht man keine zuverlässige Drehgeberauswertung! Besser funktioniert es so (und ohne "Zusatzschaltung"): Entweder entprellst Du mechanisches Prellen von Drehgebern zeitlich (wie auch bei Tastschaltern/Buttons) und wertest per Timer-Interrupt aus. Oder wenn die Loop keine expliziten und impliziten Delay-Zeiten enthält und stets in höchstens zwei Millisekunden abgearbeitet wird, kommt sogar eine Auswertung in der loop-Funktion komplett ohne Interrupts in Frage. Die (zugegebenermaßen schon oft gesehene) Auswertung von Drehgebern mit Hardware-Interrupts oder PinChange-Interrupts ist so ziemlich die schlechteste aller Möglichkeiten, um Drehgeber auszuwerten.