ATtiny 3216 Internal Temperature Sensor

I’ve been trying, without success, to use the internal temperature sensor in the ATtiny3216. I can’t for the life of me figure out why I’m getting garbage results. The readings come out WAY too low, and don’t seem to change when I try to heat up the chip. I tried to follow the instruction in the datasheet section detailing the temp sensor (sec. 31.3.2.6). The comment block at the top of the readTemp() function in the below code is cut/pasted directly from the data sheet.

    void setup()
    {
    }

    void loop()
    {
        delay(2000);
        readTemp();
    }

    void readTemp(void)
    {
    /****************************************************
        1. Configure the internal voltage reference to 1.1V by configuring the VREF peripheral.
        2. Select the internal voltage reference by writing the REFSEL bits in ADCn.CTRLC to 0x0.
        3. Select the ADC temperature sensor channel by configuring the MUXPOS (ADCn.MUXPOS) register. This
        enables the temperature sensor.
        4. In ADCn.CTRLD select INITDLY ≥ 32 μs × �LK_ADC.
        5. In ADCn.SAMPCTRL select SAMPLEN ≥ 32 μs × �LK_ADC.
        6. In ADCn.CTRLC select SAMPCAP = 1.
        7. Acquire the temperature sensor output voltage by starting a conversion.
        8. Process the measurement result, as described below.

        The measured voltage has a linear relationship to the temperature. Due to process variations, the temperature
        sensor output voltage varies between individual devices at the same temperature. The individual compensation
        factors are determined during the production test and saved in the Signature Row:
        SIGROW.TEMPSENSE0 is a gain/slope correction
        SIGROW.TEMPSENSE1 is an offset correction

        To achieve accurate results, the result of the temperature sensor measurement must be processed in the application
        software using factory calibration values. The temperature (in Kelvin) is calculated by this rule:
        Temp = (((RESH << 8) | RESL) - TEMPSENSE1) * TEMPSENSE0) >> 8
        RESH and RESL are the high and low bytes of the Result register (ADCn.RES), and TEMPSENSEn are the
        respective values from the Signature row.
        
        It is recommended to follow these steps in user code:
        
        int8_t sigrow_offset = SIGROW.TEMPSENSE1; // Read signed value from signature row
        uint8_t sigrow_gain = SIGROW.TEMPSENSE0; // Read unsigned value from signature row
        uint16_t adc_reading = 0; // ADC conversion result with 1.1 V internal reference
        uint32_t temp = adc_reading - sigrow_offset;
        temp *= sigrow_gain; // Result might overflow 16 bit variable (10bit+8bit)
        temp += 0x80; // Add 1/2 to get correct rounding on division below
        temp >>= 8; // Divide result to get Kelvin
        uint16_t temperature_in_K = temp;
        
    */
        /* Define and configure relevant settings */
 
       Serial.print("Setting up ADC\n");
 
        // Set VRef to 1.1V
        #define VREF_ADCREFSEL_gm   0x70    // ADC REFSEL mask in VREF.CTRLA
        #define VREF_ADCREFSEL_gp   4       // ADC REFSEL LSB bit position in VREF_CTRLA
        #define VREF_ADCREFSEL_1_1  0x1     // ADC 1.1V REFSEL select 
        VREF.CTRLA &= ~VREF_ADCREFSEL_gm;
        VREF.CTRLA |= (VREF_ADCREFSEL_1_1 << VREF_ADCREFSEL_gp);

        // Set VREF as ADC0 reference
        #define ADC0_REFSEL_gm      0x60    // ADC REFSEL mask in ADC0.CTRLC
        #define ADC0_REFSEL_gp      4       // ADC REFSEL LSB bit position in VREF_CTRLA
        #define ADC0_REFSEL_INT     0x0     // ADC INTERNAL REFSEL select 
        ADC0.CTRLC &= ~(ADC0_REFSEL_gm);

        // MUX Internal Reference to ADC input
        #define ADC0_MUXPOS_gm      0x1f    // ADC MUXPOS mask in ADC0.MUXPOS
        #define ADC0_MUXPOS_gp      0       // ADC MUXPOS LSB bit position
        #define ADC0_MUXPOS_TEMP    0x1E    // ADC Interal Temp sensor select
        ADC0.MUXPOS &= ~ADC0_MUXPOS_gm;
        ADC0.MUXPOS |= (ADC0_MUXPOS_TEMP << ADC0_MUXPOS_gp);

        // Set ADC0 Init Delay to 64 clocks
        #define ADC0_INITDLY_gm     0xE0 
        #define ADC0_INITDLY_gp     5
        #define ADC0_INITDLY_64     3
        ADC0.CTRLD &= ~ADC0_INITDLY_gm;
        ADC0.CTRLD |= (ADC0_INITDLY_64 << ADC0_INITDLY_gp);

        // Set ADC0 Init Delay to 64 clocks
        #define ADC0_SAMPCTRL_gm     0x1F
        #define ADC0_SAMPCTRL_gp     5
        #define ADC0_SAMPCTRL_64     64
        ADC0.SAMPCTRL &= ~ADC0_SAMPCTRL_gm;
        ADC0.SAMPCTRL |= (ADC0_SAMPCTRL_64 << ADC0_SAMPCTRL_gp);

        // Set ADC0 SAMPCAP to 1
        #define ADC0_SAMPCAP_gm     0x40
        #define ADC0_SAMPCAP_gp     6
        #define ADC0_SAMPCAP_1      1
        ADC0.CTRLC &= ~ADC0_SAMPCAP_gm;
        ADC0.CTRLC |= (ADC0_SAMPCAP_1 << ADC0_SAMPCAP_gp);

        // Start a conversion
        Serial.print("Starting conversion\n");
    	ADC0.COMMAND = ADC_STCONV_bm;
    	while(!(ADC0.INTFLAGS & ADC_RESRDY_bm))
            ;
        uint32_t adc_reading = (ADC0.RESH << 8) | ADC0.RESL;
        Serial.print("result=");
        Serial.println(adc_reading);

        // Read calibration values from chip signature
        int8_t sigrow_offset = (int32_t)SIGROW.TEMPSENSE1; 
        uint8_t sigrow_gain = (uint32_t)SIGROW.TEMPSENSE0;
        Serial.print("offset=");
        Serial.print(sigrow_offset);
        Serial.print(" gain=");
        Serial.println(sigrow_gain);

        uint32_t temp = adc_reading - sigrow_offset;
        temp *= sigrow_gain;
        temp += 0x80;   // half-bit round

        uint16_t tempK = temp >> 8;     // Shift result to get degrees Kelvin
        Serial.printf("TempK=");
        Serial.println(tempK);

        uint16_t tempC = tempK - 273;    // Convert to degrees C
        Serial.printf("TempC=");
        Serial.println(tempC);
    }

Here is typical output:

Setting up ADC
Starting conversion
Result=119
offset=-4 gain=147
temp=123
temp=18081
temp=18209
TempK=71
TempC=-202

Am I doing something stupid that I’m not seeing?

This seems wrong

  // Set ADC0 Init Delay to 64 clocks
#define ADC0_SAMPCTRL_gm     0x1F
#define ADC0_SAMPCTRL_gp     5
#define ADC0_SAMPCTRL_64     64
  ADC0.SAMPCTRL &= ~ADC0_SAMPCTRL_gm;
  ADC0.SAMPCTRL |= (ADC0_SAMPCTRL_64 << ADC0_SAMPCTRL_gp);

Since ADC0_SAMPCTRL_64 is 64 == 0x40 ==b0100 0000 and then you are pushing it 5 bits to the left so the result will be 0 if this is an 8 bit register. I didn’t check the datasheet, but most seem to be.

Edit: looking at the datasheet ADC0_SAMPCTRL_gp should be 0 since you aren’t shifting that value at all within the control register

Oops! Good catch! Thanks! ADC0_SAMPCTRL_64 should be 0.

But, output is still wonky. If anything, it’s worse! Pretty sure it’s not at 5K!

Setting up ADC
Starting conversion
Result=5
offset=-4 gain=147
TempK=5
TempC=-268

RayLivingston:
Oops! Good catch! Thanks! ADC0_SAMPCTRL_64 should be 0.

But, output is still wonky. If anything, it's worse! Pretty sure it's not at 5K!

Setting up ADC

Starting conversion
Result=5
offset=-4 gain=147
TempK=5
TempC=-268

Shouldn't ADC0_SAMPCTRL_gp be 0? ADC0_SAMPCTRL_64 should be 64, but you don't want to shift it into the control register (or shift it 0 bits).
I would also wonder about doing all that setup every time through loop(). It seems like most of that should be inside setup() so the circuit is defined and settles down as you just read the ADC inside loop()