Forum: Mikrocontroller und Digitale Elektronik Code-Optimierung uC PID Regler


von Markus P. (maxpeer)


Angehängte Dateien:

Lesenswert?

Hallo,

ich verwende für eine kleine Stromregelung einen Arduino DUE. Die 
Regelung sollte bis mind. 50kHz gut funktionieren. Das Soll- sowie das 
Ist-Signal werden über die Analog Inputs eingelesen, daraus (momentan 
als PI-Regler implementiert) ein Ausgangswert gebildet und dieser über 
einen analogen Leistungsverstärker auf die Strecke geschickt.
Mein Problem ist aber, dass der eigentlich recht einfache Code relativ 
lange braucht und somit bereits bei 10kHz eine beträchtliche 
Phasenverschiebung rein aufgrund des uC entsteht.
Gibt es Ansätze oder Lösungsvorschläge, wie ich das Programm effizienter 
schreiben kann? (Anbei noch mein Code)
Vielen Dank!

von Ingo L. (corrtexx)


Lesenswert?

Also an den Codezeilen kann es denke ich mal nicht liegen, wie lange 
bleibt er denn in der While() hängen? Setz mal in der While() einen Pin 
auf High und oszillografiere.

von Falk B. (falk)


Lesenswert?

Markus P. schrieb:
> ich verwende für eine kleine Stromregelung einen Arduino DUE.

Ok, der hat 84 MHz mit Cortex M3 CPU.

> Die
> Regelung sollte bis mind. 50kHz gut funktionieren.

Das ist schon recht anspruchsvoll. Ich hab vor Jahren mal ein Design mit 
einem PICCOLO gemacht, der läuft auf 60MHz. Die 100kHz ISR für die 
Regelung und ADC-Auslesung läuft rein in Assembler. OK, dort sind 5 
PID-Regler und noch Krümelkram drin, aber dafür braucht sie auch ca. 60% 
der CPU-Leistung.

> Das Soll- sowie das
> Ist-Signal werden über die Analog Inputs eingelesen, daraus (momentan
> als PI-Regler implementiert) ein Ausgangswert gebildet und dieser über
> einen analogen Leistungsverstärker auf die Strecke geschickt.

Da ist noch ein Fehler drin. Der I-Anteil muss VOR der Berechnung des 
Stellwerts addiert werden, sonst hast du eine runde Verspätung, sprich 
Phasenverschiebung.
Eine Warteschleife hat in so einer ISR eigentlich nix zu suchen. Deine 
ADCs und der DAC müssen so synchronisiert sein, daß man ohne Wartepausen 
die ADCs lesen und den DAC schreiben kann.

: Bearbeitet durch User
von Monk (roehrmond)


Lesenswert?

Wenn kI und kP (aus Sicht des Compilers) konstanten wären, könnte er die 
Multiplikation besser optimieren. Mit den gezeigten Werten würden sie 
sogar ganz entfallen.

von Xanthippos (xanthippos)


Lesenswert?

Wenn du von 2 Kanälen liest, muss der ADC warten bis der S&H Kondensator 
umgeladen ist. Hast du schon getestet, ob du die Default-Zeiten 
verringern kannst?

von Markus P. (maxpeer)


Lesenswert?

Ingo L. schrieb:
> Also an den Codezeilen kann es denke ich mal nicht liegen, wie lange
> bleibt er denn in der While() hängen? Setz mal in der While() einen Pin
> auf High und oszillografiere.

in der While() Schleife dürfte er vermutlich gar nicht hängen geblieben 
sein, zumindest erfasst es das Oszi gar nicht. Ich habe die Zeile jetzt 
trotzdem mal entfernt.

von Falk B. (falk)


Lesenswert?

Xanthippos schrieb:
> Wenn du von 2 Kanälen liest, muss der ADC warten bis der S&H Kondensator
> umgeladen ist.

Er liest aus den Ergebnisregistern des ADC. Der muss synchronisiert 
laufen, daß zum Zeitpunkt des Timer-Interrupts die Messung schon 
abgeschlossen ist. In der Timer-ISR ist KEINE Zeit zum Start der 
ADC-Wandlung und Warten auf das Ergebnis!

von Andreas R. (noobsen)


Lesenswert?

Über welche IDE hast du das Programm geschrieben?
z. B. hat alles was du über Arduino IDE schreibst einen riesen Overhead.

von Markus P. (maxpeer)


Lesenswert?

Falk B. schrieb:
> Eine Warteschleife hat in so einer ISR eigentlich nix zu suchen. Deine
> ADCs und der DAC müssen so synchronisiert sein, daß man ohne Wartepausen
> die ADCs lesen und den DAC schreiben kann.

Wie kann ich den ADC und DAC synchronisieren? Den ADC Clock kann man 
über einen Prescaler einstellen, aber beim DAC habe ich nichts 
dementsprechendes gefunden.
Die Warteschleife ist wie gesagt jetzt mal entfernt worden.

von Markus P. (maxpeer)


Lesenswert?

Steve van de Grens schrieb:
> Wenn kI und kP (aus Sicht des Compilers) konstanten wären, könnte er die
> Multiplikation besser optimieren. Mit den gezeigten Werten würden sie
> sogar ganz entfallen.

Also einfach außerhalb definieren? kI und kP sind nämlich konstant.

von Falk B. (falk)


Lesenswert?

Markus P. schrieb:
>> Eine Warteschleife hat in so einer ISR eigentlich nix zu suchen. Deine
>> ADCs und der DAC müssen so synchronisiert sein, daß man ohne Wartepausen
>> die ADCs lesen und den DAC schreiben kann.
>
> Wie kann ich den ADC und DAC synchronisieren?

Das kommt auf den Mikrocontroller an. Beim PICCOLO kann man den ADC 
über verschiedene Events in Hardware auslösen. In meinem Projekt per 
Timer mit der passenden Phasenverschiebung. Wie das bei deinem 
COntroller funktioniert weiß ich nicht.
Für den DAC braucht man sowas AFAIK nicht, den kann man direkt 
beschreiben, denn der ist sauschnell. Das sollte im Datenblatt stehen.

> Den ADC Clock kann man
> über einen Prescaler einstellen,

Das reicht nicht, der Meßzeitpunkt muss genau bestimmt sein.

von Markus P. (maxpeer)


Angehängte Dateien:

Lesenswert?

Falk B. schrieb:
> In der Timer-ISR ist KEINE Zeit zum Start der
> ADC-Wandlung und Warten auf das Ergebnis!

Die ADC-Wandlung wird auch schon vorher gestartet. Der Ausgang wird 
jetzt im 1us - Takt am DAC ausgegeben, siehe Bild im Anhang. 
Interessanterweise schreibt er maximal im 1us - Takt, selbst wenn ich 
die Zeit beim Timer auf z.B. 500ns herabsetze. Ich weiß allerdings auch 
nicht genau, wie ich ADC und DAC miteinander synchronisieren kann. Ich 
habe dazu nicht wirklich etwas im Datenblatt gefunden - irgendwelche 
Ideen?

von Falk B. (falk)


Lesenswert?

Markus P. schrieb:
> Also einfach außerhalb definieren? kI und kP sind nämlich konstant.

Dann kannst du aber während der Laufzeit nix mehr einstellen. Das ist 
eher ungünstig. Die Multiplikation zweier Variablen ist für so eine 32 
Bit CPU ein Klacks, der wenige Takte dauert. Dort kann man nicht 
sinnvoll optimieren.

von Falk B. (falk)


Lesenswert?

Markus P. schrieb:
> Die ADC-Wandlung wird auch schon vorher gestartet. Der Ausgang wird
> jetzt im 1us - Takt am DAC ausgegeben, siehe Bild im Anhang.
> Interessanterweise schreibt er maximal im 1us - Takt, selbst wenn ich
> die Zeit beim Timer auf z.B. 500ns herabsetze.

Und du glaubst, daß ein 500ns (NANOSEKUNDEN) Timer ISR immer mit 500ns 
aufgerufen wird? Ich nicht.

> Ich weiß allerdings auch
> nicht genau, wie ich ADC und DAC miteinander synchronisieren kann. Ich
> habe dazu nicht wirklich etwas im Datenblatt gefunden - irgendwelche
> Ideen?

Siehe oben. Du musst einen Weg finden, den ADC synchron zu starten. Im 
einfachsten Fall am Ende der Regler-ISR mittels CPU. Dann läuft die 
Messung in der Zeit bis zum nächsten Timer-Interrupt und man kann die 
Ergebnisse direkt auslesen, so wie du es jetzt schon machst.

von Markus P. (maxpeer)


Lesenswert?

Falk B. schrieb:
> Das kommt auf den Mikrocontroller an. Beim PICCOLO kann man den ADC
> über verschiedene Events in Hardware auslösen. In meinem Projekt per
> Timer mit der passenden Phasenverschiebung. Wie das bei deinem
> COntroller funktioniert weiß ich nicht.
> Für den DAC braucht man sowas AFAIK nicht, den kann man direkt
> beschreiben, denn der ist sauschnell. Das sollte im Datenblatt stehen.

"The DACC uses the master clock (MCK) divided by two to perform 
conversions. This clock is named DACC Clock. Once a conversion starts 
the DACC takes 25 clock periods to provide the analog result on the 
selected analog output."

--> das steht dazu im Datenblatt. Braucht mein DAC also für jede 
Wandlung 25 clock periods? Das wären dann bei 84MHz eine Dauer von ~ 
300ns.

Wie kann ich den ADC dann triggern?

von Falk B. (falk)


Lesenswert?

Markus P. schrieb:
> "The DACC uses the master clock (MCK) divided by two to perform
> conversions. This clock is named DACC Clock. Once a conversion starts
> the DACC takes 25 clock periods to provide the analog result on the
> selected analog output."
>
> --> das steht dazu im Datenblatt. Braucht mein DAC also für jede
> Wandlung 25 clock periods? Das wären dann bei 84MHz eine Dauer von ~
> 300ns.

Immer noch sehr schnell. Also kann man ohne Prüfung und Warten mit 50kHz 
den DAC direkt beschreiben.

> Wie kann ich den ADC dann triggern?

Frag dein Datenblatt ;-)

von Markus P. (maxpeer)


Lesenswert?

Falk B. schrieb:
> Markus P. schrieb:
>> "The DACC uses the master clock (MCK) divided by two to perform
>> conversions. This clock is named DACC Clock. Once a conversion starts
>> the DACC takes 25 clock periods to provide the analog result on the
>> selected analog output."
>>
>> --> das steht dazu im Datenblatt. Braucht mein DAC also für jede
>> Wandlung 25 clock periods? Das wären dann bei 84MHz eine Dauer von ~
>> 300ns.
>
> Immer noch sehr schnell. Also kann man ohne Prüfung und Warten mit 50kHz
> den DAC direkt beschreiben.
>
>> Wie kann ich den ADC dann triggern?
>
> Frag dein Datenblatt ;-)

Das heißt quasi, ich könnte die Interrupt Routine starten, wenn der ADC 
fertig gewandelt hat, die Daten einlesen, neuen Wert berechnen und 
wieder rausschreiben? Also den Trigger des Interrupts auf die 
Fertigstellung des ADCs legen?

P.S. Vielen Dank für die vielen Antworten!

von Falk B. (falk)


Lesenswert?

Markus P. schrieb:
> Das heißt quasi, ich könnte die Interrupt Routine starten, wenn der ADC
> fertig gewandelt hat, die Daten einlesen, neuen Wert berechnen und
> wieder rausschreiben? Also den Trigger des Interrupts auf die
> Fertigstellung des ADCs legen?

Kann man machen.

von Ingo L. (corrtexx)


Lesenswert?

Es sollte möglich sein, den ADC per Timer direkt zu triggern, sodass er 
die beiden Kanäle nacheinander misst und nach der Messung per DMA die 
Messwerte speichert und einen Interrupt erzeugt, sodass der Regler 
aufgerufen werden kann. Dann kannst du in aller Ruhe regeln...

von Falk B. (falk)


Lesenswert?

Ingo L. schrieb:
> Es sollte möglich sein, den ADC per Timer direkt zu triggern, sodass er
> die beiden Kanäle nacheinander misst und nach der Messung per DMA die
> Messwerte speichert

Muss er gar nicht, denn es gibt anscheinend für jeden Kanal ein 
Ergebnisregister.

> und einen Interrupt erzeugt, sodass der Regler
> aufgerufen werden kann. Dann kannst du in aller Ruhe regeln...

Bei 50kHz gibt es keine Ruhe ;-)

von Monk (roehrmond)


Lesenswert?

Markus P. schrieb:
> Also einfach außerhalb definieren? kI und kP sind nämlich konstant.

Ich würde es mal mit "const" vor der Deklaration versuchen. Wenn das 
nichts bringt, dann versuche sie mit #define zu definieren, anstatt als 
reguläre Variable.

Also
1
const in16_t kP = 1;
2
const in16_t kI = 0;
3
const in16_t kD = 0;

oder
1
#define kP 1
2
#define kI 0
3
#define kD 0

32 Bit Controller können 32 Bit Variablen manchmal schneller verarbeiten 
als 16 Bit, weil eine Konvertier-Schritt entfällt. Das wäre auch noch 
einen Versuch wert.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Code als PNG, gehts noch!!!

von Falk B. (falk)


Lesenswert?

Peter D. schrieb:
> Code als PNG, gehts noch!!!

Da haben wir schon DEUTLICH größeren Mist gesehen.

von C-hater (c-hater)


Lesenswert?

Falk B. schrieb:
> Peter D. schrieb:
>> Code als PNG, gehts noch!!!
>
> Da haben wir schon DEUTLICH größeren Mist gesehen.

Stimmt ;o)

Er hätte es ja auch noch mit dem Handy vom Bildschirm abfotografieren 
und dann als ~10MB-JPEG hochladen können...

von Ingo L. (corrtexx)


Lesenswert?

Falk B. schrieb:
> Muss er gar nicht, denn es gibt anscheinend für jeden Kanal ein
> Ergebnisregister.
Das is ja mal deluxe

von Purzel H. (hacky)


Lesenswert?

Wenn ADC & DAC das Timing bestimmen, wuerde ich die grad nacheinander 
oder gleichzeitig starten und den Rest drum herum ohne warten anpassen.

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.