Forum: Mikrocontroller und Digitale Elektronik Drehencoder auswerten ohne Timer


von Michael J. (jmibk)


Lesenswert?

Hallo,

es gibt zwar einige Themen bezüglich Drehencoder, aber ich mach noch 
einen ;-). Nein, ich habe keine Antwort auf die Frage gefunden:

Es geht darum, einen Drehencoder (den Pana von Pollin) an einem 
ATMEGA8515 auszuwerten.

Das Ding hängt an PD6 und PD7, der gemeinsame Anschluss auf Masse. 
Pullups sind aktiv, das klappt alles.
Ich hab ein SPeicherregister (encoder_state), welches den vorherigen 
Zustand speichern kann und 3 Temp Register (temp1, temp2, temp3) für den 
Fall zur verfügung.

Prroblem an der Sache: Ich kann weder die Pins am µC ändern (auf einen 
IRQ Pin legen oder sowas) noch einen Timer verwenden, da alle beiden 
Timer bereits vergeben sind.

Ich hab jetzt immer versucht, nachdem ich "11" oder "00" eingelesen 
habe, im nächsten Schritt zu schauen, welcher der beiden Pins ist als 
erstes gewechselt, dann gehts Zeittechnisch einfacher, da die 
Drehencodergeschichte nur ca 10x die Sekunde aufgerufen wird.
Der Zustand, in der Rasterung muss eigentlich immer 11 oder 00 sein, 
leider ist das hier nicht immer so.

Hat jemand einen Code, oder zumindest anregungen, wie man das ohne 
Hilfsmittel mit dem bloßen 2 Pins ohne Timer und IRQs lösen kann, eine 
links und rechtsdrehung zu unterscheiden und ein Unterprogramm 
aufzurufen?

Wenns geht kein C Code.

von Karl H. (kbuchegg)


Lesenswert?

Michael Juen schrieb:

> Prroblem an der Sache: Ich kann weder die Pins am µC ändern (auf einen
> IRQ Pin legen oder sowas) noch einen Timer verwenden, da alle beiden
> Timer bereits vergeben sind.

Darf ich fragen womit?

Meistens kann man auch mehrere Funktionalitäten in eine Timer-ISR legen.

von MaWin (Gast)


Lesenswert?

Du fragst jetzt nicht wirklich, wie man Drehencoder auswertet?

Was bitteschön gefällt dir an den ungefähr 10000 Beschreibungen wie man 
Drehencoder auswertet nicht ?

http://dse-faq.elektronik-kompendium.de/dse-faq.htm#F.29

Nein, man braucht keinen Timer, man muss nur nicht zu schnell (sinnlos) 
und nicht zu langsam (man verliert Schritte) abfragen.

Und geh nicht wie im psychotischen Zwang gerade den Weg, der nicht zum 
Erfolg führt, nämlich den Weg der Flankenerkennung.

von Gast123 (Gast)


Lesenswert?

Pins im Hauptprogramm in einer Endlosschleife pollen, um die Auswertung 
vorzunehmen. Dann brauchst du keinen Timer.

von Andreas K. (derandi)


Lesenswert?

Timer bräuchte er wenn er die Drehgeschwindigkeit erfassen will, z.B. 
für schnelles Scrollen (wenn man schneller dreht werden pro realen 
Schritt mehrere in Software gemacht).

von Bernhard M. (boregard)


Lesenswert?

Michael Juen schrieb:
> erstes gewechselt, dann gehts Zeittechnisch einfacher, da die
> Drehencodergeschichte nur ca 10x die Sekunde aufgerufen wird.
> Der Zustand, in der Rasterung muss eigentlich immer 11 oder 00 sein,

Das ist definitiv zu wenig, gerade weil Du die Werte zwischen zwei 
Raststellungen sicher erkennen must, um die Drehrichtung zu erkennen.
100x pro Sekunde ist meiner Meinung nach immer noch zuwenig, wenn man 
halbwegs schnell drehen will.
Ich verwende 1000x pro Sekunde...

von Hannes L. (hannes)


Lesenswert?

Ich mach`s in einem Job der Mainloop, der 1000 mal pro Sekunde 
aufgerufen wird, natürlich von einem Timer synchronisiert, der nebenher 
noch andere Dinge erledigt.

...

von A.Drei (allu) (Gast)


Lesenswert?

Falls Du Bascom verwendest, probier mal:

Dim Flag_dreh_flanke As Bit
Dim Dreh_impulse As Integer
Set Portd.6                 ' Pullups einschalten
Set Portd.7
Config Portd.6 = Input      ' Drehgeber
Config Portd.7 = Input

Do

' Drehcoder auswerten und dann die Impulse zaehlen
' ... muss möglichst oft durchlaufen werden

     If Flag_dreh_flanke = 0 Then

         If Pind.6 = 1 Then
            Set Flag_dreh_flanke

            If Pind.7 = 1 Then
               Incr Dreh_impulse
            Else
               Decr Dreh_impulse
            End If
         End If


       Else
            If Pind.6 = 0 Then
               Reset Flag_dreh_flanke

               If Pind.7 = 0 Then
               Incr Dreh_impulse
            Else
               Decr Dreh_impulse
            End If

            End If
      End If

' und alle anderen Programmteile
loop

Gruß   allu

von Jörg H. (idc-dragon)


Lesenswert?

Hallo,

Mit Bascom kann ich nicht dienen, aber mit rein interruptgesteuerter 
Verarbeitung. Ich persönlich finde das mit dem Timer-Polling auch nicht 
so doll, oft versuche ich den uC so gut es geht schlafen zu lassen.

Zugegeben hat man beim Prellen des Kontaktes einen Haufen "unnütze" 
Interrupts, aber halt auch nur wenn jemand dran dreht und nicht ständig.

Bei der Auswertung ist der "Trick" gegen Prellen, dass man eine 
State-Machine mitführen muß. Flanken auf einem Kontakt erzeugen dann 
noch keine Schritte, sondern erst wenn ein Zyklus komplett ist. Oft 
sieht man die Primitivimplementierung: Bei Flanke an dem einen Kontakt 
nimm den anderen für die Richtung. Das funktioniert nicht zuverlässig!

Den Code hier habe ich mit dem dem Panasonic-Encoder von Pollin 
erfolgreich im Einsatz. Ist aus dem Forum abgeguckt. Das Message-Senden 
an die Hauptschleife mag natürlich bei euch anders aussehen.

Es gibt auch andere Encoder, mit Rastung nach 360° der Pulsfolge, nicht 
nach 180°. Dann wird es komplizierter. Habe ich auch, aber ist nicht so 
elegant.
1
#include <stdint.h>
2
#include <avr/io.h> // for device register definitions
3
#include <avr/interrupt.h> // for interrupt handlers
4
#include <util/delay.h> // for delay loops
5
#include "msg.h" // messages waking up the main loop
6
#include "rotary.h" // own interface
7
8
// define how the encoder is connected (Port B is hard coded)
9
#define ENC_A PB4
10
#define ENC_B PB1
11
12
static uint8_t rotary_status;
13
static uint8_t rotary_step;
14
15
16
void rotary_init(void)
17
{
18
  PORTB |= _BV(ENC_B) | _BV(ENC_A); // enable pullups
19
20
  _delay_us(10); // let the lines settle before reading the current status
21
22
  rotary_status = rotary_step = PINB & (_BV(ENC_A) | _BV(ENC_B));
23
24
  PCMSK0 |= _BV(PCINT1) | _BV(PCINT4); // enable the line for pin change interrupt
25
  PCICR |= _BV(PCIE0); // enable pin change interrupt 0, for port B
26
}
27
28
29
SIGNAL(SIG_PIN_CHANGE0)
30
{
31
  uint8_t status_cur;
32
33
  status_cur = PINB & (_BV(ENC_A) | _BV(ENC_B)); // read the change
34
  if ((status_cur ^ rotary_step) == (_BV(ENC_A) | _BV(ENC_B))) // both lines have changed?
35
  {  // full gray code cycle is done
36
    struct msg message; // message for result
37
    message.id = e_rotary;
38
39
    if ((status_cur ^ rotary_status) == _BV(ENC_A)) // contact A
40
    {  // turn towards the right
41
      message.bdata = 1;
42
    }
43
    else
44
    {  // turn towards the left
45
      message.bdata = -1;
46
    }
47
    msg_post(&message); // send it
48
49
    rotary_step = status_cur; // remember stable state
50
  }
51
  rotary_status = status_cur; // remember any state
52
}

Gruß
Jörg

von Marcus S. (mstangl)


Angehängte Dateien:

Lesenswert?

Hallo,
hier noch eine Version in Assembler. Der Aufruf der Routine erfolgt alle 
277µs. Die Rückgabewerte für L/R liegen in den Variablen ,countRIGHT' 
und
,countLEFT'.

Gruß
Marcus

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.