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
Welche Datentypen haben denn Temperatur und Sensordaten?
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?
Jens schrieb: > // Temperatur berechnen, als float-Wert > > if (Sensordaten[1] == 0) { Float vergleicht man nicht mit ==
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;
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;
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.
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 | }
|
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 ;-)
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;
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
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!
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.