Forum: Projekte & Code Drehgeber auslesen


von Chrislight (Gast)


Lesenswert?

Hallo Zusammen...
Ich benuzte für ein kleines Projekt den im 2ten Beitrag vorgestellten 
Code um einen Drehgeber auszulesen... Nun habe ich die Frage, wie ich 
die Zahl in enc_delta begrenzen kann? Ich will diese auf meinen PWM 
Ausgang geben. die enc_delta variable soll nur von 0 bis 1000 zählen 
können. Wenn ich das ganze mit einer if abfrage im Hauptprogramm (nicht 
in der Interrupt Routine) mache...
1
if (enc_delta >= 1000)
2
{
3
     enc_delta=1000;
4
}

funktioniert es jedoch wenn ich...
1
if (enc_delta <=0)
2
{
3
     enc_delta=0;
4
}
habe ich das Problem, dass meine Variable nie kleiner als 0 wird. Diese 
geht danach wieder auf 65536 (16Bit Zahl) weil diese Zahl grösser als 
1000 ist geht er mir in die erste if abfrage was kann ich dagegen tun? 
mittels abspeichern der Zahl in einen OLD_Value hatte ich auch keine 
erfolge, da ich ja den Timerinterrupt verwende kann es aber irgendwie 
vorkommen, dass er mir einzelne punkte überspringt... von 1 gerade auf 3 
und nicht auf 2...

Hoffe es kann mir jemand helfen... Chrislight

von Chrislight (Gast)


Lesenswert?

Das Problem für mich ist, dass die Abfrage ja im Timer Interrupt 
passiert... Somit weis ich nie wann die Abfrage durchgeführt wird...

von Michael K. (Gast)


Lesenswert?

Ich denke, dass es am besten ist wenn du diese Abfrage auch in der ISR 
machst. Dort ändert sich der Wert immer nur um +/- 1 und deswegen kannst 
du auf x==1000+1 und x==0-1 abfragen.

Wenn du die Abfrage in der main machst, kann es sein, dass du auf 1000 
stehst, um 2 erhöhst, diesen Wert (1002) für deine PWM verwendest und 
anschließend erst die Abfrage (x>1000) und ggf. Korrektur erfolgt.


Oder du lässt die Abfrage in der main, "denkst dir eine Grenze" und 
fragst auf diese ab, z.B. 65535-1000=64535; 64535/2=32267; 
32267+1000=33267.
Dann die Abfrage ob
x>1000  &&  x<33267     => 1000
und
x>=33267                => 0

Wobei: x=enc_delta

Auf die letzte Weise kann allerdings der im 2. Absatz beschriebene Fall 
eintreten.

von Boris H. (eddi)


Lesenswert?

Hallo Chrislight

Bei mir sieht das Ganze so aus:
1
ISR (TIMER0_OVF_vect)
2
{
3
  static uint8_t last_state = 0,last_cnt = 0;
4
  uint8_t new_state;
5
6
  new_state=PIND & (_BV(PIND4) | _BV(PIND3));
7
  if ((new_state^last_cnt)==(_BV(PIND4) | _BV(PIND3)) )
8
  {
9
       if ((new_state ^ last_state)==_BV(PIND4))
10
          {  
11
      if(enc_delta < 100) enc_delta++;
12
    }
13
    else
14
    {
15
      if (enc_delta > 0) enc_delta--;
16
    }
17
        last_cnt=new_state;
18
  }
19
  last_state=new_state;
20
//
21
//  PWM
22
//
23
  if (pwm_counter++ > 100) 
24
  {
25
    PORTA = 0x00;
26
    pwm_counter = 0;
27
  }
28
}

Ich zähle dabei von 0 bis 100. enc_delta ist folgendermassen deklariert:

volatile uint8_t   enc_delta=0;

Gruß
Boris

von Chrislight (Gast)


Lesenswert?

Ey Super! Die oben erwähnte Antwort von Michael K. hat mir sehr 
geholfen! So funktionietrt es Bestens und ich habe auch keine grösseren 
Probleme, da ich die Variable enc_delta immer auf den höchswert (1000) 
oder auf null setze bei der jeweiligen abfrage. Besten Dank Chrislight

von Markus B. (wolframator)


Lesenswert?

Ich habs so gelöst bei meinem optischen Drehgeber:
(PEC11 von Bourns)
1
  while (1)
2
  {
3
    if(((PINA & 0x01) == 0) && ((PINA & 0x02) == 0)) { j=1; }
4
    if(((PINA & 0x01) == 0) && ((PINA & 0x02) != 0)) { j=2; }
5
    if(((PINA & 0x01) != 0) && ((PINA & 0x02) != 0)) { j=3; }
6
    if(((PINA & 0x01) != 0) && ((PINA & 0x02) == 0)) { j=0; }
7
8
    if(j != last)
9
    {
10
      if((j+1) % 4 == last) i--;
11
      if((last+1) % 4 == j) i++;
12
      last=j;
13
    }
14
15
    if(i>=5)
16
    {
17
      i=i-5;
18
      //Drehrichtung 1
19
    }
20
    if(i<=-5)
21
    {
22
      i=i+5;
23
      //Drehrichtung 2
24
    }
25
  }


/Edit:
Bevor jemand meckert... Natürlich kann man die Pin-Abfrage auch 
kompakter gestalten, aber so denke ich sieht selbst ein Anfänger recht 
gut was passiert :)

von Nils (Gast)


Lesenswert?

Hallo,

ich scheine irgendwie auf dem Schlauch zu stehen. Ich habe den Code aus 
dem 2. Posting in die Timer0 Interrupt Routine eingefügt und verwende 
folgenden Drehencoder

http://www3.alps.com/WebObjects/catalog.woa/E/HTML/Switch/Encoder/EC11/EC11E18244AU.html

Es scheint das der Encoder nur jede 2. Rastung zählt.

Jetzt die Frage ... ich habe das so verstanden, das in der variable 
enc_delta der Wert +1 oder -1 steht richtig? Ich habe dann in meinem 
Hauptprogramm einfach die Variable die ich hoch oder runterzählen will 
mit der Variable enc_delta addiert (Y += enc_delta;). Doch leider 
funktioniert das nicht. Es wird nicht gezählt. Habe ich da was falsch 
verstanden?

Gruss
Nils

von Michael K. (Gast)


Lesenswert?

> Habe ich da was falsch verstanden?

Ja hast du. In enc_delta steht dein Wert (-128 ... 127). Du musst nichts 
mehr rechnen, sondern kannst enc_delta so verwenden.

Lass doch mal das Beispiel-Programm so laufen wie es ist. Dann wird dir 
auf Port B der aktuelle Wert ausgegeben. Und dieser sollte erniedrigt 
oder erhöht werden, je nachdem in welche Richtung du drehst.

von Nils (Gast)


Lesenswert?

Vielen Dank, das war der entscheidende Hinweis. Jetzt zählt er auf und 
ab. Das Problem ist jetzt noch das er mit jeder Rastung 2 hoch oder 
runterzählt. Gibts da auch noch einen kleinen Tipp?

von Nils (Gast)


Lesenswert?

Ach so ... ein Problem war auch noch das ich die internen Pullups 
benutzt hatte. Erst als ich extern Pullups angeschlossen hatte und die 
internen deaktiviert habe funktionierte es. Den Common habe ich auf GND 
gelegt. Funktioniert das bei euch mit den internen Pullups und dem Code 
von Peter Danegger?

von Nils (Gast)


Lesenswert?

OK, ich habe die Lösung in einem anderen Beitrag gefunden. Da wurde es 
so gelöst:

1
ISR(TIMER0_OVF_vect)
2
{
3
  static char enc_last = 0x01;
4
    char i = 0;
5
6
    if( PHASE_A )
7
      i = 1;
8
9
    if( PHASE_B )
10
      i ^= 3;                // convert gray to binary
11
12
    i -= enc_last;            // difference new - last
13
14
    if( i & 1 )            // bit 0 = value (1)
15
  {      
16
      enc_last += i;          // store new as next last
17
18
      enc_delta += (i & 2) - 1;    // bit 1 = direction (+/-)
19
20
      if (!(enc_delta % 2))     // nur jeden 2. Schritt zählen
21
    {  
22
        if ((i & 2))       // prüfen ob auf oder ab
23
      {    
24
          ms++;        // Variable die hochgezählt werden soll
25
        }
26
        else 
27
      {
28
              ms--;     // Variable die runtergezählt werden soll     
29
        }
30
      }
31
    }
32
}

Eine Frage bleibt jetzt noch ... warum brauche ich externe Pullups damit 
es läuft. Hängt es mit PORTA zusammen?

Gruss

von M. K. (kichi)


Lesenswert?

Es sollte egtl. auch mit den internen funktionieren. Wobei ich den Code 
allerdings noch nicht auf einem AVR laufen hatte.

Bei meinen derzeitigen Encodern werden 2,2k-Pullups empfohlen und damit 
klappt es. Vielleicht sind die internen zu hochohmig...

von martin (Gast)


Lesenswert?

Hallo,

die manuelle Eingabe von größeren Werten ist mit groben Encodern ja 
etwas mühselig. Hat sich da schonmal jemand Gedanken zu einer 
"Beschleunigung" gemacht? Also z.B. dass die Schrittweite proportional 
zur Zeit zwischen zwei Impulsen ist?

von M. K. (kichi)


Lesenswert?

Ich habs mal mittels dem (im Drehgeber integrierten) Taster zwischen 1, 
10 und 100 umschaltbar gemacht. Klappt ganz gut und lässt sich auch gut 
bedienen, finde ich.

von nichts (Gast)


Lesenswert?

Ich hatte das mal ganz einfach mit einer Potenzierung gemacht...das 
klappte sehr gut:
Beim langsamen Drehen konnte man die Werte genau einstellen, wenn man 
schnell drehte, kam man schnell in die richtige Region...

Das war aber vom Programmierstil her eine ziemlich unschöne Lösung.

von martin (Gast)


Lesenswert?

Sowas lässt sich bestimmt in P.D.s Code mit ein, zwei zusätzlichen 
Variablen und ein bisschen Bit-Trickserei integrieren...? ;-)

von Igor M. (bastel-wastel)


Lesenswert?

Hab da auch mal ne Frage:

enc_delta gibt mir Zahlen zwischen -128 ... 127 aus. Wenn ich mir diese 
Variable über itoa(); umwandle und am LCD ausgebe, bekomme ich Werte von 
0..127. Negative Zahlen kann die Funktion wahrscheinlich nicht - egal.
Ich bräuchte jetzt aber inkrementale Werte.

Habs zum Testen so versucht

unsigned char Zaehler;

Zaehler  = 100;

for(;;)
{
cli();
Zaehler = Zaehler + enc_delta/2;
enc_delta = 0;
sei();
Zaehler ausgeben();
}

Problem: Beim Hochzaehler wird manchmal eine Rasterung ausgelassen und 
beim Runterzaehler werden Werte um ca. 128 Werte nach unten gesprungen. 
Der Compiler kann doch mit negativen Zahlen umgehen...
Ich befürchte, dass ich einen ganz dummen Fehler eingebaut habe.
Kann mir jemand weiter helfen?

von Falk B. (falk)


Lesenswert?

@ Igor Metwet (bastel-wastel)

>Variable über itoa(); umwandle und am LCD ausgebe, bekomme ich Werte von
>0..127. Negative Zahlen kann die Funktion wahrscheinlich nicht - egal.

Doch, eigentlich schon, heisst ja I (wie Integer) to ASCII.

>for(;;)
>{
>cli();
>Zaehler = Zaehler + enc_delta/2;

Kein gute Idee, damit werden Pulse verschluckt

>enc_delta = 0;
>sei();
>Zaehler ausgeben();
>}

Besser so.

1
uint16_t; Zaehler  = 100
2
3
for(;;)
4
{
5
  cli();
6
  Zaehler = Zaehler + enc_delta;
7
  enc_delta = 0;
8
  sei();
9
  ausgeben(Zaehler/2);
10
}

>beim Runterzaehler werden Werte um ca. 128 Werte nach unten gesprungen.
>Der Compiler kann doch mit negativen Zahlen umgehen...

Ja, aber möglicherweise hast du Variablen falsch deklariert. Poste 
VOLLSTÄNDIGEN Quelltext als ANHANG.

MfG
Falk

von Gerd (Gast)


Lesenswert?

Schau dir mal den Unterschied zwischen signed char und unsigned char an!

von Igor M. (bastel-wastel)


Angehängte Dateien:

Lesenswert?

> Kein gute Idee, damit werden Pulse verschluckt
Danke! Jetzt kann ich hoch- und runterzählen ;-)


>>Variable über itoa(); umwandle und am LCD ausgebe, bekomme ich Werte von
>>0..127. Negative Zahlen kann die Funktion wahrscheinlich nicht - egal.
>
> Doch, eigentlich schon, heisst ja I (wie Integer) to ASCII.

Wenn ich runterdrehe bis auf null und dann noch einen Puls Richtung 
negative gebe, werden am Display "127" ausgegeben.
Ich habe aber allerdings die Variable "Zaehler" als char und nichts als 
int deklariert. Da wären wir schon beim nächsten Effekt: Wird "Zaehler" 
als int deklariert, dann zählt er hoch aber anstatt runter zu zählen, 
zählt er pro Schritt 127 oder 255 hoch.

von Chris W. (squid1356)


Lesenswert?

while (1){


    cli();
    encoder += enc_delta;
    enc_delta = 0;
    sei();


    uint8_t s[3];
    sprintf (s, "encoder: %3d", encoder);
    vfd_cstring(2,s);

  }

Hi!

Ich benutze auch den Drehgeber-Code von Peter Dannegger ( Code ganz 
oben, die zweite!)

wie in dem letzten Beispiel wird auch die enc_delta benutzt um den 
zähler ("uint8_t encoder = 0;") hoch bzw. runter zu zählen.

leider springt der wert aber nur zw. 0 und 255 bei linksdrehung und 
zwischen 0 und 1 bei rechtsdrehung.

ich habe mir nun schon einige beispiele hier angesehen und beiträge 
gelesen, stehe aber leider auf dem schlauch. kann mir jemand helfen bzw. 
einen tip geben?? (ps. ich benutze einen drehgeber OHNE rastung)

Danke, Christoph

von Micha (Gast)


Lesenswert?

Hast du mal versucht
1
enc_delta
direkt zu verwenden?

von Chris W. (squid1356)


Lesenswert?

Micha wrote:
> Hast du mal versucht
1
enc_delta
direkt zu verwenden?

enc_delta springt zw. 0/ 1 (rechts) bzw. 0/ -1 (links)

von T. S. (trse)


Lesenswert?

Hallo,
Wie intigriere ich das in mein Programm???
In meiner main-Funktion steht eigentlich dass ganze Programm drinne,
Wenn ich das mit der Abtastung(
1
 for(;;)        // main loop
2
    PORTB = enc_delta;
) mache, dann läuft mein Programm doch nicht mehr in der 
while(1)-Schleife???

Würde es gehen, wenn ich das
1
PORTB = enc_delta;
 in die while-Schleife packe???

von Chris W. (squid1356)


Lesenswert?

T. S. wrote:
> Würde es gehen, wenn ich das
1
PORTB = enc_delta;
 in die
> while-Schleife packe???
1
PORTB = enc_delta;
 -> das gibt nur das enc_delta auf die LED's aus (angenommen die sitzen 
hier wie beim STK500 an Port B)

enc_delta sollte eigentlich die Anzahl an schritten enthalten die der 
Interrupt vom Drehgeber eingelesen hat. (Sehe ich doch richtig?!)

Sieh doch mal weiter oben, da steht eigentlich alles was man braucht.

mein Problem besteht übrigens noch immer...
siehe Beitrag "Problem mit Drehgeber"

von Micha (Gast)


Lesenswert?

> mein Problem besteht übrigens noch immer...
> siehe Beitrag "Problem mit Drehgeber"
Richtig angeschlossen ist alles? Bleibt das Problem wenn du enc_delta 
auf einen Port ausgibst?
So verkehrt sieht das nicht aus - die Werte würden passen. Falsch ist 
halt dass es nur 1 Schritt tut...

von Chris W. (squid1356)


Lesenswert?

Micha wrote:
> Richtig angeschlossen ist alles?
Ja. siehe auch im anderen Forum, habe den Beitrag mal da rein gestellt, 
gehört ja hier eig. nicht hin!
>Bleibt das Problem wenn du enc_delta auf einen Port ausgibst?
hm, wie genau meinst du das ?
> So verkehrt sieht das nicht aus - die Werte würden passen. Falsch ist
> halt dass es nur 1 Schritt tut...
eben. denke ich bin ganz nah dran (oder meilenweit entfernt...)
habe grade nochwas interessantes festgestellt: wenn ich GANZ schnell 
drehe (so schnell wie normalerweise niemand an dem ding drehen würde) 
dann kommen auch vernünftige schritte raus. nur halt zu schnell. ist mir 
irgendwie rätselhaft...

naja, ich warte noch ne weile, vielleicht kann mir jemand den 
entscheidenden Tip geben. ansonsten besorg ich mir (leider) nen neuen 
Geber...

von Micha (Gast)


Lesenswert?

> hm, wie genau meinst du das ?
Naja du schreibst deine Werte jetzt ja auf ein Display, oder? Ich meine 
du sollst mal enc_delta direkt auf einen Port ausgeben.

Und poste wenn möglich mal deinen gesamten Source-Code.

von Chris W. (squid1356)


Lesenswert?


von Stephan S. (outsider)


Lesenswert?

Ich habe die Drehencoder Funktionen benutzt die hier beschrieben sind:

http://www.mikrocontroller.net/articles/Drehgeber

Nachdem ich ewig rumgemacht habe und nichts funktioniert hat, aber 
eigentlich funktionieren hätte sollen hab ich festgestellt dass die 
enc_delta außerhalb der ISR nicht vorhanden ist, obwohl es eine globale 
Variable ist.

Bin leider nicht sehr erfahren mit C und µC, hatte aber was von volatile 
im Hinterkopf. Nachdem ich die Variable volatil deklariert hab hat alles 
funktioniert.

Jetzt frag ich mich: Was ist beim Author anders dass es bei ihm ohne 
volatile funktioniert und bei mir nicht? So wie ich das verstanden habe 
muss man als volatile deklarieren wenn eine Variable nicht vom 
angetastet werden darf weil sie z.B. von Ports oder ISRs verändert 
werden kann, was ja bei dem Code der Fall ist.

von Michael K. (Gast)


Lesenswert?

> Jetzt frag ich mich: Was ist beim Author anders dass es bei ihm ohne
> volatile funktioniert und bei mir nicht?
Ich denke hier http://www.mikrocontroller.net/articles/Drehgeber wurde 
das volatile einfach vergessen, weil hier 
Beitrag "Re: Drehgeber auslesen" ist es ja mit drin (und 
das funktioniert auch).

von Stephan S. (outsider)


Lesenswert?

Ja verlinkt ist dieser Thread, aber hier kann ich keine Infos finden zu 
den Funktionen von dem Artikel.

Und wo ich schon dabei bin: Hat jemand eine Lösung wie man verhindern 
kann dass das erste mal Drehen nicht funktioniert? Beim 4-fach Schalter 
sollte das doch möglich sein eine eindeutige Aussage zu treffen in 
welche Richtung es geht weil man ja auch ohne bekannte Vorbedingung 
davon ausgehen kann dass die Vorbedingung so aussieht dass bei beiden 
Phasen eine Null war. Ich denke das ist vielleicht auch der Grund warum 
es solche Schalter überhaupt gibt.

von Peter D. (peda)


Lesenswert?

Stephan S. wrote:
> Jetzt frag ich mich: Was ist beim Author anders dass es bei ihm ohne
> volatile funktioniert und bei mir nicht?

Ja, das sind so die kleinen Gemeinheiten des GCC.
Er will unbedingt auch Funktionen inlinen, die nicht static sind und 
dadurch ergeben sich unschöne Seiteneffekte:

Einer Funktion, die eine Variable nur einmal benötigt, ist es egal, ob 
sie volatile ist oder nicht, sie muß sie einlesen. Volatile und der 
damit verbundene größere Code ist dann unnötig.

Wird nun allerdings diese Funktion im gleichen Modul aufgerufen, kann es 
sein, daß der Compiler sie inlined und damit optimiert er sie weg, wenn 
die Variable nicht volatile ist.
Hier im Listing sieht man schön, daß die Funktion komplett fehlt:
1
int8_t encode_read1( void )
2
{
3
  int8_t val;
4
5
  cli();
6
 118:   f8 94           cli
7
  val = enc_delta;
8
  enc_delta = 0;
9
  sei();
10
 11a:   78 94           sei


Diese unschöne Disoptimierung kann man aber abschalten mit folgender 
Compileroption:

-fno-inline-small-functions


Peter

von Stephan S. (outsider)


Lesenswert?

Wow, das ist mal echt eine gute Antwort... Wenn ich das aber nicht 
wüsste und die Variable doch als volatile deklariere, handle ich mir 
damit irgendwelche nennenswerten Nachteile ein?

Und was ist mit dem Problem dass der erste Schritt beim Drehen nicht 
richtig erkannt wird?

von Michael K. (Gast)


Lesenswert?

> Wenn ich das aber nicht wüsste und die Variable doch als volatile
> deklariere, handle ich mir damit irgendwelche nennenswerten Nachteile ein?
Würde mich auch interessieren.

> Und was ist mit dem Problem dass der erste Schritt beim Drehen nicht
> richtig erkannt wird?
Ich habe das so gelöst:
1
static char enc_last = 0;
2
3
SIGNAL (SIG_OVERFLOW0)
4
{
5
   char i = 0;
6
7
   if... (siehe oben, 2. Beitrag)
8
}
9
10
void init_rot_enc(void)
11
{
12
   if( PHASE_A )
13
      enc_last = 1;
14
15
   if( PHASE_B )
16
      enc_last ^= 3;
17
}
Während der Initialisierung einmal init_rot_enc aufrufen und es sollte 
funktionieren.

von Peter D. (peda)


Lesenswert?

Stephan S. wrote:
> Und was ist mit dem Problem dass der erste Schritt beim Drehen nicht
> richtig erkannt wird?

Michael hats ja schon erklärt.

Ich hab das Beispiel korrigiert:

http://www.mikrocontroller.net/articles/Drehgeber#Beispielcode_in_C


Peter

von Michael K. (Gast)


Lesenswert?

@ Peter
Hat es einen bestimmten Grund dass du aus
1
  if( PHASE_A )
2
    i = 1;
3
4
  if( PHASE_B )
5
    i ^= 3;
folgendes gemacht
1
  if( PHASE_A )
2
    new = 3;
3
4
  if( PHASE_B )
5
    new ^= 1;
also ...= 3; und ...= 1; getauscht hast?

von Peter D. (peda)


Lesenswert?

Michael K. wrote:
> @ Peter
> Hat es einen bestimmten Grund dass du
> ...= 3; und ...= 1; getauscht hast?

Ja, hat es.
Der AVR-GCC erzeugt äußerst umständlichen Code, wenn ein Bit abhängig 
von einem anderen gesetzt wird.
Er expandiert erstmal zu 16Bit und schiebt dann entsprechend oft bis an 
die richtige Bitposition.


Peter

von Udo S. (Firma: allround) (1udo1)


Lesenswert?

Hallo,

bin Quereinsteiger mit meinem optischen encoder GP1A038 von Sharp.

Ich beziehe mich auf den Code der unten noch einmal kurz aufgeführt ist:

An PortD,3 schließe ich Kanal A an und an PortD,4 Kanal B, oder? Ich 
verstehe nicht diese 4 ".set directiven". Sind die Werte 4,3 und 2 die 
Binärwerte, die von Kanal A und B generiert werden? uND WAS IST DER 
bUTTONß

Bitte mal jemand um eine Hilfestellung. Ich werfe sehr wahrscheinlich 
noch etwas durcheinander.

Gruß
Udo


Autor: avusgalaxy (Gast)
Datum: 02.02.2005 20:33

An PORT D,0 und PORT D, eins kommt jeweils eine Led.

Ab PORT D... 4 und 3 den Drehgeber.. Ah ja, so solls ausehen:


.include "2313def.inc"

.set DIPGPin=PinB    ;Port mit Drehimpulsgeber
.set DIPGChA=4       ;Pin mit Channel A
.set DIPGChB=3       ;Pin mit Channel B
.set DIPGBT=2        ;Pin mit Button
.def DIPGAlt=r4      ;Vorriger Zustand von Channel A
.def DIPG=r5         ;Drehimpulsgeber-Aktion. Nach Abfrage löschen
.def ButtonAlt=r6    ;Vorriger Zustand des Buttons

von Otto W. (Gast)


Lesenswert?

Hallo!
Ich möchte gerne einen Drehgeber mit einem Atmega 8 auswerten und auf 
eine LED Zeile ausgeben.
Leider kann ich  peter dannegger "C" Code nicht nachvollziehen. Gibt es 
diesen Code auch in Assembler und somit auch für mich nachvollziebar?
Danke für eure Hilfe Otto.

von Steffen H (Gast)


Angehängte Dateien:

Lesenswert?

Ich hab die hier noch. Läuft auf einen ATtiny2313. Ergebnisse werden im 
RAM gespeichert.

Gruß
Steffen

von Peter D. (peda)


Lesenswert?

Steffen H schrieb:
> Ich hab die hier noch. Läuft auf einen ATtiny2313.

Ooch nöö, das ist ja ein Monstercode.

In Assembler kann man Register für Interrupts reservieren bzw. auch 
Variablen in Registern halten. Spart ne Menge Code.

Hier mal mein Code in Assembler (enc_delta = 16-bittig):
1
.include "tn261def.inc"
2
3
.def    save_SREG       = r14    ; for general interrupt usage
4
.def    int_i           = r15    ; for general interrupt usage
5
6
.def    int_val3        = r16    ; for encoder interrupt only
7
.def    enc_last        = r13    ; for encoder interrupt only
8
.def    count0          = r18    ; encoder value low
9
.def    count1          = r19    ; encoder valeu high
10
11
.equ    GRAY_IN         = PINA
12
.equ    PHASE_A         = PA0
13
.equ    PHASE_B         = PA1
14
15
        rjmp    init
16
17
.org    OC1Aaddr
18
        in      save_SREG, SREG
19
        clr     int_i
20
        sbic    GRAY_IN, PHASE_A        ; if( PIND & 1 )
21
        inc     int_i                   ;   i++;
22
        sbic    GRAY_IN, PHASE_B        ; if( PIND & 2 )
23
        eor     int_i, int_val3         ;   i ^= 3;
24
        sub     int_i, enc_last         ; i -= enc_last;
25
        sbrs    int_i, 0                ; if( i & 1 ){
26
        rjmp    _ioc1a1
27
        add     enc_last, int_i         ;   enc_last += i;
28
        sbrs    int_i, 1                ;   if( i & 2 )
29
        rjmp    _ioc1a2
30
        subi    count0, byte1(1)        ;     enc_delta--;
31
        sbci    count1, byte2(1)
32
        out     SREG, save_SREG
33
        reti
34
_ioc1a2:                                ;   else
35
        subi    count0, byte1(-1)       ;     enc_delta++;
36
        sbci    count1, byte2(-1)
37
_ioc1a1:                                ; }
38
        out     SREG, save_SREG
39
        reti                            ; 24 cycle
40
41
init:
42
        ldi     int_val3, 3             ; for usage in interupt
43
main:
44
; hier T1 starten und SPI-Slave bearbeiten
45
        rjmp    main


Peter

von Thomas (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Peter!

Ich habe mal eine Lösung für das vieldiskutierte Thema Dein 
Peda-Drehgeber-Code aus dem  Wiki ( 
http://www.mikrocontroller.net/articles/Drehgeber ) und der Pollin 
Drehencoder (Panasonic) ausgetüftelt.

Bekanntlich läuft der Pollin Encoder asymmetrisch. Nur Signal A ist bei 
jeder Raste stabil. Signal B kann in jeder zweiten Raste zwei Zustände 
einnehmen. (siehe Bild)

(1) Da bei dem Pollin "two step encoder" der Ausgabewert halbiert wird, 
muss bei der Initialisierung von enc_delta berücksichtigt werden, dass 
die instabilen Zwischenstufen bei (2*n) und (2*n+1) liegen, damit beim 
rechts shiften das letzte Bit keinen Übertrag erzeugt und das Ergebnis 
flattert.

(2) Falls mit internen Pullups gearbeitet wird, muss nach der 
Portinitialisierung und dem Einlesen der Port-Pegel in der Funktion 
encode_init() eine kleine Pause eingelegt werden, damit ggf. offene 
Leitungen auf High gezogen werden (Leitungskapazitäten)

(3) Der Pollin Encoder dreht gegenüber dem PeDa-Code falsch herum. Ich 
habe die Anschlüsse von A und B vertauscht.

Das ergibt dann folgende minimale Codeanpassung und der Pollin Drehgeber 
arbeitet absolut bullet proof.
1
void encode_init( void )
2
{
3
  int8_t new;
4
  _delay_ms(1);  //Verzögerung nach Initial. interner Pullups erforderlich
5
  new = 0;
6
  if( PHASE_A )
7
    new = 3;
8
  if( PHASE_B )
9
    new ^= 1;          // convert gray to binary
10
  last = new;          // power on state
11
12
 //enc_delta = 0;    
13
  enc_delta = (new&1?0:1);     //Pollin Encoder-Zählwerk auf stabile Startposition 0/1 setzen  
14
  
15
  TCCR0 = 1<<WGM01^1<<CS01^1<<CS00;    // CTC, XTAL / 64
16
  OCR0 = (uint8_t)(F_CPU / 64.0 * 1e-3 - 0.5);  // 1ms
17
  TIMSK |= 1<<OCIE0;
18
}
der restl. Code wie in http://www.mikrocontroller.net/articles/Drehgeber

von Klaus (Gast)


Lesenswert?

Hallo,

hat jemand von euch sein Programm bei einem Motor mit ca. 1500 U/min 
oder auch mehr zum laufen bekommen???
Habe erst Peters genommen und danach eins mit Interrupt 
Flankenauswertung geschrieben, klappen jedoch beide bei dieser Drehzahl 
nicht :-(

Ich wäre für Programmbeispiele sehr dankbar.

Gruß Klaus

von thomas55 (Gast)


Lesenswert?

Hallo Klaus,

es sind doch bei 1500 U/min nur 25 U/sec.

Da würde ich mit 250 Interrupts anfangen.

Welche Art Geber hast Du angeschlossen?
Möglich, dass es bei Gebern mit normalen Kontakten zu Schwierigkeiten 
kommt.

Willst Du damit eine Maschine positionieren?

Gruß Thomas

von Bernhard M. (boregard)


Lesenswert?

thomas55 schrieb:
> Hallo Klaus,
>
> es sind doch bei 1500 U/min nur 25 U/sec.
>
> Da würde ich mit 250 Interrupts anfangen.
>
> Welche Art Geber hast Du angeschlossen?
> Möglich, dass es bei Gebern mit normalen Kontakten zu Schwierigkeiten
> kommt.
>
> Willst Du damit eine Maschine positionieren?
>
> Gruß Thomas

??? Wirklich??

Wie viele Impulse / Schritte gibt der Geber denn pro Umdrehung? 16?
Dann sind es 4000 pro Sekunde, ein ordentliches Oversampling von 10 
eribt schon 40000 Timeraufrufe pro Sekunde oder ein Timerinterrupt alle 
25 mikrosekunden. Das ist sportlich....

von Peter D. (peda)


Lesenswert?

Bernhard M. schrieb:
> Wie viele Impulse / Schritte gibt der Geber denn pro Umdrehung? 16?
> Dann sind es 4000 pro Sekunde, ein ordentliches Oversampling von 10
> eribt schon 40000 Timeraufrufe pro Sekunde oder ein Timerinterrupt alle
> 25 mikrosekunden. Das ist sportlich....

Ob da ein mechanischer Encoder überhaupt mitmacht?

Bei hohen Impulsraten würde ich die Zählung in nen extra ATtiny oder 
CPLD machen und den per SPI oder I2C vom Master auslesen.


Peter

von Michael H. (mah)


Lesenswert?

> Wie viele Impulse / Schritte gibt der Geber denn pro Umdrehung? 16?
> Dann sind es 4000 pro Sekunde, ein ordentliches Oversampling von 10
> eribt schon 40000 Timeraufrufe pro Sekunde oder ein Timerinterrupt alle
> 25 mikrosekunden. Das ist sportlich....

ich habe das mit einem Arduino und 20usec Timerrate brauchbar 
hingekriegt, ohne Assembler, und optional mit Peter's Entprellung der 
A/B/Indexsignale ; da bleibt noch Luft für serielle I/O und einen 
einfachen Kommandointerpreter

siehe 
http://mah.priv.at/cgi-bin/viewvc.cgi/arduino-rotary/rotary-v6.pde?revision=1.1&root=CVS&sortby=date&view=markup 
- das war ein Debug-Vehikel von mir und ist daher kein als solches 
brauchbares Projekt

hatte folgendes Problem: CNC-Fräse mit EMC2; selbstgebauter Drehgeber 
mit 360 Impulsen/Umdrehung und Indexpuls auf einer Spindel mit bis zu 
5000 U/min zu erfassen und so runterzuteilen, so dass EMC2 mit einem 
Parallelport-Input "mitkommt"

habe das schlussendlich aber mit einer FPGA-basierten Lösung gemacht 
(Mesatech 5i20) und einem Heidenhain-Drehgeber mit 3600 Pulse/U; Grund 
war meine mangelhafte mechanische Präzision beim Drehgeber, und 
Störungen des Signals - manche Sachen kauft man besser und ein 
Drehstromumrichter müllt ganz schön

falls jemand dazu Anregungen braucht - der VHDL-Code und EMC2 selbst 
sind Open Source


-Michael

von Maurice K. (maurice)


Lesenswert?

Hallo zusammen,
ich habe gerade ihre Kommentare alle gelesen.Ich bion ganz neu hier.Ich 
mach gerade meine Technikerarbeit und ich soll auch einen Encoder ( 
inkremental)von der Firma Wachendorff verwenden.kann ich dafür auch den 
LS7366 Decoder nehmen? Und woher weiß ich welches Modell ich nehmen 
sollte,bezüglich der Anzahl der Impulse des Drehgebers????Ich soll 
nämlich die geschwindigkeit ermitteln eines Förderbands.Auch bei 
Geschwindigkeitänderungen. damit ich immer die perfekte position für ein 
Bauteil berechnen kann, um ein Foto mit ner Camera auszulösen.Die 
Position des Bauteils darf maximal 1mm +/- unter der Camera vom 
optimalen Punkt versetzt sein.Wisst ihr was ich meine????
Ich würde mich über eine Antwort sehr freuen.
Mfg Maurice

von Mathias O. (m-obi)


Lesenswert?

Macht da eine Lichtschranke nicht mehr Sinn?

von Maurice K. (maurice)


Lesenswert?

ein sogenannter triggersensor ist selbstverständlich vorhanden dieser 
kann aber aufgrund von baulichen gegebenheiten nicht direkt an der 
kamera positioniert werden desweiteren ist die geschwindigkeit des 
förderbandes nicht immer 100 prozent konstant.

von Mathias O. (m-obi)


Lesenswert?

Ist denn das Förderband an einer Steuerung angeschlossen, wenn ja kann 
man den Encoder mit der verbinden. Je langsamer das Band läuft desto 
mehr Impulse müssen es sein. D.h. läuft das Band mit einer sehr hohen 
Drehzahl, brauchst du ja nicht soviele Impulse. Wie schnell läuft denn 
das Band so im Minimum und Maximum?

von Dawn (Gast)


Angehängte Dateien:

Lesenswert?

heyhey,

hier mal mein Mockup, was auf der 2. Version von oben aufbaut. Ich 
verwende externe Interrupts, um die Auslastung so gering wie möglich zu 
halten. Der hohen Sensitivität was das bouncing angeht begegne ich mit 
einem kleinen Kniff: Das Bouniving ist ja im Prizp nichts anderes als 
das Hin- und Herpendeln zwischen zwei Zahlen. Wenn man jetzt nur 
weiterzählen lässt, wenn der aktuelle Schritt in die selbe Richtung 
gegangen ist, wie der vorherige, wir das Pendeln quasi ausgeblendet. Das 
ganze funktioniert, weil der erste Kontakt mit der neuen Stufe eines 
Schrittes in die selbe Richtung geht wie der letzte beim Bouncing. Die 
Richtung des vorherigen Schrittes wird durch die Variable Rtg_pos 
angegeben.

Beim Testen läufts bei mir in der Praxis sehr stabil, bin gespannt auf 
eure Meinungen.

VG, Dawn

von Falk B. (falk)


Lesenswert?

@  Dawn (Gast)

>Beim Testen läufts bei mir in der Praxis sehr stabil, bin gespannt auf
>eure Meinungen.

Wirklich? OK!

Du bist ebenso auf dem Holzweg wie Tausende vor dir. Ein funktionierndes 
Einzelexemplar ist kein Beweis. Das könnte bestenfalls ein Stresstest 
bringen. Wird er aber nicht. Warum? Siehe Artikel Drehgeber.

MfG
Falk

P S Kein "Aber . . ."

von Dawn (Gast)


Lesenswert?

...das schöne an so einem Forum ist doch, dass man abers bringen kann so 
viel wie man möchte :)
Wer bin ich denn, dass ich hier einen Beweis erbringen möchte, ich sage 
blos,dass das ganze für interupts sehr stabil  läuft. Für meine 
Anwendung, wo die Drehgeber nur selten verwendet werden ist pollen 
ziemlich überzogen. Einer Überlastung beuge ich durch Ausschalten der 
Interruots während der Routine vor. Zudem, was tun, wenn die Timer 
anderweitig gebraucht werden?

von BMJ (Gast)


Lesenswert?

Hi,

ich habe im Rahmen eines Projekts in der Uni die Aufgabe mit Hilfe eines 
Heidenhain Inkrementaldrehgebers und einem ATmega2561 den Drehwinkel 
bzw. die Winkelgeschwindigkeit eines Motors zu messen. Der Motor läuft 
mit maximal 2000 U/min und der Drehgeber hat 3000 Striche, das wäre also 
eine maximale Signalfrequenz von 100 KHz. Zu welcher Methode würdet ihr 
mir da raten? Eigentlich wollte ich das mit den externen Interrupts 
machen, aber ich habe jetzt hier öfter gelesen, dass es mit 
Timerinterrupts sicherer ist. Kommt der Mikrocontroller überhaupt mit 
dieser hohen Frequenz klar?

Vielen Dank!

von Thorsten O. (Firma: mechapro GmbH) (ostermann) Benutzerseite


Lesenswert?

Hallo BMJ!

Du musst einfach die Eingänge schnell genug abfragen. Um mit einem AVR 
100kHz noch sicher handeln zu können sollte die entsprechende Routine 
aber in Assembler geschrieben werden und beim Aufruf aus einem Timer 
möglichst wenig auf dem Stack zwischengelagert werden.

Mit freundlichen Grüßen
Thorsten Ostermann

von Peter D. (peda)


Lesenswert?

ATmega2561 klingt so, als ob dieser Bolide noch vieles andere machen 
soll.
Daher würde ich nen extra ATtiny25 nehmen und per SPI oder I2C auslesen.

Es gibt zwar noch alte Encoder-ICs, aber die sind exorbitant teuer 
(Museumszuschlag).


Peter

von BMJ (Gast)


Lesenswert?

Hallo,

danke schon mal für die Antworten. Also ich möchte eigentlich keine 
zusätzlichen IC's benutzen. Ich habe von der Uni diesen fertigen 
Sensorknoten mit dem ATmega2561 bekommen und der muss dann auch nichts 
anderes mehr machen als die Daten aufzunehmen und auf dem PC in LabView 
darzustellen.
Wie groß müsste den die Abtastfrequenz sein, wenn ich das mit 
Timerinterrupts mache? (Die maximale Signalfrequenz sind doch "nur" 75 
kHz, da der Motor mit max 1500 U/min läuft) Der ATmega läuft mit etwas 
weniger als 8 MHz, ich habe da etwas bedenken, dass ich nicht mehr genug 
Rechenzyklen zur Verfügung habe, weil ich auch nur im Notfall auf 
Assembler zurückgreifen möchte. Der Drehgeber hat außerdem auch ein 
Referenzsignal bei jeder Umdrehung. Vielleicht würde es ausreichen, wenn 
ich bei jeder vollen Umdrehung eine Korrektur durchführe (Zähler auf 
Null setzen) und es doch mit den externen Interrupts mache. Wenn bei 
3000 Strichen bei einer Umdrehung ein Paar nicht erkannt werden, wäre 
das nicht so schlimm denke ich. Was meint ihr?

Viele Grüße!
BMJ

von Bob (Gast)


Lesenswert?

peter dannegger schrieb:
> Bei einer Änderung über einen Schritt, ergeben sich je nach
> Drehrichtung folgende Werte (in Klammern der 2-Bit Wert als
> Dezimalzahl):
>
> Vorwärts:
> 00 - 11 = 01 (1)
> 01 - 00 = 01 (1)
> 10 - 01 = 01 (1)
> 11 - 10 = 01 (1)
>
> Rückwärts:
> 00 - 01 = 11 (3)
> 01 - 10 = 11 (3)
> 10 - 11 = 11 (3)

Hallo, ich verstehe nicht, wie diese Tabelle zu Stande kommt. Kann mir 
das einer bitte erklären?

LG

von Thorsten O. (Firma: mechapro GmbH) (ostermann) Benutzerseite


Lesenswert?

Die Tabelle ist meiner Meinung nach falsch. Encoder geben einen Graycode 
aus, d.h. es ändert sich pro Schritt immer nur eine der beiden 
Leitungen:

00 -> 01
01 -> 11
11 -> 10
10 -> 00

In Pfeilrichtung wäre dann vorwärts, gegen die Pfeilrichtung rückwärts. 
Bei den anderen 8 möglichen Zustandsänderungen hätten sich beide Signale 
geändert, was auf einen Fehler hindeutet (Zustandswechsel verpasst da 
Encoder zu schnell dreht oder Auswertung zu langsam ist).

Mit freundlichen Grüßen
Thorsten Ostermann

von Frank (Gast)


Lesenswert?

Hallo,

ich bin auch relativ neu auf diesem Gebiet und soll mithilfe eines 
magnetischen Drehgebers einen Schrittmotor regeln.

Soweit habe ich mich in das Programm von Herrn Dannegger eingelesen und 
vieles verstanden ;)

Was ich z.B. nicht verstehe ist die Diskussion um die Rastpunkte. Blöd 
gesagt was ist das?

Mein magnetischer Encoder bietet eine Auflösung von 1024 
Impulsen/Umdrehung und arbeitet eben mit den Kanälen A & B (& Z, aber 
das scheint ja egal zu sein). Das dieser in irgendeiner Einstellung 
"spürbar" einrastet kann ich nicht bestätigten....

Vielen Dank schon einmal!

Frank

von dreilira (Gast)


Lesenswert?

Hallo Frank,
die 'Rastpunkte' sind für die mechanischen Drehgeber. Die haben auch nur 
8-32 Flanken. Für magnetische Drehgeber spielt das Ganze keine Rolle. 
Und bei 1024 Pulsen wird wohl auch niemand von Hand an der Achse drehen 
:-)

Gruß
Ralf

von Frank (Gast)


Lesenswert?

Ach cool, danke Ralf für die schnelle Antwort!

Ich kann also blöd gesagt, einfach das (erste) Programm von Herrn 
Dannegger verwenden (http://www.mikrocontroller.net/articles/Drehgeber)?

Denn bei meinen bisherigen Versuch (lasse mir die Gesamtzahl an 
Schritten an einem angeschlossenen LCD anzeigen) habe ich folgende 2 
Probleme:

1. Ich brauch für die bisher manuelle Betätigung des Drehgebers (--> ca. 
2 Umdrehungen/s :D ) schon eine Auslesefrequenz von 100kHz was ja schon 
enorm ist

2.Ich denke es ist ein Variablenspeicherproblem: Sobald ich a) einen 
Overflow habe (Also bei ca. 32k Schritten) oder b) erst in die eine 
Richtung drehe (z.B. bis 11000) und dann in die andere bis es <10000 
Schritte sind, bleibt die letzte Ziffer "einfach" stehen, also z.B. 
99994 statt 9999... und ab dann springen die Schritte auf dem LCD 
absolut zufällig....


Erneut, vielen Dank schon einmal :)

Frank

von Tishima (Gast)


Lesenswert?

Hallo,

bei der Pulsfrequenz die dein Drehgeber liefert wirst Du wahrscheinlich 
nicht glücklich mit einer Software Lösung.
Kommt auf ein MC System an ob der das schafft.

Da wirst Du mit spez. IC's oder nem CPLD arbeiten müssen.
Von den spez. IC's fällt mir aber grade nicht mehr die Bezeichnung ein.

gruß,
Bjoern

von jonas biensack (Gast)


Lesenswert?

>2.Ich denke es ist ein Variablenspeicherproblem: Sobald ich a) einen
>Overflow habe (Also bei ca. 32k Schritten) oder b) erst in die eine
>Richtung drehe (z.B. bis 11000) und dann in die andere bis es <10000
>Schritte sind, bleibt die letzte Ziffer "einfach" stehen, also z.B.
>99994 statt 9999... und ab dann springen die Schritte auf dem LCD
>absolut zufällig....

Versuch mal den Datentyp zu erweitern, z.b. auf int32_t.

Gruß Jonas

von Peter D. (peda)


Lesenswert?

Frank schrieb:
> 1. Ich brauch für die bisher manuelle Betätigung des Drehgebers (--> ca.
> 2 Umdrehungen/s :D ) schon eine Auslesefrequenz von 100kHz was ja schon
> enorm ist

Bei 2 Umdrehungen/s reichen 5kHz.


Frank schrieb:
> 2.Ich denke es ist ein Variablenspeicherproblem: Sobald ich a) einen
> Overflow habe (Also bei ca. 32k Schritten) oder b) erst in die eine
> Richtung drehe (z.B. bis 11000) und dann in die andere bis es <10000
> Schritte sind, bleibt die letzte Ziffer "einfach" stehen, also z.B.
> 99994 statt 9999... und ab dann springen die Schritte auf dem LCD
> absolut zufällig....

Nö, ein Darstellungsproblem. Vermutlich erweiterst Du nicht mit 
Leerzeichen und alte Ziffern bleiben bei kürzeren Zahlen stehen.


Peter

von Frank (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Nö, ein Darstellungsproblem. Vermutlich erweiterst Du nicht mit
> Leerzeichen und alte Ziffern bleiben bei kürzeren Zahlen stehen.


Danke für die schnelle Antwort. Das stimmt, das macht Sinn :). Weiß zwar 
noch nicht ganz wie das zu bewerkstelligen ist, da ich bisher auf eine 
vorgefertigte Header Datei für das LCD zurückgreife aber wird schon 
irgendwie :).

Kommst du auf die 5 kHz über: 2 Umdrehungen/s *1024*2= 4100 Hz? Bei der 
Frequenz funktionierts aber leider nicht zuverlässig... Hab glaub auf 
rn-wissen mal gelesen dass es das 20-fache der Grundfrequenz sein 
soll...

Noch eine Verständnisfrage:
Soweit ich das Programm verstanden habe ist in enc_delta je nach 
Richtung entweder 10 oder 01 gespeichert oder? Warum ist es denn 
enc_delta += und nicht nur =? Denn angenommen ich würde es in der 
Abtastfrequenz wirklich schaffen 2 Impulse weiterzudrehen, also 01+01= 
10, dann interpretiert es doch der µC als 1 Schritt in die andere 
Richtung..

Wenn ich das richtig verstanden habe ist die Auswertung eines Drehgebers 
mit Interrupts die durch Flanken ausgelöst worden sind nur wegen den 
prellenden (mechanischen) Tastern ungeeignet/schlechter. Wäre es aber 
nicht für meinen magnetischen bzw. später optischen Drehgeber nicht 
sogar besser? Oder tritt auch hier Prellen auf?


Ich hoffe es ist ok dass ich soviele Fragen gestellt habe. Freu mich auf 
jeden Fall über eure Antworten. Danke!

Frank

von Falk B. (falk)


Lesenswert?

@  Frank (Gast)

>Frequenz funktionierts aber leider nicht zuverlässig... Hab glaub auf
>rn-wissen mal gelesen dass es das 20-fache der Grundfrequenz sein
>soll...

Ist hier falsch.

>Richtung entweder 10 oder 01 gespeichert oder? Warum ist es denn
>enc_delta += und nicht nur =?

Weil die Differenz (delta) addiert werden muss.

>Wenn ich das richtig verstanden habe ist die Auswertung eines Drehgebers
>mit Interrupts die durch Flanken ausgelöst worden sind nur wegen den
>prellenden (mechanischen) Tastern ungeeignet/schlechter.

u.a.

> Wäre es aber
>nicht für meinen magnetischen bzw. später optischen Drehgeber nicht
>sogar besser? Oder tritt auch hier Prellen auf?

Kann sein, muss nicht. Ist aber für dein Problem egal.

Dein Ansatz ist falsch. Für eine einfach Drehzahlmessung braucht man die 
echte Dekodierung nicht. Man nutzt einfach eine Spur, A oder B, und 
macht einen stinknormalen Frequenzzähler. Das macht der AVR mit links 
und gähnt dabei. Ein Timer zählt die Pulse am Eingang, ein zweiter 
generiert die Messzeit. Das passiert alles in Hardware. Die CPU muss nur 
ab und an die Zähler auslesen und daraus die Drehzahl berechnen.

MFG
Falk

von Frank (Gast)


Lesenswert?

Falk Brunner schrieb:
> für eine einfach Drehzahlmessung braucht man die
> echte Dekodierung nicht.

Hey danke Falk für deine Antwort.

Da habe ich mich wohl missverständlich ausgedrückt. Ich brauche im 
Endeffekt eigentlich keine Drehzahlmessung, sondern soll einen Schlitten 
der über einen Schrittmotor angetrieben wird regeln. Also eine 
Positionsbestimmung / -regelung. Deshalb benötige ich schon die Anzahl 
zurückgelegter Schritte. Welches Verfahren ist denn dann deiner Meinung 
nach am Sinnvollsten?


Viele Grüße

Frank


P.S: Warum Ist die Abtastfrequenz als 20-faches der Grundfrequenz 
falsch?

von Thorsten O. (Firma: mechapro GmbH) (ostermann) Benutzerseite


Lesenswert?

Weil du hier kein sinusförmiges Signal hast, was noch rekonstruierbar 
sein soll, sondern ein einfaches Rechtecksignal.

Mit freundlichen Grüßen
Thorsten Ostermann

von Frank (Gast)


Lesenswert?

Ich bin es nochmal :)

Es wäre schön wenn mir jemand weiterhelfen würde und mir sagt, welches 
Verfahren (zur Debatte stehen ja wohl nur Timer und Flankeninterrupts, 
oder?) für die Auswertung eines optischen Drehgebers mit 350 Impulsen/U 
in Bezug auf Zuverlässigkeit und Belastung des µC geeigneter ist. Wie in 
einem vorigen Beitrag erwähnt handelt es sich bei der Auswertung um die 
Positionsbestimmung, ich muss also schon die Schritte mitzählen, 
insofern sollte es schon sehr genau sein.

Vielen Dank,

Grüße,

Frank

von Albert .. (albert-k)


Lesenswert?

Frank schrieb:
> rehgebers mit 350 Impulsen/U
Eine Umdrehung pro Minute/Sekunde/Millisekunde? Da musst du schon etwas 
genauer werden.

von Frank B. (hansdampf66)


Lesenswert?

Albert ... schrieb:
> Frank schrieb:
>> rehgebers mit 350 Impulsen/U
> Eine Umdrehung pro Minute/Sekunde/Millisekunde? Da musst du schon etwas
> genauer werden.


Stimmt da fehlt n halber Satz. Der Drehgeber gibt 350 Impulse / U aus, 
der Schrittmotor dreht mit max. 300U/min

von Peter D. (peda)


Lesenswert?

Die neueren AVRs haben ja nen Pin-Change-Interrupt. Da kann man diesen 
nehmen anstelle eines Timerinterrupts. Also für beide Eingänge den 
Pin-Change-Interrupt aktivieren.
Damit ist die max CPU-Last für hohe Drehzahlen geringer.


Peter

von MaWin (Gast)


Lesenswert?

Vielleicht habe ich ja was nicht mitbekommen,
aber in der Überschrift steht Drehgeber
und im Beitrag von Frank erwähnt er magnetische Drehgeber,
ich geh also mal davon aus, daß es um Drehgeber geht.

Und da empfiehlt ein Peter Dannegger auf die Frage
"zur Debatte stehen ja wohl nur Timer und Flankeninterrupts"
allen Ernstes die komplett untauglichen Flankeninterrupts,
auch nicht mit der vollkommen falschen Begründung
"Damit ist die max CPU-Last für hohe Drehzahlen geringer."
(es wäre natürlich umgeklehrt).

Wahnsinn, das kann ja wohl nicht sein, Drehgeber liest man NIE
mit Flankeninterrupts aus, das wäre als ob man Tastendrücke
per pin change Interrupt zählen wollte (egal ob es jetzt um
Inkrementposition, Drehzahl oder Positionsbestimmung geht)

Gerade ein Peter Dannegger sollte das nach seine Entprellroutine
für Tasteneingabe verstanden haben.

Zu Drehgebern ist eigentlich alles gesagt:
http://www.dse-faq.elektronik-kompendium.de/dse-faq.htm#F.29

Auch magnetische Drehgeber prellen, elektrische Störungen im
Übergang können das bewirken.

Die Probleme (100kHz, falsche Ausgabe) sind ja schon versucht
worden zu erklären und beantworten, leider ohne Rückmeldung.

von Peter D. (peda)


Lesenswert?

Du hast schon richtig gelesen.
Der Pin-Change-Interrupt funktioniert sehr gut. Besonders, wenn einem 
Leute mit schnellem Drehen beweisen wollen, daß die Software sich 
verzählt.
Man muß also nicht ständig eine hohe Interruptlast erzeugen, sondern 
nur, wenn wirklich schnell gedreht wird.

Es gibt natürlich bei Positionsgebern an vibrierenden Maschinen die 
seltene Möglichkeit, daß eine hohe Impulszahl entsteht und die CPU 
verlangsamt (nur ein Main-Loop Befehl zwischen den Interrupts).
Aber dagegen kann man einfach in dem Pin-Change-Interrupt einen Zähler 
einbauen, der bei einem Maximalwert den Interrupt disabled und einen 
Fehler meldet. Ansonsten setzt ein Timerinterrupt diesen Zähler zyklisch 
zurück.

Auch eine Möglickeit wäre, der Pin-change-Interrupt disabled sich, setzt 
einen Timer-Compare-Interrupt auf 10µs und dieser enabled ihn wieder.
Man hat dann max 100kHz Abtastrate, aber nur bei Bedarf.


Peter

von Frank B. (hansdampf66)


Lesenswert?

Puh, jetzt bin ich leicht verwirrt. Ist also für (letztendlich) optische 
Drehgeber der Flankeninterrupt das sinnvollere?

Ich denke mal in Verbindung mit einem Zustandsvergleich durch eine Gray 
Code Tabelle oder?

Die Idee mit der Fehlermeldung bzw. dem switchen auf Timer-interrupt 
hört sich gut an (ich hoff ich schaff es gscheit zu implementieren), 
danke Peter!

Frank

von Frank B. (hansdampf66)


Lesenswert?

MaWin schrieb:
> Die Probleme (100kHz, falsche Ausgabe) sind ja schon versucht
> worden zu erklären und beantworten, leider ohne Rückmeldung.

Hab erst beim zweiten Mal erkannt, dass das an mich gerichtet war.

100 kHz sind bei mir "komischerweise" tasächlich noch notwendig und 
sogar da kommt es zu Fehlern wenn man sehr schnell mit der Hand dreht. 
Da ich aber letztendlich einen optischen Drehgeber mit 360 Impulsen/U 
verwenden werde statt den magnetischen mit 1000 Imp/U hoffe ich dass 
sich das Problem von "allein löst", ich melde mich ggf. nochmal :)

Die falsche Ausgabe lag tatsächlich am LCD und wurde von mir "behoben" 
indem ich die Werte per RS232 an den Computer ausgebe.

Möchte mir vllt noch jemand verraten ob denn nun Flanken oder doch Timer 
ISRs für meine Auswertung von optischen Drehgebern sinnvoll ist?

Grüße,

Frank

von Bjoern B. (per)


Lesenswert?

Hallo,

ich muß demnächst einen optischen Incrementalgeber (5000 Impulse pro 
Drehung) Auslesen und würde gerne die ENCODE.C variante wählen, nun muß 
ich um die Hardware anpassung vorzubehmen eine parametriebare 
Drehreichtungsumkehr mit einbauen, dazu kann ich ja entweder PHASE A mit 
PHASE B Vertauschen oder woanders ansetzen, nur sehe ich nicht genau wo 
ich das am besten im Code bewerkstellige, kann mit jemand da mal einen 
Denkanstoß geben, hab leider die Hardware noch nicht fertig um damit 
rumzuspielen, aber dieses Problem geistert mir die ganze Zeit durch den 
Kopf.

Was ich möchte das die Drehrichtung durch einen Port PIN umschaltbar 
ist, dies muß ich machen der der Incremantalgeber entweder von links 
oder von rechts ans Band geschraubt wird.

mfg,
Bjoern

von Bjoern B. (per)


Lesenswert?

Hallo,
nach dem x mal ansehen des codes, fällt mir grade etwas auf.

langt es einfach von der varibale i bit 1 zu invertieren um das zu 
erreichen was ich möchte ?

mfg,
Bjoern

von Alex (Gast)


Lesenswert?

Hallo an alle,

ich versuche schon seit Tagen den zweiten Code zu entschlüsseln. Für 
meinen optischen Impulsgeber brauche ich die Richtung. Also ob er Rechts 
oder Links herum dreht.
Kann mir vielleicht jemand erklären, wie sich die Variable enc_delta für 
die beiden Richtungen verhält? Damit kann ich dann schauen, welche 
Ausgänge am PortB freigeschaltet werden.
Momentan habe ich für einen Richtungssinn den Code : 1111 -> 1110 -> 
1101 -> 1100. Dieser Code müsste nacheinander dann am Ausgang PB0 und 
PB1 zu erkennen sein. Oder sehe ich hier irgendetwas falsch.


#include <io.h>
#include <interrupt.h>
#include <signal.h>


#define PHASE_A  (PINC & 1<<PINC0)  // PINC.0
#define PHASE_B (PINC & 1<<PINC1)  // PINC.1


volatile char  enc_delta;    // -128 ... 127


int main( void )
{
  TCCR0 = 1<<CS01;      //divide by 8 * 256
  TIMSK = 1<<TOIE0;      //enable timer interrupt

  DDRB = 0xFF;
  sei();
  for(;;)        // main loop
    PORTB = enc_delta;
}


SIGNAL (SIG_OVERFLOW0)
{
  static char enc_last = 0x01;
  char i = 0;

  if( PHASE_A )
    i = 1;

  if( PHASE_B )
    i ^= 3;        // convert gray to binary

  i -= enc_last;      // difference new - last

  if( i & 1 ){        // bit 0 = value (1)
    enc_last += i;      // store new as next last

    enc_delta += (i & 2) - 1;    // bit 1 = direction (+/-)
  }
}

von obbedaja (Gast)


Lesenswert?

Kann mir jemand erklären, wie ich aus der variable enc_delta eine 
Drehrichtungserkennung machen kann? Mich interessieren ja nur die ersten 
beiden Bits?, oder nicht?
Momentan zerbreche ich mir den Kopf darüber.

Weiterhin, möchte ich gerne wissen, wann der Drehgeber eine volle 
Umdrehung gemacht hat. Der Geber hat 500 Impulse.
Vielleicht kann ich das ja über die For Schleife in der mainloop 
realisieren? Aber irgendwie komme ich da nicht weiter....

von Bernhard M. (boregard)


Lesenswert?

obbedaja schrieb:
> Kann mir jemand erklären, wie ich aus der variable enc_delta eine
> Drehrichtungserkennung machen kann? Mich interessieren ja nur die ersten
> beiden Bits?, oder nicht?
> Momentan zerbreche ich mir den Kopf darüber.
>
> Weiterhin, möchte ich gerne wissen, wann der Drehgeber eine volle
> Umdrehung gemacht hat. Der Geber hat 500 Impulse.
> Vielleicht kann ich das ja über die For Schleife in der mainloop
> realisieren? Aber irgendwie komme ich da nicht weiter....

Die Drehrichtung erkennst Du daran, ob enc_delta positiv oder negativ 
ist. Für die volle Umdrehung must Du oft genug enc_delta in Deiner 
mainloop auslesen und aufaddieren, wenn die Summe 500 ist hat er eine 
volle Rechtsdrehung gemacht, bei -500 eine volle Linksdrehung (oder 
umgekehrt...)

von captnhanky (Gast)


Lesenswert?

wow das ist der längste thread den ich je gesehn hab

von obbedaja (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich habe es jetzt geschafft den Drehgeber und die Drehrichtung 
auszugeben. Weiterhin zählt mir ein Zähler die Inkremente und gibt pro 
voller Umdrehung ein digitales Signal aus.
Dies klappt perfekt bis zu einer bestimmten Drehzahl (ca. 500U/min), ab 
dieser werden plötzlich rechts und links Drehungen erkannt und ich kann 
damit nicht mehr weiter arbeiten.

Kann mir jemand sagen, ob dies am Inkrementalgeber liegt? Oder eher an 
der Abtastung mittels Timer. Ich lasse den Timer ohne Vorteiler laufen, 
denn mit Vorteiler kann ich auch die kleinen Drehzahlen nicht auswerten.

Ich freue mich auf eine Antwort.

von obbedaja (Gast)


Angehängte Dateien:

Lesenswert?

Der Code nochmals ein bisschen aufgeräumt.

von Werner (Gast)


Lesenswert?

obbedaja schrieb:
> Kann mir jemand sagen, ob dies am Inkrementalgeber liegt?

Das liegt wohl eher am Abtast-Theorem. Vermutlich verpaßt du Phasen des 
Gebers. Guck' dir auf 'nem Oszi mal deine Gebersignale und den 
Abtasttakt an.

von obbedaja (Gast)


Lesenswert?

Danke für die schnelle Antwort. Habe soeben die Spuren A und B mit einem 
Messrechner aufgenommmen, hatte kei Oszi zur Hand.

Im unteren Drehzahlbereich laufen die Rechtecksignale (mit leichter 
Rampenbildung) sehr genau und 90° verschoben. Erhöhe ich die Drehzahl, 
werden aus den Rechtecksignalen -> spitze Dreiecksignale und ca. jeder 
fünfte Strich wird noch als Rechteck erkannt.

Dabei hatte ich den Geber beim Drehen festgehalten und bemerkt, dass er 
eine leichte Unwucht aufweist. Ich hoffe, dass der Fehler daher kommt 
und meine Messergebnisse verfälscht.
Neuer Inkrementalgeber ist bestellt und werde sobald er da ist, neues 
berichten.

Aber eine allgemeine Frage zu dem ablaufenden Timer0 (8-bit) habe ich 
noch.
Ich benutze einen neuen Inkrementalgeber mit 1024 Impulsen. Meine 
Drehzahl ist 1333U/min.
Der Geber hat somit eine Frequenz von 22749Hz=23KHz.

Nach dem Abtasttheorem müsste ich mit 46KHz das Signal abtasten.

Wie kann ich meinen Timer einstellen, damit er dies auch so macht? D.h. 
evtl einen Vorteiler einbauen?

zur Info: Der Micrcontroller hat einen externen Quarz mit 3,6864MHz.

von obbedaja (Gast)



Lesenswert?

Anbei der Signalverlauf der mittleren und hohen Drehzahlen.

von Marcel (Gast)


Lesenswert?

Hallo,

bin echt beeindruckt von dieser Routine. Es funktioniert super. Wie 
würdet ihr den Taster des Inkrementalgebers mit einbinden? Auch in der 
ISR vom Timer? Wie würdet ihr den am besten Entprellen?
1
ISR (TIMER0_COMPA_vect)               // 1ms for manual movement
2
{
3
  int8_t neu;
4
  int8_t diff;
5
6
  neu = 0;
7
  
8
  if (!ENC_TASTER)                 // Taster des Inkrementalgebers                
9
  {
10
    enc_taster = 1;
11
  }
12
  
13
  if (ENC_CHA)
14
    neu = 3;
15
  
16
  if (ENC_CHB)
17
    neu ^= 1;                 // convert gray to binary
18
  
19
  diff = last - neu;                  // difference last - neu
20
  
21
  if (diff & 1)             // bit 0 = value (1)
22
  {                           
23
    last = neu;                      // store neu as next last
24
    enc_delta += (diff & 2) - 1;     // bit 1 = direction (+/-)
25
  }
26
}

von M. K. (kichi)


Lesenswert?

Marcel schrieb:
> Wie
> würdet ihr den Taster des Inkrementalgebers mit einbinden? Auch in der
> ISR vom Timer? Wie würdet ihr den am besten Entprellen?

Ich würde dafür Peters Entprellroutine benutzen und diese ebenfalls in 
der Timer-ISR aufrufen.

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.