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!
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.
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.
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.
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?
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.
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!
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.
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.
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.
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?
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.
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.
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.
"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.
"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?
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?
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...
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...
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
constin16_tkP=1;
2
constin16_tkI=0;
3
constin16_tkD=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.