Temperature Sensing with the LM60

LM60 circuit

Measuring the temperature of a PCB is quite a common thing to have to do in embedded systems and I have been working on a board that needs to do just that. There are dozens of ways to achieve this but for this project we chose to use the LM60 from Texas Instruments. The data sheet for this part can be found at http://www.ti.com/lit/ds/snis119d/snis119d.pdf. Note that for this project I’m using a PIC18 MCU and the CCS C compiler. If you’re using another development environment you’ll need to adapt the source code accordingly.

The LM60 isn’t exactly the most accurate temperature sensor in the world, accuracy in the data sheet is quoted at +-3.0-4.0% but in this application we were looking to provide PCB temperature over a CAN interface so that this can be monitored and correlated to any PCB failure that might occur in the field. The accuracy of the LM60 is good enough in this particular instance.

What this part does have going for it though is that it is small, it comes in SOT-23 as well as TO-92 packages, and it has a linear voltage output with respect to temperature which makes it pretty straightforward to convert to an absolute value in degrees Celsius using an ADC in a microcontroller. The LM60 is capable of measuring temperatures from -40 to +125 degrees and there is an automotive spec version, the LM60-Q1 which is fortunate as that was exactly what I needed.

The output voltage of the LM60 ranges from 174mV at -40ºC to 1205mV at 125ºC regardless of input voltage which can range from 2.7 to 10V. The output voltage with respect to temperature is calculated using the equation:

Vo = (+6.25mV/ºC x TºC) + 424mV

Translating that into the general form for a linear equation y = mx + c we can see that the two coefficients are:

Gradient m is 6.25
Offset c is 424

The next step is to figure out what values these voltages will be converted to by the ADC. The PIC18 I used in this project has 12 bit ADCs and the circuit runs at 3.6V. By dividing the ADC range (0-4095) by the voltage range (0-3600mV) we find that each 1mV at the ADC results in a value of 1.14… or it would if the value wasn’t an integer.

With that in mind we can convert the Vo to ºC equation into an ADC value to ºC equation by multiplying each of the coefficients by 1.14. This results in the new equation:

ADCin = (7.11 x TºC) + 482.3

Finally we need to move the equation around a bit as we’re not trying to calculate the ADCin value, we’re going to be getting that from the MCU. What we’re interested in is converting from the ADCin value to a temperature. Re-arranging the equation gives us:

TºC = (ADCin – 482.3) / 7.11

There’s just one other trick used in this driver and that is to avoid the use of floating point maths by multiplying up the values by a fixed amount and returning a result in milli-Celsius. Floating point maths, especially on an 8-bit PIC18, is an incredibly slow thing and best avoided where at all possible. To get around this I used a fairly unsophisticated technique of multiplying the input value and c-coefficient by 1,000,000 and the m-coefficient which will be the divisor in the equation by 1,000. By having the dividend an order of magnitude higher than the divisor and outputting in 1000ths of a degree, we can perform integer division without losing too much precision.

The final code for the driver is listed below. There are plenty of ways in which it could be extended and improved. For example, the code configures the ADC to use a VREF of VDD which is circuit dependent and affects the coefficients used in the equation. There are of course other options. You could for example fix VREF at a specific voltage if your PIC supports that. For example, the PIC18 I was using supports a fixed voltage reference (FVR) of 2.048V which would make better use of the 12-bit ADC and make it VDD independent (as long as VDD > 2.048V obviously!) Alternatively you could pass the value of VDD into the initialisation method and calculate the coefficients at that point.


 * File:   lm60_driver.h
 * Author: Jon Masters
 * Created on 22 January 2014, 11:11
 * Driver code for lm60 temperature sensing using a 10 or 12-bit ADC channel.
 * Assumes that the lm60 chip is being driven at VDD. All temperatures are
 * returned as degrees "milli" Celsius to avoid FP maths.
 * ==============
 * Code must include a #use delay.
 * Code should include either of the following:
 * #device ADC=10
 * #device ADC=12
 * If #device ADC=10 is used, include the following pre-processor definition:
 * #define __LM60_10BIT_ADC
 * Before using any ADC, CCS requires you to call setup_adc(). This driver
 * library will call this as part of the initialisation method. If you don't
 * want it to do that then include the following pre-processor directive:
 * #define __LM60_INIT_NOSETUP
 * You are then responsible for ensuring the call to setup_adc() is made.
 * ======
 * Start by calling the lm60_init method passing in the analog channel information
 * corresponding to the channel that the LM60 is connected to. Note that the method will,
 * by default initialise the ADC to use the internal clock. To override this
 * behaviour use __LM60_INIT_NOSETUP and call setup_adc() yourself.
 * Once this is done you can call lm60_read_temp_mC() to read the temperature in
 * thousandths of a degree Celsius.

#ifndef LM60_DRIVER_H
#define     LM60_DRIVER_H

#ifdef     __cplusplus
extern "C" {

/* Constants used to convert voltages to temperatures. */
#define lm60_y_intercept 482300000
#define lm60_y_grad 7110

 * Holds the index of the ADC channel that the LM60 Vout is connected to.
int8 lm60_adc_channel;

 * Initialises the driver library.
 * ARGS:
 *  sANx - the analog port that the LM60 Vout is connected to. Use the constants
 *          defined in the PIC header e.g. sAN0
 *  channel - the index of the analog channel that matches the sANx argument e.g.
 *          for sAN0, this will be the index 0, sAN25 will be 25.
 *  Nothing.
void lm60_init(unsigned int16 sANx, int8 channel);

 * Returns a temperature reading from the LM60 in milli-Celsius i.e. in 1/1000's
 * of a Celsius. E.g. 25C is returned as 25000mC. The means that FP maths isn't
 * required which should improve performance.
 * ARGS:
 *  NONE.
 *  Temperature in mC.
signed int32 lm60_read_temp_mC();

#ifdef     __cplusplus

#endif     /* LM60_DRIVER_H */


 * File:   lm60_driver.c
 * Author: Jon Masters
 * Created on 22 January 2014, 11:05
 * See header for pre-reqs, usage and function documentation.

#include "lm60_driver.h"

void lm60_init(unsigned int16 sANx, int8 channel)
#ifndef __LM60_INIT_NOSETUP
    setup_adc(ADC_CLOCK_INTERNAL | ADC_TAD_MUL_16);
#endif //__LM60_INIT_NOSETUP

    setup_adc_ports(sANx, VSS_VDD);
    lm60_adc_channel = channel;

signed int32 lm60_read_temp_mC()
    unsigned int16 r = read_adc();

#ifdef __LM60_10BIT_ADC
    r = r << 2;

    unsigned int32 adc_val = r;
    adc_val *= 1000000;

    // calculate result in milli-Celsius
    signed int32 rv = adc_val - lm60_y_intercept;
    rv /= lm60_y_grad;
    return rv;

As always I welcome your feedback. Please feel free to post comments / questions below, especially if I’ve made some horrendous gaff! Otherwise, I hope this has been in some way useful and I’ll see you next time.

  1. Hi, why did u put a resistor and a capacitor after the lm60 ?
    Thanks !

    • Hi Edouard, they’re there to act as a simple filter to take out some of the noise and stabilise the readings.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

%d bloggers like this: