Thermoelementauswertung mit integriertem Differenzverstärker

Kürzlich brauchte ich einen Temperaturschalter, der 400 °C aushalten musste. Ein Thermoelement schafft das leicht, erzeugt aber leider nur unschön kleine Spannungen. Es zeigt sich allerdings, dass der integrierte Differenzverstärker eines ATTiny216A für eine grobe Messung absolut ausreicht, und damit auf alle Fälle für eine Entscheidung, ob „heiß oder nicht“.

Im Schaltbild sind zwei Thermoelemente antiseriell mit ADC0 und ADC2 des Mikrocontrollers verbunden. Der gemeinsame Knoten ist mit ADC1 verbunden und wird mit einem von der Versorgungsspannung gespeisten 10 kΩ/1 kΩ-Spannungsteiler mit einem 100 nF-Abblockkondensator auf einer kleinen Spannung gehalten. Im Schaltbild sind zwei Thermoelemente antiseriell mit ADC0 und ADC2 des Mikrocontrollers verbunden. Der gemeinsame Knoten ist mit ADC1 verbunden und wird mit einem von der Versorgungsspannung gespeisten 10 kΩ/1 kΩ-Spannungsteiler mit einem 100 nF-Abblockkondensator auf einer kleinen Spannung gehalten.
Schaltplanskizze

Hier sind zwei Thermoelemente mit 3 ADC-Eingängen verbunden, wobei ADC1 als Referenzpunkt für die differenziellen Messungen dient. Die beiden Widerstände sorgen für eine Vorspannung, damit der Differenzverstärker nicht um 0 V herum arbeiten muss. Da sich die interne Referenzspannung nicht nach außen führen lässt, ist dieser Spannungsteiler nötig.

Der Offset kann kompensiert werden, indem zuerst eine Wandlung durchgeführt wird, bei der beide Differenzverstärkereingänge mit demselben Pin verbunden sind, deren Ergebnis später abgezogen wird. Mit einer Verstärkung von 32 und etwas Überabtastung ergibt sich eine brauchbare Messung. Noch einmal, besonders viel Genauigkeit kann man nicht erwarten (man bemerke zudem die Fehlende Kaltstellenkompensation).

int16_t readadc10s(void)
{
    int16_t buf = 0;
    int16_t temp = 0;
    for(uint8_t i = 0; i < 16; i++)
    {
        ADCSRA |= (1 << ADSC);
        while(ADCSRA & (1 << ADSC));
        temp = ADCL;
        temp |= ADCH << 8;
        if(temp & 0x0200)
        {
            temp |= 0xfc00;
        }
        buf += temp;
    }
    return buf / 4;
}

void dummyconversion(uint8_t ct)
{
    while(ct > 0)
    {
        ADCSRA |= (1 << ADSC);
        while(ADCSRA & (1 << ADSC));
        ct--;
    }
}

int16_t readtemp(uint8_t channel)
{
    int16_t temp = 0;

    ADCSRA &= ~(1 << ADEN);

    ADCSRB = (1 << BIN) | (1 << GSEL) | (1 << MUX5);
    ADMUX = (1 << REFS1) | (1 << MUX4) | (1 << MUX3) | (1 << MUX1); // measure offset
    ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1);

    dummyconversion(2);

    temp = -readadc10s();

    if(channel == 0)
    {
        ADMUX = (1 << REFS1);
    }
    else
    {
        ADMUX = (1 << REFS1) | (1 << MUX2) | (1 << MUX1);
    }

    dummyconversion(2);

    temp += readadc10s();

    return temp;
}