Thermocouple via Integrated Differential Amplifier

Recently, I needed a simple temperature switch which could withstand 400 °C. A thermocouple can easily manage this but its generated voltage is unpleasantly small. However, it turns out that a ATTiny216A’s integrated differential amplifier is absolutely sufficient for a, albeit not very accurate, measurement. Enough to determine if something is hot or not, at least.

In the schematic, two thermocouples are connected anti-serially between the ADC0 and ADC2 pins of the microcontroller. The common node of the two thermocouples is connected to ADC1 and is, through a 10 kΩ/1 kΩ voltage divider with an 100 nF bypass capacitor, held at a small voltage. In the schematic, two thermocouples are connected anti-serially between the ADC0 and ADC2 pins of the microcontroller. The common node of the two thermocouples is connected to ADC1 and is, through a 10 kΩ/1 kΩ voltage divider with an 100 nF bypass capacitor, held at a small voltage.
Basic schematic

Two thermocouples are connected and ADC1 serves as a reference for ADC0 and ADC2. A voltage divider adds some common mode voltage so the differential amplifier doesn’t have to work around 0 V (since the internal reference cannot be routed to the outside, this is necessary).

The differential amplifier’s offset can be compensated by connecting both of its inputs to the same pin and later subtracting the obtained conversion result. With an amplification factor of 32 and some oversampling this generated a usable indication. Again, don’t expect an accurate measurement (also note the lack of cold junction compensation).

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;
}