www.mikrocontroller.net

Forum: Compiler & IDEs usigned long long sprtinf


Autor: blonkel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hiho,

Leider hab ich nach längerem suchen herausgefunden, das es nicht möglich 
ist ein "unsigned long long" mit sprintf/printf in ein Char Array zu 
schreiben um dieses auf der UART auszugeben. Nun hab ich dies durch 
folgenden C Code realisiert:

void uart_ulltoa(unsigned long long val)
{
    char Buffer[50];
    char tmp;
    int i=0;

    while(val > 0)
    {
        Buffer[i++]='0'+val%10;
        val/=10;
    }

    for(int j = 0; j < i / 2; ++j ) {
        tmp = Buffer[j];
        Buffer[j] = Buffer[i-j-1];
        Buffer[i-j-1] = tmp;
    }

    Buffer[i] = 0;

    uart_puts(Buffer);
}

Funktioniert auch sehr gut soweit, das Problem dabei ist nur das die 
Funktion 8kbyte(!) Flash sowie ~250byte Ram benötigt. Das problem liegt 
in folgender Zeile: val/=10;

Gibt es eine Möglichkeit dies nun noch auf "speicher" zu optimieren? CPU 
last wäre mir realitiv egal.

Vielen Dank
Blonkel

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
blonkel schrieb:
> sowie ~250byte Ram benötigt.

Genauer gesagt: 256 Bytes.  Die kommen aus einer Bibliotheksfunktion,
sie werden für das Array __clz_tab[] belegt.  Wenn man danach gugelt,
findet man, dass die Gleitkommabibliothek das zwar für die
Gleitkommadivision ausgeräumt hat, aber offenbar schleppt's der GCC
beim (wenig optimierten) long-long-Code dann wieder mit ein.  Du
müsstest wohl die Funktion __udivdi3() reimplementieren, um das
loszuwerden.

p.s.: "Bitte den Controllertyp im Titel mit angeben."

Autor: Andreas Ferber (aferber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn du sonst keinerlei Division mit "unsigned long long" in deinem Code 
brauchst, gibt es einen Weg, die Division (und damit die Einbindung der 
Routinen/Tabellen) komplett zu vermeiden. Dazu ist ein kleiner Umweg 
über die Packed-BCD-Darstellung notwendig. Gebraucht werden dann (auf 
AVR) nur die Register plus ein wenig Stack zum Sichern der benutzten 
Register.

Zunächst wird dazu die Zahl komplett in Packed BCD umgewandelt. Dazu 
musst du den Algorithmus zu bin2BCD16 aus folgender Appnote verwenden:

http://www.atmel.com/dyn/resources/prod_documents/...

Die Erweiterung von 16 Bit Input, 3 Byte Output auf 64 Bit Input, 10 
Byte Output (2^64-1 hat 20 Dezimalstellen, also 10 BCD-Bytes) ist 
ziemlich "straight forward".

Sinnvoll implementieren lässt sich der Algorithmus IMO eigentlich nur in 
Assembler, da dabei fast nur auf Bitebene mit Einbeziehung von Carry 
gearbeitet wird. Wenn ich mich beim Überschlagen gerade nicht grob 
vertan habe, dürfte dieser Part dann so um die 10000 Takte plus/minus 
brauchen.

Wenn es nicht auf das letzte Code-Byte ankommt, würde ich die in der 
Appnote im Flussdiagramm dargestellte innere Schleife (das untere 
Drittel) übrigens nicht als Schleife schreiben, sondern komplett 
ausrollen (die Zahl der Durchläufe ist immer konstant). Nur so können 
auch die kompletten Outputbytes in Registern gehalten werden.

Anschliessend muss dann noch von BCD nach ASCII umgewandelt werden, das 
braucht nur noch Byte-Division bzw. -Modulo mit 16, also per 
Bitoperationen zu erledigen. Hierbei könnte es zweckmässig sein, die 
generierten Zeichen einfach direkt in den UART zu schieben, anstatt erst 
einen String zu schreiben und den dann an uart_puts() zu übergeben.

Andreas

Autor: blonkel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch schrieb:
> ...aber offenbar schleppt's der GCC
> beim (wenig optimierten) long-long-Code dann wieder mit ein...
Wenig optimiert bezieht sich hier wohl schätz ich nicht auf meine 
Optimierungstufe sondern auf das verabeiten von ull's von gcc?

> p.s.: "Bitte den Controllertyp im Titel mit angeben."
Tut mir leid, es handelt sich um hierbei um einen AVR atmega162.

Andreas Ferber schrieb:
> Sinnvoll implementieren lässt sich der Algorithmus IMO eigentlich nur in
> Assembler, da dabei fast nur auf Bitebene mit Einbeziehung von Carry
> gearbeitet wird. Wenn ich mich beim Überschlagen gerade nicht grob
> vertan habe, dürfte dieser Part dann so um die 10000 Takte plus/minus
> brauchen.
Vielen Dank für die vorgeschlagene Lösung, einziger Nachteil dabei ist 
das meine Assembler Kenntnisse ein wenig zurück liegen. Sollte es 
dennoch keine Alternative geben, werde ich wohl den oben genannten 
Algorithmus implementieren.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
blonkel schrieb:
> Jörg Wunsch schrieb:
>> ...aber offenbar schleppt's der GCC
>> beim (wenig optimierten) long-long-Code dann wieder mit ein...
> Wenig optimiert bezieht sich hier wohl schätz ich nicht auf meine
> Optimierungstufe sondern auf das verabeiten von ull's von gcc?

Ja, auf die interne Bibliothek (libgcc.a).  Deren 64-bit-Code ist
einfach stur maschinengeneriert, während viele andere Dinge (32-bit
integer und floating-point) handoptimierten oder manuell
nachgebesserten Code benutzen.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
blonkel schrieb:

> dennoch keine Alternative geben, werde ich wohl den oben genannten
> Algorithmus implementieren.

Die unsigned long long überhaupt loszuwerden, geht nicht?


Was ist mit Subtraktionen, brauchen die weniger?

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:

> Was ist mit Subtraktionen, brauchen die weniger?

Ich denke schon, Addition und Subtraktion lassen sich ja relativ
geradlinig implementieren.

Dieses __clz_bits[] ist irgendwie ein Array, das das Zählen der
führenden Nullen ("count leading zeros") vereinfachen soll, wobei
halt die Implementierer wohl davon ausgegangen waren, dass das Opfern
von 256 Bytes RAM für den erzielten Geschwindigkeitsgewinn (auf
einem PC) "Peanuts" sind.  Dummerweise gibt's offenbar halt keine
Möglichkeit, das pro Target umzuschalten, und erst recht fehlt die
Infrastruktur im GCC, um für verschiedenen -Os-Stufen verschiedene
libgcc's zu linken, die ihrerseits wiederum selbst der jeweiligen
Optimierungsstufe gerecht werden.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch schrieb:
> Karl heinz Buchegger schrieb:
>
>> Was ist mit Subtraktionen, brauchen die weniger?
>
> Ich denke schon, Addition und Subtraktion lassen sich ja relativ
> geradlinig implementieren.

Schön ist es nicht unbedingt aber zyklenmässig würde man wahrscheinlich 
mit fortgesetzter Subtraktion runterbringen können. Problem ist 
natürlich, dass ein ull schon sehr hoch reicht und man viele einzelne 
Subtraktionsstufen braucht.

Autor: Andreas Ferber (aferber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch schrieb:
> und erst recht fehlt die
> Infrastruktur im GCC, um für verschiedenen -Os-Stufen verschiedene
> libgcc's zu linken

Doch, das geht eigentlich schon, über Änderungen/Ergänzungen der specs. 
Der "*libgcc"-Spec-String ist speziell dazu vorgesehen, beim Linken die 
passende libgcc-Variante auszuwählen. Beim avr-gcc wird damit z.B. dafür 
gesorgt, dass bei den ganz kleinen ATtiny (11, 12, 15, 28) keine 
libgcc gelinkt wird. "Verzweigung" anhand der gewählten "-O"-Einstellung 
lässt die Spec-Syntax dabei auch zu, z.B. so:
*libgcc:
%{!mmcu=at90s1*:%{!mmcu=attiny11:%{!mmcu=attiny12:%{!mmcu=attiny15:%{!mmcu=attiny28: %{Os|O0:-lgcc_small; O*:-lgcc_fast; :-lgcc_small }}}}}}

Voraussetzung ist natürlich, dass beim Linken dann auch "-O..." mit 
übergeben wird, was normalerweise eigentlich nicht unbedingt nötig ist, 
aber auch nicht schadet und oft sowieso im Makefile gemacht wird.

Hilft aber alles nichts, solange sich keiner findet, der entsprechend 
auf Größe optimierte Funktionen schreibt ;-)

Andreas

Autor: blonkel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Für alle die das Problem auch irgendwann haben hier meine Lösung (Ansatz 
dafür war irgendwo im Netz, leider hab ich die Quelle nicht mehr):

void bin2bcd64(unsigned long long inValue)
{
   unsigned char acc[21]={0};
   unsigned char carry[22]={0};

   for (int loop=0;loop<64;loop++)
   {
      if (( inValue & 0x8000000000000000) != 0)
         carry[0] = 1;
      else
         carry[0] =0;
      inValue <<= 1;

/*
If carry generated to next digit, convert and add carry.
If no carry generated to next digit, shift left 1 step and add carry.
*/
    for(int i=1; i < 21;i++)
    {
        if (carry[i] == 1)
           acc[i-1] = ((acc[i-1]-5)*2) + carry[i-1];
        else
           acc[i-1] = (acc[i-1] << 1) + carry[i-1];
    }

    // Check each accumulator if >= 5. Generate carry if so.
    for(int i=0; i < 21;i++)
    {
        if (acc[i] >= 5)
           carry[i+1] = 1;
        else
            carry[i+1] = 0;
    }

   }

   //Output without leeding zeros
   char zero=1;
   for(int i=19; i >= 0;i--)
   {
       if((zero == 1 && acc[i] != 0) || i == 0)
    {
      zero=0;
    }
       if(zero == 0)
    {
      uart1_putchar('0' + acc[i]);
    }
   }

}

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.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

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