Forum: Compiler & IDEs Problem mit Berechnung negativer Zahlen?


von Christian W. (orikson)


Lesenswert?

Hallo,

ich möchte mit meinem Mega8 eine positive und negative Spannung messen. 
Das ganze muss nicht extrem genau sein, daher hab ich hier aus dem Forum 
einen Vorschlag mit Spannungsteilern gebaut. Das ganze läuft auch, ich 
bekomme ordentliche ADC Werte und die Umrechnung in Volt funktioniert 
auch großteils.

Ich habe allerdings noch ein Problem mit der negativen Berechnung, 
sobald diese >0 wird. Ich bekomme dann immer den Wert -3835.0 
angezeigt...

Hier die wichtigen Auszüge des Codes (AVR-GCC unter Windows):
1
int main( void )
2
{
3
int n_volt0_adc = 513;    // ADC-Wert wenn Messpin für negative Spannung auf GND liegt
4
float n_volt_val = -13.0;  // negative Spannung in V (MIT Vorzeichen)
5
int n_volt_adc = 291;    // ADC-Wert bei negativer Spannung
6
7
float n_volt_div = (n_volt0_adc - n_volt_adc) / n_volt_val;
8
float n_volt;
9
10
 while(1)
11
 {
12
  n_volt = ( n_volt0_adc - ReadChannel(5) ) / n_volt_div;
13
   dtostrf(n_volt, 5, 1, buffer);
14
   lcd_pos( 1, 10 );
15
   lcd_text(buffer);
16
 }
17
}

Folgende Spannungen mit dazugehörigen ADC-Werten hab ich schon gemessen:
--------------------------------
Spannung in V | -13 |  0  | 2,67
--------------------------------
ADC Wert      | 291 | 513 | 559
--------------------------------
Die +2,67 Volt bekomme ich, wenn ich den Messpin nicht angeschlossen 
habe.

Wenn ich das ganze mit meinem Taschenrechner nachrechne, komme ich auf 
die richtigen Ergebnisse, also vermute ich das Problem irgendwo im Code?

Wenn es ein bekanntes Problem ist dann umgeh ich das eben mit einer 
if-Abfrage. Da an diesem Pin eigentlich nie positive Spannungen anliegen 
werden ist das kein Problem. Mich würde nur interessieren, woran das 
liegt?!

von (prx) A. K. (prx)


Lesenswert?

Wenn du den ADC so konfigurierst, dass die 10 Bits linksbündig im 
Ergebnisregister liegen, dann lässt sich der Wert direkt als 16-Bit 
Integer mit Vorzeichen auslesen (int16_t). Zerebrale Knotenbildung durch 
Plus/Minus-Rechnerei entfällt, denn negative Werte sind schon negativ, 
nut eben im Wertebereich +/-32K statt +/-512.

von Helfer (Gast)


Lesenswert?

> Spannung in V | -13 |  0  | 2,67
                  ^^^

Bist du sicher, dass dein AVR das überlebt hat?

von Christian W. (orikson)


Lesenswert?

Ja, der µC lebt noch. Die Schaltung die ich verwende um die Spannungen 
zu messen ist diese hier: 
http://www.mikrocontroller.net/attachment/107327/Bild1.png (die untere 
Z-Diode einfach ignorieren, die is falsch eingezeichnet...)

A. K. schrieb:
> Zerebrale Knotenbildung durch Plus/Minus-Rechnerei entfällt
Ansich stört es mich ja nicht, wenn mit den +/- Werten gerechnet wird, 
der Wertebereich ist auch unkritisch, die +/-512 würden ja locker 
reichen.

Die Funktion ReadChannel() liefert mir auch schon einen uint16_t Wert, 
aber ich kann mir trotzdem noch nicht erklären, warum beim Überschreiten 
der 0,0V Grenzen auf einmal ein so seltsamer Wert rauskommt...

von Stefan E. (sternst)


Lesenswert?

Christian Wächter schrieb:
> Die Funktion ReadChannel() liefert mir auch schon einen uint16_t Wert,
> aber ich kann mir trotzdem noch nicht erklären, warum beim Überschreiten
> der 0,0V Grenzen auf einmal ein so seltsamer Wert rauskommt...

Genau deshalb. Weil ReadChannel ein uint16_t zurück gibt, erfolgt die 
Berechnung in der Klammer als unsigned, und auch das Ergebnis der 
Berechnung in der Klammer wird als unsigned interpretiert.

von Christian W. (orikson)


Lesenswert?

Stefan Ernst schrieb:
> Weil ReadChannel ein uint16_t zurück gibt, erfolgt die
> Berechnung in der Klammer als unsigned

Ah, verstehe. Was müsste ich dann ändern, damit es korrekt berechnet 
wird? Für float, double etc. kann man ja ein
1
(float)
 davor setzten, um es zu erzwingen. Gibt es das für (un)signed Zahlen 
auch? Oder muss ich das Ergebniss aus (n_volt0_adc - ReadChannel(5)) 
nochmal in eine signed Variable zwischenspeichern?

von Stefan E. (sternst)


Lesenswert?

Entweder
1
 n_volt = (int16_t)(n_volt0_adc - ReadChannel(5)) / n_volt_div;
oder
1
 n_volt = (n_volt0_adc - (int16_t)ReadChannel(5)) / n_volt_div;
oder einfach ReadChannel ein int16_t zurück geben lassen, statt 
uint16_t.

von Christian W. (orikson)


Lesenswert?

Ah, sehr gut. Jetzt passt es, danke.

Eine kleine Frage zum Vorzeichen hätte ich aber noch: Kann ich es 
"erzwingen", dass das Vorzeichen immer angezeigt wird? Bei negativen 
Werten wird das "-" automatisch ergänzt, bei positiven wird aber das "+" 
weggelassen?

von Sven H. (dsb_sven)


Lesenswert?

Du könntest prüfen, ob ein '-' vorhanden ist. Wenn nicht, machst du ein 
'+' davor.

von (prx) A. K. (prx)


Lesenswert?

sprintf kann das von Haus aus ("+" Option).

von Christian W. (orikson)


Lesenswert?

A. K. schrieb:
> sprintf kann das von Haus aus ("+" Option).

Die Funktion sieht ja gut aus, aber wie bekomme ich der folgendes 
gesagt:

1) Vorzeichen immer anzeigen (+)
2) Insgesamt 6 Stellen : +00.00 (6)
3) 2 Stellen vor dem Komma (automatisch?)
4) 1 Stelle nach dem Komma (1)
5) Vorne evtl. mit einer Leerstelle auffüllen (blank?)
6) Es is ne Float-Zahl (f)

So geht's jedenfalls nicht, ich bekomme nichts angezeigt?!
1
char buffer[10];
2
float n_volt = 55.1;
3
sprintf(buffer, "%+61blankf", n_volt);

von Karl H. (kbuchegg)


Lesenswert?


von (prx) A. K. (prx)


Lesenswert?

Christian Wächter schrieb:

> 1) Vorzeichen immer anzeigen (+)
> 2) Insgesamt 6 Stellen : +00.00 (6)
> 3) 2 Stellen vor dem Komma (automatisch?)
> 4) 1 Stelle nach dem Komma (1)
> 5) Vorne evtl. mit einer Leerstelle auffüllen (blank?)
> 6) Es is ne Float-Zahl (f)

Also was jetzt? +00.00 sind 2 Stellen nach dem "Komma". +00.0 sind 
insgesamt 5 Zeichen, nicht 6.

"%+5.1f" = immer Vorzeichen, Feldgrösse 5, eine Nachkommastelle, 
Festpunktformat.

von Nils S. (kruemeltee) Benutzerseite


Lesenswert?

1
sprintf(buffer, "%+61blankf", n_volt);
Du braucht ein Komma dazwischen, so wärens 61 Stellen vom Datentyp 
blankf :P

"%+02.04f" gibt dir immer im selben Format, selbe länge aus:
+03.0100V
+03.0000V
-03.1234V
+00.0000V
...

von Christian W. (orikson)


Lesenswert?

A. K. schrieb:
> Also was jetzt? +00.00 sind 2 Stellen nach dem "Komma". +00.0 sind
> insgesamt 5 Zeichen, nicht 6.
Sorry, meinte schon 5 Zeichen

> "%+5.1f" = immer Vorzeichen, Feldgrösse 5, eine Nachkommastelle,
> Festpunktformat.
Perfekt! 1000-Dank, genau so wollte ich es haben. Hätte ich mal lieber 
hier gesucht, Google hat nur Müll gefunden, daher auch der Blödsinn mit 
dem blankf...

von Karl H. (kbuchegg)


Lesenswert?

Nils S. schrieb:
>
1
sprintf(buffer, "%+61blankf", n_volt);
> Du braucht ein Komma dazwischen, so wärens 61 Stellen vom Datentyp
> blankf :P
>
> "%+02.04f" gibt dir immer im selben Format

Fast.
Die Zahl vor dem . ist die Gesamtbreite der Ausgabe. Es ist nicht 
sinnvoll, wenn diese Zahl kleiner als die Anzahl der Nachkommastellen 
ist. In diesem Sinne wäre deine Ausgabe

+3.0100

und nicht

+03.0100

wenn du vor dem Komma 2 Stellen haben willst, dann musst du eine 
Feldbreite von 8 anfordern.

"%+08.04f"

von Karl H. (kbuchegg)


Lesenswert?

Christian Wächter schrieb:
> A. K. schrieb:
>> Also was jetzt? +00.00 sind 2 Stellen nach dem "Komma". +00.0 sind
>> insgesamt 5 Zeichen, nicht 6.
> Sorry, meinte schon 5 Zeichen
>
>> "%+5.1f" = immer Vorzeichen, Feldgrösse 5, eine Nachkommastelle,
>> Festpunktformat.
> Perfekt! 1000-Dank, genau so wollte ich es haben. Hätte ich mal lieber
> hier gesucht, Google hat nur Müll gefunden, daher auch der Blödsinn mit
> dem blankf...

Jedes C-Buch hätte dir das sauber erklären können. Ziemlich am Anfang, 
wenn es darum geht, den Leser mit printf bekannt zu machen.

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.