Temperaturement Measurmement using the Microchip Linear Active Thermistor

copyright, Peter H Anderson, Baltimore, MD, July, 2009


Introduction.

This discussion focuses on the Microchip MCP9701 Linear Active Thermistor. These are available for nominally $0.25 in a convenient TO-92 package. The name linear active thermistor; the device uses an internal thermistor, but the use of an external voltage (active) results in a linear behaviour.

The operation is similar to an LM34 or LM35 with a voltage source and an output which is a linear function of temperature. That is, read an analog to digital converter and compute the temperature.

For the MCP9701, the Vout is 19.53 mV per degree C with a 0.400 volt offset to permit measuring negative temperatures. Thus;

      Vout = Tc * 0.01953 + 0.400.  (1)
Thus, if Tc is -10.0, 0.0 and 25 degrees C, the corresponding voltages are 0.2047, 0.400 and 0.92825, respectively.

Assume the output of the MCP9701 is measured using a 10-bit A/D as are provided by most Microchip PICs, the PICAXE and the Arduino, with a Vref of 5.0 VDC.

      Vout = Vref / 1024 * ADVal (2)
      Vout = 5.0 / 1024 * ADVal  (3)
      Vout = 0.00488 * ADVal (4)

Equating (4) and (1) and solving for Tc;

     0.00488 * ADVal = Tc * 0.01953 + 0.400

     Tc = 0.00488 / 0.01953 * ADVal - 0.4 / 0.01953

     Tc = ADVal / 4 - 20.5

     Tc_100 = 25 * ADVal - 2050

Note that Microchip has selected the 0.01953 (19.5 mV) coefficient as it is precisely four times the magnitude of each A/D band of 4.88 mV. The result is very simple calculations. Note that this ratio of 4 does not hold if the Vref on the processor is radically different than 5.0 VDC.

PIC6F886-I/SP using Sourceboost C

// MCP9701.C Temperature Sensor - SourceBoost C
//
// PIC16F886 - debugged using an ICD2, MPLAB
//
// MPCP9701 temperature sensor on AN0 (term 2).
//
// Continually performs A/D AN0, calculates and display temperature in degrees C.
//
// copyright, P H Anderson, July 10, '09

#include <system.h>
#include <icd2.h>
#include <string.h>
#include <stdio.h>

#pragma DATA 0x2007, 0x28e4
#pragma DATA 0x2008, 0x3eff
#pragma CLOCK_FREQ 4000000

typedef unsigned char byte;

unsigned int ad_conv(byte channel, byte num);
void display_val(int X, byte places);
void print_str(char *s);
void asynch_setup(void);

char s[25]; // global to avoid constantly passing the string.

void main()
{
    unsigned int adval;
    int Tc_100;


    osccon = 0x61; // 4.0 MHz internal RC clock
    anselh = 0x00;

    ansel = 0x01;  // AN0 (term 2)

    asynch_setup();
    delay_s(1);
    strcpy(s, "?f");
    print_str(s);

    while(1)
    {
        adval = ad_conv(0, 10); // perform A/D on CH0
        Tc_100 = 25 * adval - 2050;
        display_val(Tc_100, 2);  // display with two sig digits after decimal point

        delay_s(5);
    }
}

unsigned int ad_conv(byte channel, byte num)
{  // assumes proper ansel bits have been set
   // perfrom num A/D on the specified channel and averages.

   unsigned int adval, sum = 0, avg;
   byte n;
   adcon1 = 0x80;
   adcon0 = 0xc1;
   adcon0 = adcon0 | (channel << 2);
   delay_ms(1);
   for (n=0; n<num; n++)
   {
       adcon0.1 = 1;
       while(adcon0.1)
       {
       }
       adval =  adresh;
       adval = (adval << 8) | adresl;
       sum = sum + adval;
   }
   avg = sum / num;
   return(avg);
}

void asynch_setup(void)
{   // for UART, see Manual pages beginning at page 151
    trisc.7 = 1;
    trisc.6 = 1;    // TX
    rcsta.SPEN = 1; // enable serial port
    txsta.TXEN = 1; // enable transmitter
    txsta.SYNC = 0; // asynchronous
    txsta.BRGH = 1;

    spbrg = 25; // 9600 baud at 4.0 MHz
}

void display_val(int X, byte places)
{
// Need to add provision for the number of places after the decimal point.
// This implementation assumes two places.

    int whole, fract;
    if (X < 0)
    {
       X = -X;
       strcpy(s, "-");
       print_str(s);

    }
    whole = X / 100; // more work here
    fract = X % 100;
    sprintf(s, "%d", whole);
    print_str(s);
    strcpy(s, ".");
    print_str(s);
    if (fract < 10)
    {
      strcpy(s, "0");
      print_str(s);
    }
    sprintf(s, "%d", fract);
    print_str(s);
    strcpy(s, "?n");
    print_str(s);
}


void print_str(char *s)
{
    while(*s != '\0')
    {
        txreg = *s;
        while(txsta.TRMT == 0)
        {
        }
        ++s;
    }
}


Arduino

// MCP9701.pde - Arduino
//
// Illustrates an interface with an MCP9701 temperature sensor.
//
// Continually measures the output of the MCP9701 on ADC0, calculates
// Tc_100 and displays.
//
// copyright, Peter H. Anderson, July, '09

void setup()
{
  Serial.begin(9600);
  delay(1000);
  digitalWrite(7, LOW);
  pinMode(7, OUTPUT);
}

void loop()
{
    while(1)
    {
        int ADVal, Tc_100, whole, fract;
        digitalWrite(7, HIGH);
        delay(100);
        ADVal = analogRead(0);
        digitalWrite(7, LOW);
        Tc_100 = 25 * ADVal - 2050;

        // Serial.println(ADVal);
        whole = Tc_100 / 100;
        fract = Tc_100 % 100;

        Serial.print(whole);
        Serial.print(".");
        if (fract < 10)
        {
            Serial.print("0");
        }
        Serial.println(fract);

        delay(1000);
    }
}


PICAXE-08M

' MCP9701_1.Bas (PICAXE-08M)
'
' Continually measures the temperature in degrees C using a Microchip MCP9701, typically costing
' a fraction of a dollar.
'
' Performs 64 measurements and averages the 10-bit A/D Value.
'
' The temperature in degrees C times 100 is 25 * ADVal - 2050.
'
' The temperature is then displayed in decimal format.
'
' Note that the resolution is 0.04 degrees C.
'
' If the temperature is less than 28.00 degrees C, a relay on Out1 is operated.  If
' greater than 35.0, the relay is released.
'
' This was tested on a PICAXE-08M and the program used 70 bytes (of 256), leading me to believe this 
' concept could be expanded to four sensers with four realys using a PICAXE-18M or 20M for only a 
' few dollars.
'
' For future improvement.  Negative temperatures.  Degrees F. 
'
'  MCP9701			PICAXE-08M
'
'   Out (2) ------------ ADC4 (term 3)
'
' copyright, Peter H Anderson, Baltimore, MD, July, '09

Symbol ADVal = W0
Symbol Sum = W1
Symbol Tc_100 = W1

Symbol N  = B4

Symbol Whole = B5
Symbol Fract = B6


Main:

   Sum = 0

   For N = 1 to 64			' sum 64 readings
      ReadADC10 4, ADVal
      Sum = Sum + ADVal
   Next

   ADVal = Sum / 64			' calculate the average

   Tc_100 = 25 * ADVal    
   Tc_100 = Tc_100 - 2050	
   
   Whole = Tc_100 / 100		' Tc whole
   Fract = Tc_100 % 100		' Tc tenths of a degree

   SerTxD (#Whole, ".", #Fract, 13, 10)

   If Tc_100 < 2800 Then OperateRelay

   If Tc_100 > 3500 Then ReleaseRelay

Main_1:
   Pause 1000
   GoTo Main

OperateRelay:
   High 1
   GoTo Main_1

ReleaseRelay:
   Low 1
   GoTo Main_1