Wednesday, April 30, 2014

The Easy Way to Use Thermistors

 A Thermistor is a resistor that varies its values with temperature. These are fairly inexpensive and easy to use with a microcontroller that has an analog to digital converter (ADC). However, figuring out how to compute the exact temperature is not simple. Here I go through the process that I use. I call it "The Easy Way" because I use Excel and Word to do the heavy lifting for me.

There are two types of thermistors, Negative Temperature Coefficient (NTC) and Positive Temperature Coefficient (PTC). Simply, for NTC thermistors, the resistance goes DOWN when the temperature goes UP.

In a circuit, thermistors are typically half of a voltage divider. In our example, the top half of the voltage divider is a 10k Ohm, 1% fixed resistor, powered by Vref, 2.5V. The bottom half of the voltage divider is a 10k Ohm 3% NTC thermistor.

Step 1: Get the temperature to resistance table

The math to convert from temperature to resistance is quite complex, so the manufacturers publish a table containing the resistance to temperature values. For example, see this link.

Step 2: Get them into Excel

For the table in the previous link the values were in a PDF sheet. But we need them in Excel. If you have a lot of time on your hands then you can manually retype them all. But I'm lazy, so I did some creative cut/paste from the PDF file into a Notepad file. You need a table consisting of many rows, each row containing the Temperature (in C) and also the Resistance (in Ohms). In Excel I also added another column containing the temperature in degrees Fahrenheit. This is just for convenience and not used in any calculations. The equation to convert from C to F in excel is the following, assuming the value in C is in cell A24:
=CONVERT(A24, "C", "F")
Below we see what our table looks like after we have all our values. We only computed for the temperature range of 0C to 85C because that was our product requirements.

Step 3: Get your ADC working

Use an oscilloscope to measure the output voltage of the voltage divider, while also measuring with the ADC. This will help you calibrate your calculations. Be sure that you know the resolution of your ADC to get the corresponding maximum counts. For example, a 12 bit ADC has 4096 possible values. Obviously to be able to measure temperature you will need to measure the voltage output from the thermistor voltage divider.

Step 4: Math Time

First, a few definitions:
  • R1 = our "top" resistor, in our case the fixed 10kOhm value
  • Rt = the varying thermistor resistance
  • Vref = the voltage supply to the top of the voltage divider, in our case 2.5V
  • Vt = the output voltage of this voltage divider
The equation for the thermistor being on the BOTTOM leg of the voltage divider is:
Rt                    Vt
-------       =   ---------
(R1 +Rt)          Vref

This can be rearranged to:
Rt = R1 / ( ( Vref/Vt) - 1)
Now, test this with your circuit, and verify that you are seeing reasonable values.
Next, we need to convert this to ADC measurements. But first a couple more definitions:
  • ADC= the value we measure from the ADC, in "counts"
  • Resolution = The resolution of the ADC. In our case, this is 4096 because we have a 12-bit ADC.
The ADC equation is:
Vt = (ADC / Resolution) * Vref
We can substitute this into the previous equation to get our magic equation:
ADC = (4096) / ( (R1/Rt) + 1)

Step 5: Make a Table

We can use this equation to create a table in Excel that contains the following tuplets:
  • Temperature (in C)
  • Resistance (in Ohms)
  • ADC Measurement
Using the Microsoft Excel Equation shown below. This assumes that our other resistor is 10k ohms, and our ADC has 4096 counts of resolution. In this case the raw resistance value is in cell C22. We
However, this will give us decimal values. We want to round this off, so we use the following:
=ROUND(((4096)/((10000/C22)+1)), 0)

Below shows the table with the ADC values added:

Step 5: Convert this into a C Array

Here's where our laziness comes into play. We're going to use Microsoft Word to convert the list of values into a C array. First, select only the ADC column, starting at the value for 0C. Paste this as plain text into a new Microsoft Word file. Be sure to paste as plain text.
Now we have a wonderful list of values. Next, use Word's find/replace option to replace the carriage return with a comma and a space, as shown below. Note the find string is "^p" without the quotes, and the replace string is ", " without the quotes.
This performs a bit of magic, turning our long list into something that's starting to look like an array:
Now, just wrap this with the variable definition and a comment explaining what it is, and we're done.

/** ADC values which correspond to temperature starting at 0C up to 85C*/
const uint16_t adcToTemperature[] = {3008, 2973, 2937, 2900, 2864, 2827, 2789, 2752,
2714, 2675, 2636, 2598, 2558, 2519, 2480, 2440, 2401, 2362, 2322, 2283, 2243, 2204,
2165, 2126, 2086, 2048, 2010, 1971, 1934, 1896, 1859, 1822, 1785, 1749, 1713, 1678,
1643, 1609, 1575, 1541, 1508, 1476, 1443, 1412, 1381, 1350, 1320, 1290, 1261, 1232,
1204, 1177, 1150, 1123, 1097, 1072, 1047, 1022, 998, 975, 952, 929, 907, 886, 865,
844, 824, 804, 785, 766, 748, 730, 712, 695, 679, 662, 646, 631, 616, 601, 587,
573, 559, 545, 532, 519};

Step 6: Get the Temperature for ADC Reading

We now have our array of values. To get the temperature we just iterate through this table, looking for the closest match. We're not doing any interpolation or anything fancy, just a straight look-up. If you need more accuracy then do something different.
/** Gets the temperature in degrees C for the specified ADC reading.
 * @param adc the reading from the ADC
 * @return the temperature in C, or ERROR_TEMPERATURE_NOT_FOUND if the value was not found.
uint8_t getTemperatureForAdc(uint16_t adc)
#define ADC_RESOLUTION                        (4096)

    if (adc > ADC_RESOLUTION)
        return ERROR_TEMPERATURE_NOT_FOUND;  //error - this should not happen

    /* Now iterate through the table, looking for the nearest match */
    uint32_t iterator = 0;
    while (adc < adcToTemperature[iterator])
    if (iterator == ADC_TO_TEMP_LOOKUP_TABLE_ROWS)
        // We've gone through the entire table but haven't found a match
        return ERROR_TEMPERATURE_NOT_FOUND;  //error - this should not happen
    return (iterator);   // The temperature, in degrees C


We now have an easy and fast way to get the temperature from the thermistor.