Forum: Mikrocontroller und Digitale Elektronik Motorsteuerung über Impulsfolgen


von Max (Gast)


Lesenswert?

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?

von Stefan G. (steg13)


Lesenswert?

verstanden habe ich es nicht - Zeichnung ?
Vieleicht fehlt ein Sample/Hold für das analoge Signal?

von unbeschreiblicher Rahul (Gast)


Lesenswert?

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...

von Max (Gast)


Lesenswert?

@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.




von fieser, klugscheissender Rahul (Gast)


Lesenswert?

>Gibt es bei den AVR's einen A/D-Wandler Interrupt?
Zumindest ein ADIF..

von Matthias (Gast)


Lesenswert?

wenn der atmel einen AD-wandler hat, gibt es einen "adc conversion 
complete interrupt"

von fieser, klugscheissender Rahul (Gast)


Lesenswert?

Kommen die Impulse denn wenigstens in festen Abständen?
(Irgendwie klingt das Problem nach ein Infrarot-Ferbedienung...)

von Max (Gast)


Lesenswert?

> 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.

von fieser, klugscheissender Rahul (Gast)


Lesenswert?

>Und bei 70ms müsste doch genug Zeit zum A/D-Wandeln und Regeln sein.

Genau das hätte ich dann nämlich vorgeschlagen.

von Max (Gast)


Lesenswert?

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?

von Karl heinz B. (kbucheg)


Lesenswert?

> 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

von Max (Gast)


Lesenswert?

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




von unbeschreiblicher Rahul (Gast)


Lesenswert?

>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)))).

von Max (Gast)


Lesenswert?

Aber die Wandlung im Hintergrund beeiträchtigt in keinster Weise den 
restlichen Programmablauf?

von Karl H. (kbuchegg)


Lesenswert?

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.

von Max (Gast)


Lesenswert?

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?

von Max (Gast)


Lesenswert?

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?

von fieser, klugscheissender Rahul (Gast)


Lesenswert?

>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
Noch kein Account? Hier anmelden.