Forum: Mikrocontroller und Digitale Elektronik Problem bei negativen Temperaturen mit Temperatursensor DS18S20


von Jens (Gast)


Lesenswert?

Wer kann mir mal bitte weiterhelfen?

Habe ein kleines Thermometer mit Attiny 2312 und einem DS18S20 
Temperatursensor aufgebaut.

Den C-Code zum auslesen der Sensorwerte habe ich allerdings nicht selbst 
geschrieben, sondern aus dem Netz.
Dieser funktioniert zumindest für positive Temperaturen auch wie 
gewünscht, jedoch zeigt das Thermometer bei Temoeraturen statt -1°C 
-28°C an. Für -2°C -27°C usw.

An meinem eigenen Code zum Ansteuern der Anzeige liegt es wohl nicht, da 
diese stimmt, sofern ich die Termperaturvariable fest auf bsw. -3°C 
setze.

Der Fehler muss in folgender Umrechnung (ab "else") stecken, ich erkenne 
ihn aber nicht.

        // Temperatur berechnen, als float-Wert

  if (Sensordaten[1] == 0) {
    // Positive Temp.-Werte
    Temperatur = Sensordaten[0]/2.0;
    }

                else {

                // Neg. Temp.-Werte
    Temperatur = (~Sensordaten[0])+1; // 2er-Komplement
    Temperatur = Temperatur/(2.0);
  }
  return Temperatur;

Kann mir jemand auf die Sprünge helfen? Ist die Bildung des 
2er-Komplements nicht richtig gelöst?

Danke Jens

von R. R. (elec-lisper)


Lesenswert?

Welche Datentypen haben denn Temperatur und Sensordaten?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Jens schrieb:
> Dieser funktioniert zumindest für positive Temperaturen auch wie
> gewünscht, jedoch zeigt das Thermometer bei Temoeraturen statt -1°C
> -28°C an. Für -2°C -27°C usw.

Denken wir mal kurz nach:

  Temp    Anzeige
   -1      -28
   -2      -27
   -3      -26
   ...     ...

Also Korrektur:

  if (temp < 0) temp = -29 - temp;

Dann:

  Temp    Anzeige
   -1      -1
   -2      -2
   -3      -3
   ...     ...

War das so schwierig?

von Pete K. (pete77)


Lesenswert?

Jens schrieb:
> // Temperatur berechnen, als float-Wert
>
>   if (Sensordaten[1] == 0) {

Float vergleicht man nicht mit ==

von Jens (Gast)


Lesenswert?

Ich vergleiche ja auch kein float. Sensordaten ist "unsigned char 
Sensordaten[9]".
Die Temperatur letztendlich ist hingenen float.

       // 9 Byte großen Scratch Pad-Inhalt einlesen
  for (i = 0; i < 9; i++) {
    Sensordaten[i] = Byte_Lesen();

  }

  // Temperatur berechnen, als float-Wert

                if (Sensordaten[1] == 0) {
    // Positive Temp.-Werte
    Temperatur = Sensordaten[0]/2.0;
    } else {
    // Neg. Temp.-Werte
    Temperatur = (~Sensordaten[0])+1; // 2er-Komplement
    Temperatur = Temperatur/(2.0);
  }
  return Temperatur;

von Jens (Gast)


Lesenswert?

Auch das von Frank  M. angesprochene Umrechnen klappt nicht. Dann 
bekomme ich plötzlich -55°C statt -1°C. Bei Plustemperaturen klappt 
alles wunderbar, der Fehler muss also irgendwo anders liegen bzw. 
scheint in dem Übergebenen negativen Wert Temperaturwer der Wurm zu 
stecken.

Hier nochmal der ganze Code:

float Temperatur_Messung(void)
{
  unsigned char i;
  float Temperatur;

  // 1.Befehlszyklus: Messung starten
  // Start mit Master-Reset-Impuls u. Abfrage: Slave presence
  Bus_Reset();
  // Skip ROM-Befehl, da nur ein DS1820 angeschlossen ist
  Byte_Schreiben(0xCC);
  Byte_Schreiben(0x44); // Befehl: Start Conversion an DS1820
  _delay_ms(1000);      // >800 ms warten

  // 2.Befehlszyklus: Messwerte (Scratch Pad) auslesen
  // Start mit Master-Reset-Impuls u. Abfrage: Slave presence
  Bus_Reset();
  // Skip ROM-Befehl, da nur ein DS1820 angeschlossen ist
  Byte_Schreiben(0xCC);
  Byte_Schreiben(0xBE);   // Auslesen des Scratch Pads vom DS1820
  // 9 Byte großen Scratch Pad-Inhalt einlesen

  for (i = 0; i < 9; i++) {
    Sensordaten[i] = Byte_Lesen();

  }

    // Temperatur berechnen, als float-Wert
  if (Sensordaten[1] == 0) {
    // Positive Temp.-Werte
    Temperatur = Sensordaten[0]/2;
    } else {
    // Neg. Temp.-Werte
    Temperatur = (~Sensordaten[0])+1; // 2er-Komplement
    Temperatur = Temperatur/2;
  }
  return Temperatur;
}


in der main dann der Aufruf:
.
.
.
        float Temperatur;
  uint16_t intnumber=0;
  uint16_t alttemp=0;
  uint16_t i=0;
  uint8_t nachkomma;
  uint8_t einer;
  uint8_t zehner;
  uint8_t z=0;
while (1) {
    int8_t faktor=1;
    Temperatur = Temperatur_Messung(); //Messung starten

    //prüfen, ob Gradzahl negatives VZ besitzt

    if (Sensordaten[1] != 0)
    {
    faktor = -1;
    }

    //immer positiv machen
    intnumber=Temperatur*10*faktor;


                //Ziffernaufteilung
                nachkomma=intnumber%10;
    einer=(intnumber%100)/10;
    zehner=(intnumber%1000)/100;

von John-eric K. (mockup)


Lesenswert?

Also ich würde nicht float als variable nehmen sondern das so machen.
1
int16_t temperature;
2
// Sensordaten[1] ist MSB
3
// Sensordaten[0] ist LSB
4
temperature = (Sensordaten[1]<<8) | Sensordaten[0];
5
temperature \=2;

Das was der DS18S20 dir ausgibt ist doch schon ein int16, nur in 0,5°C 
schritten. Wenn du das ganze in ein int16 packst und durch 2 teilst 
bekommst du das gleiche und brauchst kein float nehmen.

von eProfi (Gast)


Lesenswert?


von Joachim B. (jar)


Lesenswert?

für dein subzero Problem könnte das helfen
1
/* 
2
   convert raw value from DS18x20 to Celsius
3
   input is: 
4
   - familycode fc (0x10/0x28 see header)
5
   - scratchpad-buffer
6
   output is:
7
   - cel full celsius
8
   - fractions of celsius in millicelsius*(10^-1)/625 (the 4 LS-Bits)
9
   - subzero =0 positiv / 1 negativ
10
   always returns  DS18X20_OK
11
*/
12
uint8_t DS18X20_meas_to_cel( uint8_t fc, uint8_t *sp, 
13
  uint8_t* subzero, uint8_t* cel, uint8_t* cel_frac_bits)
14
{
15
  uint16_t meas;
16
  uint8_t  i;
17
  
18
  meas = sp[0];  // LSB
19
  meas |= ( (uint16_t)sp[1] ) << 8; // MSB
20
  
21
  //  only work on 12bit-base
22
  if( fc == DS18S20_FAMILY_CODE ) { // 9 -> 12 bit if 18S20
23
    /* Extended res. measurements for DS18S20 contributed by Carsten Foss */
24
    meas &= (uint16_t) 0xfffe;    // Discard LSB, needed for later extended precicion calc
25
    meas <<= 3;                   // Convert to 12-bit, now degrees are in 1/16 degrees units
26
    meas += ( 16 - sp[6] ) - 4;   // Add the compensation and remember to subtract 0.25 degree (4/16)
27
  }
28
  
29
  // check for negative 
30
  if ( meas & 0x8000 )  {
31
    *subzero=1;      // mark negative
32
    meas ^= 0xffff;  // convert to positive => (twos complement)++
33
    meas++;
34
  }
35
  else {
36
    *subzero=0;
37
  }
38
  
39
  // clear undefined bits for B != 12bit
40
  if ( fc == DS18B20_FAMILY_CODE || fc == DS1822_FAMILY_CODE ) {
41
    i = sp[DS18B20_CONF_REG];
42
    if ( (i & DS18B20_12_BIT) == DS18B20_12_BIT ) { ; }
43
    else if ( (i & DS18B20_11_BIT) == DS18B20_11_BIT ) {
44
      meas &= ~(DS18B20_11_BIT_UNDF);
45
    } else if ( (i & DS18B20_10_BIT) == DS18B20_10_BIT ) {
46
      meas &= ~(DS18B20_10_BIT_UNDF);
47
    } else { // if ( (i & DS18B20_9_BIT) == DS18B20_9_BIT ) { 
48
      meas &= ~(DS18B20_9_BIT_UNDF);
49
    }
50
  }
51
  
52
  *cel  = (uint8_t)(meas >> 4); 
53
  *cel_frac_bits = (uint8_t)(meas & 0x000F);
54
  
55
  return DS18X20_OK;
56
}

von Wolfgang (Gast)


Lesenswert?

Frank M. schrieb:
> Also Korrektur:
>
>   if (temp < 0) temp = -29 - temp;

Eine solche Lösung offenbart genauso viel Verständniss, wie copy&paste 
eines nicht funktionierenden Codes.

Man kann auch einfach die Anzeige abdecken, dann sieht man den Fehler 
auch nicht ;-)

von ThomasF (Gast)


Lesenswert?

Hallo,

ich würde auch mit Integer rechnen.

Auf die Schnelle könnte man dieses versuchen.

    // Neg. Temp.-Werte
    Temperatur = (int8_t)Sensordaten[0];
    Temperatur = Temperatur/2;

von John-eric K. (mockup)


Lesenswert?

In dem Tread den eProfi verlinkt hat, hat unser guter Karl Heinz 
(kbuchegg) (Moderator) im endeffekt genau das gesagt was ich geschrieben 
habe.

Beitrag "Re: DS18S20 - extended resolution bei Temperaturen um 0°C"

Na mal schauen ob Jens sich noch mal meldet. :-)

: Bearbeitet durch User
von Jens (Gast)


Lesenswert?

Hallo John-eric K. und Joachim B..

Bin soeben dazu gekommen das ganze nochmal zu probieren. Letztendlich 
war deine Lösung John, richtig bzw. die Aussage von Karl-Heinz. Ohne die 
"zusätzliche" Komblementbildung läufts richtig.

Komisch, hatte dieses halt so in mehren Quelltexten gelesen und für 
richtig angenommen.

Vielen Dank jedenfalls euch beiden!

von Karl H. (kbuchegg)


Lesenswert?

Jens schrieb:

> hatte dieses halt so in mehren Quelltexten gelesen und für
> richtig angenommen.

Ja das sieht man oft.
Völlig unnötig. Dallas hat sich alle Mühe gegeben, den Messwert direkt 
als signed 16 Bit int zu liefern. Nur eben in 2 Häppchen.
Nur weil alle Welt das Scratchpad als unsigned char ausliest, denken 
immer alle, das man da einen Mordsaufwand treiben muss. Dabei muss man 
nur erkennen, das man die beiden Bytes einfach nur richtig rum zu einem 
16 Bit signed Wert zusammensetzen braucht, der dann auch gleich so 
richtig im Speicher liegt, wie ihn eine Maschine die 2-er Komplement 
benutzt (also heutzutage praktisch alle) als vorzeichenrichtigen Wert 
sehen will.

: Bearbeitet durch User
von Rene Z. (renezimmermann)


Lesenswert?

Da hatte ich auch schon meine liebe Mühe mit. Raus gekommen ist damals 
das:
1
#include <avr/io.h>
2
#include <avr/pgmspace.h>                  // für PSTR()
3
#include <stdlib.h>
4
5
//#define F_CPU 14745600                   // evtl. bereits via Compilerparameter definiert
6
7
#define BAUD 19200                      // Baudrate
8
#include <util/setbaud.h>                  // festlegen
9
10
void uart_init(void){
11
  UBRR0H = UBRRH_VALUE;                  // Baudrate
12
  UBRR0L = UBRRL_VALUE;                  // einstellen
13
  #if USE_2X                        // U2X-Modus erforderlich
14
    UCSR0A |= (1 << U2X0);
15
  #else                          // U2X-Modus nicht erforderlich
16
    UCSR0A &= ~(1 << U2X0);
17
  #endif
18
  UCSR0B |= (1<<TXEN0);                  // Transmitter ein
19
  UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);             // Frame Format: Asynchron 8N1
20
}
21
22
void uart_putc(char c){
23
    while (!(UCSR0A & (1<<UDRE0))){}            // warten bis Senden moeglich
24
  UDR0 = c;                        // sende Zeichen
25
}
26
 
27
void uart_puts(char *s){                  // puts ist unabhaengig vom Controllertyp
28
    while (*s){                        // so lange *s != String-Endezeichen
29
        uart_putc(*s);                    // Zeichen ausgeben
30
        s++;                        // Stringzeiger erhöhen
31
    }
32
}
33
34
void uart_puts_P(const char *s){              // puts_P ist unabhaengig vom Controllertyp
35
  register char tempchar;                  // Speicher für ein Byte
36
    while((tempchar = pgm_read_byte(s++))){          // Zeichen aus Flash lesen bis != String-Endezeichen
37
        uart_putc(tempchar);                // Zeichen ausgeben
38
    }
39
}
40
41
void ConvTemp2Str(uint8_t msb, uint8_t lsb, char* pointer){  // Scratchpadbytes in Temperatur wandeln
42
  uint8_t komma = lsb & 0x01;                // Nachkommastelle merken
43
  if(msb) lsb = 0xFF - lsb + 1;              // wenn negativ Kehrwert bilden
44
  lsb >>= 1;                        // durch 2 teilen
45
  if(msb){ *pointer++ = '-';}                // wenn negativ dann Vorzeichen in String
46
  //else{ *pointer++ = '+';}                // wenn positives Vorzeichen gewünscht
47
  uint8_t temp = lsb / 10;                // zehner Stelle isolieren
48
  if(temp){ *pointer++ = ('0' + temp);}          // keine führende Null dann Ziffer in String
49
  *pointer++ = ('0' + (lsb % 10));            // einer Stelle isolieren und Ziffer in String
50
  *pointer++ = '.';                    // Dezimalpunkt in String
51
  if(komma) *pointer++ = '5';                // Nachkommastelle auswerten, halbes Grad
52
  else *pointer++ = '0';                  // Nachkommastelle auswerten, volles Grad
53
  *pointer = 0;                      // String abschliessen
54
}
55
56
void PrintTemp2Uart(uint8_t msb, uint8_t lsb){        // Ausgabe der Temperatus über Uart
57
  char Buffer[6];                      // String Buffer
58
  ConvTemp2Str(msb, lsb, Buffer);              // Temperatur in String 
59
  uart_puts(Buffer);                    // String zur Uart
60
  uart_puts_P(PSTR(" Grad Celsius\r\n"));          // Ausgabe der Einheit
61
}
62
63
int main(void){
64
  uart_init();
65
  uart_puts_P(PSTR("\r\nProgrammstart\r\n"));        // Startmeldung
66
  // Testausgabe siehe Datenblatt
67
  PrintTemp2Uart(0x00, 0xAA);                // 85   Grad Celsius
68
  PrintTemp2Uart(0x00, 0x32);                // 25   Grad Celsius
69
  PrintTemp2Uart(0x00, 0x01);                // 0.5  Grad Celsius
70
  PrintTemp2Uart(0x00, 0x00);                // 0    Grad Celsius
71
  PrintTemp2Uart(0xFF, 0xFF);                // -0.5 Grad Celsius
72
  PrintTemp2Uart(0xFF, 0xCE);                // -25  Grad Celsius
73
  PrintTemp2Uart(0xFF, 0x92);                // -55  Grad Celsius
74
  uart_puts_P(PSTR("Fertig\r\n"));            // Endemeldung
75
  for(;;);
76
}
Gruß Rene

: Bearbeitet durch User
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.