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?
verstanden habe ich es nicht - Zeichnung ? Vieleicht fehlt ein Sample/Hold für das analoge Signal?
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.
wenn der atmel einen AD-wandler hat, gibt es einen "adc conversion complete interrupt"
Kommen die Impulse denn wenigstens in festen Abständen? (Irgendwie klingt das Problem nach ein Infrarot-Ferbedienung...)
> 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.
>Und bei 70ms müsste doch genug Zeit zum A/D-Wandeln und Regeln sein.
Genau das hätte ich dann nämlich vorgeschlagen.
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)))).
Aber die Wandlung im Hintergrund beeiträchtigt in keinster Weise den restlichen Programmablauf?
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?
>Was kann man da machen und woran liegt das?
Könnte am Poti liegen.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.