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
unsignedintcounter;
15
16
voidInitializeClocks(void);
17
voidencoder();
18
19
voidmain(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
voidInitializeClocks(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
voidencoder()
53
{
54
staticunsignedcharoldstate;
55
unsignedcharport,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
}elseif(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
__interruptvoidPort_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?
@ 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.
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?
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.
@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.
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...
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...
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.
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...
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...
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.
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.