mikrocontroller.net

Forum: Projekte & Code schnelle Wandlung long -> ASCII


Autor: Michael (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.

Autor: H.Joachim Seifert (crazyhorse)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: m.n. (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.

Autor: m.n. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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 ;-)

Autor: H.Joachim Seifert (crazyhorse)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: m.n. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: H.Joachim Seifert (crazyhorse)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Nico W. (nico_w)
Datum:

Bewertung
0 lesenswert
nicht 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/...
/// list of powers of ten, used for dividing down decimal numbers for sending, and also for our crude floating point algorithm
const uint32_t powers[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};

/** write decimal digits from a long unsigned int
  \param v number to send
*/
void write_uint32(void (*writechar)(uint8_t), uint32_t v) {
  uint8_t e, t;

  for (e = 9; e > 0; e--) {
    if (v >= powers[e])
      break;
  }

  do
  {
    for (t = 0; v >= powers[e]; v -= powers[e], t++);
    writechar(t + '0');
  }
  while (e--);
}

Autor: m.n. (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.

Autor: Michael Bertrandt (laberkopp)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Michael Bertrandt (laberkopp)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ok, man sollte den code nehmen, der erfolgreich kompiliert wird.

Autor: asdfasd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
t >>= 1; if (t == 0) /* stellenwechsel */ { t=8; ... }
 würde reichen.

Autor: Michael Bertrandt (laberkopp)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
asdfasd schrieb:
> Eine mögliche Optimierung

Tja, macht es auch komplexer, aber sparte weitere 16 Takte.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.