mikrocontroller.net

Forum: Compiler & IDEs sprintf verfälscht adc werte


Autor: Michael H. (overthere)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

ich habe vielleicht ein komisches Problem: Ändere ich die beschriftung 
meiner werte, kommen plötzlich falsche adc werte raus. Woran liegt das?

RS232 Log:
a 228 b 3 a 371
a 229 b 3 a 371
a 229 b 3 a 371
a 229 b 2 a 371
adc 228 b -2 a 24063
adc 14656 b 192 a 23744
adc 14656 b 192 a 23744
adc 14656 b 192 a 23744



Codeausschnitt:
    char buffer[20];
    send=adc_read_and_null(ADC_highmosfet);
    sprintf( buffer, "adc %i ", send);
    usart_puts( buffer);
    send=adc_read_and_null(0);
    sprintf( buffer, "b %i ", send);
    usart_puts( buffer);
    send=adc_read_and_null(ADC_supplyvoltageref);
    sprintf( buffer, "a %i ", send);
    usart_puts( buffer);

Autor: Michael H. (overthere)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nachtrag:
Entspricht der 3. String dem ersten oder 2. geht es, sonst kommt müll..

a 14656 b 192 c 23744
a 14656 b 192 c 23744
a 255 b -2 b 371
a 229 b 3 b 371

Hoffe ihr könnt mir helfen, schonmal besten Dank im Vorraus...

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wieviel Platz hast du noch im µC?

    sprintf( buffer, "adc %i ", send);
    sprintf( buffer, "b %i ", send);
    sprintf( buffer, "a %i ", send);

Braucht 8+6+6 Bytes im ROM und nochmal im RAM für die Formatstrings.

    sprintf( buffer, "a %i ", send);
    sprintf( buffer, "b %i ", send);
    sprintf( buffer, "b %i ", send);

Braucht 6+6 Bytes im ROM und nochmal im RAM für die Formatstrings.

Autor: P. S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rate mal, warum dir so keiner helfen kann...

Autor: Michael H. (overthere)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Im Rom ist noch mehr als genug Platz >5kByte...

@Peter: was brauchst du denn an files?

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

Bewertung
0 lesenswert
nicht lesenswert
Datentyp für send?

Autor: Klaus Skibowski (skibby)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Michael H. schrieb:
> Im Rom ist noch mehr als genug Platz >5kByte...
>
> @Peter: was brauchst du denn an files?

Dein Buffer ist zu klein !!! Zähl mal die Zeichen nach.

Autor: Klaus Skibowski (skibby)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus Skibowski schrieb:
> Michael H. schrieb:
>> Im Rom ist noch mehr als genug Platz >5kByte...
>>
>> @Peter: was brauchst du denn an files?
>
> Dein Buffer ist zu klein !!! Zähl mal die Zeichen nach.

sorry, muss mich korrieren, ich habe den Code nicht vollständig gelesen.
Du schickst den String ja nicht am Stück...

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

Bewertung
0 lesenswert
nicht lesenswert
Michael H. schrieb:

> Entspricht der 3. String dem ersten oder 2. geht es, sonst kommt müll..

Zwei gleiche Strings werden vom Compiler als einer abgebildet.

Riecht alles danach, als wäre dein SRAM voll und wird vom Stack
zerschossen.

Autor: Michael H. (overthere)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
meint ihr den Stack von uart_puts? der beträgt 64 bytes, sollte 
eigentlich reichen... Auf dem Ram ist auchnoch genug Platz vorhanden 
(800 Bytes)...

Autor: Michael H. (overthere)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nachtrag: An sprintf liegts selbst nicht, folgendes funktioniert:

code:
char buffer[60];
sprintf( buffer, "s:%i hv%i o%i", send3, send2, OCR2);
usart_puts( buffer);
sprintf( buffer, "a%i b%i c%i d%i", 123, 456, 789, 12);
usart_puts( buffer);

log:
restart
s:22464 hv216 o0a123 b456 c789 d12
s:22528 hv13888 o14a123 b456 c789 d12
s:22464 hv13888 o14a123 b456 c789 d12
s:22528 hv13888 o14a123 b456 c789 d12
s:22464 hv13888 o14a123 b456 c789 d12

nur irgendwie verreists mir die adc werte.

Danke für eure Hilfe

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

Bewertung
0 lesenswert
nicht lesenswert
Dann zeig jetzt endlich mal das komplette Programm und nicht immer nur 
nichtssagende Ausschnitte.

Autor: Michael H. (overthere)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
ja klar.

hier die hauptdatei main.c

Autor: Michael H. (overthere)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
und hier die datei für die adcs

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

Bewertung
0 lesenswert
nicht lesenswert
Soweit so gut.
Und wo sind jetzt besagte sprintf?

Edit:
Bis jetzt habe ich eigentlich nur gesehen, dass du Zugriffe auf _adcdata 
nicht gegen Interrupts abgesichert hast. Da _adcdata ein int ist, musst 
du das tun, da die Operation nicht atomar ausgeführt werden kann.

zb
unsigned int adc_read(unsigned char channel){
  unsigned int readit;
  do{
    cli();
    readit=_adcdata[channel];
    sei();
  } while(readit==ADC_value_nulled);

  cli();  
  readit = _adcdata[channel];
  sei();
  return readit;
}

Wenn man es ganz exakt machen will, müsste man anstelle des sei() 
eigentlich immer den vorhergehenden Zustand wiederherstellen. 
Schliesslich sagt ja niemand, dass Interrupts generell aktiviert sind, 
wenn adc_read aufgerufen wird. Aber in deinem Fall kann man wohl davon 
ausgehen, dass das immer so sein wird.


Nitpicking:
Namen, die mit einem _ beginnen, sind für den Compiler bzw. die Runtime 
Library reserviert.

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
cli() und sei() tun's nicht (genauer: tun's oft, müssen aber nicht), 
denn der Compiler ist frei, sie umzugruppieren.  Besser sind die 
ATOMIC_BLOCK-Macros der AVR-libc aus atomic.h, die sind für solche Fälle 
gedacht.

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

Bewertung
0 lesenswert
nicht lesenswert
Hazeh Zimmerer schrieb:

> cli() und sei() tun's nicht (genauer: tun's oft, müssen aber nicht),
> denn der Compiler ist frei, sie umzugruppieren.

Ich stimme dir zwar zu, dass man die atomic-Makros nehmen soll,
aber cli() expandiert zu _asm__ __volatile_, das sollte der
Compiler nicht umgruppieren.

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

Bewertung
0 lesenswert
nicht lesenswert
Hazeh Zimmerer schrieb:
> Besser sind die
> ATOMIC_BLOCK-Macros der AVR-libc aus atomic.h, die sind für solche Fälle
> gedacht.

:-)
Old habit.
An die ATOMIC Makros muss ich mich erst noch gewöhnen

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Jörg,

> cli() expandiert zu asm__ __volatile, das sollte der
> Compiler nicht umgruppieren.

ohne mich jetzt sooo besonders auszukennen:  Um nicht umgruppiert zu 
werden, müssten sei() und cli() dem Compiler mitteilen, dass ihre 
Seiteneffekte beliebigen Speicher und beliebige Register modifizieren 
können (was aus Gründen der Optimierung aber sonst unerwünscht ist, aber 
oben im Bezug auf den ADC-Wert der Fall ist).  So jedenfalls habe ich es 
aus einer längeren und sehr interessanten Diskussion vor ca. 2 Monaten 
in comp.embedded (mitlesend) geschlossen.  Der Compiler darf solange 
umgruppieren, wie das Ergebnis inklusive aller Seiteneffekte dasselbe 
bleibt, so mein Stand.

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

Bewertung
0 lesenswert
nicht lesenswert
Ja, könnte passen.  Es fehlt halt zunehmend im C-Standard ein
volatile, das man auf einen Block Code anwenden kann, sowas
wie:
    cli();
    volatile { readit = _adcdata[channel]; }
    sei();

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja.  Die Optimierungen moderner Compiler, die dem Einen ein Segen sind, 
werden sonst leicht zum Fluch.

Autor: Michael H. (overthere)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Das Atomarmachen hat leider nichts geholfen. Anbei mal eine main.c mit 
sprintf. Hoffentlich habt ihr noch andere ideen.

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> INITZILASATION

;-)

sprintf sehe ich keine.

Autor: Michael H. (overthere)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
scheise, im verzeichniss verrutscht

Autor: Michael H. (overthere)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
keiner eine lösung?

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

Bewertung
0 lesenswert
nicht lesenswert
Ausser, dass %i der falsche Formatspecifier für einen unsigned int ist, 
sehe ich im geposteten Códe nichts mehr. Aber das ist hier nicht das 
Problem. Ich denke, dass irgendwo der Stack durcheinanderkommt.

Autor: Michael H. (overthere)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lösung gefunden. In meiner ADC Datei kommt beim ADMUX (Analog 
Multiplexer) scheinbar was durcheinander. Ich habe das jetzt ersetzt und 
wunderweis, es geht!
folgendes
  char temp;
  temp=ADMUX;
  temp&=~((1<<MUX3)+(1<<MUX2)+(1<<MUX1)+(1<<MUX0));
  temp|=_adc_current_pos;
  ADMUX=temp;;
durch:
ADMUX=ADMUX_CONFIG+_adc_current_pos;

ersetzt und es geht!

Hat jemand zufällig eine Erklärung dafür?

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

Bewertung
0 lesenswert
nicht lesenswert
Hmm

Ersetze mal
  char temp;
durch
  unsigned char temp;

(WEnn du mit Bytes arbeitest, sei immer explizit!
char benutzt du nur dann, wenn du es auch tatsächlich mit Charactern im 
Sinne von Textverarbeitung zu tun hast.
Du weißt nie (ausser du siehst in der Doku nach) ob der Compiler char 
als signed oder unsigned ansiehst. Je nachdem kann da einiges passieren.

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

Bewertung
0 lesenswert
nicht lesenswert
Oder halt gleich sich an C99-Stil gewöhnen und uint8_t / int8_t
aus <stdint.h> benutzen, wenn man gar keine druckbaren Zeichen
sondern kleine Integer-Zahlen meint.  Auch uint_least8_t und
uint_fast8_t sind einen Moment des Nachdenkens wert, falls der
Code ggf. auf größere Prozessoren portabel sein soll.

Autor: meha (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und das hier sollte man auch lassen:
_adc_current_pos;
Unterstriche vor dem Variablen-/Funktionsnamen sind doch den Libs und 
Headern vorbehalten!?

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

Bewertung
0 lesenswert
nicht lesenswert
7.1.3 Reserved identifiers
...
-- All identifiers that begin with an underscore are always reserved
   for use as identifiers with file scope in both the ordinary and
   tag name spaces.

Autor: Michael H. (overthere)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich bin kein Freund von uint8_t da mein Prog das nicht hervorhebt.

Jetzt müsst ihr mich aber mal ein bisschen Aufklären: was ist der 
unterschied zwischen uint_least8_t und uint_fast8_t? Bzw. was heißt das?

Übersetzt mir das Englische jetzt noch jemand bitte ins Deutsche? Ich 
komm trotz meinen 8 Jahren Englisch nicht dahinter was gemeint ist...

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

Bewertung
0 lesenswert
nicht lesenswert
Michael H. schrieb:
> Ich bin kein Freund von uint8_t da mein Prog das nicht hervorhebt.

Dann kannst du dir immer noch einen anderen Editor suchen oder aber
denjenigen, der ihn gebaut hat, darauf hinweisen, dass es an der
Zeit wäre, 10 Jahre nach der Verabschiedung des aktuellen C-Standards
sowas mit zu unterstützen.

> Jetzt müsst ihr mich aber mal ein bisschen Aufklären: was ist der
> unterschied zwischen uint_least8_t und uint_fast8_t?

uint8_t verlangt eine vorzeichenlose Ganzzahl, die exakt 8 bit
breit ist.  Wenn die zu Grunde liegende Maschine eine derartige
Zahl nicht darstellen kann (und der Compiler sie auch nicht emulieren
kann oder will), dann gibt es diesen Typ nicht, und entsprechender
Code lässt sich nicht compilieren.  Eine mögliche Folge davon ist,
dass eine derartige Zahl im Zugriff u. U. weniger effektiv ist, als
eine größere Zahl auf einer bestimmten Maschine wäre.  Damit ist
dieser Datentyp prädestiniert für Informationen, bei denen es wirklich
auf die genaue Bitanzahl ankommt, bspw. in irgendwelchen Kommunikations-
protokollen (Netzwerk, SCSI etc.) oder beim Zugriff auf bestimmte
Hardwarekomponenten.

uint_least8_t verlangt eine vorzeichenlose Ganzzahl, die mindestens
8 bit breit ist.  Falls die zu Grunde liegende Maschine eine solche
Zahl nicht darstellen kann, kann sie aber auch einen größeren Daten-
typ substituieren.  Das Augenmerk liegt aber auf dem Einsparen von
Speicherplatz, wenn die Maschine das also implementieren kann (auch
wenn es auf Kosten der Effektivität geht), dann ist er auch 8 bit
breit.

uint_fast8_t verlangt eine vorzeichenlose Ganzzahl, die mindestens
8 bit breit ist.  Falls die zu Grunde liegende Maschine jedoch mit
einer größeren Bitanzahl schnelleren Code erzeugt (weil bspw. auf
einer 32-bit-Maschine Zahlen von 32 bits schneller zu bearbeiten
sind als deren 8-bit-Teilwerte), dann soll dieser Typ substituiert
werden.  Augenmerk ist also die Geschwindigkeit, auch wenn es auf
Kosten des Speicherbedarfs möglicher Variablen geht.

Damit wäre an vielen Stellen, wo sich in der AVR-Community derzeit
uint8_t eingebürgert hat, eigentlich uint_fast8_t zu bevorzugen,
wenn der Code ggf. auch auf andere (größere) Architekturen portierbar
sein soll.

Die ganzen Datentypen gibt's dann auch noch ohne dem führenden u
für vorzeichenbehaftete Zahlen.

Autor: Michael H. (overthere)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank Jörg für deinen ausführliche Erklärung. Ich nutze zur Zeit 
geany und frage gleich mal in der Newsgroup nach, was man da machen 
kann...

>7.1.3 Reserved identifiers
>...
>-- All identifiers that begin with an underscore are always reserved
>   for use as identifiers with file scope in both the ordinary and
>   tag name spaces.
Kann mir das bitte noch jemand übersetzten, ich komme nicht dahinter was 
gemeint ist.

Update: geany unterstützt es seit der neuen Version, ich hatte es nur 
mit der alten probiert... Nochmal vielen Dank für deine Erklärungen!

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

Bewertung
0 lesenswert
nicht lesenswert
Michael H. schrieb:

>>7.1.3 Reserved identifiers
>>...
>>-- All identifiers that begin with an underscore are always reserved
>>   for use as identifiers with file scope in both the ordinary and
>>   tag name spaces.

> Kann mir das bitte noch jemand übersetzten, ich komme nicht dahinter was
> gemeint ist.

Die Details, an welchen Stellen man einen führenden Unterstrich auch
als Autor eines normalen Anwenderprogramms schreiben darf und an
welchen nicht, sind ein wenig, naja, nicht sofort offensichtlich.
Solange du dich mit den Details nicht befassen willst, kannst du
also oberste Regel nehmen: schreib keine Bezeichner, die mit einem
Unterstrich beginnen.  Die vielleicht prominenteste Ausnahme davon
wäre diese hier:
struct _foo {
   ...
};

Dort ist er immer zulässig, sofern es ein Unterstrich ist (nicht
zwei) und sofern danach ein Kleinbuchstabe folgt.

Ansonsten sind Bezeichner mit einem führenden Unterstich ,reserviert',
d. h. für die Benutzung durch den Compiler oder die Systembibliotheken
vorbehalten.  Natürlich darfst du sie dann trotzdem benutzen, aber
halt genau dann, wenn dir die Dokumentation des Compilers oder der
Bibliothek sowas sagt.  Beispiele dafür sind:
char x[] __attribute__((progmem)) = "Hi";
     /* Dokumentation von GCC beschreibt das */
#define __STDIO_FDEVOPEN_COMPAT_12 /* avr-libc-Dokumentation beschreibt,
                                      was dabei passiert */
PORTE = _BV(1);  /* ebenfalls Bestandteil der avr-libc */

Autor: Michael H. (overthere)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank, ich hab's kapiert! Danke auch für die ausführliche 
Erklärung.

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.