Forum: Compiler & IDEs ATmega8: sprintf & float liefert nur "?"


von Uwe (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Leute,

ich hab ein Problem mit sprintf und floats. Ich möchte (wie viele andere 
auch) floats auf einem Display anzeigen lassen. Ich habe auch schon 
ziemlich viele Beiträge aus dem Forum gelesen, deswegen bitte ich um 
Entschuldigung, wenn ich die Lösung übersehen haben sollte.

Da mein aktuelles Projekt zu groß wäre, um hier komplett reingestellt zu 
werden habe ich ein Testprojekt angelegt, das die gleiche Problematik 
zeigt.

An den dabei benutzten Bibliotheken für TWI und Display liegt es nicht, 
wie auch der AVR-Debugger bestätigt.


Meine Umgebung:

WinAVR 20090313
AVR Studio 4.18 SP2 build 700

Projekteinstellungen (aus dem AVR Studio):

General:
atmega8, 10Mhz, -Os, -funsigned-char, -funsigned-bitfields, 
-fpack-struct, -fshort-enums

Libraries:
libc.a
libprintf_flt.a
libm.a

Custom options:
[All files]
-Wall -gdwarf-2 -std=gnu99 -DF_CPU=10000000UL -Os -funsigned-char 
-funsigned-bitfields -fpack-struct -fshort-enums

[Linker Options]
-Wl,-u,vfprintf

Das Makefile habe ich angehängt.

Meine Testdatei "sprintf_float_test.c":
1
#include <stdio.h>
2
#include <stdint.h>
3
#include <avr/io.h>
4
#include "LIB_TWI_Master.h" // TWI-Lib
5
#include "LIB_PCF2119x.h"   // Display-Lib
6
7
int main(void){
8
  
9
  char  c[]     = "abc";
10
  uint8_t  ui    = 1;
11
  double  d    = 7.5;
12
  char  erste_Zeile[]  = "abcdefghijklmnop";
13
  char  zweite_Zeile[17];
14
15
  Init_TWI(200000);
16
  PCF2119X_Init();
17
  PCF2119X_Clear();
18
  PCF2119X_Write_Line(erste_Zeile, 16, TRUE);
19
  sprintf((char *)&zweite_Zeile, "%s %04u %08.2f", c, ui, d);
20
  PCF2119X_Write_Line(zweite_Zeile, 16, FALSE);
21
22
  while (1){
23
    ;
24
  }
25
26
  return 0;
27
}

Die Ausgabe vom AVR Studio nach dem Kompilieren ist folgende:
1
Build started 6.4.2010 at 22:14:20
2
avr-gcc  -mmcu=atmega8 -Wall -gdwarf-2 -std=gnu99   -DF_CPU=10000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT sprintf_float_test.o -MF dep/sprintf_float_test.o.d  -c  ../sprintf_float_test.c
3
avr-gcc  -mmcu=atmega8 -Wall -gdwarf-2 -std=gnu99   -DF_CPU=10000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT LIB_PCF2119x.o -MF dep/LIB_PCF2119x.o.d  -c  ../LIB_PCF2119x.c
4
avr-gcc  -mmcu=atmega8 -Wall -gdwarf-2 -std=gnu99   -DF_CPU=10000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT LIB_TWI_Master.o -MF dep/LIB_TWI_Master.o.d  -c  ../LIB_TWI_Master.c
5
avr-gcc -mmcu=atmega8 -Wl,-u,vfprintf -Wl,-Map=sprintf_float_test.map sprintf_float_test.o LIB_PCF2119x.o LIB_TWI_Master.o    -lc -lprintf_flt -lm  -o sprintf_float_test.elf
6
avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature  sprintf_float_test.elf sprintf_float_test.hex
7
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 --no-change-warnings -O ihex sprintf_float_test.elf sprintf_float_test.eep || exit 0
8
avr-objdump -h -S sprintf_float_test.elf > sprintf_float_test.lss
9
10
AVR Memory Usage
11
----------------
12
Device: atmega8
13
14
Program:    3222 bytes (39.3% Full)
15
(.text + .data + .bootloader)
16
17
Data:         36 bytes (3.5% Full)
18
(.data + .bss + .noinit)
19
20
21
Build succeeded with 0 Warnings...

Für Tips wäre ich dankbar.... ;)


Einen schönen Abend!

  Uwe

von Uwe (Gast)


Lesenswert?

Uwe schrieb:
> Libraries:
> libc.a
> libprintf_flt.a
> libm.a

Tue ich?

Laut 
http://www.mikrocontroller.net/articles/FAQ#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio 
ist die Lib mit float-Unterstützung.

  Uwe

von Grrrr (Gast)


Lesenswert?

sprintf(zweite_Zeile, "%s %04u %08.2f", c, ui, d);

von Uwe S. (flightcontrol)


Lesenswert?

So, ich hab mir jetzt nen Benutzer angelegt ;) Editieren macht manches 
einfacher..

Die sprintf-Zeile habe ich zwischenzeitlich auch abgeändert. Die kommt 
leider noch von einer Version, als die Variablen "erste_Zeile" und 
"zweite_Zeile" unsigned chars waren.

Aber selbst wenn... einen char-Pointer auf char-Pointer zu casten sollte 
keinen Unterschied machen.

Trotzdem danke!

  Uwe

von Stefan E. (sternst)


Lesenswert?

Nimm das -lc raus (also libc.a).

von Uwe S. (flightcontrol)


Lesenswert?

Guten Morgen,

wenn ich die libc.a entferne funktioniert es plötzlich. Allerdings bläht 
sich das Programm um einiges auf:

Größe des erzeugten Codes (Reihenfolge der Libs beachten!)
1
Libraries       |      Beispielprojekt     | mein Großprojekt
2
-------------------------------------------------------------------
3
libc            | Program: 3222 bytes 39.3%| Program: 6200 bytes 75.1%
4
libprintf_flt   | Data:      36 bytes      | Data:     247 bytes
5
libm            | Kauderwelsch auf Display | "?" anstelle float
6
-------------------------------------------------------------------
7
libprintf_flt   | Program: 4732 bytes 57.8%| Program: 7710 bytes 94.1%
8
libm            | Data:      36 bytes      | Data:     247 bytes
9
                | Zahlen werden richtig    | richtige Darstellung
10
                | dargestellt              |
11
-------------------------------------------------------------------
12
libprintf_flt   | Program: 4746 bytes 57.9%| Program: 7714 bytes 94.2%
13
libm            | Data:      36 bytes      | Data:     247 bytes
14
libc            | Zahlen werden richtig    | richtige Darstellung
15
                | dargestellt              |
16
-------------------------------------------------------------------

Danke für den Tip! Kannst du auch erklären, was dahinter steckt? Und ob 
man nicht evt. doch an die Optimierungen der libc kommen kann, auch wenn 
man floats braucht? Immerhin würde ich ja knappe 20% Speicher sparen, 
wenn ich die libc benutzen könnte.


  Uwe

von Karl H. (kbuchegg)


Lesenswert?

Uwe Sauter schrieb:

> man nicht evt. doch an die Optimierungen der libc kommen kann, auch wenn
> man floats braucht? Immerhin würde ich ja knappe 20% Speicher sparen,
> wenn ich die libc benutzen könnte.

Du solltest dich lieber fragen, ob du unbedingt sprintf brauchst, oder 
ob es andere Nicht-Code-Monster nicht auch tun (zb dtostrf()). Und bei 
der Gelegenheit kannst du dich auch fragen, ob es eigentlich überhaupt 
float sein muss, oder ob Fixed Point Arithmetik nicht auch zum Ziel 
führt.
Floating Point Arithmetik ist für einen µC in der Leistungklasse eines 
Mega8 Knochenarbeit.

von Uwe S. (flightcontrol)


Lesenswert?

Es beantwortet zwar nicht die Frage, aber als gedanklicher Anstoß ist 
der Kommentar natürlich auch gerne gesehen ;)

Ich bin mir der Problematik mit den floats (zumindestens in groben 
Zügen) bewusst. Allerdings habe ich von Fixed Point Arithmetik keine 
Ahnung (was nicht heißt, dass das nie der Fall sein wird). Ich stehe 
aber etwas unter Druck und habe deswegen weder Lust noch Zeit, mich 
darin jetzt zu vertiefen.

Karl heinz Buchegger schrieb:
> andere Nicht-Code-Monster nicht auch tun (zb dtostrf()).

Die Funktion muss ich wohl übersehen haben. Aber damit müsste man ja die 
Funktion der "großen" libprintf_flt durch die "kleine" libprintf_min 
ersetzen können, oder?

Welche anderen "Nicht-Code-Monster" gibt es denn noch, die aus einer 
float einen string (char array) machen?

  Uwe

von Karl H. (kbuchegg)


Lesenswert?

Uwe Sauter schrieb:

> Die Funktion muss ich wohl übersehen haben. Aber damit müsste man ja die
> Funktion der "großen" libprintf_flt durch die "kleine" libprintf_min
> ersetzen können, oder?

Yep. Du kannst zurückfallen auf die 
'Nicht-Spezial-printf-Library'-Variante

> Welche anderen "Nicht-Code-Monster" gibt es denn noch, die aus einer
> float einen string (char array) machen?

dtostrf() ist ein guter Ersatz.
printf und Konsorten lohnen sich meistens nur dann, wenn du spezielle 
Formatieranforderungen hast, wie zb Ausrichtung in einem Feld, führende 
0-en, Einbettung einer Zahl in einen Ausgabestring (aber das ist schon 
Grnazfall). In allen anderen Fällen bist du meistens mit den atoi(), 
atol(), dtostrf() Funktionen meistens besser bedient.

> Allerdings habe ich von Fixed Point Arithmetik keine
> Ahnung (was nicht heißt, dass das nie der Fall sein wird).
Aber geh. Du machst das jeden Tag, wenn du anstelle von Euros (mit 
Kommazahlen) in Cents (ohne Kommazahlen) rechnest. Fixed Point ist im 
Grunde nichts anderes als: alles wird in Einheiten einer kleinen 
Basiseinheit ausgedrückt. Anstelle von Grad Celsius (mit Kommazahlen) 
drücken deine Zahlen einfach Hunderstel Grad Celsius aus und schon 
kannst du Temperaturen mit 1/100 Grad genau berechnen.
Additionen und Subtraktionen laufen wie gewohnt. Bei 
Multiplikationen/Divisionen muss man den Skalierfaktor einmal 
rausrechnen und bei der Ausgabe/Eingabe wird der Kommapunkt an die 
entsprechende Stelle eingeschmuggelt.
Das ist die Kurzfassung von Fixed Point Arithmetik :-)

von Falk B. (falk)


Lesenswert?

@  Uwe Sauter (flightcontrol)

>Zügen) bewusst. Allerdings habe ich von Fixed Point Arithmetik keine
>Ahnung (was nicht heißt, dass das nie der Fall sein wird).

Na dann lies mal was über Festkommaarithmetik.

MFG
Falk

von Uwe S. (flightcontrol)


Lesenswert?

Karl heinz Buchegger schrieb:
> Einbettung einer Zahl in einen Ausgabestring

Das ist ja genau das, was ich mache. Eine float in einen string füttern, 
der dann auf dem Display ausgegeben werden soll.

Es ist nur so, dass ich in der Menüstruktur, die hinter der ganzen Sache 
steht, die Formatierungsstrings der auszugebenden Zeile hinterlegt habe 
und meine Funktion, die den string ans Display schickt dadurch ohne 
überlegen zu müssen für ints, floats oder sogar andere strings geeignet 
ist.

Aber das werde ich wohl dann noch umschreiben auf dtostrf().

Karl heinz Buchegger schrieb:
>> Allerdings habe ich von Fixed Point Arithmetik keine
>> Ahnung (was nicht heißt, dass das nie der Fall sein wird).
> Aber geh. Du machst das jeden Tag, wenn du anstelle von Euros (mit
> Kommazahlen) in Cents (ohne Kommazahlen) rechnest.

Hm. Dann hab ich wohl das Konzept der FP-Arithmetik völlig falsch 
verstanden.

Meine Meinung war bisher:

- Ich Teile ein Byte/Word auf in x Bit vor dem Komma und y Bit nach dem 
Komma
- die x Bit vor dem Komma werden wie eine (u)intx_t behandelt
- die y Bit nach dem Komma werden als Kehrwert betrachtet (und rückwärts 
gelesen)

Beispiel (der Punkt trennt x von y):
1
x=4, y=4:    0001.0000 wäre 1
2
             0000.1000 wäre 1/2
3
             0000.0100 wäre 1/3
4
             0000.1100 wäre 1/4

Allerdings geht mir gerade auf, dass ich dann Probleme hätte, wenn ich 
etwas anderes als 1 durch irgendwas darstellen wollte...

Die deutsche Wikipedia-Seite ist auch keine große Hilfe. Der Link von 
Falk verspricht mehr ;) Danke!

von Karl H. (kbuchegg)


Lesenswert?

Uwe Sauter schrieb:

> Es ist nur so, dass ich in der Menüstruktur, die hinter der ganzen Sache
> steht, die Formatierungsstrings der auszugebenden Zeile hinterlegt habe
> und meine Funktion, die den string ans Display schickt dadurch ohne
> überlegen zu müssen für ints, floats oder sogar andere strings geeignet
> ist.

Aber du hast ja wahrscheinlich nur einen Wert pro Menüpunkt. D.h. eine 
Kennung samt Abfrage in der Display-Schick Funktion, die je nachdem 
itoa, utoa, ltoa oder dtostrf benutzt, tuts auch bei wahrscheinlich 
massiver Code-Einsaprung :-)

Nichts gegen sprintf und Konsorten. Ich benutz die auch gerne. Aber wenn 
ich in Speichernöten bin, ist das das erste was ersetzt wird.


> - Ich Teile ein Byte/Word auf in x Bit vor dem Komma und y Bit nach dem
> Komma
> - die x Bit vor dem Komma werden wie eine (u)intx_t behandelt
> - die y Bit nach dem Komma werden als Kehrwert betrachtet (und rückwärts
> gelesen)
>
> Beispiel (der Punkt trennt x von y):
>
1
> x=4, y=4:    0001.0000 wäre 1
2
>              0000.1000 wäre 1/2
3
>              0000.0100 wäre 1/3
4
>              0000.1100 wäre 1/4
5
>


Du bist gerade auf dem Weg eine echte Floating Point Unit zu entwickeln. 
Gib noch einen Exponenten dazu und du bist dort :-)

von Uwe S. (flightcontrol)


Lesenswert?

Die Idee zu dieser etwas verqueren Vorstellung kommt letztlich von 
Wikipedia, wie mir eben wieder bewusst wurde. Dort steht:

R =real, F=Festkomma, 4Bit vor dem Komma, 4 dahinter
1
0R = 00000000F
2
1R = 00010000F
3
10R = 10100000F
4
0,5R = 00001000F
5
0,625R = 00001010F
6
0,0625R = 00000001F
7
15,9375R = 11111111F

Wenn ich mir also die Zeile
1
0,5R = 00001000F
betrachte, dann bin ich genau bei dem, was ich oben auch schon 
geschrieben habe. Allerdings passt
1
0,625R = 00001010F
schon nicht mehr in mein Schema.

Auch Festkommaarithmetik geht weniger auf die technische Darstellung 
ein sondern mehr auf die Vermeindung von Gleitkommazahlen durch Wählen 
von anderen Einheiten bzw. eines anderen Wertebereichs.

Kann mir da jemand noch auf die Sprünge helfen?
Was steht im Nachkommateil einer FP-Variable wenn ich 0.5, 0.382, etc. 
speichern will? Oder anders gefragt: wie wird der Nachkommateil 
aufgebaut?

Die Annahme, dass der Teil vor dem Komma wie eine normale int 
interpretiert wird, ist doch hoffentlich richtig?

von Falk B. (falk)


Lesenswert?

@  Uwe Sauter (flightcontrol)

>Was steht im Nachkommateil einer FP-Variable wenn ich 0.5, 0.382, etc.
>speichern will?

Eben diese Zahl, nur halt binär.

> Oder anders gefragt: wie wird der Nachkommateil aufgebaut?

Normal.

>Die Annahme, dass der Teil vor dem Komma wie eine normale int
>interpretiert wird, ist doch hoffentlich richtig?

Nöö, Fließkommazahlen haben gar keine Vorkommastellen ;-), die sind 
nämlich normalisiert. Sprich, die Zahl wird immer als 0,irgendwas 
gespeichert. Der Exponent gibt dann die Kommastelle an.

Also 57,78 wird als 0,5778e2 gespeichert, das ganze naürlich aber als 
Binäzahl.

MFG
Falk

von Karl H. (kbuchegg)


Lesenswert?

Uwe Sauter schrieb:

> Kann mir da jemand noch auf die Sprünge helfen?
> Was steht im Nachkommateil einer FP-Variable wenn ich 0.5, 0.382, etc.
> speichern will? Oder anders gefragt: wie wird der Nachkommateil
> aufgebaut?

In dem Fall gehst du das falsch an.
Die erste Frage lautet
  Was will ich eigentlich darstellen? Was sind meine Werte?
Die zweite Frage lautet
  Wieviele Nachkommastellen benötigt ich dafür

Von diesen beiden Antworten hängt es ab, was du als Vielfaches wählst.

> Die Annahme, dass der Teil vor dem Komma wie eine normale int
> interpretiert wird, ist doch hoffentlich richtig?

Die komplette Zahl wird wie ein normaler int interpretiert. Es gibt da 
keine Bits für 1/8 oder dergleichen.

ANgenommen du willst Euros miteinander verrechnen.
Das kannst du machen ....

    2.45
 + 12.68
 +  8.99
  ------
   24.12

und dafür die Floating Point Unit auspacken.

Das musst du aber nicht. Du kannst auch ganz einfach sagen: Anstelle von 
Euros rechne ich einfach alles in Cent und (euromässig gesehen) 2 
Nachkommastellen reichen mir

   245   Cent
  1268   Cent
   899   Cent
  -----
  2412   Cent

Ade Floating Point Unit. Welcome Integer Addition

Alles was du berücksichtigen musst, ist
 Bei der Umrechnung von Euro in Cent sind die Dezimalpunkte weggefallen. 
Bei der Ausgabe willst du wieder Euros haben und musst daher den 
Dezimalpunkt wieder einschmuggeln. Das ist aber einfach, da du ja weißt, 
dass der zwischen Hunderter und Zehnerstelle kommen muss (eine direkte 
Konsequenz der Festlegung auf 2 Euro-Nachkommastellen)

Sagst du dir: Hmm 2 Nachkommastellen werden nicht reichen, ich will ja 
auch Prozente berechnen und wenn ich die addiere brauch ich eine 
Kommastelle mehr, dann, ja, dann mach das doch einfach. Aus 2 Euro 45 
werden dann eben 2450 anstelle von 245

    2450
   12680
    8990
  -------
   24120

und da deine Zahlen jetzt offensichtlich Zehntelcent darstellen, sind 
auch das wieder 24 euro und 12 Cent (und 0 Zehntelcent)

Eigentlich unglaublich simpel :-)
(Es sei aber auch nicht verschwiegen, dass man jetzt ein bischen auf die 
Wertebereich aufpassen muss. Stichwort Overflow)

von Uwe S. (flightcontrol)


Lesenswert?

Ich glaube, ich habe ein richtig GROSSES Verständnisproblem.

Was du gerade erklärt hast ist für mich einfach eine Änderung der Basis, 
auf die sich die int bezieht. Man könnte das evt. auch als Normierung 
bezeichnen.

Soll soviel heißen wie: die Einheit wird eben von Volt in Millivolt 
geändert, deswegen muss die Zahl mit 1000 multipliziert werden. Die 
Genauigkeit von 0,01V oder 0,001V wird damit erreicht, ich brauche aber 
nur ganze Zahlen, keine rationalen.

Die Trennung zwischen vor und nach dem Komma findet also quasi durch die 
Normierung statt.



Der Wikipedia-Artikel allerdings scheint mir ein Byte in zwei Bereiche 
aufzuteilen: x Bit sind für die Zahl vor dem Komma zuständig, y Bit sind 
für die Zahl nach dem Komma zuständig.
...

Ach nein, der Wiki-Artikel ist einfach nur doof geschrieben *mir geht 
ein Licht auf*. Leider erwähnt der Artikel nichts von der Basis, auf die 
sich der Wertebereich bezieht.



Manchmal steh ich aber auch echt auf dem Schlauch...

Dann jetz noch die Frage, ob es in der avr-libc Ausgabe-Funktionen für 
festkommazahlen gibt. Also quasi eine Funktion, der ich ne int übergebe 
und zusätzlich die Anzahl Stellen hinter dem Komma oder eben die Basis, 
mit der die int normiert ist.


DANKE!!

von Uwe S. (flightcontrol)


Lesenswert?

Zurück zur Ausgangsfrage:

Uwe Sauter schrieb:
> Danke für den Tip! Kannst du auch erklären, was dahinter steckt? Und ob
> man nicht evt. doch an die Optimierungen der libc kommen kann, auch wenn
> man floats braucht? Immerhin würde ich ja knappe 20% Speicher sparen,
> wenn ich die libc benutzen könnte.

Kann mir jemand erklären, warum es ohne libc funktioniert?

Stimmt meine Vermutung, dass die libc zwar für den AVR optimiert ist, 
aber floats nicht unterstützt?

Und warum in aller Welt ist die Reihenfolge der Libs ausschlaggebend, 
wenn man gegen alle drei libs linkt?

Wenn gegen die libc als letzte Lib gelinkt wird, werden nur 14 Bytes 
mehr Platz verbraucht. Wird die libc dann allerdings auch genutzt oder 
kann man sich die 14 Bytes auch sparen?


Und sollte man im FAQ nicht explizit erwähnen, dass man die libc nicht 
benutzten darf?

von Karl H. (kbuchegg)


Lesenswert?

Uwe Sauter schrieb:
> Ich glaube, ich habe ein richtig GROSSES Verständnisproblem.
>
> Was du gerade erklärt hast ist für mich einfach eine Änderung der Basis,
> auf die sich die int bezieht. Man könnte das evt. auch als Normierung
> bezeichnen.

Bingo!
Jetzt hast du's :-)

> Dann jetz noch die Frage, ob es in der avr-libc Ausgabe-Funktionen für
> festkommazahlen gibt. Also quasi eine Funktion, der ich ne int übergebe
> und zusätzlich die Anzahl Stellen hinter dem Komma oder eben die Basis,
> mit der die int normiert ist.

Nein. Leider nicht.
Ist aber keine Hexerei sich sowas zu schreiben.

Wenn du dir sprintf leisten kannst (ohne Floating Point), dann geht zb 
sowas:
1
    int Euros;    // in Wirklichkeit Cent
2
3
    sprintf( buffer, "%d.%02d", Euros / 100, Euros % 100 );

Kannst du dir sprintf auch nicht leisten, dann kann man natürlich auch 2 
Ausgabefunktionen für zb int benutzen: Die eine gibt eine Zahl so aus, 
wie sie ist, die andere fügt noch führende 0-en ein
1
void print_int( int i )
2
{
3
  char buffer[5];
4
  itoa( buffer, i, 10 );
5
  print_s( buffer );
6
}
7
8
void print_int_zero( int i )
9
{
10
  if( i < 10 )
11
    print_c( '0' );
12
  printf_int( i );
13
}
14
15
void print_Euro( int euroCent )
16
{
17
  print_int( euroCent / 100 );
18
  print_int_zeros( euroCent % 100 );
19
}

und natürlich gibt es dann auch noch die Möglichkeit, den Dezimalpunkt 
während der Ausgabe an die richtige Stelle einzuschmuggeln
1
void print_Euro( int euroCent )
2
{
3
  int vor = euroCent / 100;
4
  int nach = euroCent % 100;
5
6
  print_int( vor );
7
  print_c( '.' );
8
  if( nach < 10 )
9
    print_c( '0' );
10
  print_int( nach );
11
}

Ob man das dann so wie hier auf 2 Nachkommastellen ausprogrammiert oder 
ob man sich generelle Funktionen schreibt, hängt im wesentlichen auch 
davon ab welchen Skalierfaktor man wählt.

PS: 10 ist kein so guter Skalierfaktor. Eine 2-er Potenz wäre besser, 
dann sind Disivion bzw. Modulo-Division trivial :-)

von Uwe S. (flightcontrol)


Lesenswert?

Danke für die Beispiele, wären nicht nötig gewesen. Ich wollte ja nur 
wissen, ob es Funktionen gibt oder ob noch etwas Fleißarbeit nötig ist.

Karl heinz Buchegger schrieb:
> PS: 10 ist kein so guter Skalierfaktor. Eine 2-er Potenz wäre besser,
> dann sind Disivion bzw. Modulo-Division trivial :-)

Da geb ich dir recht. Aber um noch ein wenig auf mein Projekt 
einzugehen:

Ich benutzte Timer2 als Zeitbasis für Timer1. Timer2 läuft also mit 
Systemtakt (10MHz) im CTC-Modus und toggelt alle 50 Takte (=5µsec) den 
Pin PD5 per Interrupt. Timer1 ist so eingestellt, dass eine steigende 
Flanke an PD5 als Takt benutzt wird. Die Zeitbasis für Timer1 ist also 
10µsec.

Basierend auf dieser Basis lasse ich Timer1 im Output-Compare-Modus 
laufen und schalte in den ISR für OCR1A und OCR1B Pins ein und aus.

Die Werte für OCR1A/B werden aus Werten berechnet, die durch Taster und 
das oben erwähnte Menü eingegeben werden können.

Ich muss also quasi die Darstellung des Eingabewerts auf dem Display 
ausgeben.

Wenn also der Wert an sich "50" ist, muss ich auf dem Display "50" * 
Zeitbasis = 0,5 ms ausgeben (ja, in Millisekunden, nicht Mikro). Sonst 
wäre ich auch gar nicht auf die Idee gekommen, sprintf zu benutzen. Ich 
habe nur eben in den einzelnen Menüpunkten unterschiedliche 
Formatierungsanforderungen, da die Sequenz, die der Kern des Projekts 
ist auch beliebig oft wiederholbar sein soll. Und bei Wiederholungen 
machen reelle Zahlen keinen Sinn....

von Stefan E. (sternst)


Lesenswert?

Uwe Sauter schrieb:
> Kann mir jemand erklären, warum es ohne libc funktioniert?

Wenn du die libc entfernst, linkst du nicht ohne sie. Sie wird immer 
automatisch der Linkliste hinzugefügt, aber am Ende.

> Stimmt meine Vermutung, dass die libc zwar für den AVR optimiert ist,
> aber floats nicht unterstützt?

Es geht hier nur um die printf-Familie. Da man selten Floats braucht, 
ist in der libc eine Variante, die keine Floats unterstützt, um Platz zu 
sparen. Du hast doch schon selber festgestellt, um wie viel größer die 
Float unterstützende Version ist.

> Und warum in aller Welt ist die Reihenfolge der Libs ausschlaggebend,
> wenn man gegen alle drei libs linkt?

Weil Objekte mit gleichem Namen in mehreren Libs sein können, und nur 
das erste gefundene wird genommen. Konkret geht es hier um die Funktion 
vfprintf (dort passiert die eigentliche Arbeit der printf-Famile). In 
beiden Libs ist sie drin, in libc (ohne Floats) und in libprintf_flt 
(mit Floats). Welche Implementation dieser Funktion dann tatsächlich 
genommen wird, hängt dann halt davon ab, welche der beiden Libs zuerst 
gelinkt wird.

> Wenn gegen die libc als letzte Lib gelinkt wird, werden nur 14 Bytes
> mehr Platz verbraucht. Wird die libc dann allerdings auch genutzt oder
> kann man sich die 14 Bytes auch sparen?
>
> Und sollte man im FAQ nicht explizit erwähnen, dass man die libc nicht
> benutzten darf?

Wie bereits gesagt, die libc wird immer benutzt, durch das manuelle 
hinzufügen änderst du nur die Reihenfolge.

von Uwe S. (flightcontrol)


Lesenswert?

So ganz versteh ich dann aber nicht, warum sich dann die Version (2) und 
(3) in meiner Tabelle in der Größe unterscheiden.

Zur Erinnerung: in (2) wurde nur libprinft_flt und libm explizit 
angegeben; in (3) danach auch noch libc.

Wenn die libc sowieso automatisch am Ende angefügt wird, warum gibt es 
dann einen Unterschied?

Stefan Ernst schrieb:
> Konkret geht es hier um die Funktion
> vfprintf (dort passiert die eigentliche Arbeit der printf-Famile). In
> beiden Libs ist sie drin, in libc (ohne Floats) und in libprintf_flt
> (mit Floats).

Wozu gibt es dann noch die libprintf_min? Ist es nicht so, dass in der 
libc überhaupt keine *printf-Funktionen enthalten sind?

Und wenn in der libc keine *printf hinterlegt sind, sollte es dann keine 
Rolle spielen, wann gegen sie gelinkt wird?

von Grrrr (Gast)


Lesenswert?

Uwe Sauter schrieb:
> Aber selbst wenn... einen char-Pointer auf char-Pointer zu casten sollte
> keinen Unterschied machen.

Das schon. Aber das machst Du dort nicht.

Du castest ein (char *) [17] auf ein (char *). Mag sein das das hier 
nicht relevant ist, aber das Du es überhaupt tust ist ersten 
überflüssig, zeigt zweitens das Du den Unterschied nicht kennst und kann 
drittens zu weiteren Unklarheiten führen wenn Du denn tatsächlich mal 
eine Funktion benutzt die aus irgendeinem Grunde (char *) [] erwartet.

von Stefan E. (sternst)


Lesenswert?

Uwe Sauter schrieb:
> So ganz versteh ich dann aber nicht, warum sich dann die Version (2) und
> (3) in meiner Tabelle in der Größe unterscheiden.

Der Unterschied ist der, dass die libc bei (2) nur einmal bearbeitet 
wird, und bei (3) zweimal. Was bei der doppelten Bearbeitung die 
zusätzlichen Bytes verursacht, kann ich dir so nicht sagen. Ein Blick in 
das Map-File könnte dir darüber Aufschluss geben.

> Wozu gibt es dann noch die libprintf_min?

Die hat Einschränkungen bei den verwendbaren Conversion-Flags, um noch 
mehr Platz zu sparen.

> Ist es nicht so, dass in der
> libc überhaupt keine *printf-Funktionen enthalten sind?
>
> Und wenn in der libc keine *printf hinterlegt sind, sollte es dann keine
> Rolle spielen, wann gegen sie gelinkt wird?

Du hast doch sicher auch schon mal ein printf benutzt, ohne 
libprintf_flt oder libprintf_min hinzu gelinkt zu haben, oder? Auf welch 
magische Weise hast das dann wohl funktioniert?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Uwe Sauter schrieb:
> So ganz versteh ich dann aber nicht, warum sich dann die Version (2) und
> (3) in meiner Tabelle in der Größe unterscheiden.

Dazu müsstest du die Symboltabellen der beiden Ergebnisse vergleichen
(das ist die Ausgabe des Kommandos »avr-nm <yourfile.elf>«).

> Wozu gibt es dann noch die libprintf_min?

Falls man mal unbedingt für irgendwas ein printf benötigt (und sei's
zum Debuggen), die Standardvariante aber "gerade so" nicht mehr passt,
und man daher alle verzichtbaren Dinge (wie Feldbreitenbegrenzungen
etc.) weglassen möchte.

> Ist es nicht so, dass in der
> libc überhaupt keine *printf-Funktionen enthalten sind?

Ja, es ist nicht so. ;-)  Die Standardvariante ist in der normalen
libc.a enthalten.  Die Bibliotheken libprintf_min.a und
libprintf_flt.a enthalten nur die Sondercompilate.

Ganz glücklich bin ich mit dieser Lösung ringsum nicht.  Sie skaliert
miserabel, insbesondere bei "orthogonalen" Eigenschaften.  Wenn man
also beispielsweise noch Unterstützung für 64-bit-Integer-Formate
mit reinbringen will, dann würden sich praktisch schon doppelt so
viele Varianten ergeben (min, min_int64, std, std_int64, flt,
flt_int64).  Noch ein weiteres Kriterium mehr, und man hätte nochmal
doppelt so viele...  Leider ist mir einfach bislang nichts besseres
eingefallen.

von Stefan E. (sternst)


Lesenswert?

Jörg,
mal so am Rande. Hat das etwas umständliche Vorgehen mit -Wl,-u,vfprintf 
einen besonderen Grund? Wäre es nicht praktischer, einfach Kopien der 
Frontend-Funktionen mit in die Speziallibs zu packen?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Stefan Ernst schrieb:
> Wäre es nicht praktischer, einfach Kopien der
> Frontend-Funktionen mit in die Speziallibs zu packen?

Ja, wäre es, hat nur niemand gemacht.  Die Idee, dass man das natürlich
auch machen könnte, ist mir leider erst gekommen, als die umständliche
Variante mit -uvfprintf schon in allerlei FAQs und Makefile-Templates
angekommen war. :-/

Manchmal hat man eben Tomaten auf den Augen. ;-)

von Uwe S. (flightcontrol)


Angehängte Dateien:

Lesenswert?

Stefan Ernst schrieb:
> Du hast doch sicher auch schon mal ein printf benutzt, ohne
> libprintf_flt oder libprintf_min hinzu gelinkt zu haben, oder? Auf welch
> magische Weise hast das dann wohl funktioniert?

Nein ;) Bis jetzt habe ich itoa() benutzt und dann den Inhalt des 
bearbeiteten Strings in den Sammel-String Byte für Byte kopiert ;) 
Bisher musste ich aber auch noch keine floats ausgeben.


Die beiden Ausgaben von "avr-nm" habe ich mal angehängt. Wenn ich die 
Dateien richtig interpretieren unterscheiden sich nur einige Adressen. 
Aber insgesamt kann ich wenig damit anfangen.

Gibt es in der Doku einen Hinweis, welche libprintf welchen 
Funktionsumfang hat?

Den Hinweis, dass libc nicht eingetragen sein darf, habe ich mal in den 
FAQ hinzugefügt.

von Hc Z. (mizch)


Lesenswert?

Uwe Sauter schrieb:

> Die beiden Ausgaben von "avr-nm" habe ich mal angehängt. Wenn ich die
> Dateien richtig interpretieren unterscheiden sich nur einige Adressen.
> Aber insgesamt kann ich wenig damit anfangen.

Wenn ich das richtig sehe, unterscheiden sie sich ab vfprintf. 
Vermutlicher Grund: mit der expliziten Angabe von libc hast Du die 
Version in avr/lib/libc.a zuerst gelinkt und deren vfprintf hat eine 
etwas andere Länge als die spezifischeren in avr/lib/avr5/libc.a, 
avr/lib/avr6/libc.a usw., die der Compiler auswählt.  Schau Dir die 
Verzeichnisse mal an und vergleiche die Größen der libc.a.  Die 
differieren etwas.

> Den Hinweis, dass libc nicht eingetragen sein darf, habe ich mal in den
> FAQ hinzugefügt.

Das halte ich für eher überflüssig.  Es ist ein Grundelement der Sprache 
C, dass die libc dazugelinkt wird, ohne dass sie speziell zu erwähnen 
ist. Sie gehört bei C einfach automatisch dazu. (Ich verstehe zwar, dass 
Du andere vor demselben Fehler schützen willst, aber so häufig ist der 
nicht und wenn alle Fehler, die irgendwann begangen werden, erwähnt 
würden, würde das doch ziemlich unübersichtlich.)

von Rolf Magnus (Gast)


Lesenswert?

Uwe Sauter schrieb:

> So ganz versteh ich dann aber nicht, warum sich dann die Version (2)
> und (3) in meiner Tabelle in der Größe unterscheiden.
>
> Zur Erinnerung: in (2) wurde nur libprinft_flt und libm explizit
> angegeben; in (3) danach auch noch libc.
>
> Wenn die libc sowieso automatisch am Ende angefügt wird, warum gibt es
> dann einen Unterschied?

Am Ende wird nicht nur libc, sondern noch andere Dinge angehängt. Deren 
Reihenfolge hast du durch explizite Angabe von libc verändert. Ich 
schätze mal, daß es damit in irgendeiner Form zusammenhängt.

von Stefan E. (sternst)


Lesenswert?

Jörg Wunsch schrieb:

> Die Idee, dass man das natürlich
> auch machen könnte, ist mir leider erst gekommen, als die umständliche
> Variante mit -uvfprintf schon in allerlei FAQs und Makefile-Templates
> angekommen war. :-/

Och, das ist ja kein Hinderungsgrund, es wäre ja quasi 
abwärtskompatibel. ;-)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Der Hinderungsgrund ist nur, dass es Arbeit macht, das Makefile-
Gewurschtel entsprechend anzupassen. ;-)

Lieber wäre mir eigentlich eine geniale Idee, wie man davon ganz
weg kommen könnte...

von Rolf Magnus (Gast)


Lesenswert?

> Lieber wäre mir eigentlich eine geniale Idee, wie man davon ganz
> weg kommen könnte...

Ich würde ja vorschlagen, C++ zu nehmen, aber das wird wohl nicht arg 
hilfreich sein ;-)
Bei printf ist ja das Problem, daß alles in einer Funktion steckt und 
man daher den Code für alle unterstützten Formatierungen braucht, auch 
wenn er im konkreten Programm gar nicht verwendet wird.
Man kann sich in C++ aber eine Klasse basteln, die einen iostream in 
großen Teilen nachbildet. Dann hätte man ein cout statt printf. Der 
große Vorteil gegenüber printf ist, daß man für jeden Typ eine separate 
Funktion hat, und wenn man die nicht aufruft, muß sie auch nicht mit 
gelinkt werden. Damit ist das Problem da gar nicht existent.

von Uwe S. (flightcontrol)


Lesenswert?

Da ich mich nicht wirklich mit Libraries auskenne, kommt hier ein 
bescheidener Vorschlag eines Laien:

Wie wäre es, wenn man den Funktionsumfang der gewünschten printf durch 
#define begrenzt, die in den printf-Sourcen eben die jeweiligen 
Codeabschnitte ein-/ausschalten.

Mir ist bewusst, dass man dann printf jedes mal neu übersetzen muss und 
es dementsprechend eigentlich keine Lib mehr ist. Andererseits könnte 
man so nur die Funktionalität auswählen, die printf haben soll.


Falls das kein guter Vorschlag ist, lese ich gerne eine Erklärung, 
warum. Ich bin ja lernfähig ;)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rolf Magnus schrieb:

> Ich würde ja vorschlagen, C++ zu nehmen, aber das wird wohl nicht arg
> hilfreich sein ;-)

Richtig, das hilft für printf nicht, ich weiß, dass die ostream-
Formatierungen in C++ das Problem lösen.  Dafür sind sie halt reichlich
umständlich zu bedienen im Vergleich zum "Schweizer Messer" printf.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Uwe Sauter schrieb:
> Wie wäre es, wenn man den Funktionsumfang der gewünschten printf durch
> #define begrenzt, die in den printf-Sourcen eben die jeweiligen
> Codeabschnitte ein-/ausschalten.

Genau so ist es ja im Sourcecode gelöst.  Aber du hast das Problem
schon erkannt: es ist dann halt keine Library.  Im Moment sind es
halt derer drei, je nachdem, mit welchen -D-Optionen compiliert worden
ist.

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
Noch kein Account? Hier anmelden.