Forum: Mikrocontroller und Digitale Elektronik Encoder Verständnisfrage


von B. B. (bebu1)


Lesenswert?

Ich verstehe leider nicht warum meine Encoderauswertung funktioniert ;)
Der Code ist eine Abwandlung von einem gefundene Bsp.
1
#include <msp430.h> 
2
#include "main.h"
3
4
/*
5
 * main.c
6
 */
7
#define ENC_A BIT1 // Encoder Pin A to P1.1
8
#define ENC_B BIT2 // Encoder Pin B to P1.2
9
#define LED1 BIT0
10
#define LED2 BIT6
11
12
13
14
unsigned int counter;
15
16
void InitializeClocks(void);
17
void encoder();
18
19
void main(void) {
20
21
  InitializeClocks();
22
    WDTCTL = WDTPW | WDTHOLD;  // Stop watchdog timer
23
    P1SEL = 0x00;        // I/O function is selected
24
    P1SEL2 = 0x00;
25
    P1OUT = ENC_A + ENC_B;    // need to set so Pullup resistor will be selected
26
  P1DIR = LED1 + LED2;    // Set Port 1 as input and LEDs as output
27
  P1REN = ENC_A + ENC_B;    // enable Pullup resistor
28
  P1IES = 0x00;        // select low to high transition for interrupts on ENC_A and ENC_B port pin
29
  P1IE |= ENC_A + ENC_B;    // enable interrupt for ENC_A and ENC_B
30
  //P1IFG &= ~ENC_A + ENC_B ;
31
32
  __enable_interrupt();
33
  //__bis_SR_register(GIE);
34
  while(1)
35
  {
36
37
  }
38
39
}
40
41
42
void InitializeClocks(void)
43
{
44
45
  BCSCTL1 = RSEL0 +RSEL1+RSEL2+RSEL3;                    // Set range
46
  DCOCTL = DCO0+DCO1 + MOD0+MOD1+MOD2+MOD4;  //tune to  F Average = 16Mhz
47
  BCSCTL2 = SELM_0 ;
48
49
}
50
51
52
void encoder()
53
{
54
  static unsigned char oldstate;
55
  unsigned char port, state;
56
57
  if(P1IFG & 0x06){
58
    __delay_cycles(6000);
59
60
    port = P1IN;
61
    state = (port & ENC_B)>>2 | (port & ENC_A);
62
63
    if(state == 0x03){
64
      if(oldstate == 0x01){
65
        counter++;
66
        P1OUT ^= LED1;
67
      }else if (oldstate == 0x02){
68
        counter--;
69
        P1OUT ^= LED2;
70
      }
71
    }
72
    oldstate = state;
73
  }
74
  __delay_cycles(6000);
75
  P1IFG = 0x00;
76
77
}
78
79
#pragma vector=PORT1_VECTOR
80
__interrupt void Port_1(void)
81
{
82
  __disable_interrupt();
83
     encoder();
84
     __enable_interrupt();
85
}

Der Taster hat seine Rastpunkte bei 00. Die Drehrichtungserkennung 
verstehe ich soweit. Da einzige was ich nicht verstehe ist warum die 
Zwischenpunkte überhaupt erkannt werden. Warum ist die Überprüfung auf 
die steigende Flanke (if(P1IFG & 0x06)) überhaupt jemals true?  Es gibt 
ja nur eine steigende Flanke entweder auf A oder B?
Ich bin ein sehr unerfahrener Programmierer wie man schnell bemerkt ;)
Wo liegt mein Denkfehler?

von Jörg H. (sonichazard)


Lesenswert?

Schon mal hier die Grundlagen angeschaut?
https://www.mikrocontroller.net/articles/Drehgeber
Wie werden Interrups an Ports maskiert?

von B. B. (bebu1)


Lesenswert?

Den Artikel hätte ich mir schon durchgelesen.. dann noch einmal ;)

von Falk B. (falk)


Lesenswert?

@ Bernhard B. (bebu1)

>Ich verstehe leider nicht warum meine Encoderauswertung funktioniert ;)
>Der Code ist eine Abwandlung von einem gefundene Bsp.

Dann hast du ein schlechtes Beispiel gefunden. Du machst einige 
elementare Fehler.


>  P1IE |= ENC_A + ENC_B;    // enable interrupt for ENC_A and ENC_B
>  //P1IFG &= ~ENC_A + ENC_B ;

Falsch! Man nutzt KEINE Pin Change Interrupts für die Auswertung eines 
Drehencoders sondern tastet sie in einem periodischen Timerinterrupt ab 
und wertet aus. Warum das so besser ist, steht im Artikel Drehgeber.

>  if(P1IFG & 0x06){
>    __delay_cycles(6000);

Das ist schon mal totaler Mist! Delays sind meist ein Zeichen von 
schlecthem Stil und Konzept. Mit einem Timer-Interrupt wäre das 
überflüssig und du verheizt nicht sinnvol CPU-Leistung.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Bernhard B. schrieb:
> Es gibt ja nur eine steigende Flanke entweder auf A oder B?
Der Witz einer gut funktionierenden Encoderauswertung ist, nicht die 
Flanken auszuwerten, sondern die Zustände bzw. Pegel und damit eine 
Zuatandsmaschine weiterzuschalten. Das hört sich jetzt erstmal arg 
wissenschaftlich an, verliert aber sofort den Schrecken,  wenn man weiß, 
dass jeder simple Zähler auch eine Zuatandsmaschine ist.

Bernhard B. schrieb:
> Den Artikel hätte ich mir schon durchgelesen.. dann noch einmal
Durchlesen allein reicht nicht. Du musst ihn verstehen. Was ist da 
unklar? Wo klemmt es beim Verstehen?

von c-hater (Gast)


Lesenswert?

Falk B. schrieb:

> Falsch! Man nutzt KEINE Pin Change Interrupts für die Auswertung eines
> Drehencoders sondern tastet sie in einem periodischen Timerinterrupt ab
> und wertet aus. Warum das so besser ist, steht im Artikel Drehgeber.

Das ist in dieser verabsolutierten Form kompletter Blödsinn und du 
weisst das auch nur zu genau, da bin ich mir absolut sicher.

Warum also wiederholst du also diese offensichtliche Falschdarstellung 
mit nicht nachlassender Intensität? Das hört sich für mich wie das 
Rezitieren von Glaubenssätzen an, nicht wie die Darstellung von Fakten.

Fakt ist, es gibt sicher sehr viele Anwendungen, für die Polling (auch 
Abfrage in einem Timerinterrupt ist Polling!) der bessere Ansatz ist. Es 
gibt aber auch viele Anwendungen, wo der Betrieb über Pin Change 
Interrupts vorzuziehen wäre.

Damit sind wir gleich bei den Hauptnachteilen des 
Pinchange-Interruptbetriebs:

1) man benötigt für einen strukturell sicheren Betrieb auch mit 
mechanischen Encodern Marke billich ZWEI unabhängig voneinander 
sperrbare Interrupts. Das ist zugegebenermassen ein grosser Nachteil, 
denn es schränkt zumindest die Wahl der Pins ein, u.U. sogar ganz 
erheblich. Außerdem schränkt es auch erheblich die Zahl der Encoder ein, 
die man behandeln kann.

2) es ist nicht möglich, sozusagen im "gleichen Aufwasch" auch noch 
einen mechanischen Taster mit zu behandeln (also den Druck auf die 
Encoder-Achse), denn dafür ist eine klassische Entprellung nötig, also 
irgendwie doch wieder ein Timer.

Wann also sollte man, trotz dieser nicht wegzudiskutierenden Nachteile, 
auf eine Pinchange-Interrupt-getriebene Lösung setzen?

Dafür gibt es genau drei Szenarios:

1) Einer (oder zumindest sehr wenige) Encoder, aber hohe Anforderungen 
an die maximal mögliche Schrittrate.

2) Einer (oder zumindest sehr wenige) Encoder, aber hohe Anforderungen
bezüglich des minimalen Energieverbrauchs.

3) Die Kombination aus 1) und 2).

>>  if(P1IFG & 0x06){
>>    __delay_cycles(6000);
>
> Das ist schon mal totaler Mist! Delays sind meist ein Zeichen von
> schlecthem Stil und Konzept.

Da gehe ich absolut mit. Delays jenseits einiger (weniger) MCU-Takte 
sind immer böse und unsinnig. Und: man braucht sie auch für eine 
Pinchange-Interrupt-Lösung nicht, wenn diese der o.g. Anforderung 
genügt.

Denn das ist der springende Punkt: Auch wenn es sich um mechanische 
Kontakte handelt, ist ein Rotationsencoder OHNE explizite Entprellung 
zu behandeln. Das resultiert aus der Codierung der Sache. Genauso, wie 
man etwa für einen mechanischen Wechsler keine Entprellung benötigt.

von Falk B. (falk)


Lesenswert?

@c-hater (Gast)

>> Falsch! Man nutzt KEINE Pin Change Interrupts für die Auswertung eines
>> Drehencoders sondern tastet sie in einem periodischen Timerinterrupt ab
>> und wertet aus. Warum das so besser ist, steht im Artikel Drehgeber.

>Das ist in dieser verabsolutierten Form kompletter Blödsinn und du
>weisst das auch nur zu genau, da bin ich mir absolut sicher.

Auch du hast mal wieder Schaum vorm Mund, nix neues. Weder liest noch 
verstehst du meinen obigen Satz, von den Sätzen im Artikel ganz zu 
schweigen.

Nein, das Thema werde ich keine Sekunde mehr diskutieren, denn alles was 
ich zu sagen hätte steht im Artikel Drehgeber, incl. der Notation 
von was zwingend nötoig und was EMPFEHLENSWERT ist.


>1) Einer (oder zumindest sehr wenige) Encoder, aber hohe Anforderungen
>an die maximal mögliche Schrittrate.

Nö, denn bei maximaler Schrittrate muss die CPU das so oder so schaffen. 
Da ist der Timer nicht im Nachteil, eher im Vorteil, denn der ist 
deterministisch und stellt eine konstante CPU-Last dar.

>2) Einer (oder zumindest sehr wenige) Encoder, aber hohe Anforderungen
>bezüglich des minimalen Energieverbrauchs.

Na gut. Das allerletzte Argument, das halbwegs was taugt ;-)
Ist aber für 90% aller Anwednungen egal.

von c-hater (Gast)


Lesenswert?

Falk B. schrieb:

>>1) Einer (oder zumindest sehr wenige) Encoder, aber hohe Anforderungen
>>an die maximal mögliche Schrittrate.
>
> Nö, denn bei maximaler Schrittrate muss die CPU das so oder so schaffen.

Das ist nicht einmal prinzipiell richtig.

Du vergisst hier schlicht das Nyquist-Kriterium. D.h.: du mußt mehr als 
doppelt so schnell pollen, wie die eigentliche Rate der Signaländerungen 
beträgt. Die PCINT-getriebene Lösung hingegen kommt mit der Rate der 
Signaländerungen aus...

Darüber hinaus:

Wenn man mal annimmt, dass die maximale Rate der Grenzfall ist, also 
höchstens kurzzeitig auftritt, dann könnte eine PCINT-getriebene Lösung 
nach diesem Peak einfach weiter tun, was sie halt tun soll, nur für die 
Zeit dieses Maximums ist sie praktisch nicht reaktionsfähig. Eine per 
Polling betrieben Lösung muss aber ständig mit Vmax pollen, die 
Auswertung des Ergebnisses kommt hier also (bei gleicher zu erfassender 
Schrittrate) praktisch nie zum Zuge, selbst wenn an den Eingängen rein 
garnix mehr passiert.

> Da ist der Timer nicht im Nachteil, eher im Vorteil, denn der ist
> deterministisch und stellt eine konstante CPU-Last dar.

Tsss, sehr schwaches Argument, deutliches Zeichen von: Prinzip nichtmal 
richtig begriffen. Natürlich sind zwei sich gegenseitig verriegelnde 
PCINT-ISRs hier exakt genauso deterministisch wie eine Timer-ISR, also 
vollständig...

> Na gut. Das allerletzte Argument, das halbwegs was taugt ;-)
> Ist aber für 90% aller Anwednungen egal.

Das sagst du. Ich würde hingegen sagen, dass gerade batteriebetriebene 
Anwendungen einen nicht unerheblichen Anteil bei den AVR-Anwendungen 
darstellen. Denn: wenn Energie keine Rolle spielt, kann man mit den 
größeren Eisen viel leichter (und heutzutage ironischerweise oft auch 
noch billiger) zum gleichen Ergebnis kommen...

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Sind wir hier bei "du und ich" oder gehts um eine Sache?

c-hater schrieb:
> Natürlich sind zwei sich gegenseitig verriegelnde PCINT-ISRs hier exakt
> genauso deterministisch wie eine Timer-ISR, also vollständig...
Aber viel undurchschaubarer und viel schlechter wartbar. Denn allein 
schon die Frage nach der Priorität der beiden Interrupts untereinander 
ist spannend.
Mein Fazit aus vielen Jahren Programmiererei: vermeide Interrupts wo 
immer möglich...

von Wolfgang (Gast)


Lesenswert?

c-hater schrieb:
> Wenn man mal annimmt, dass die maximale Rate der Grenzfall ist, also
> höchstens kurzzeitig auftritt, dann könnte eine PCINT-getriebene Lösung
> nach diesem Peak einfach weiter tun, was sie halt tun soll, nur für die
> Zeit dieses Maximums ist sie praktisch nicht reaktionsfähig.

Wenn man jetzt aber annimmt, dass der Encoder dazu verwendet wird, eine 
Drehung zu erfassen, dann ist alleine schon auf Grund des 
Trägheitsmomentes des sich drehenden Teils so ein Maxiumum recht weich. 
Die Welle wird also nicht nur mal eben kurz auf Maximaldrehzahl/Frequenz 
geht.

Und so lange willst du den Prozessor quasi lahm legen?
Da geht dann jegliche Echtfähigkeit den Bach runter.

von c-hater (Gast)


Lesenswert?

Lothar M. schrieb:

> Aber viel undurchschaubarer

Nicht für Leute, die Interuptprogrammierung einfach mal beherrschen.

> Denn allein
> schon die Frage nach der Priorität der beiden Interrupts untereinander
> ist spannend.

In diesem Fall ganz sicher nicht. Denn es ist (abgesehen von dem 
Zeitraum zwischen Programmstart und allererster Interruptauslösung) 
immer nur maximal einer der beiden Interrupts aktiv. Genau das ist ja 
der Trick mit der gegenseitigen Verriegelung (die eigentlich eine 
gegenseitige ENT riegelung ist)

> Mein Fazit aus vielen Jahren Programmiererei: vermeide Interrupts wo
> immer möglich...

So viele Jahre und rein garnix Nützliches dazu gelernt? Tsss...

von c-hater (Gast)


Lesenswert?

Wolfgang schrieb:

> Und so lange willst du den Prozessor quasi lahm legen?

Nun, zumindest längst nicht so lange wie eine Polling-Lösung. Das ist 
der springende Punkt, falls du das nicht begriffen hast...

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

c-hater schrieb:
>> Denn allein schon die Frage nach der Priorität der beiden Interrupts
>> untereinander ist spannend.
> In diesem Fall ganz sicher nicht. Denn es ist (abgesehen von dem
> Zeitraum zwischen Programmstart und allererster Interruptauslösung)
> immer nur maximal einer der beiden Interrupts aktiv.
Allein schon das Sperren und Verriegeln der Interrupts ist mir zu 
umständlich. Und warum irgendwas umständlich machen, wenn es einfach 
auch geht.
Und es kann immer noch sein, dass diese Ver- und Entriegelungsmimik bei 
so einem simplen Kandidaten wie dem AVR noch "problemlos" geht, weil der 
sowieso nur 1 Interruptebene hat und Interrupts nicht unterbrochen 
werden können. Aber sobald da mal ein etwas aufwändigerer 
Interruptcontroller im Spiel ist, dann initialisiert man genau 1x seine 
Interrupts und lässt fürderhin die Finger weg.

c-hater schrieb:
>> Und so lange willst du den Prozessor quasi lahm legen?
> Nun, zumindest längst nicht so lange wie eine Polling-Lösung.
Ich kenne solche interruptgetriebene Softwarekonstrukte. Die waren 
irgendwann kurz vor der Jahrtausendwende so richtig hip.
Und vor einem halben Jahr habe ich in so einer Steuerung wieder einen 
Fehler gefunden: ein Sensoreingang wurde hochohmig und produzierte 
Rauschen und Spikes am Interruptpin. Dadurch wurde der Prozessor mehr 
oder weniger mit "unerwarteten" Interrupts be- und überlastet, so dass 
keine Zeit mehr für die restlichen Interrupts und das Hauptprogramm war 
und die seltsamsten Fehlerbilder auftraten. Als ich die Fehlerursache 
gefunden hatte, brachte die Nachfrage beim Kundendienst, dass sowas 
schon immer wieder mal vorkam und dann alles Mögliche getauscht wurde 
(weil ja alle möglichen Sekundärfehler auftraten) bis die Maschine zu 
guter Letzt irgendwann wieder lief.

c-hater schrieb:
>> Mein Fazit aus vielen Jahren Programmiererei: vermeide Interrupts wo
>> immer möglich...
> So viele Jahre und rein garnix Nützliches dazu gelernt? Tsss...
Doch, ich konnte in dieser Zeit viel Nützliches lernen, in der ich 
diesen Interruptverundentriegelungsproblemen nicht hinterhergerannt 
bin.

von Wolfgang (Gast)


Lesenswert?

c-hater schrieb:
> Nun, zumindest längst nicht so lange wie eine Polling-Lösung.

Wenn man dermaßen an die Prozessorgrenzen geht, ist beim 
"Hochgeschwindigkeitspolling" die Diagnose wesentlich einfacher, als 
wenn manchmal die Encoder-Interrupst die Kiste zum Stehen oder gar den 
Stack zum überlaufen bringen. Da wird es dann Zeit, über eine 
Hardwareunterstützung für Encoderauswertung nachzudenken.

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.