Temperatur Messung mit dem DS18x20

Einleitung

Für die Aktualisierung einer älteren Wetterstation, habe ich diesen Sensor als Ersatz verwendet. Da ich die Software dieser Wetterstation seiner Zeit in C++ geschrieben habe, war es natürlich unumgänglich auch eine Abfrage dieser Sensoren in C++/C zu realisieren. Im folgenden beschreibe ich meine Erfahrungen.

Schnittstelle

1-Wire (Eindraht-Bus) beschreibt einen digitalen, seriellen Bus des Herstellers Dallas Semiconductor Corp. (seit 2001 Maxim), der mit lediglich einer Daten- und einer Masseleitung auskommt. Die Bezeichnung 1-Wire leitet sich daraus ab und beschreibt damit den Umstand dass zu der ohnehin vorhandenen Masseleitung lediglich eine weitere Leitung zur Versorgung und zur Buskommunikation benötigt wird.

Der Sensor kann die benötigte Energieversorgung direkt von der Datenleitung ableiten ("parasite power"), wodurch die Notwendigkeit einer externen Stromversorgung entfällt. Er kann aber auch herkömmlich über eine weitere Leitung versorgt werden.

Ursprünglich wurde die Technik, ähnlich wie auch der I2C-Bus, für die Kommunikation innerhalb eines Gerätes entwickelt. Sie eignet sich besonders gut für den Einsatz in der Sensorik z.B. Temperaturmessung, Akkuüberwachung, Echtzeituhr usw..

Kenndaten des Bus

DS18x20

Bei den Temperatursensoren der DS18x20 Reihe handelt es sich um bereits geeichte Temperatursensor mit einem Messbereich von -55°C bis +125°C, der DS1820 (Nachfolger ist der DS18S20) bildet hier eine Ausnahme, sein Messbereich reicht lediglich von -10°C bis 85°C. Alle Sensoren, mit Ausnahme des DS1822, besitzen im Bereich von -10 °C bis +85 °C, eine Genauigkeit von ±0,5°C. Der DS1822 besitzt in diesem Temperaturbereich lediglich eine Genauigkeit von ±2.0°C. Die Auflösung der Sensoren beträgt 9- oder 12-Bit.

Anhand des "Family-Code" können die Sensoren eindeutig einer Gruppe "Family" zugeordnet werden die Folgende Tabelle verdeutlicht dies noch einmal.

Sensor 1-Wire-Family-Codes
DS1820, DS1820S, DS18S20 10h
DS18B20 28h
DS1822 22h

Alle Temperatursensoren der DS18xxx Serie besitzen einen 9 Byte großen internen Speicher, das s.g. „SCRATCHPAD“. Der Aufbau dieses Speichers hat, in Abhängigkeit vom Sensor, einen anderen Aufbau. In der folgenden Tabelle ist die Bedeutung der 9 Byte des „SCRATCHPAD“ aufgeführt.

Byte Nr. DS1820, DS18S20, DS1820S DS18B20 Read/Write
0 Temperatur LSB   Read
1 Temperatur MSB (0x00 = positiv; 0xff = negativ)   Read
2 TH Register User Byte 1   Read / (Write) (DS 18B20)
3 TL Register User Byte 2   Read / (Write) (DS 18B20)
4 Reserviert immer FFh = 256   Read / (Write) (DS 18B20)
5 Reserviert immer FFh = 256   Read
6 Count Remain   Read
7 Count Per °C   Read
8 CRC; berechnet über die Byte 0 … 7   Read

Anschluss

Der Anschluß eines DS18x20 kann auf unterschiedliche Weise erfolgen, allen gemeinsam ist die Verwendung eines Pull-Up Widerstands für die Datenleitung (DQ), dieser wird im Datenblatt mit 4,7 Kiloohm angegeben.


Anschlussarten des DS18x20

Die wohl am häufigsten verwendete, Anschlussart dürfte die Variante 1 sein. Sie ist für kurze Leitungen gedacht. Für längere Leitungen ist die Variante 2 besser geeignet.

Allerdings muss darauf geachtet werden, dass am GPIO-Pin des Raspberry Pi niemals eine Spannung angelegt wird die höher als 3v3 ist, dies würde ihn unweigerlich zerstören.

Weiterhin gibt es noch eine dritte Variante, bei dieser werden lediglich zwei Leitungen benötigt (GND und DQ). Die Sensorpins GND (1) und VCC (3) werden miteinander verbunden. Die Versorgungsspannung bezieht der Sensor dann über die Datenleitung (DQ), die einen kleinen Kondensator im Sensor auflädt. Auf diese Weise lässt sich also eine Zuleitung einsparen. Die Datenleitung DQ wird genau wie bei den Varianten 1 und 2 mit dem Pull-Up Widerstand auf 3v3 Pegel gelegt.

Die zwei noch benötigten Zuleitungen sollten jedoch verdrillt Leitungen (twisted pair) sein.

Der in der Abbildung gezeigte Pull-Up Widerstand muss am 1-Wire Bus nur einmal vorhanden sein. Er kann sich direkt in der Nähe des Raspberry Pi befinden oder auf auf einer Erweiterungsplatine. Ich habe mir für den 1-Wire Bus einen Adapter auf einem Stück Lochrasterplatine aufgebaut und den Pull-Up Widerstand dort untergebracht.

Software

CRC Berechnung

Viel gibt es dazu eigentlich zu sagen. Der CRC der DS 18x20 Sensoren wird über die Bytes 0 bis 7 des "SCRATCHPAD" berechnet. Der folgende Code zeit eine Funktion zur Berechnung dieses CRC.

// Berechnen des Dallas Semiconductor 8 Bit CRC, der in den ROM- und
// Scratchpad-Registern verwendet wird.
//
// *addr - Puffer (Byte-Array)
// len   - Anzahl der Byte, die in die Berechnung einfließen
uint8_t crc8(const uint8_t *addr, uint8_t len) {
        uint8_t crc = 0;

        while (len--) {
                uint8_t inbyte = *addr++;
                for (uint8_t i = 8; i; i--) {
                        uint8_t mix = (crc ^ inbyte) & 0x01;
                        crc >>= 1;
                        if (mix) crc ^= 0x8C;
                        inbyte >>= 1;
                }
        }
        return crc;
}
 
DS18x20 CRC berechnen

Als nächstes folgt noch ein kurzes Beispiel, das die Anwendung demonstriert.

#include <inttypes.h>
#include <stdio.h>

// Funktion crc8 einfügen

//
// Compilieren mit z.B. cc -o ds18x20_crc_bsp ds18x20_crc_bsp.c

int main() {
        // SCRATCHPAD Daten
        // CRC = Byte Nr. 8 = 0x78
        uint8_t buf[9] = {0x2f, 0x00, 0x4b, 0x46, 0xff, 0xff, 0x08, 0x10, 0x78};
        uint8_t crc;
       
        crc = crc8(buf, 8); // Bytes 0 ... 7 einbeziehen
       
        if (crc == buf[8]) {
                printf("Ok. CRC ist gültig.\n");
                printf("buf[8]    = 0x%02x\n", buf[8]);
                printf("berechnet = 0x%02x\n", crc);
        }
       
        return 0;
}
 
Beispiel DS18x20 CRC berechnen

1-Wire Bus durchsuchen

Wie bereits erwähnt sind die Sensoren mit einem "Family" Code versehen, anhand diesem können alle am 1-Wire Bus angeschlossenen DS18x20 gefunden werden. Die folgende Funktion beschreibt eine Art der Suchfunktion.

#include <dirent.h>


/*
 * Struktur, um die DS18x20 Daten in einer verketteten Liste zu
 * speichern.
 */

struct DS18x20 {
        char deviceID[16];
        struct DS18x20 *next;
};

/* Suche alle, am mit path, angegebenen 1-Wire Bus und
 * erzeugt eine Liste mit den gefunden Geräten.
 *
 * @param path          1-Wire Bus
 * @param devices       Rückgabewert verkette Liste
 * @return                      Anzahl der gefunden Geräte
 *
 */

int8_t findDevices(const char *path, struct DS18x20 *devices) {
        DIR *dir;
       
        struct dirent *dirent;  
        struct DS18x20 *newDev;
       
        uint8_t famcode = 0;    // Temp Family Code
        uint8_t devsFound = 0;  // Anzahl gefundener Sensoren
        dir = opendir(path);
       
        if (dir != NULL) {
                while ((dirent = readdir(dir))) {
                        // DS18x20 Family Code ermitteln, 10, 22 oder 28
                        (void) sscanf(dirent->d_name, "%2x-", &famcode);
                       
                        // Die Geräte Namen in /sys/bus/w1/devices sind Links
                        // also werden auch nur diese verarbeitet
                        if (dirent->d_type == DT_LNK) {
                                // Alle DS18x20 zählen
                                switch (famcode) {
                                        case 0x10: // DS1820, DS1820S, DS18S20
                                        case 0x22: // DS1822
                                        case 0x28: // DS18B20
                                                devsFound++;
                                                // Speicher reservieren für neuen Listeneintrag
                                                newDev = malloc( sizeof(struct DS18x20) );
                                                //
                                                strcpy(newDev->deviceID, dirent->d_name);

                                                newDev->next = 0;
                                                devices->next = newDev;
                                                devices = devices->next;
                                                break;
                                }
                        }
                }
                (void) closedir(dir);
        } else {
                devsFound = -1;
        }
        return devsFound;
}
 
Funktion durchsuchen des 1-Wire Bus

Temperatur lesen

#define DEVICE_ERROR -126

// Auslesen des DS1820 Temperaturwertes  
//
float readTemperatur() {
    FILE *devFile = fopen(path, "r");
    if (devFile == NULL) {
        printf("Count not open %s\n", path);
        perror("\n");
    }
    float temp = -1;

    if (devFile != NULL) {
        if (!ferror(devFile)) {
            unsigned int tempInt;
            int buf[10];

            // Skip 1. Line, 39 = length of first line
            fseek(devFile, 39, SEEK_SET);
            // Read 2. Line, and get data
            (void) fscanf(devFile, "%x %x %x %x %x %x %x %x %x t=%5d",
                    &buf[0], &buf[1], &buf[2], &buf[3], &buf[4],
                    &buf[5], &buf[6], &buf[7], &buf[8], &tempInt);

            if (crc8(buf, 8) == buf[8]) {
                    temp = (float) tempInt / 1000.0;
            } else {
                    // Fehler
                    printf("CRC %x invalid.\n", buf[9]);
                    temp = DEVICE_ERROR;
            }
        }
        fclose(devFile);
    }
    return temp;
} // readTemperatur
 
Funktion zum lesen der DS18x20 Temperatur

Alle zuvor gezeigten Funktionen habe ich in einer Klasse zusammen gefasst die hier geladen werden kann.

 

Da die dargestellten Schaltungen und Programme nur dem Grundverständnis dienen sollen, kann ich für die Funktion keine Gewähr übernehmen.
Wie üblich kann ich für Schäden die durch die Verwendung der hier veröffentlichten Schaltungen und Programme entstehen keine Haftung übernehmen.

Alle genannten und durch Dritte geschützten Marken- und Warenzeichen unterliegen uneingeschränkt den Bestimmungen des jeweils gültigen Kennzeichenrechts und den Besitzrechten der jeweiligen eingetragenen Eigentümer. Allein aufgrund der bloßen Nennung ist nicht der Schluss zu ziehen, dass Markenzeichen nicht durch Rechte Dritter geschützt sind!