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