andrewg
Joined: 17 Aug 2005 Posts: 316 Location: Perth, Western Australia
|
Temperature sensor with internal diode and CTMU |
Posted: Sat Mar 16, 2013 11:31 pm |
|
|
For a recent project involving a PIC18F67K22, I investigated the use of the internal diode as a temperature sensor in combination with the CTMU peripheral. Microchip say it can be done, but with no guidance on the sort of accuracy possible. You only find out by doing, I guess!
Code follows, but in short, at the very best you should only expect a temperature resolution of about 1.7-1.8 degC. In practice something like +/- 3.6 degC or even +/- 7 degC would be expected.
It would be usable for a general "too hot" or "too cold" type sensor, but I wanted something a little more precise (+/- 1 degC) so I'm going with an external sensor.
I know other people will be tempted, like me, to see what the built-in temperature sensor is like. Here's the code to find out!
The implementation of adc_read() is left as an exercise for the reader
inttemp.h: Code: | #ifndef INTTEMP_H
#define INTTEMP_H
#include <stdint.h>
#define INTTEMP_CALIBRATION_SIZE 6
// Initialise the internal temperature module
void inttemp_init(void);
// Perform a single point calibration using a known correct current temperature
void inttemp_calibrate(int16_t temperature); // 0.1degC units
// Load/save calibration
void inttemp_load_calibration(uint8_t *cal);
void inttemp_save_calibration(uint8_t *cal);
// Read the internal temperature sensor
int16_t inttemp_read(void); // 0.1degC units
#endif /* INTTEMP_H */ |
inttemp.c: Code: | #include "inttemp.h"
#pragma module
/*
References:
* SBAA073A "Measuring Temperature with the ADS1216, ADS1217, or ADS1218"
* DS39660D "PIC18F87K22 Family, 27.8 Measuring Temperature with the CTMU Module"
* TB3016 "Using the PIC MCU CTMU for Temperature Measurement"
* AN1375 "See What You Can Do with the CTMU"
In short, taking diode voltage measurements using two different currents
eliminates all the device specific variables (such as the +/-60% CTMU current
accuracy, and variations with temperature). Calibration can then be performed
via a single measurement.
From SBAA073A:
delta diode voltage = alpha * temperature
where alpha is a constant made up of various physical constants and the ratio of
the excitation currents, the temperature is in Kelvin
For calibration:
alpha = delta diode voltage / temperature
For calibrated temperature:
temperature = delta diode voltage / alpha = delta diode voltage * (1 / alpha)
Worked example:
Assume temperature is 290 (29.0 degC), v1=998, v2=827, dv=171
alphan = 171 bits = 0.138V (Vref+=3.3V,Vref-=0V)
alphad = 30215 (0.01K units)
alpha = 2189 K/V = 1.767 K/bit
Normally +/- 1 ADC bits is pretty good, but we're reading twice, so the expected
error is actually more like +/- 2 bits, which is +/-3.5 K.
*/
#byte CTMUICON = getenv("SFR:CTMUICON")
#bit CTMUEN = getenv("BIT:CTMUEN")
#bit EDG1STAT = getenv("BIT:EDG1STAT")
#define ADC_CHANNEL_DIODE 29
#define ZERO_DEGC_IN_CENTIK 27315 // centi-K = 0.01K units
struct {
// alpha numerator and denominator
uint16_t alphan; // ADC bits
int32_t alphad; // 0.01K units
} calibration;
#if sizeof(calibration) != INTTEMP_CALIBRATION_SIZE
#error Inconsistent calibration size
#endif
// Initialise module
void inttemp_init(void)
{
// arbitrary values
calibration.alphan = 171;
calibration.alphad = 290 * 10 + ZERO_DEGC_IN_CENTIK;
}
// Measure delta voltage across internal diode at two currents.
static uint16_t inttemp_deltav(void)
{
uint16_t v1, v2;
CTMUICON = 0x03; // 100x base current, 55uA
CTMUEN = 1; // enable CTMU
EDG1STAT = 1; // ADC output
v1 = adc_read(ADC_CHANNEL_DIODE);
EDG1STAT = 0; // disable output
CTMUEN = 0; // disable CTMU
CTMUICON = 0x02; // 10x base current, 5.5uA
CTMUEN = 1;
EDG1STAT = 1;
v2 = adc_read(ADC_CHANNEL_DIODE);
EDG1STAT = 0;
CTMUEN = 0;
CTMUICON = 0x00; // current source disabled
// v1 will always be > v2 due to higher current
return v1 - v2;
}
// Single point calibration to assumed ambient temperature
void inttemp_calibrate(int16_t temperature)
{
// alpha = delta diode voltage / temperature
calibration.alphan = inttemp_deltav();
calibration.alphad = ((int32_t)temperature * 10 + ZERO_DEGC_IN_CENTIK);
}
// Load a saved calibration
void inttemp_load_calibration(uint8_t *cal)
{
memcpy(&calibration, cal, INTTEMP_CALIBRATION_SIZE);
}
// Save the calibration
void inttemp_save_calibration(uint8_t *cal)
{
memcpy(cal, &calibration, INTTEMP_CALIBRATION_SIZE);
}
// Read current internal temperature
int16_t inttemp_read(void)
{
// temperature = delta diode voltage * (1 / alpha)
return ((int32_t)inttemp_deltav() * calibration.alphad / calibration.alphan - ZERO_DEGC_IN_CENTIK) / 10;
} |
_________________ Andrew |
|