Forum: Compiler & IDEs sprintf verfälscht adc werte


von Michael H. (overthere)


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);

von Michael H. (overthere)


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...

von Stefan B. (stefan) Benutzerseite


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.

von P. S. (Gast)


Lesenswert?

Rate mal, warum dir so keiner helfen kann...

von Michael H. (overthere)


Lesenswert?

Im Rom ist noch mehr als genug Platz >5kByte...

@Peter: was brauchst du denn an files?

von Karl H. (kbuchegg)


Lesenswert?

Datentyp für send?

von Klaus S. (skibby)


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.

von Klaus S. (skibby)


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...

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


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.

von Michael H. (overthere)


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)...

von Michael H. (overthere)


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

von Karl H. (kbuchegg)


Lesenswert?

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

von Michael H. (overthere)


Angehängte Dateien:

Lesenswert?

ja klar.

hier die hauptdatei main.c

von Michael H. (overthere)


Angehängte Dateien:

Lesenswert?

und hier die datei für die adcs

von Karl H. (kbuchegg)


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
1
unsigned int adc_read(unsigned char channel){
2
  unsigned int readit;
3
  do{
4
    cli();
5
    readit=_adcdata[channel];
6
    sei();
7
  } while(readit==ADC_value_nulled);
8
9
  cli();  
10
  readit = _adcdata[channel];
11
  sei();
12
  return readit;
13
}

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.

von Hc Z. (mizch)


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.

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


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.

von Karl H. (kbuchegg)


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

von Hc Z. (mizch)


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.

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


Lesenswert?

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

von Hc Z. (mizch)


Lesenswert?

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

von Michael H. (overthere)


Angehängte Dateien:

Lesenswert?

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

von Stefan B. (stefan) Benutzerseite


Lesenswert?

> INITZILASATION

;-)

sprintf sehe ich keine.

von Michael H. (overthere)


Angehängte Dateien:

Lesenswert?

scheise, im verzeichniss verrutscht

von Michael H. (overthere)


Lesenswert?

keiner eine lösung?

von Karl H. (kbuchegg)


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.

von Michael H. (overthere)


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?

von Karl H. (kbuchegg)


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.

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


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.

von meha (Gast)


Lesenswert?

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

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


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.

von Michael H. (overthere)


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...

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


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.

von Michael H. (overthere)


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!

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


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:
1
struct _foo {
2
   ...
3
};

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:
1
char x[] __attribute__((progmem)) = "Hi";
2
     /* Dokumentation von GCC beschreibt das */
3
#define __STDIO_FDEVOPEN_COMPAT_12 /* avr-libc-Dokumentation beschreibt,
4
                                      was dabei passiert */
5
PORTE = _BV(1);  /* ebenfalls Bestandteil der avr-libc */

von Michael H. (overthere)


Lesenswert?

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

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.