//Und erhöhe die position, da wir das zweite Element auch bereits abgearbeitet haben!
27
pwmPos++;//Nun ist pwmPos 2. also bereits beim letzten wert!
28
29
if(pwmPos<3)
30
{//Ist neuer Wert pwmPos identisch mit altem?
31
if(compValues[pwmPos]==compValues[pwmPos-1])
32
{//Wenn ja, setze auch die LED des zweiten werts!
33
setLED(pwmChannel[pwmPos]);
34
//Und erhöhe die position, da wir das zweite Element auch bereits abgearbeitet haben!
35
pwmPos++;//Nun ist pwmPos 3. also wurden alle LEDs bereits aktiviert!
36
}
37
}
38
39
}
40
}
41
42
//Wenn pwmPos kleiner als 3 ist, können wir einen neuen Wert setzen!
43
//Andernfalls sind wir bereits am ende, und haben alle LEDs eingeschaltet!
44
if(pwmPos<3)OCR1B=compValues[pwmPos];
45
}
Nun möchte ich herausfinden, wie viele CPU_Takte dieser im worst_Case
Benötigt, und ob ich irgendwo noch optimieren kann.
Wie kann ich den zugehörigen Assembler Code anzeigen lassen?
Danke schonmal!
Wieviel Threads willst du denn jetzt noch eröffnen für dein PWM Zeugs?
Findest du nicht, das einer reicht? Und wie wäre es, wenn du dir endlich
mal die Hinweise auf Soft PWM zu Herzen nimmst und dich da mal
reinbuddelst.
Beitrag "Compare von Timer verschieben"Beitrag "3Phasen PWM mit Atmega48"
Matthias S. schrieb:> ieviel Threads willst du denn jetzt noch eröffnen für dein PWM Zeugs?
Man soll einen Thread pro Frage eröffnen.
Es handelt sich hierbei um eine andere Frage.
Zudem ist das PWM Problem grundsätzlich gelöst.
Holger K. schrieb:> Man soll einen Thread pro Frage eröffnen.
Ich stimme dem zu.
Jedoch sollte dann in jeder Frage auch die nötigen Informationen über
die Umgebung enthalten sein.
Steffen R. schrieb:> Jedoch sollte dann in jeder Frage auch die nötigen Informationen über> die Umgebung enthalten sein.
Stimmt.
Also es handelt sich um einen Atmega48 und AVR-GCC
Eclipse Indigo.
:)
Nachfolgend das Dissassembly.
Ich bin kein Experte für Assembler. Schätze die Ausführungszeit jedoch
auf etwa 400 Takte.
Zum schätzen würde ich ein PortPin am Start der ISR setzen und am Ende
zurücksetzen. Logicanalyser dran und Mann sieht zumindest die
"Applikationsdauer" der ISR. Für's Optimieren reicht dies aus, es soll
ja schneller gehen.
Wer's genau braucht, muß eben Takte zählen (auch durch Schleifen) oder
einen Simulator benutzen, der zählt in der Regel mit.
Carl D. schrieb:> Wer's genau braucht, muß eben Takte zählen (auch durch Schleifen) oder> einen Simulator benutzen, der zählt in der Regel mit.
Oder merkt sich am Anfag des zu messenden Teils einen freilaufenden
Timer der z.B. mit 1us (oder bei höherer Auflösung mit 100ns) läuft. Am
Ende nochmal auslesen und Differenz bilden. Lässt sich beim Debuggen
schön auslesen.
Durchlaufzeit = Timer;
...
Durchlaufzeit = Durchlaufzeit - Timer;
rgds
taktvoll schrieb im Beitrag #4254927:
> @holger: Glaubts du wirklich hier jemand zu finden, der dir diese> Arbeit abnimmt? Für umme?>> Verachtender gehts wohl nicht?
Nö
Ich habe selbst gezählt.
Es gibt ja keine schleife.
somit kann ich stop-start rechnen = 274 Zeilen.
Gehe ich davon aus dass einige mehr als 1Takt benötigen mal 1.5 rechnen
= 411 Takte
Nun müsste ich das ganze eifach noch optimieren können.
Holger K. schrieb:> Nun müsste ich das ganze eifach noch optimieren können.
klar:
> setLED(pwmChannel[pwmPos]);
mache mal SetLED static. Wenn immer noch nicht weniger push und Pop sind
dann inlinen.
Holger K. schrieb:> Nun möchte ich herausfinden, wie viele CPU_Takte dieser im worst_Case> Benötigt
Simuliere den Code. Dort hast du dann einen Zykluszähler, der Taktgenau
angibt, wie lang das gedauert hat...
Holger K. schrieb:> Nun müsste ich das ganze eifach noch optimieren können.
Da ghets auch um das Thema und es wurde auch eine Lösung gefunden:
Beitrag "C versus Assembler->Performance"
Leider ist der Thread dann ein wenig aus dem Ruder gelaufen...
Peter II schrieb:> mache mal SetLED static. Wenn immer noch nicht weniger push und Pop sind> dann inlinen.
Danke habe ich gerade getestet.
Es bliebt unverändert.
Ich habe nun den Code von setLED direkt in die ISR geschrieben.
Ergebnis:
Was soll das Zählen von Takten beim Einschalten einer LED bringen? Das
Auge ist sowieso viel zu träge, um eine Änderung in der Taktzahl
signifikant zu erkennen.
vermutlich ist auch compValues volatile. Wenn du das weglässt sollte der
Code auch kleiner und schneller werden.
wie ist pwmPos definiert? Kannst es nicht static in der Funktion sein?
Peter II schrieb:> vermutlich ist auch compValues volatile. Wenn du das weglässt> sollte der> Code auch kleiner und schneller werden.>> wie ist pwmPos definiert? Kannst es nicht static in der Funktion sein?
compValues ist tatsächlich Volatile.
compValues enthält die CompareWerte für den Zähler und entspricht
deshalb auch dem DutyCycler einer LED.
diese Werte können mommentan im main angepasst werden.
Deshalb volatile. Im ISR werden diese aber nur gelesen und nie
geschrieben!
pwmPos ist global als Volatile definiert.
pwmPos wird nur innerhalb zweier ISR verwendet.
Einmal beim überlauf des Zählers und dann im Vector12 bei Comparematch.
Dieser Wert wird sowohl gelesen als auch geschrieben im ISR
Holger K. schrieb:> diese Werte können mommentan im main angepasst werden.> Deshalb volatile. Im ISR werden diese aber nur gelesen und nie> geschrieben!
kommt auf den code in der main an. Vermutlich kann das volatile weg und
es geht dann alles noch (und sogar schneller)
> pwmPos ist global als Volatile definiert.> pwmPos wird nur innerhalb zweier ISR verwendet.
dann muss es nicht volatile sein.
Achja die alten Zeiten. Daran erinnere ich mich noch gut. Als ich mich
das erste Mal mit Mikrocontrollern beschäftigt habe, war der Einstieg
natürlich auch über C. Dann musste ich auch irgend wann einmal Takte
zählen. Natürlich auch den ASM Code angeschaut (ohne vorher jemals ASM
gesehen zu haben...) und solange rumoptimiert, bis die Takte nach
einigen Stunden endlich gepasst haben.
Später musste ich in der gleichen Sub an einer ganz anderen Stelle, also
ausserhalb des timing-kritischen Bereichs, einige Zeilen Code hinzufügen
und plötzlich stimmten die Timings nicht mehr, weil C auf einmal die
ganze Sub anders behandelt hat. Das war für mich der Ausschlag, mich
endlich mal mit dem sadistischem ASM zu beschäftigen. Gerade bei uC
Programmierung kommt es nämlich dauernd vor, dass die Takte eine Rolle
spielen. Eigentlich bei jedem Protokoll. Selbst bei SPI und I2C, die
zwar synchron arbeiten, jedoch auch dort Vorgaben über Mindesttaktlängen
machen. Deshalb braucht es dann dort auch eine Art Balancing, damit es
gut skalierbar ist.
Tja, was soll ich sagen, C fass ich nicht mehr an. ASM ist viel
einfacher, wenn man mal ein System entwickelt hat, was funktioniert.
Leider ist man da ziemlich auf sich und sein Hirn gestellt, da wirklich
alles möglich ist und es so gut wie keine Vorgaben gibt. Die Lernkurve
ist gar nicht mal so steil. Die paar Befehle hat man in 2 Tagen drin.
Nur bis man ein System entwickelt hat, um nicht mit den ganzen Registern
auf Kriegsfuss zu stehen, wenn man mal mehrere Subs in Subs aufruft,
dauert es dann doch eine gewisse Zeit.
Ich vermisse C wie Zahnschmerzen, wenn ich nur an folgende Fragen denke,
die ich mir dauernd stellen musste:
Welches Keyword brauche ich hier?
Was für einen internen Cast macht er hier?
Welchen Pointertyp muss ich jetzt hier konvertieren?
Wie viele Klammern will er jetzt hier haben?
Wie macht man das nochmal volatile? Ist das hier zwingend nötig?
Sicher werden jetzt einige C-Pros kommen und sagen "lerns halt, dann ist
es doch einfach!". Nun, das gilt aber auch für ASM, nur habe ich da alle
Freiheiten, ohne irgendwelche komischen Inline-Konstrukte zu basteln,
die irgendwann kaum noch lesbar sind.
Christian S. schrieb:> was soll ich sagen, C fass ich nicht mehr an. ASM ist viel einfacher
Darum geht es hier nicht und es interessiert hier keinen!
Kurz: es wird nicht schon wieder ein Thread gekapert.
> Sicher werden jetzt einige C-Pros kommen und sagen ...
Ich werde jeglichen Kommentar in Richtung Pro-Contra-C einfach löschen.
Es ist mir zu blöd, dass laufend irgendwer in irgendwelchen nicht von
ihm gestarteten Threads unnötigerweise verkündet, wie toll Assembler
doch ist.
In einer ISR (Standard AVR, d.h. mit gesperrten INT's) sorgt volatile
dafür, daß der Compiler den Wert nicht in einem Register zwischenlagern
kann. Für globale Variablen (nicht HW-Register) gibt es aber niemand,
der die, während die ISR läuft, ändern könnte. Braucht man also
volatile, weil die Variable Daten von außen zur ISR, oder umgekehrt,
oder beides, transportiert, dann kann man doch innerhalb der ISR lokale
Kopien benutzen, das freut den Optimizer.
Bei mir haben die dann den Originalnames mit einem _ davor, werden am
Anfang aus den Volatilen geladen und am Ende wieder in die Originale
gespeichert. Spart je Zugriff auf eine Volatile ein LDS und eventuell
ein STS, beide 2 Flash-Worte und 2 Takte. Da das Gänze aber die
Registerlast erhöht, was zu mehr zu sichernden Registern führt, gilt wie
immer: beide Varianten bezüglich des Ergebnisses vergleichen!
@Christian S.:
Fragen nach welcher Opcode macht was, Nebenwirkungen auf Flags,
singned/unsigned-Gemeinheiten, etc. , stellen da natürlich nicht, da man
das DB ja auswendig kennt ;-((
(wie viele Pseudonyme hat der dicke Fisch eigentlich noch?)
Hallo Lothar,
ich glaube, worum es hier geht, entscheidet der TO und so wie ich das
interpretiert habe, ging es darum, die Anzahl der Takte eines bestimmten
Codes zu wissen. Dass das in C alles andere als trivial ist, habe ich
deutlich gemacht und gleichzeitig einen anderen Ansatz vorgeschlagen.
Dabei ist es ja wohl auch mein Recht, darzulegen, dass ich mit meiner
Entscheidung, nur noch in ASM zu programmieren, sehr zufrieden bin.
Ich werde mir in Zukunft die Zeit für solche Postings sparen.
Anscheinend ist damit wirklich jedem geholfen. Mir wohl am meisten.
Holger K. schrieb:> somit kann ich stop-start rechnen = 274 Zeilen.> Gehe ich davon aus dass einige mehr als 1Takt benötigen mal 1.5 rechnen> = 411 Takte
Ich kann es nicht ganz abschätzen, da ich auf diesem Prozessor nicht so
sehr zu hause bin. Würde aber denken, dass der Faktor für die Schätzung
viel zu niedrig ist.
- Befehle mit je 2/4 Byte lesen
- zusätzliche Speicherzugriffe (push, pop)
Durch die Verwendung einer lokalen Kopie für RAM 0x13A (pwmPos) könnte
man im ganz oben geposteten ASM-Listing 14 LDS/STS Zugriffe je 2 Takte
sparen. Und da dies ein Index eines Arrays ist, käme noch jede Menge
Pointer-Setup in Z, R30:R31 hinzu/bzw. weg. Zugriffe würden über LD Z+n
gemacht.
Wobei es dabei nicht um das Tweaken des Maschinencodes geht, sondern
darum, dem Compiler mitzuteilen, was Sache ist. Daß sich nämlich pmwPos
innerhalb der ISR nicht magisch ändert (außerhalb schon, deshalb
volatile). Man kann sich jetzt natürlich ein "volatile-weg" cast
wünschen, oder man benutzt die gegebene(n) Vokabel/Gramatik, um sich
verständlich zu machen. Andere Sprachen würden es mit
1
VAR pwmPos : BYTE VOLATILE;
2
VAR pwmPosNonVolatile : BYTE AT pwmPos;
machen, ob das schöner ist, mag (besser nicht) diskutiert werden, oder
brauchen volatile garnicht, weil das nur einen existierenden Optimizer
überhaupt interessiert.