Teil 7 des Rasberry Pi I2C Projektes

ADS1115 – 4 Channel AD/ Converter with PGA

Im Teil 7 geht es um den ADS1115, einen 16-BIT 4 Kanal AD-Wandler mit Gain Controller. Ziel ist es das Software Interface zu schreiben um die Daten der 4 Kanäle auszulesen und dann weiter zu bearbeiten. Auf dem Extension Board wird der Wandler u.a. dazu verwendet die Daten vom ACS 712 (Strommesser) aufzunehmen. Der ADS1115 lässt sich am besten als fertiges Modul kaufen. Das bestücken auf der Platine ist nicht wirklich spannend und schon gar nicht auf den ersten Versuch erfolgreich. Die Kosten des gesamten Moduls sind übrigens unwesentlich höher als die für den Chip selbst.

ADS1115 Modul bestückt auf einer Platine

Der ADS1115 hat einiges an Zeit gekostet um das Software Modul zu erstellen. Es gibt zwar viele Beispiele im Internet aber die Beschreibung ist dann meist nicht so verständlich wie ich es hätte erwartet. Also musste ich mich da etwas selber durchbeissen. Das Ergebnis war, mal ging es, mal wieder nicht. Verstanden hatte ich es erst nicht. Dabei ist das wenn man es verstanden hat doch so einfach. Letztendlich war das eigentliche Problem eine Fehlinterpretation des Datenblattes. Da die BCM2835 Routinen zum schreiben und lesen auf dem I2C Bus nunmehr gut laufen und der Logic Analyzer fest verkabelt ist, geht das Debuggen auch recht gut von remote und ich muss nicht immer an die Werkbank laufen.

Der ADS1115 hat folgende Kenndaten:

  • Bis zu 16 Bit Auflösung
  • Programmierbaren Verstärker
  • 4 Kanäle mit einstellbarem Multiplexer
  • 2 Modi für die Eingänge zur Differentialmessung oder Messung gegen Masse
  • Interne Referenzspannung
  • 8 bis 860 Samples per Second

Bei der Auflösung ist es wichtig das Datenblatt zu lesen. Der AD Wandler hat die 16 Bit Auflösung nicht über die gesamten Messbereiche. Das ist entscheident wenn das Ergebnis berechnet wird. Bei einem Betrieb an 3.3V Betriebsspannung kann im Messbereich 0 und 1 (6.144Volt und 4.096Volt) nur bis max. 3.3. Volt gemessen werden. Auch ist es sinnvoll im „single Shot“ Modus mehr als eine Messung zu machen. Insbesondere bei umschalten des Multiplexers zwischen den Messungen läuft man Gefahr die Werte von der letzten Messung zu bekommen. Das passiert wenn der Wandler die gemessenen Werte noch nicht in das Ergebnisregister geschrieben hat und der I2C Bus bereits abgefragt wurde. Die Mehrfache Messung hat zu stabilen Ergebnissen geführt. Wie viele Messungen gemacht wreden bis das Ergebnis zurückgeliefert wird ist in der Variablen Messungen in der Routine definiert.

Viele Beispiele im Internet basieren auf Hochsprachen wie Python. Diese sind um ein vielfaches langsamer als in C geschriebener Code. Mein Vermutung ist , bei Hochsprachen passieren aufgrund von timings diese Probleme nicht. Durch Abfragen des Bit 15 kann man herausbekommen ob die Wandlung und der Transfer des Ergbenisses in das Register abgeschlossen ist. Das ist ist Im Code implementiert.

Auflösung je Messbereich lt. Datenblatt

Die Auflösung ergibt sich durch FSR/LSB Size und das sind 15 Bit (32768 Schritte). Die Angabe +-6.144V ergibt sich durch den Betriebsmodus. Im Singel Ended Mode Ain gegen GND sind es 6.144V im Differenzmodus sind es Minus 6.144V bis Plus 6.144V.

Beachte die Reihenfolge: Das höherwertige Byte befindet sich vor dem niederwertigeren Byte

Das CONFIG Register und auch das Ergebnisregister wird in der Reihenfolge MSB nach LSB geschrieben, das beudetet das CONFIG Byte 1 enthällt die Daten Bit 15 – Bit 8 und Config Byte 2 enthält die Bits 7 – Bit 0. Ich hatte damit gerechnet (oder es so verstanden) erst wird Config Byte 1 und dann Config Byte 2 abgelegt, die Reihenfolge ist genau anders herum!

Testroutine und Anzeige der Werte

Folgendes Codeschnipsel ist das Software Modul zur abfrage des ADS1115.

int16_t ADS1115(char port, char Messbereich, char AD_Address) {

    int         I2C_Frequenz = 100000;
    char        AD_readBuf[2];
    char        AD_writeBuf[3];
    int16_t     AD_result = 0;
    char        Kanal;
    char        Bereich;
    int         a, counter;
    int         messungen = 2;                      //Anzahl Messungen

    //
    // Init I2C Bus
    //
    if (!bcm2835_init()) return -1;                 // I2C INIT
    bcm2835_i2c_begin();                            // Start I2C operations.
    bcm2835_i2c_setSlaveAddress(AD_Address);        // I2C address
    bcm2835_i2c_set_baudrate(I2C_Frequenz);         // 100kHz baudrate

    // Eigentlich sollte es mit #define funktionieren, geht aber irgendwie nicht
    // aus dem Grund ist es mit const char gelöst
    // config Bit 7-0 Optionen
    #define    AD_config_DataRate_8      (0)
    #define    AD_config_DataRate_16     (16)
    #define    AD_config_DataRate_32     (32)
    #define    AD_config_DataRate_64     (96)
    #define    AD_config_DataRate_128    (128)
    #define    AD_config_DataRate_250    (160)
    #define    AD_config_DataRate_475    (192)
    #define    AD_config_DataRate_860    (224)

    #define    AD_config_COMPMODE_tra    (0)
    #define    AD_config_COMPMODE_win    (16)

    #define    AD_config_COM_POL_low     (0)
    #define    AD_config_COM_POL_high    (8)

    #define    AD_config_COM_LAT_non     (0)
    #define    AD_config_COM_LAT_lat     (4)

    #define    AD_config_COM_QUE_1       (0)
    #define    AD_config_COM_QUE_2       (1)
    #define    AD_config_COM_QUE_3       (2)
    #define    AD_config_COM_QUE_dis     (3)

    // config Bit 15-8 Optionen
    #define    AD_config_OS_noeffect     (0)
    #define    AD_config_OS_single       (128)

    #define    AD_config_MUX_A0_GND      (64)
    #define    AD_config_MUX_A1_GND      (80)
    #define    AD_config_MUX_A2_GND      (96)
    #define    AD_config_MUX_A3_GND      (112)

    #define    AD_config_PGA_2048        (4)
    #define    AD_config_PGA_4096        (2)
    #define    AD_config_PGA_6144        (0)

    #define    AD_config_MODE_cont       (0)
    #define    AD_config_MODE_single     (1)


    // ********

    #define    config_register           (1)
    #define    conversion_register       (0)

    Messbereich = Messbereich << 1;

    if (port == 0) {Kanal = AD_config_MUX_A0_GND;}
    if (port == 1) {Kanal = AD_config_MUX_A1_GND;}
    if (port == 2) {Kanal = AD_config_MUX_A2_GND;}
    if (port == 3) {Kanal = AD_config_MUX_A3_GND;}

    for (counter = 0; counter <= messungen; counter++) {
        for (a=0;a<4;a++) {
            AD_writeBuf[a]=0;
        }

        // Achtung: CONFIG Byte 2 wird zuerst geschrieben! Siehe Datenblatt Figure 36
        //
        AD_writeBuf[0]  = config_register;             //set pointer to config register
        AD_writeBuf[1]  = Kanal + AD_config_MODE_single + AD_config_OS_single + Messbereich;
        AD_writeBuf[2]  = AD_config_DataRate_128 + AD_config_COM_QUE_dis;

        // Schreibe die CONFIG Register in das DEVICE
        //
        bcm2835_i2c_write(AD_writeBuf,3);

        // Init the Buffers
        //
        AD_readBuf[0] = 0;
        AD_readBuf[1] = 0;

        // start Conversion and wait to be finished, result is stored in register on device
        //
        while ((AD_readBuf[0] && 0x80) == 0) {
            bcm2835_i2c_read(AD_readBuf, 2);
        }

        AD_writeBuf[0] = conversion_register;       //set pointer to conversion register;
        bcm2835_i2c_write(AD_writeBuf, 1);
        bcm2835_i2c_read(AD_readBuf, 2);
        // Verschiebe MSB um 8 stellen nach links und dann addiere LSB
        AD_result = (AD_readBuf[0] * 256) + AD_readBuf[1];
    }
    // end and close I2C communication
    //
    bcm2835_i2c_end();
    bcm2835_close();

    // Bei Werten um 0V kann der Wandler auch negative Werte ausgeben. Das wird hier kompensiert
    //
    if (AD_result<0) AD_result = 0;
    return AD_result;
}