Pages: [1] 2   Go Down
Author Topic: Internal Temperature Measurement ATMega328  (Read 8732 times)
0 Members and 1 Guest are viewing this topic.
Adelaide, South Australia
Offline Offline
Newbie
*
Karma: 0
Posts: 4
If Arduino is the answer, what is the question?
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I noticed there is a new section 21.8 Temperature Measurement in the Analog-to-Digital Converter section of the ATMega48 - 328 full specification (the 8025.pdf ATMEL datasheet).  This section explains how selecting ADC8 provides an internal voltage measurement which is linearly proportional to chip temperature ~1mv/Degree C), with 242mV ~-45C and 380mV ~85C.  The claimed accuracy is very poor, (+/-10C), but calibration can of course overcome that.  Possibly more importantly, in designs that need to work at temperature extremes or where the ATMega is driving some heavy loads, this feature could be used to check and save the ATMega chip temperature at start-up and regularly compare the current temperature during operation, enabling the designer to take the appropriate action when needed.

I checked whether an Arduino (0015) would correctly report this internal temperature measurement and found that line 45 of
\\hardware\cores\arduino\wiring_analog.c needs to be changed from:

      ADMUX = (analog_reference << 6) | (pin & 0x07);

to

      ADMUX = (analog_reference << 6) | (pin & 0x0f);

otherwise ADC8 gets ANDed to ADC0 and you read the external ADC0 channel.

With two 328 chips at 23C I got digital readings of 361 and 391 (328's were date coded 0850, i.e. made mid Dec last year).  Both chips showed a rise of 6 counts with a warm finger on the DIP IC test, which slowly dropped back to the original reading when the finger heat source was removed, so this definitely works with 328 chips.  I got an open circuit reading with an ATMega168 date code 0811, so I'm not sure if this feature is present in newer production lots of the older AT models.  The internal temperature measurement feature was added to the datasheet in 33.8 Rev. 2545B-01/08.
Logged

Connecticut, US
Offline Offline
Edison Member
*
Karma: 2
Posts: 1036
Whatduino
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Very cool!  Now what does it read if you chill your board, and where is the thermal failure-point for the chip?  I'm imagining oil-submersion cooling baths and overclockers...
Logged

Left Coast, CA (USA)
Online Online
Brattain Member
*****
Karma: 331
Posts: 16565
Measurement changes behavior
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

That is an interesting find. Now we need to think of a useful application for using it in a sketch.

If the Arduino was using the internal R/C oscillator it might have been useful for a calibration trimming value adjustment but with an external crystal, not sure if it would be a useful value to use. I would think that internal core temperature would be effected mostly by how much current was being sunk/sourced by the I/O pins at any given time?

Lefty
Logged

Adelaide, South Australia
Offline Offline
Newbie
*
Karma: 0
Posts: 4
If Arduino is the answer, what is the question?
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

In response to Halley, section 30.4 of the afore mentioned datasheet gives the operational temperature range as Industrial (-40C to +85C), which surprise, surprise is the same as the extreme points given in the section on temperature measurement section.  From my design and QA experience, it will be the interconnects that will be the failure points in any design operating over this extreme temperature range.   Generally failure is caused by mismatched temperature coefficients in joining materials.  In your case, if you plan on immersing your module in oil, you just need to ensure the oil remains clean and non-conductive (i.e. remove any water moisture that collects due to condensation from the atmosphere) and avoid sudden temperature transitions.  I'd recommend calibrating your module with a Dallas DS18b20; it's easy to get working with the Arduino and for the cost, you may as well just leave it in the design unless you need the interface pin for it (it's only '1 wire').

Lefty is right; the factors affecting the measured temperature are obviously the local environment (including power dissipating components like resistors and semiconductors controlling loads) and the on-chip power dissipation, which can be calculated by multiplying the drive current by the voltage dropped across the output pin(s) sourcing/sinking current.  (You could even use the Arduino's ADC pins to measure the actual output voltage drops and calculate the power!)  The on-chip temperature rise is determined by the thermal resistance to the environment, which depends on the ATMega package and any heatsinking.  The 28pin DIP would have a reasonably low thermal resistance with all those pins dragging out the heat.  If someone is so inclined, the thermal resistance can be easily calculated by dumping a set amount of power into the chip and measuring the on-chip temperature rise.

If there's enough interest, I'll publish a sketch demonstrating reporting of the on-chip ADC result vs the output from the Dallas chip.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 1
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Awesome find!

Should you decide to sketch this function I'd certainly like to see it. I've built a small multidrop network of Dallas DS18B20s and would love to be able to compare their performance against the Arduino's onboard ability.

Keep reading that fine print...
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 3
Nerd up, yo.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I would love to see a sketch with this. Is it just reading from an analog pin? If so, which one?
Logged

Adelaide, South Australia
Offline Offline
Newbie
*
Karma: 0
Posts: 4
If Arduino is the answer, what is the question?
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

To answer Scott, ADC8 is an internal node (point in the microprocessor circuitry) with a known temperature dependency.  You can consider it a virtual analogue pin.  By selecting ADC8, the microprocessor switches the ADC to this node and measures it's voltage rather than that of an external analogue pin.  The beauty of this approach is that you are accurately measuring the microprocessor chip temperature, which is very useful when you are using the micro to drive heavy loads, as the micro silicon chip dissipates power doing this and heats up the integral sensor with minimal time delay.   (You can build in fail-safe features by reducing the time you power these loads if the temperature gets above a threshold point.)  Previously, to do this, you'd have to have a temperature sensor in physical contact with the micro and that results in additional cost, complexity, the loss of an ADC pin and further, the temperature measured would be lower than the actual micro chip temperature plus there would be a time delay in any micro temperature changes as measured by the external sensor).  

Unfortunately the latest release (0016) of the Arduino software implementation hasn't included my patch to support ADC8 - it still masks it out  The patch is easy to make to the  wiring_analog.c file per my initial posting.  Please read that again in conjunction with this and reply if further point need clarifying so I can include it in a sketch example.

I've further experimented with 5 ATMega328's and found that you absolutely must calibrate a chip before you use it, as the ADC8 voltage varies considerably from chip to chip - even within the same production batch.  I'll work on a sketch to demonstrate the above and also provide info on how different methods of measuring temperature compare over a range of -10C to +25C say.
Logged

SF Bay Area (USA)
Offline Offline
Tesla Member
***
Karma: 106
Posts: 6381
Strongly opinionated, but not official!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This seems to be a feature of the ATMEGAxx8P chips (note the P - designating a chip with "picopower" features.)
So it won't be present on ATmega8 or ATmega168 chips (but you MIGHT be able to drop in an ATmega168P, I don't know what the other compatibility issues might be.)  (there is no non-P 328.)
Logged

Tokyo, Japan
Offline Offline
Jr. Member
**
Karma: 0
Posts: 87
Trackballer
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I tried Internal Temperature Measurement with a ATMEGA328P.

AussieNeil said:
Quote
Both chips showed a rise of 6 counts with a warm finger on the DIP IC test, which slowly dropped back to the original reading when the finger heat source was removed, so this definitely works with 328 chips.
but my result is a bit different.

At a normal state, I got 38 or 39 as ADC8 values.
My "warm" finger on a chip never changed the value.
As a trial I softly roasted the chip on fire, the ADC8 value raised to 42.
I think my result is consistent with an explanation on the datasheet (doc8025.pdf).
Although ADC8 has such a rough resolution, it's available for fail-safe features.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 20
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This is a feature I wish to enable for my robot arduino. Big Congrats for this work. I'm using 0017 version with WIN7 and NANO3-328P. I have not tried editing the .C file, not my thing. I'm looking for the c++ commands that would enable this in my sketch. Please post if you have this approach working. My thanks in advance.
Logged

Cefalu' (PA), Sicily, Italy
Offline Offline
Newbie
*
Karma: 0
Posts: 5
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
This is a feature I wish to enable for my robot arduino. Big Congrats for this work. I'm using 0017 version with WIN7 and NANO3-328P. I have not tried editing the .C file, not my thing. I'm looking for the c++ commands that would enable this in my sketch. Please post if you have this approach working. My thanks in advance.

The .c file is already patched in the 0017 version... here is some sketch code that activates the temp sensor and outputs values on the serial port:
Code:
int mv;

void setup() {
  analogReference(INTERNAL);
  Serial.begin(9600);
}

void loop() {
  // Calculate millivolt value
  mv = 1100 / 1024 * analogRead(8);
  Serial.println(mv, DEC);
  delay(100);
}

In my duemilanove this feature works... but the value is off by *dozens* of millivolts from datasheet values.
Logged

Adelaide, South Australia
Offline Offline
Newbie
*
Karma: 0
Posts: 4
If Arduino is the answer, what is the question?
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks JackTheVendicator for confirming that patching is no longer required for this feature to work if you use 0017.  Yes you really **MUST** calibrate for each chip or you can be out by 30 or more degrees C.

Below is a comprehensive sketch that enables you to calibrate a specific ATMega328 against a DS180b20.  It also enables temperature calibration using a couple of diodes and reports the battery voltage at the same time.  The diodes provide about 4 times the sensitivity of the the internal ATMega328, but of course they don't measure the ATMega internal temperature.  (You could of course glue the diodes in contact with the chip if accuracy is more important than responsiveness and you don't want to invest in a DS180b20.

Here's the
Code:
/*
Temperature Calibration using the Dallas DS18b20
 
Using copyrighted OneWire code of May 6, 2007 with permission of Peter H Anderson, Baltimore, MD
per http://www.phanderson.com/arduino/ds18b20_1.html

Internal Temperature, External Diode voltage and supply voltage measurement code by
Neil Matthews, Adelaide, South Australia, 26 April 2009
Updated 24 Sep 2009

*/

// This sketch regularly reports temperature and temperature dependent voltages using:
// 1) A Dallas DS18B20 using the parasitic power mode on Arduino pin 8 (+/- 0.5C accuracy)
// 2) The voltage across 2 small signal silicon diodes (Temp change -2 x 2.3mV/DegC)
// 3) The Internal Temperature Measurement facility per section 21.8 of ATMega328 spec
// 4) Concurrently measures and reports supply voltage so you can check V supply dependency
//
// Displays results on an LCD Module (makes for easier calibration if you don't have a laptop)
// Output concurrently sent to PC via USB/Serial interface
//
// ** NOTE: Power the LDC module off and on after uploading the code to the Arduino to reset the display ***
//
//  LCD Line 1  VSupplyRaw  VSupplyCalc  DiodeVTempRef
//  LCD Line 2  DS18b20Temp ATMega328InternalVTempRef
//
// e.g.
//  LCD Line 1  464  4.93V   955
//  LCD Line 2  19.12C 379
//
// Suggestions for calibration:
// 1) Place board in fan forced oven, fridge and freezer  **DON'T EXCEED TEMP SPECS OF COMPONENTS**
// 2) Leave board outside - not in direct sun and record temperatures over a few days
// 3) Plot measurements obtained at different temperatures per 1 & 2 above to calibrate ATMega or diode output
// 4) Leave board sufficient time (say 5 minutes) at each temperature point so sensors stabilise
//
// Arduino Board Pins          Other Connections
//-------------------          -----------------
//
//                    /---------------- +5 VDC
//                    |
//                   ---
//                   | |
//                   | |   4k7
//                   | |
//                   ---
// Digital Pin        |
// 8 -----------------|---------------- 2 (DQ) on DS18b20
//                             Note Dallas pins 1 and 3 are grounded.
//
//
// Tx --------------------------------- to PC Serial Comms
//
//
// Analog Pin
// 2 -----------------
//                    \
//                    |
//           100k     |    2 x Small signal diodes
//           _____    |
// Vdd -----|_____|--------|>|--|>|---- Vss
//
// VRef measured at 1.087mV = 1023 count
// Hence Vfwd in mV = ADC Count x (1.087/1023)
// i.e. ADC count x 1.0626 which is close to + 1/16th
//
//
// Hence Vdd in V = ADC Count x (1.087/1023) * 10
//   i.e. ADC count x 10.626 which is close to 10 * (ADC count + 1/16th)
// This 'trick' gives a fast calculation and any inaccuracy due to variations in the ATMega
//  internal VRef from chip to chip etc, should be less than the +/- 0.5C Dallas DS18b20 accuracy  

const int Analog2      = 2;    // Analogue input pin for Vdd fwd
const int Analog3      = 3;    // Analogue input pin for V Supply/10
const int intlTempSens = 8;    // AD channel for temperature measurement
const int OneW_Pin     = 8;    // Digital I/O pin for One Wire I/O
// Note: using const int above optimises code size (and is better coding)

int AnalogVal          = 0;    // variable to store the value coming from ADC

void setup() {

  digitalWrite(OneW_Pin, LOW);
  pinMode(OneW_Pin, INPUT);       // Sets the digital pin as input (logic 1)
  analogReference(INTERNAL);      // Use the ATMega328 internal VRef
  Serial.begin(9600);
  delay(3000); //wait 3 secs for LCD Module to initialise
  
  
// *** Uncomment these lines the first time you use an LCD Module to configure the display size  ***
// *** Recomment them to same RAM once LCD Module configured
// Serial.print("?G216?B20"); // set LCD for 2 lines X16 chars and set backlight intensity on LCD - change as needed
// (Initialises LCD Module and saves settings in EEPROM, so no need to repeat)
//  delay(100);  // Allow time for LCD Module to process above commands
  
}

void loop() {

  int HighByte, LowByte, TReading, SignBit, Tc_100, Whole, Fract;
  int Vdd, Vdd_V, Vdd_mV;
  
  // Measure and print the Arduino Supply Voltage
  
  AnalogVal = analogRead(Analog3);    // read the value from ADC3
  Vdd = (AnalogVal+(AnalogVal>>4));   // Calculates Divider voltage in mV  = ADC * 1.0625
  Serial.print("?f?a");  // clear screen & home cursor

// Print AnalogVal and some spaces
  Serial.print(AnalogVal, DEC);
  Serial.print("  ");

// Print Voltage units (~4.87V from USB; divider O/P = 483mV)
  Serial.print(Vdd/100, DEC);
  Serial.print(".");
  
// Calculate mV to be printed
  Vdd_mV = Vdd - (Vdd/100*100);  // Round to 2 decimal places

// We have 3 digits of precision, with ADC count typically 400 - 550, i.e ~ 4.00 to 5.50V
// After stripping away voltage units, remaining units will be 00 to 99 in range.
// If in range 00 to 09, leading 0 will be suppressed so we need to add it back in.
  if (Vdd_mV < 10) {
    Serial.print("0");   // print leading '0'
    }
  Serial.print(Vdd_mV, DEC);   // prints millivolts
  Serial.print("V ");  // Print units


// Measure and print the diode forward voltage measurement
  AnalogVal = analogRead(Analog2);    // read the value from ADC2
  Serial.print(AnalogVal+(AnalogVal>>4), DEC);   // prints value * 1.0625
  Serial.print("  ");  // 2 spaces

//  Use Peter H Anderson's OneWire code to read & report temperature from DS18b20
  OneWireReset(OneW_Pin);
  OneWireOutByte(OneW_Pin, 0xcc, 0);
  OneWireOutByte(OneW_Pin, 0x44, 1); // perform temperature conversion, strong pullup for one sec

  OneWireReset(OneW_Pin);
  OneWireOutByte(OneW_Pin, 0xcc, 0);
  OneWireOutByte(OneW_Pin, 0xbe, 0);

  LowByte = OneWireInByte(OneW_Pin);
  HighByte = OneWireInByte(OneW_Pin);

  TReading = (HighByte << 8) + LowByte;
  SignBit = TReading & 0x8000;  // test most sig bit
  if (SignBit) // negative
  {
    TReading = (TReading ^ 0xffff) + 1; // 2's comp
  }
  Tc_100 = (6 * TReading) + TReading / 4;    // multiply by (100 * 0.0625) or 6.25

  Whole = Tc_100 / 100;  // separate off the whole and fractional portions
  Fract = Tc_100 % 100;

  if (SignBit) // If its negative
  {
    Serial.print("-");
  }
  Serial.print(Whole);
  Serial.print(".");
  if (Fract < 10)
  {
    Serial.print("0");
  }
  
  Serial.print(Fract);
  Serial.print("C ");

// End of DS18b20 routine

// Read internal ATMega328 temperature dependant voltage sensor
  AnalogVal = analogRead(intlTempSens);    // read the value from ADC8
  Serial.println(AnalogVal+(AnalogVal>>4), DEC);   // prints value * 1.0625
  delay(2000);    //wait 2 seconds
  Serial.print("?l");  // Home cursor and clear LCD line ready for next set of readings
}

// End of Temperature Sensing coding




//OneWire Interface coding (Reset, Output and Read Byte) provided by Peter H Anderson

// OneWire reset code
void OneWireReset(int OneW_Pin) // reset.  Should improve to act as a presence pulse
{
  digitalWrite(OneW_Pin, LOW);
  pinMode(OneW_Pin, OUTPUT); // bring low for 500 us
  delayMicroseconds(500);
  pinMode(OneW_Pin, INPUT);
  delayMicroseconds(500);
}

// OneWire Output code
void OneWireOutByte(int OneW_Pin, byte d, byte strong) // output byte d (least sig bit first).
{

  for(byte n=8; n!=0; n--)
  {
    if ((d & 0x01) == 1)  // test least sig bit
    {
      digitalWrite(OneW_Pin, LOW);
      pinMode(OneW_Pin, OUTPUT);
      delayMicroseconds(5);
      pinMode(OneW_Pin, INPUT);
      delayMicroseconds(60);
    }
    else
    {
      digitalWrite(OneW_Pin, LOW);
      pinMode(OneW_Pin, OUTPUT);
      delayMicroseconds(60);
      pinMode(OneW_Pin, INPUT);
    }

    d=d>>1; // now the next bit is in the least sig bit position.
  }
  if(strong)
  {
    digitalWrite(OneW_Pin, HIGH); // One sec of strong +5 VDC
    pinMode(OneW_Pin, OUTPUT);
    delay(1000);
    pinMode(OneW_Pin, INPUT);
    digitalWrite(OneW_Pin, LOW);
  }
}

// OneWire Read Byte code
byte OneWireInByte(int OneW_Pin) // read byte, least sig byte first
{
  byte d, b;

  for (byte n=0; n<8; n++)
  {
    digitalWrite(OneW_Pin, LOW);
    pinMode(OneW_Pin, OUTPUT);
    delayMicroseconds(5);
    pinMode(OneW_Pin, INPUT);
    delayMicroseconds(5);
    b = digitalRead(OneW_Pin);
    delayMicroseconds(50);
    d = (d >> 1) | (b<<7); // shift d to right and insert b in most sig bit position
  }
  return(d);
}

Strip out what code you need.  
Logged

Forum Administrator
Cambridge, MA
Offline Offline
Faraday Member
*****
Karma: 9
Posts: 3538
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

In Arduino 0018, you'll need to patch the file again, because doing so breaks analog inputs 8-15 on the Mega, so I'm reverting it.  If someone has time to find a fix that allows for reading the temperature sensor on the 328 without breaking the inputs on the Mega, that would be great (but please test on both the 328 and Mega).
Logged

Stockholm, Sweden
Offline Offline
Newbie
*
Karma: 0
Posts: 31
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I wrote an Arduino program for this that is not dependant on Arduino IDE or libraries. See http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1252144888

With calibration at three different temperatures (room temperature, fridge and freezer) I get decent precision. My guess is that I am about 2-3 degrees Centigrade off at most, which is OK for me and my current needs. If I would need better precision I would just use an external sensor. If I need some clue about the chip temperature, or the ambient temperature in a chip not working too hard, I find this solution rather elegant (and cheap).

Regarding calibration I recommend reading Atmel's application note 122 (link in thread mentioned above).

The ATmega 1280/2560 does not have this feature, so there is no use trying to use it on an Arduino Mega1280. There are some other AVR chips that does support it, such as the tiny25/45/85.

I made a simple spreadsheet for calibration, check the thread mentioned above.
Logged

Stockholm, Sweden
Offline Offline
Newbie
*
Karma: 0
Posts: 31
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I just want to clarify a few things.

Just as AussieNell I wrote my code based on Atmel datasheets (and application notes). My approach was different, doing all work in my program, not by patching the Arduino code base. When I was done and had it all working someone told me about this thread (and that was the first I heard about AussieNell's attempts). I saw that Mellis asked about  a solution that would not break the code base compatibility with the Mega, and decided to reply since my solution does not need any code base patching.

The (software readable) internal temperature sensor is available on the ATmega 48p/88p/168p/328p series, but not on the 640/1280/2560 (or the 644 or 644p of the Sanguino), so to keep the common Arduino code working on all platforms I think that a library would be a safer and better way to implement this, rather than a patched code base. I guess I could convert my code into a library, but it would take some work making sure it does not mess up something else, and I do not have the time right now to do that.

For replies and discussions specific to my implementation I would suggest making them in the thread I started (http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1252144888).
Logged

Pages: [1] 2   Go Up
Jump to: