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 - ADValThere 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 truncate2200 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); }