Die Routine l2a(long l, char *s) wandelt 'l' in einen ASCII-String
's'. Zur Wandlung werden keine Divisionen benötigt, sondern
Subtraktionen, was die Ausführung erheblich beschleunigt.
Mit einem optimierenden Compiler, der die Variablen in Registern hält,
wird die Zahl 1999999999 in weniger als 100µs gewandelt (AVR mit
16MHz).
Auf 'int' abgemagert werden Codegröße reduziert und
Ausführungs-geschwindigkeit gesteigert.
Entsprechend angepaßt lassen sich auch Mantissen von 'float' sehr
schnell wandeln.
Alt, aber gut :-)
Im schlechtesten Fall (1999999999) rund 5600 Takte statt rund 23.000
(ltoa), ansonsten je nach Wert auch deutlich schneller, während ltoa
immer in etwa gleich lang braucht.
Compiler CodeVision 3.12
Dann hat es ja doch mal jemand brauchen können ;-)
Auch, wenn bei Dir die Taktzyklen von 23000 auf 5600 um den Faktor 4
gesunken sind, ist da noch viel Luft zur Beschleunigung. Die Routine
brauchte damals mit einem anderen Compiler < 1600 Taktzyklen.
Aus Neugier habe ich den Quelltext etwas modifiziert und mit einem
Arduino R3 (ATmega328 mit 16 MHz) getestet und dabei auch besser
formatiert. Die Endung .ino soll nicht stören, es ist einfacher C-Code.
Die Durchlaufzeit für eine Wandlung beträgt <= 135 µs, was umgerechnet
<= 2160 Taktzyklen eines AVR entspricht.
Beschleunigend bei dem Code wirkt sich aus, die Tabelle ausserhalb der
Wandelroutine global anzulegen. Damit sind die Zugriffe je nach Compiler
etwas schneller.
Falls man nur 16-Bit Werte wandeln möchte:
Die Ausführungszeit von l2a() für die Wandlung von 32767 beträgt 59 µs.
Eine auf int16_t reduzierte Routine braucht für 32767 rund 45 µs.
Kleine Ergänzung: auf einem STM32F407 mit 168 MHz braucht die gezeigte
Routine aus l2a.ino <= 5 µs Ausführungszeit.
Augen auf bei der Prozessorwahl ;-)
liegt wohl hauptsächlich daran, dass CodeVision prinzipiell keine
long-Variablen in Registern hält. Ich habe auch nicht weiteren
Laufzeitverbesserungen gesucht, da ginge sicher noch was.
z.B 2.Tabelle mit 500.0000.000,...
Das wäre dann jeweils ein zus. Vergleich und und könnte bei Ziffer 9 4
Subtraktionen sparen, max. Laufzeit würde also gesenkt.
Vielleicht auch schauen, ob die Restzahl schon in uint16 passt und dann
nur noch mit 16bit weiter arbeiten.
Mir reicht es so wie es ist, Konvertierung ist schneller als das
tatsächliche Senden mit 115kBaud, also lückenlose Ausgabe mehrerer
long-Zahlen.
H.Joachim S. schrieb:> Mir reicht es so wie es ist, Konvertierung ist schneller als das> tatsächliche Senden mit 115kBaud, also lückenlose Ausgabe mehrerer> long-Zahlen.
Dann ist ja gut. Entscheidener als eine schnellere Wandlung ist, daß im
Gegensatz zu ltoa() aus der Lib jede erzeugte Ziffer sofort zur
Verfügung steht. Selbst, wenn die Wandlung schneller wäre, müßte der µC
wieder auf die UART warten.
Alternativ könnte man einen Ausgabepuffer verwenden und die Ausgabe per
ISR erledigen. Aber das weißt Du sicherlich selber.
Ich habe ja den Tx-Buffer, einen weiteren wollte ich eben nicht
verwenden und der hätte auch mein eigentliches Problem (Buszeit RS485)
nicht gelöst. Master fragt die Werte ab und der slave liefert nun ohne
Kunstpausen zwischen den einzelnen Zählerwerten.
Ähnlich wird das ganze bei Teacup gemacht. Anstatt den String zu
speichern geht das hier nur direkt an die Ausgabe. (Display oder Serial)
Int, hex und fixpoint sind auch implementiert. Vielleicht hilft es ja.
https://github.com/Traumflug/Teacup_Firmware/blob/master/msg.c
1
/// list of powers of ten, used for dividing down decimal numbers for sending, and also for our crude floating point algorithm
Nico W. schrieb:> Ähnlich wird das ganze bei Teacup gemacht.
Nicht schlecht, zumal hier die Optimierungen neuerer Compiler wirksam
werden. Seinerzeit (alter IAR H8-Compiler) mußte ich noch eine
'temp'-Variable verwenden, damit die Zugriffe auf die Tabelle nicht zur
Bremse wurden.
Die angepasste Routine mit Arduino UNO R3 getestet braucht für die
Wandlung in einen String jetzt <= 83 µs. Auch beim STM32F407 (s.o.)
sinkt der Zeitbedarf von <= 5 µs auf <= 3,4 µs.
H.Joachim S. schrieb:> Alt, aber gut :-)> Im schlechtesten Fall (1999999999) rund 5600 Takte statt rund 23.000> (ltoa), ansonsten je nach Wert auch deutlich schneller, während ltoa> immer in etwa gleich lang braucht.
Wenn's schnell sein soll: 936 Takte = 58us@16MHz (für 1999999999), bei
mehr Speicherbedarf.
Die Variante von Michael gefällt mir am besten.
Ein Fehler ist drin: bei 0x80000000 gibt's nen Absturz in der
Vorzeichenunterdrückung (im 2er-Komplement gibt's zwei Zahlen bei den
x==-x gilt).
Eine mögliche Optimierung: die digit Tabelle wird nicht wirklich
benötigt, da die Reihenfolge immer 8, 4, 2, 1, 8, 4 ... ist.
Ein
1
t >>= 1; if (t == 0) /* stellenwechsel */ { t=8; ... }