Sunday, February 19, 2017

Library for reading internal temperature sensor on AVR microcontrollers

A few AVR microcontrollers have internal on-chip temperature sensors. The sensor is a diode that produces a temperature dependent voltage. This voltage is measured with the ADC. According to Atmel the typical accuracy of temperature measurements over the full temperature range of the AVR is ±10°C but after calibration the accuracy can be ±1°C over 20°C to 55°C using one-point calibration.


Library for reading internal temperature sensor on AVR microcontrollers

The calibration can be done using one-point calibration and compensation or two-point calibration and compensation. One-point calibration is the easiest - subtract the ambient

temperature from the AVR sensor temperature. In the case of two-point calibration you need to have a controlled temperature environment and use a special equation. For more details visit the Atmel's application note AVR122: Calibration of the AVR's Internal Temperature Reference.

When calibrating the temperature, find the offset error using the value when you first start it because after that it will increase (by 2°C in my case). If the CPU clock is more than 1MHz the ADC Noise Reduction Mode should be used for more accurate results.

Contents

 

Library Usage

 

Read temperature

This function makes use of the provided adc library to read the internal on-chip temperature sensor. The ADC needs to be configured before using this function. ADC voltage reference will be set to 1.1V inside the function.

The documentation for the ADC library can be found here: https://www.programming-electronics-diy.xyz/2018/05/analog-to-digital-converter-adc-library.html

float avrOnchipTemp(uint8_t adc_channel, int8_t offset_cal)
 

Parameters

adc_channel

ADC channel for internal temperature sensor. This can be found in the datasheet for your particular microcontroller, in the ADC chapter where the registers are described.  Here are ADC channel numbers for the temperature sensor for some AVR microcontrollers:

  • ATmega328P, ATmega328PB, ATmega88PB, ATmega48PB, ATmega168PB: 8
  • ATtiny25, ATtiny45, ATtiny85: 31 (ADC4) 
  • ATtiny202, ATtiny402: 30 (0x1E in hexadecimal)

offset_cal

This value is expressed in degrees Celsius and it will be added to the measured temperature. If negative it will be subtracted. Use a value of 0 if no offset calibration is needed.

Return: the measured temperature in degrees Celsius.

Code example for non-UPDI devices

// See https://www.programming-electronics-diy.xyz/2024/01/defining-fcpu.html
#ifndef F_CPU
    #warning	"F_CPU not defined. Define it in project properties."
#elif F_CPU != 16000000
    #warning	"Wrong F_CPU frequency!"
#endif

#include "avrTemp.h"
#include "uart.h"

int main(void){
    // Local variables
    float temperatureC = 0.0;
	
    // USART Initialization
    UART_begin(&uart0, 115200, UART_ASYNC, UART_NO_PARITY, UART_8_BIT);
	
    // ADC initialization
    // 16000000 / 128 = 125kHz ADC clock
    adcSetup(ADC_PRESCALER_128);
	
    // 10-bit ADC resolution
    adcResolution(ADC_10bit);
	
    // Enable/Disable ADC Noise Reduction Sleep Mode
    adcUseNoiseReduction(ADC_NOISE_REDUCTION_ENABLED);
	
    while(1){
        /* Read internal temperature sensor every 1s */
        UART_sendString(&uart0, "Temp = ");
		
	// Wait for UART transmission to complete
	// when ADC Sleep Mode is used
	UART_isSending(&uart0, F_CPU, 115200);
		
	// Read ADC channel 8 which is the internal temperature sensor
	temperatureC = avrOnchipTemp(8, -7);
		
	UART_sendFloat(&uart0, temperatureC, 2);
	UART_send(&uart0, '\n');
	_delay_ms(1000);
    }
}

Code example for UPDI devices

// Not necessary in this project but useful.
// See https://www.programming-electronics-diy.xyz/2024/01/defining-fcpu.html
#ifndef F_CPU
    #warning	"F_CPU not defined. Define it in project properties."
#elif F_CPU != 3333333
    #warning	"Wrong F_CPU frequency!"
#endif

#include "avrTemp.h"
#include "uart.h"

int main(void){
    // Local variables
    float temperatureC = 0.0;
	
    // Set UART TX pin as output
    PORTA.DIR |= PIN6_bm;
	
    // USART Initialization
    UART_begin(&uart0, 115200, UART_ASYNC, UART_NO_PARITY, UART_8_BIT);
	
    // ADC initialization
    adcSetup(ADC_PRESCALER_32);
	
    // 10-bit ADC resolution
    adcResolution(ADC_10bit);
	
    // Initialization delay
    adcSetInitDelay(ADC_DLY32);
	
    // Sampling length
    adcSetSampleLength(4);
	
    // Reduced size of sampling capacitance. Recommended for reference voltages higher than 1V.
    adcSelectSampleCapacitance(ADC_SAMP_CAP_5pf);
	
    // Number of samples in a burst that will be averaged (optional).
    // Useful for noise filtering. All done in hardware.
    //adcSetSampleAccumulation(ADC_SAMP_ACC_8, ADC_SAMP_DLY_0, ADC_SAMP_DLY_AUTO_DISABLED);
	
    while(1){
        /* Read internal temperature sensor every 1s */
	UART_sendString(&uart0, "Temp: ");
		
	// Read ADC channel 0x1E which is the internal temperature sensor
	temperatureC = avrOnchipTemp(0x1E, 0);
		
	UART_sendFloat(&uart0, temperatureC, 2);
	UART_send(&uart0, '\n');
	_delay_ms(1000);
    }
}

How to measure internal AVR temperature sensor

The library implements two ways of measuring and calculating the temperature depending on whether the microcontroller is UPDI type or not. In both cases, the voltage reference is configured for 1.1V internal reference as per datasheet recommendation and a 1ms delay is added for voltage stabilization in case another reference voltage was used before.

non-UPDI

The following code is used by the library:

uint16_t adc_val = 0;
adcStartConversion(adc_channel);	// Start a new conversion
while(!adcIsReady());			// Wait for conversion to complete
adc_val = adcReadValue();		// Get ADC value
		
// Result is in Kelvin - subtract 273 to convert to Celsius
return (adc_val - 273) + offset_cal;

The ADC value, or in other words, the measured voltage, represents the temperature in Kelvin. One Kelvin equals one degree Celsius. The major difference is that when water freezes, a Celsius thermometer will read 0°C, and a Kelvin thermometer will read 273.15 K. The scales differ by 273.15. For this reason 273 is subtracted from measured value to convert it to Celsius.

UPDI devices

The code for UPDI devices such as ATtiny402 is given in the datasheet except the part in the return line:

uint16_t accum_samples = adcAccSize();

adcStartConversion(adc_channel);	// Start a new conversion
while(!adcIsReady());			// Wait for conversion to complete
adc_val = adcReadValue();		// Get ADC value
	
int8_t sigrow_offset = SIGROW.TEMPSENSE1;    // Read signed value from signature row
uint8_t sigrow_gain = SIGROW.TEMPSENSE0;    // Read unsigned value from signature row
int32_t temp = (adc_val / accum_samples) - sigrow_offset; // Divide ADC result by number of accumulated samples
		
temp *= sigrow_gain;	// Result might overflow 16 bit variable (10bit+8bit)
temp += 0x80;		// Add 1/2 to get correct rounding on division below
temp >>= ADC_ANALOG_RESOLUTION;	// Divide result to get Kelvin
		
return ((temp - 32) * (5.0 / 9.0)) + offset_cal; // Convert Fahrenheit to Celsius

Variable 'accum_samples' holds the number of accumulated samples in a burst in case the ADC accumulator is used. This can be configured using the adcSetSampleAccumulation() to filter out potential noise in the system.

What i like about this newer AVR devices, is that they have this two calibration registers for offset and gain with compensation values written in the factory.

The last line is just the formula for converting Fahrenheit to Celsius.

The datasheet recommends the followings:

4. In ADCn.CTRLD Select INITDLY ≥ 32 µs × fCLK_ADC 

5. In ADCn.SAMPCTRL Select SAMPLEN ≥ 32 µs × fCLK_ADC 

6. In ADCn.CTRLC Select SAMPCAP = 5 pF

The values for Initialization Delay and Sample Length depends on the ADC clock. The formula for calculating them can be found at https://www.programming-electronics-diy.xyz/2018/05/analog-to-digital-converter-adc-library.html#Calculating_INITDLY_and_SAMPLEN.

The sampling capacitor can easily be selected as shown in the usage example.

Download

Changelog and license can be found at the beginning of the header files
avrTemp v1.1

avrTemp.h

avrTemp.c
Other Links

ADC library project page Useful serial terminal to use with UART

UART library project page Useful to display temperature on a serial terminal.
Changelog
v1.1 31-01-2024:
- added support for modern AVR (UPDI devices). Tested on ATiny402.

No comments:

Post a Comment