Hallo,
ich habe hier eine DAC-Routine, die mir eine "bit-codierte" Spannung am
Ausgang ausgeben soll. Die Auflösung ist 1 mV / Bit.
Die Plattform auf der diese Routine läuft ist ein 32 Bit Controller.
Der DAC sowie der ADC arbeiten mit 12 Bit.
Ich möchte nun erreichen, dass beide Routinen möglichst schnell diese
Umrechnung vornehmen, damit ich eine höhere Abtastrate / Ausgabe Rate
erreichen kann.
Als Beispiel poste ich jetzt den Code für meinen externen DAC (via SPI).
Kernpunkt sind hier die Umrechnungen der Spannung. Falls jemand
Verbesserungsmöglichkeiten sieht, wie die Routine schneller laufen kann
wäre ich sehr dankbar!!
mit UREF=5477 folgt: KONSTANTE = 24506
Setzt man die Formel 5477(mV) ein, so folgt: CODE=2048
Ein u32-Überlauf findet erst bei Sollspannung>=175261(mV) statt.
Damit umgehst du dir die komplizierte Division.
>> mit UREF=5477 folgt: KONSTANTE = 24506>> Setzt man die Formel 5477(mV) ein, so folgt: CODE=2048> Ein u32-Überlauf findet erst bei Sollspannung>=175261(mV) statt.>> Damit umgehst du dir die komplizierte Division.
Vielen Dank für die schnelle Antwort.
Hätte nicht gedacht, dass man da noch so viel rausholen kann. Du hast
eine komplette Division eingesparrt. Das macht Performanz-technisch
einiges aus !!!
Werde das ganze nochmal durchrechnen mit Bitbreiten, Überlaufen etc.
Die Herleitung hast du wirklich sehr schön verdeutlicht.
Nochmals vielen Dank.
>Das macht Performanz-technisch einiges aus !!!
Ob und wieviel das ausmacht, musst du mal prüfen. Auch die Überläufe.
Kann ja sein, dass ich einen Schusselfehler gemacht habe. Das Prinzip
ist aber offenslichtlich rübergekommen.
Matthias Lipinsky wrote:
>>Das macht Performanz-technisch einiges aus !!!>> Ob und wieviel das ausmacht, musst du mal prüfen. Auch die Überläufe.> Kann ja sein, dass ich einen Schusselfehler gemacht habe. Das Prinzip> ist aber offenslichtlich rübergekommen.
Ergebnisse sind da:
die routine rechnet nun nach [Matthias Lipinsky] so:
1
u32_dac_code=(u32)(u32_Reference*u16_output_a);
2
u16_dac_code=(u16)(u32_dac_code>>16);
Die Routine braucht jetzt konstant 54 Mikrosekunden !
Im Vergleich zu vorher 98 Mikrosekunden (Spitze) ist das eine
Verbesserung um fast Faktor 2 !!!
Besten Dank für die hervorragende Unterstützung.
Matthias Lipinsky wrote:
>>Die Routine braucht jetzt konstant 54 Mikrosekunden !>> Wie hast du das ermittelt?>> Das klassische Debugging-Verfahren?> ..Portpin togglen..?
Genau.
Hier setzte ich die "Messpunkte" mit dem Macro:
mBASICDEF_Set_MeasurePoint()
und BASICDEF_BREAKPOINT ist wie richtig vermutet ein Port-Pin ;-)
Interrupts sind nicht deaktiviert muss ich dazu sagen, aber wie gesagt
ist das Muster auf dem Oszi konstant.
Was ist das eigentlich für ein lahmer Controller?
Ich kenne jetzt nicht wirklich viele 32bit Controller, aber die die ich
kenne, lasen sich alle mit etlichen 10MIPs takten, und die haben alle
einen Hardaremultiplizierer, der nur wenige Takte braucht.
Daher kommen mir ein paar 10µs für die Berechnung sehr lange vor.
Ich würde daher darauf tippen, dass die SPI Schnittstelle die
eigentliche Bremse ist.
Matthias Lipinsky wrote:
> Dazu müsste man sich mal die Funktion>
1
>SPI_WriteData
2
>
> ansehen...
kein Problem.
Aber an SPI_WriteData dürfte es meiner Meinung nach nicht liegen, da die
Funktion den Schreibauftrag nur entgegenimmt und in einen Ring-Puffer
schreibt. Das eigentliche Versenden wird von der SPI-Interrupt Service
Routine erledigt.
>Was ist das eigentlich für ein lahmer Controller?
MCF51QE128 von Freescale. Low-Power Controller.
Ich habe das identische System auf dem MC9S08QE128 und auf dem
MCF51QE128 laufen lassen und erstaunlicherweise zieht der 32 Bit
Controller im Vergleich zum 8 Bit Controller 20 mA WENIGER Strom.
Die interne Clock ist auf 36 kHz getrimmt.
Das ergibt nach den Clock Einstellungen einen Bus-Takt von 4,608 MHz,
die CPU läuft mit einem Takt von BUSCLK * 2, also 9,216 MHz.
Sebastian B. wrote:
> MCF51QE128 von Freescale. Low-Power Controller.
OK, also eine CISC CPU. Das erklärt dann, wieso es etwas länger dauert.
Außer wenn SPI sehr langsam getaktet ist, kommt mir die Sende Routine
ineffizient vor:
Ich gehe mal von 2 Takten pro C-Befehl aus (was eher wenig ist), selbst
dann benötigt SPI_WriteData rund 30 Takte. Wenn die SPI Schnittstelle
also mit mehr als 2,6MHz läuft, dann ist es schneller die Daten direkt
zu Senden und auf das Ende zu warten (bzw. währenddessen andere
Berechnungen zu machen, was man in C nicht immer wirklich gut festlegen
kann, da der Compiler die Reihenfolge der Befehle öfters umsortiert).
>> Wie groß sind denn di beiden Konstanten?> Das ist doch die Pufferlänge-1, stimmts?>> Nimm da 2^N-1 Werte, dann kansnt du aus dem Modulo ein AND machen
1
/* lokale Definitionen-------------------------------------------------------*/
2
#define MAX_BUFFER_COUNT_1 128
stimmt, bei "modulo" wird ja auch nochmal richtig gerechnet ...
das lasse ich mir nochmal durch den Kopf gehen.
>Außer wenn SPI sehr langsam getaktet ist, kommt mir die Sende Routine>ineffizient vor:>Ich gehe mal von 2 Takten pro C-Befehl aus (was eher wenig ist), selbst>dann benötigt SPI_WriteData rund 30 Takte. Wenn die SPI Schnittstelle>also mit mehr als 2,6MHz läuft, dann ist es schneller die Daten direkt>zu Senden und auf das Ende zu warten (bzw. währenddessen andere>Berechnungen zu machen, was man in C nicht immer wirklich gut festlegen>kann, da der Compiler die Reihenfolge der Befehle öfters umsortiert).
Auch das werde ich mir mal überlegen und ob es in meinem System "direkt"
zu realisieren wäre. Im Moment schreibe ich ja alle "Aufträge" in einen
Puffer, der abgearbeitet wird. Jeder Auftrag setzt u.a. auch Port-Pins,
welche spezielle Signale für die Hardware darstellen.
Man muss dazu sagen, dass das System ja eigentlich für einen 8 Bit
Controller konzipiert war. Deshalb fand ich diese Entkopplung von
Sende-Auftrag und eigentlicher Abarbeitung in der ISR ganz vernünftigt.
Nun läuft das System allerdings auf einem 32 Bit Controller und ich
versuche noch so viel Performanz wie möglich rauszukitzeln.
Vielen Dank für die Beiträge!!
>> Wenn ja, dann hast du ja 0..128, also 129 Array-Elemente.
Nein!
Ein Array array[4] hat die Elemente array[0], array[1], array[2] und
array[3] also 4 insgesamt.
Das passt schon. Wenn der Compiler intelligent ist, macht er dann auch
automatisch ein &127 aus dem %128. Nur leider sind nicht alle Compiler
so intelligent.
>Nein!>Ein Array array[4] hat die Elemente array[0], array[1], array[2] und>array[3] also 4 insgesamt.
Ja. Schusselfehler.
(Ich war noch bei ST von Arbeit)
>Wenn der Compiler intelligent ist, macht er dann auch>automatisch ein &127 aus dem %128.
Darauf verlasse ich mich nicht.
Den einzigen Nachteil den ich hier sehe ist die "Eingeschränktheit" der
Puffergroesse. Da diese immer das Format 2^N haben muss. Bei Modulo
hingegen kann theoretisch jeder mögliche Wert des Datentyp angegeben
werden.
Ich werde morgen mal messen was für eine Zeitersparnis der oben genannte
Code bringt. ;-) Bin mal gespannt.
Viele Gruesse
Sebastian
Sebastian B. wrote:
> Den einzigen Nachteil den ich hier sehe ist die "Eingeschränktheit" der> Puffergroesse. Da diese immer das Format 2^N haben muss. Bei Modulo> hingegen kann theoretisch jeder mögliche Wert des Datentyp angegeben> werden.
Schnell und universell sind meist zwei dinge die sich nicht immer unter
einen Hut bringen lassen... ;)