Introduction
This note discusses how a common NTC thermistor may be linearized to simplify calculations. The technique utilizes two identical thermistors and two resistors. I was able to get a reasonable straight line but opted for three piece wise lines to achieve greater accuracy.
Detailed Description
Figure #1 is a schematic of the linearized network which includes two identical NTC thermistors both located at the measurement point and two fixed resistors R1 = 15K and R2 = 4.7K. In Figure #2, I have redrawn the two thermistors to stress that this might be thought of as a three termianl device. Yellow Spring Instrument, acquired by Measurements Specialties offers such a three terminal thermistor as a 44201 for nominally $52.00.
Yellow Spring apparently used this technique for linearization and protected it in US Patent 3,316,765 issued in 1967. The patent helped but, of course, lacked any detailed calculations. I have spent a lot of time using Excel to exploring this problem over the past few weeks and knowing the limited computational capabilities of some 44 years ago, my hat is sure off to these folks.
I improvised with two small Vishay 2381 640 66103 10K NTC thermistor which cost me about 25 cents. The resistors are a few pennies. So, you should be able to configure this type of circuit for about a $1.00.
I used a spreadsheet quickly determine the linearity of Req as I modified the values of fixed resistors R1 and R2. Col A are the temperatures in degrees C and Col B are the corresponding thermistor resistances. (I cut and pasted these from the data sheet. Col D is the series equivalent of the 15K fixed resistor and the left most thermistor. Col E is the second thermistor and Col F is the paralell equivalent of Col D and E. Col H is the Req between point A and B.
A plot of Col H vs Col A appears reasonably linear. I spent a good deal of time striving for Req series that was linear enough to be accurately approximated with a single equation of the form y = mx + b, actuall did come quite cloase. But, in the end, I had to check myself and argue, "but today even the most promitive microprocessor handle three separte straight lines. At the bottom of Figure #3 I developed these equations and the results are in Col I. The plot of Col I this lies right on top of that of Col H as a function of temperature.
Interfacing with a Processor
Figure 5 illustrates the use of a volatage divider to determine the Req of the network. Knowing Req, the Tc may be caculated using the straight line approximations.
Using voltage division;
(1) Vin = Req * Vref / (Req + 2200); Expressing Vin as a function of ADVal (2) Vin = ADVal * Vref / 1024.0 Equating (1) and (2), note that Vref cancels out; (3) Req = 2200 / ((1024 / ADVal) - 1) or (4) Req = 2200 * ADVal / (1024 - ADVal)It is worthy of note that the product of 2200 and ADVal is larger that 16-bits.
Sample Program - PICAXE
In the following, 32 A/D conversions are performed and these are averaged. Equation (2) is used to determine Req. As 2200 and ADVal are both 16-bit quantities, I used the ** operator.
NumHigh = 2200 ** ADVal ' High 16 bits in NumHigh
NumLow = 2200 * ADVal ' Low 16 bits in NumLow
Denom = 1024 - ADVal
There may well be a more elegant technique for determining the quotient, but I used repeated subtraction of Denom for the 32-bit NumHigh and NumLow.
Req = 0
Do While NumHigh <> 0 Or NumLow >= Denom
If NumLow < Denom Then
NumHigh = NumHigh - 1
End If
NumLow = NumLow - Denom
Req = Req + 1
Loop
The Tc is then calculated using one of three different linear equations.
Range 1. Tc_10 = 974 - Req * 10 / 39 Range 2. Tc_10 = 1084 - Req * 10 / 31 Range 3. Tc_10 = 1340 - Req * 10 / 17
This code uses a mere 200 program words (of 4096 on a PICAXE-20X). With a few simple modifications, I mapped this over to a PICAXE-08M which has a 256 byte program capacity. My guess is that one might be able to accommodate at least one thermistor (perhaps two) and control an output so as to realize a thermostat for nominally $2.50 in parts.
' THERM_LIN - Linear Thermistor
'
' PICAXE-20X2 ' Uses about 200 program words
'
' Peter H Anderson, Sept 29, '10
Symbol ADVal = W0 ' note that I am doubling up on RAM locations
Symbol Sum = W1
Symbol NumHigh = W1
Symbol NumLow = W2
Symbol Denom = W3
Symbol Req = W4
Symbol Req_10 = W1
Symbol Tc_10 = W2
Symbol X = W0
Symbol N = B9
Symbol Whole = B10
Symbol Fract = B11
DirsB = %11111111
ADCSetup = %0000000000001000 ' ADC3
Pause 1000
Do
GoSub ADConv
GoSub CalcReq
SerTxD ("Req = ", #Req, CR, LF);
GoSub CalcTc_10
GoSub DisplayTc
Pause 60000 ' one minute
Loop
ADConv:
Sum = 0
For N = 1 to 64 ' sum 64 readings
ReadADC10 3, ADVal
Sum = Sum + ADVal
Next
ADVal = Sum / 64 ' calculate the average
Return
CalcReq:
NumHigh = 2200 ** ADVal
NumLow = 2200 * ADVal
Denom = 1024 - ADVal
' Now do the division by repeated subtraction
Req = 0
Do While NumHigh <> 0 Or NumLow >= Denom
If NumLow < Denom Then
NumHigh = NumHigh - 1
End If
NumLow = NumLow - Denom
Req = Req + 1
Loop
Return ' result in word Req
CalcTc_10:
Req_10 = Req * 10
SerTxD ("Req_10 = ", #Req_10, CR, LF)
If Req > 3700 Then
Tc_10 = 999 ' error
ElseIf Req > 1838 Then
X = Req_10 / 39 ' Req * 10 / -39 - 3797 * 10 / -39
Tc_10 = 974 - X
SerTxD ("Tc_10 = ", #Tc_10, CR, LF)
ElseIf Req > 827 Then
X = Req_10 / 31 ' Req * 10 / -31 - 3760 * 10 / -32
Tc_10 = 1084 - X
ElseIf Req > 570 Then
X = Req_10 / 17 ' Req * 10 / -17 - 2278 * 10 / -17
Tc_10 = 1340 - X
Else
Tc_10 = 0
EndIf
Return ' result is Tc_10
DisplayTc:
Whole = Tc_10 / 10 ' Tc whole
Fract = Tc_10 % 10 ' Tc tenths of a degree
SerTxD (#Whole, ".", #Fract, CR, LF)
Return
Sample Code - Arduino
The Arduino code closely follows the PICAXE.
Note that in performing the calculations, I opted not to use floating point math, only to confirm I could.
But, I did at first run into a problem which I concluded was truncation.
R_therm = 2200 * adval / (1024 - adval); // 2200 * adval will truncate
2200 and adval are both integers and thus the computed product is 16 bits, with the most important higher order bits beinging lost.
This is remedied by typecasting either 2200 or adaval before multiplying.
R_therm = ((unsigned long) 2200) * adval / (1024 - adval);
// LIN_THERM - Arduino
//
// Continually measures temperature using linearized thermistor.
//
// copyright, Peter H Anderson, Oct 1, '10
unsigned int ad_meas(byte channel, byte num);
void display_temperature(int T);
void setup()
{
Serial.begin(9600);
delay(2000);
}
void loop()
{
unsigned long Rtherm;
unsigned int adval, R_therm, R_therm_10;
int T_10;
Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!");
while(1)
{
adval = ad_meas(0, 32);
R_therm = ((unsigned long) 2200) * adval / (1024 - adval);
if (R_therm > 3700)
{
T_10 = 9999; // out of range
}
else if (R_therm > 1830)
{
T_10 = (R_therm * 10 - 37970) / -39;
}
else if (R_therm > 827)
{
T_10 = (R_therm * 10 - 33600) / -31;
}
else if (R_therm > 570)
{
T_10 = (R_therm * 10 - 22780) / -17;
}
else
{
T_10 = 9999; // out of range
}
display_temperature(T_10);
delay(5000);
}
}
unsigned int ad_meas(byte channel, byte num)
{
unsigned int sum = 0, adval;
byte n;
for (n=0; n<num; n++)
{
adval = analogRead(channel);
sum = sum + adval;
}
return(sum / num);
}
void display_temperature(int T)
{
int whole, fract;
whole = T / 10; fract = T % 10;
Serial.print(whole, DEC);
Serial.print(".");
Serial.println(fract, DEC);
}