Introduction.
This discussion focuses on the 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
// 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