schnelle Wandlung long -> ASCII

Gast #186209
Lesenswert?

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.
Angehängte Dateien:
Gast #4882252
Lesenswert?

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.
Angehängte Dateien:
#4882676
Lesenswert?

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.
Gast #4882746
Lesenswert?

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.
#4887324
Lesenswert?

Ä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
}
Gast #4890838
Lesenswert?

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.
Angehängte Dateien:
Gast #4891246
Lesenswert?

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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren