Hallo,
ich habe zwei Programme für einen Atmega geschrieben. Das eine ist eine
Motoregelung/steuerung. Der Motor wird über einen Mototreiber
angesteuert und sendet seine aktuelle Position in Form einer
Spannung(Poti am Motor) zurück zum Controller. Durch einen Soll -
Istwert_Vergleich kann ich die Position bestimmen. Funktioniert bestens.
Das zweite Programm ließt ein Signal ein. Das Signal hat 8 Impulse,
welche als eine 8-Bit Variable interpretiert werden. Je nach länge
dieser Impulse wird ein Bit als logische 0 oder 1 angesehen.
Funktioniert auch einwandfrei.
Nun wollte ich diese beide Programme zusammenfügen, um die Position des
Motors über das Signal zu bestimmen, was aber irgendwie nicht
funktioniert.
Nach einer kleinen Fehleranaylyse habe ich festgestellt, dass der
Sollwert(8-Bit Wert des Signals) nicht konstant ist. Das liegt am
A/D-Wandler, der mir den Rückgabewert von Servo auswertet. Daher wird
das Signal nicht richtig eingelesen.
Nun habe ich es so gehandhabt, dass die A/D-Wandlung und die Regelung
erst dann startet, wenn das Signal komplett eingelesen wurde. Sobald der
Motor sich eingeregelt hat, wird die A/D-Wandlung und die Regelung
ausgeschaltet und das Signal wieder eingelesen.
Funktioniert auch alles soweit, nur die Regelung ist extrem langsam.
Woran kann das liegen, bzw. wie kann ich das beheben?
Die Impulslänge per ICP messen...
Den ADC auch per Interrupt steuern...
Am besten alles über einen Timer steuern. Dann hast du eine konstante
Regelzeit...
@Rahul
> Die Impulslänge per ICP messen...
Hatte ich erst vor. Jedoch will ich das Programm später auf nem Attiny
laufen lassen, und der hat leider keinen ICP. Jedoch ist das Vorgehen
ähnlich. Über einen externen Interrupt detektiere ich mir die positive
und die negative Flanke und bestimme mit einem Timer die Zeit
dazwischen.
> Den ADC auch per Interrupt steuern...
Hab ich noch nie ausprobiert. Gibt es bei den AVR's einen A/D-Wandler
Interrupt?
> Am besten alles über einen Timer steuern. Dann hast du eine konstante> Regelzeit...
Müsste man mal versuchen...
@Stefan
> Vieleicht fehlt ein Sample/Hold für das analoge Signal?
Also das Signal ist mehr als konstant und das einlesen bereitet ja auch
keinerei Schwierigkeit. Das Problem ist der Ablauf zwischen den
Funktionen Signal auswerten, A/D-Wandeln und Regeln.
Alles einzeln Funktioniert bestens. Nur wenn ich alles zusammen laufen
lassen, dann stört die A/D-Wandlung das einlesen des Signals. Ist ja
auch klar, wenn 8 Impulse am Stück über einen externen Interrupt
eingelesen werden sollen, deren Impulslängen nur ein paar µs
entsprechen. Das sind 18-Interrupts. Wenn dazwischen ne A/D-Wandlung
ausgeführt wird, werden schnell mal 1-2 Impulse übersprungen.
> Kommen die Impulse denn wenigstens in festen Abständen?
Ja, die Impulse kommen in festen Abständen. Ein Signal, also alle 8
Impulse dauern ca. 1,5ms. Danach ist 70ms Pause und es kommen wieder 8
Impulse.
Und bei 70ms müsste doch genug Zeit zum A/D-Wandeln und Regeln sein.
Das klappt so einigermaßen.
Nun muss ich es irgendwie hinbekommen, dass er erst das nächste Signal
einließt, wenn der Motor sich eingeregelt hat.
Im Moment mach ich das so:
if((Iswert <= (Sollwert + 4)) && (Istwert >= (Sollwert - 4)))
{
Motor_Anhalten = 1; //Motor stoppen
Signal_Einlesen = 1; //Signal einlesen zulassen
}
Das Problem ist das er diese Bedingung ja auch schon beim Darüberlaufen,
also dann wenn der Motor noch nicht eingeregelt ist, erfüllt.
Diese Bedingung müsste also eine bestimmte Zeit lang erfüllt sein, bevor
die anderen Sachen wieder freigegeben sind.
Wie kann ich das programmiertechnisch am besten umsetzen. Einer ne Idee?
> Nun habe ich es so gehandhabt, dass die A/D-Wandlung und die Regelung> erst dann startet, wenn das Signal komplett eingelesen wurde. Sobald der> Motor sich eingeregelt hat, wird die A/D-Wandlung und die Regelung> ausgeschaltet und das Signal wieder eingelesen.
Du musst das ganze dynamisch ineinanderschachteln.
Im Moment macht dein Program (wenn ich das richtig
interpretiere
1 Warte auf Signal -> Sollwert
2 lies Motorposition
3 Vergleich Soll - Ist
4 Motor entsprechend Vergleich ansteuern
5 Sollwert erreicht? Nein -> goto 2
6 goto 1
D.h. du wartest immer bis ein Teilprozess komplett
fertig ist, sei es das Einlesen des Signals oder
das Erreichen der Sollposition
Was du brauchst ist ein mehr dynamischer Prozess.
Deine Hauptschleife sollte ständig dieses machen:
1 Motorposition per ADC feststellen
auf ADC fertig warten
Sollwert holen
ADC neu starten
2 Sollwert/Istwert vergleichen
3 Motor entsprechend ansteuern
4 Hat die Signal-ISR ein neues Bit empfangen Nein -> goto 8
5 Bit an Nachrichtentelegram dranhängen
6 Telegram vollständig Nein -> goto 8
7 Neue Sollposition aus dem Telegram extrahieren
8 goto 1
d.h. die Regelschleife läuft immer!
Sie wird nur kurz von einem Interrupt unterbrochen
der Auftritt wenn am Signaleingang eine Flanke detektiert wurde.
Der Interrupt entscheidet noch schnell was als nächstes zu
tun ist
* bei steigender Flanke Timer Zählerstand auslesen
und Interrupt auf fallende Flanke umstellen
* bei fallender Flanke Timer Zählerstand auslesen
und Interrupt auf steigende Flanke umstellen
Aus den Zählerständen bestimmen ob das eine 0 oder 1 war.
Der Hauptschleife hinterlassen, dass wieder ein Bit
vorhanden ist
Auf die Art sind die einzelnen Aktionen ineinander geschachtelt.
Jede Aktion macht ein bischen was und gibt dann die Rechenzeit
weiter an die nächste Aktion, die auch wieder nur ein bischen
was macht
* Die Motorregelung regelt nicht den Motor komplett aus sondern
stellt nur die Richtung ein in die es geht
* Die Signalempfangskomponente wartet nicht auf ein komplettes
Telegram sondern wenn ein Bit eintrudelt, dann wird es
verarbeitet.
* Ist ein neues Telegram komplett empfangen worden, so wird
der neue Sollwert gesetzt, unabhängig davon ob der Motor
die vorhergehende Sollposition schon erreicht hat oder
nicht.
* Während die Regelschleife eine Runde dreht, sampelt der
ADC schon wieder den nächsten Messwert im Hintergrund
Hallo Karl Heinz,
vielen Dank für den tollen Beitrag! Hab es jetzt ungefähr so gemacht wie
du oben beschrieben hast. Der Regelung bzw. Steuerung funktioniert
mitlerweile richtig gut.
Was ist nicht ganz verstanden hab ist folgendes:
> Während die Regelschleife eine Runde dreht, sampelt der ADC schon wieder > den
nächsten Messwert im Hintergrund
Wie läuft den der A/D-Wndler im Hintergrung? Wird das parallel
ausgeführt? Eigentlich nicht, oder?
Meine Wandler Funktion sieht folgendermaßen aus:
void ADC_Wandlung_Steuerung() //PORTC, Bit2
{
ADCSRA |= (1<<ADSC); //AD Wandlung
while (ADCSRA & (1<<ADSC))
{
;
}
ADCSRA &= ~(1<<ADSC); //AD Wandlung deaktivieren
lenkung_ist = ADCH;
}
Ein weiteres Problem ist, das der Servo anfängt zu zittern wenn er in
die Endposition, also nach ganz links oder ganz rechts fährt. Woran kann
das liegen? Die anderen positionen fährt der Servo wunderbar an.
Gruß Max
>while (ADCSRA & (1<<ADSC))
Das lässt du an der Stelle einfach mal sein.
Der ADC wandelt vor sich hin, und wenn du der Meinung bist, dass du mal
nachgucken willst, ob der ADC fertig ist, guckst halt nach (per
if(!(ADCSRA & (1<<ADSC)))).
Schau noch mal genau wie ich das in der Zusammenfassung
vorgechlagen habe.
Das Prinzip ist:
Wandlung starten
while( 1 ) {
Warten auf AD Wandler
Messwert abholen
Nächste Messung starten
Messwert verarbeiten bzw. andere Dinge tun
}
Der ADC braucht dein Programm nicht um seine Arbeit
zu tun. Du startest den AD Wandler und tust dann was
auch immer du sonst noch zu tun hast. Erst dann, wenn
das Restliche alles erledigt ist und du wieder Zeit hast
siehst du nach ob der AD schon fertig ist und du den
Messwert abholen kannst.
Du musst dir das "Warten auf irgendetwas" abgewöhnen.
Du willst nicht warten. Stattdessen dreht die Hauptschleife
immer wieder ihre Runden und sieht nach ob es irgendwo
etwas zu tun gibt. Wobei das "etwas" immer nur kleine, kurze,
schnelle Dinge sein sollten.
Muss ich den A/D-Wandler nach dem ich den Messwert abgeholt habe mit
ADCSRA |= (1<<ADSC); neu starten, oder läuft er automatisch weiter?
Hab vorhin in einem Beitrag gelesen, dass es möglich ist den A/D-Wandler
bei nur 8-Bit etwas schneller zu takten. Würde das in meinem Fall in
Frage kommen und was bringen?
So, funktioniert soweit alles recht gut. Nur halt noch das Problem mit
den Grenzbereichen.
Wenn das Poti an die äußeren Grenzen kommt, also bei einem Abgriff der
gegen 0 geht und bei einem Abgriff der nahe der Betriebsspannung liegt,
fängt der Motor an zu zittern und regelt die Position anscheinend nicht
vernünftig ein.
Was kann man da machen und woran liegt das?