Step 1. Reduce range.
Only use what you need, no more!
This applies to EVERYTHING, by the way -- not just a voltage or temperature as in this case, but to analog inputs (V/I range, protection), digital inputs (logic high/low, protection), bandwidth / frequency response / bit rate (at inputs, outputs, internal nodes, whatever), dynamic range (noise level, number of bits computed) and more.
Mark with chalk and cut with an axe!
(This isn't absolute, as it depends on what's handy and cheap (a 32-bit ARM "axe" is pretty darn cheap these days, even when you only need an 8-bit butterknife). Engineering is comprehensive, and the best solution is the one that meets all specs, including time and cost!)
So what to do here?
First, what's the cheapest and easiest approach?
- NTC thermistors have high tempco (and only modest uncertainty -- kind of marginal for present purposes, really). That sounds good for starters.
- Make a voltage divider. Over the full NTC range, this won't give the best precision (readings bunch together at very low and very high temperatures), but for R_pullup = R25 (i.e., the thermistor's nominal value at 25C), precision is maximized in the middle (around 25C), which is ideal for a limited range.
- Amp up the signal. Put an op-amp in front, to subtract the offset (it'll be around VREF/2) and magnify the difference. Use a good DC-stable op-amp. Chopper amps (Vos < 50uV, typically under 1MHz GBW -- still way more than needed here!) are available with low current consumption, at cheap prices.
- Convert it as normal. You can -- nay, should -- still do at least a little digital filtering. You start by filtering the analog signal (maybe with a basic RC(RC) section) to the bandwidth required (for a thermostat, 10Hz is more than enough!), which ensures the analog signal being sampled, isn't moving around much between samples. This makes the analog voltage consistent, so you know any sample-to-sample errors will be due to the converter. Filter this with something basic, like a 1st or 2nd order IIR filter.
You can sample at a pretty high rate, like kHz, and accumulate a few extra bits of precision. But don't put too much trust in these, because the distance between steps is not guaranteed to be very good. That is, the analog voltage change between ADC steps 0x20 to 0x21, need not be the same as steps 0x7D to 0x7E, or anywhere else (this is the differential error DNL).
The analog signal might not be changing all that frequently, either; if it's sitting stable on one ADC code, then you can't get extra precision no matter how many times you oversample that one code! You can intentionally add noise (often not white noise, but something that has a long period against the ADC sample rate) to dither between values. If the noise has an even distribution over several codes' range, then those codes will be represented equally often, except for the ones at the ends of the spread, which vary with input.
A trick might be to set the ADC sample rate at 1kHz, and set a PWM timer for 1.01kHz (or whatever's closest to 10Hz away from the actual ADC sample rate). Run it through a good solid filter, so the square wave gets filtered down to a triangle or quasi-sinusoidal shape. Use a voltage divider to shrink it to a few mV, then add this into the ADC input. Now on the digital side, if you accumulate samples for 100ms and use that as a decimating filter (i.e., save the accumulator value, then reset it, every 100ms; thus generating an internal 10Hz sample rate), the dithering will have gone through a full 360 degree rotation with respect to the sample rate, so you've sampled every possible dither value, not just random ones. Nice!
- This general plan can be tweaked and expanded, however you like. Swap out thermistor for semiconductor sensor (LM34?). Or Pt1000 sensor and suitable adjustments to the analog circuit (divider with compensation resistors). Or thermocouple plus cold junction compensation (there are ICs that handle this all for you).
Tim