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
|
2 | const uint32_t powers[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000}; |
3 | |
4 | /** write decimal digits from a long unsigned int
|
5 | \param v number to send
|
6 | */
|
7 | void write_uint32(void (*writechar)(uint8_t), uint32_t v) { |
8 | uint8_t e, t; |
9 | |
10 | for (e = 9; e > 0; e--) { |
11 | if (v >= powers[e]) |
12 | break; |
13 | }
|
14 | |
15 | do
|
16 | {
|
17 | for (t = 0; v >= powers[e]; v -= powers[e], t++); |
18 | writechar(t + '0'); |
19 | }
|
20 | while (e--); |
21 | }
|
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.
Ok, man sollte den code nehmen, der erfolgreich kompiliert wird.
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; ... } |
würde reichen.
asdfasd schrieb: > Eine mögliche Optimierung Tja, macht es auch komplexer, aber sparte weitere 16 Takte.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.