Author Topic: MCP9700A ADC reading to Temp conversion without floats-How?  (Read 1745 times)

0 Members and 1 Guest are viewing this topic.

Offline djsbTopic starter

  • Super Contributor
  • ***
  • Posts: 1114
  • Country: gb
MCP9700A ADC reading to Temp conversion without floats-How?
« on: February 14, 2025, 09:46:41 pm »
How do I convert the voltage output of an MCP9700A temperature sensor using the 10 bit ADC of a PIC16F1847 MCU without using floating point numbers? How can I use scaled integers if this is a faster and more resource efficient use of the MCU?

I have a code snippet here of what I've been doing so far

Code: [Select]
void my_function(void)
 {
setup_oscillator(OSC_8MHZ); //Set clock speed
setup_vref(VREF_ON|VREF_ADC_2v048); // 1v024,2v048,4v096 available. Using 2V as max output of MCP9700A is 1.75 Volts. Experiment to observe effects of changing the reference voltage. Try LSB_TWO_V first.
setup_adc(ADC_CLOCK_INTERNAL);
setup_adc_ports(ALL_ANALOG);
set_adc_channel(0);
delay_us(10);
//unsigned long value;
float adc_value = 0;
float voltage = 0;
float Temperature = 0; // MCP9700A Temp range -40 to +125 centigrade (100mV to 1.75V output).
const float DC_OFFSET = 0.5;
const float LSB_THREE = 0.0032258;//Using 10 bit ADC of PIC16F1847
const float LSB_FIVE = 0.0048875; //Using 10 bit ADC of PIC16F1847
const float LSB_ONE_V = 0.004;
const float LSB_TWO_V = 0.008;
const float LSB_FOUR_V = 0.016;



//setup_wdt(WDT_ON|WDT_1S);          // Wakeup every second.

//while(TRUE)
// {
    adc_value=read_adc();
    Temperature = (((adc_value * LSB_FIVE) - DC_OFFSET)) * 100; // I'm using 5v supply. If using 3.3v change to LSB_THREE. Try the others      listed                     
                                                                //LSB_TWO_V first.
    voltage = adc_value * (5.0 / 1023);
    printf("\r\nMCP9700A Voltage: %f", voltage);
    printf("\r\nADC count is: %f",adc_value);
    printf("\r\nCalculated temperature is: %f",Temperature);
    delay_ms(500);
    //Not figured out how this stuff (below) works yet.
    //restart_wdt();// Restart watchdog at this point.
    //Log_Data();   // Call your data logging function. Got to write it first though.
    //printf("\r\nHello world");
   //sleep();      // Go to sleep.

// }
}

The MCP9700A has an accuracy of 2% so its a waste to try for any readings that are TOO accurate. Any help appreciated. Thanks.
« Last Edit: February 14, 2025, 09:49:58 pm by djsb »
David
Hertfordshire, UK
University Electronics Technician, London, PIC16/18, CCS PCM C, Arduino UNO, NANO,ESP32, KiCad V10+, Altium Designer 21.4.1, Alibre Design Expert 28 & FreeCAD beginner. LPKF S103,S62 PCB router Operator, Electronics instructor. Credited KiCad French to English translator.
 

Online Ian.M

  • Super Contributor
  • ***
  • Posts: 14084
Re: MCP9700A ADC reading to Temp conversion without floats-How?
« Reply #1 on: February 14, 2025, 11:25:10 pm »
For 3.3V Vdd, try:
Code: [Select]
temperature=(((adc_value-155)*165+127)/2)>>8;
« Last Edit: February 14, 2025, 11:27:04 pm by Ian.M »
 
The following users thanked this post: djsb

Online mikerj

  • Super Contributor
  • ***
  • Posts: 3659
  • Country: gb
Re: MCP9700A ADC reading to Temp conversion without floats-How?
« Reply #2 on: February 15, 2025, 12:04:17 am »
If you don't want to use floats, how would you prefer the temperature to be represented as an integer value?  e.g. scaled by 10 (to give 0.1C resolution) so 50.3C = 503, or a fixed point value using some number of bits to represent the fractional part? e.g. using 3 bits gives 0.125C resolution, so 50.3C would give 50<<3|(0.3/0.125) = 402.
Scaling by a power of ten can useful for display purposes, but representing as a fixed point value makes it faster to apply any further calculations.

Scaled by ten.  Keeping the divisor a power of two should result in a shift operation rather than a divide.
Code: [Select]
int16_t temperature;
temperature = ((adc*40038)/8192)-500;

Fixed point - 3 fractional bits
Code: [Select]
int16_t temperature;
temperature = ((adc*64061)/16384)-400;

These values are based on your original floating point multiplier, I haven't checked if that is correct!
 
The following users thanked this post: djsb

Offline fchk

  • Frequent Contributor
  • **
  • Posts: 392
  • Country: de
Re: MCP9700A ADC reading to Temp conversion without floats-How?
« Reply #3 on: February 15, 2025, 08:59:57 am »
How do I convert the voltage output of an MCP9700A temperature sensor using the 10 bit ADC of a PIC16F1847 MCU without using floating point numbers? How can I use scaled integers if this is a faster and more resource efficient use of the MCU?

The MCP9700A can output a voltage of 500mV + 10mv/°C*125°C = 1.75V. Your PIC has got a FVR Fixed Voltage Reference of 2.048V or 4.096V. If you set the FVR to 2.048V and use it as VREF each count of the ADC equals 2.048V/2^10 = 0.002V. So left shift the right justified ADC count by one bit and you get the voltage in mV. Easy?

Knowing that 10mV is 1°C you can divide by 10 and subtract the offset of 50 and have your °C. Or skip the costly division and work with units of 0.1°C.
« Last Edit: February 15, 2025, 09:05:17 am by fchk »
 
The following users thanked this post: djsb

Offline djsbTopic starter

  • Super Contributor
  • ***
  • Posts: 1114
  • Country: gb
Re: MCP9700A ADC reading to Temp conversion without floats-How?
« Reply #4 on: February 15, 2025, 09:18:18 am »
How do I convert the voltage output of an MCP9700A temperature sensor using the 10 bit ADC of a PIC16F1847 MCU without using floating point numbers? How can I use scaled integers if this is a faster and more resource efficient use of the MCU?

The MCP9700A can output a voltage of 500mV + 10mv/°C*125°C = 1.75V. Your PIC has got a FVR Fixed Voltage Reference of 2.048V or 4.096V. If you set the FVR to 2.048V and use it as VREF each count of the ADC equals 2.048V/2^10 = 0.002V. So left shift the right justified ADC count by one bit and you get the voltage in mV. Easy?

Knowing that 10mV is 1°C you can divide by 10 and subtract the offset of 50 and have your °C. Or skip the costly division and work with units of 0.1°C.

Thanks. Could you rephrase your reply with a little pseudocode/example code, perhaps? I don't fully understand the "left shift right justified ADC count by one bit" part of your reply. I also don't fully understand the last paragraph either?
« Last Edit: February 15, 2025, 09:26:40 am by djsb »
David
Hertfordshire, UK
University Electronics Technician, London, PIC16/18, CCS PCM C, Arduino UNO, NANO,ESP32, KiCad V10+, Altium Designer 21.4.1, Alibre Design Expert 28 & FreeCAD beginner. LPKF S103,S62 PCB router Operator, Electronics instructor. Credited KiCad French to English translator.
 

Offline fchk

  • Frequent Contributor
  • **
  • Posts: 392
  • Country: de
Re: MCP9700A ADC reading to Temp conversion without floats-How?
« Reply #5 on: February 15, 2025, 09:42:41 am »
ok.
FVR Setup
Code: [Select]
FVRCON=0;
FVRCONbits.ADFVR=2; // ADC FVR=2.048V
FVRCONbits.FVREN=1; // Enable FVR
while (!FVRCONbits.FVRRDY); // wait untio FVR is ready and voltage is stable

ADC Setup
Code: [Select]
ADCCON1=0;
ADCCON2=0;
ADCCON1bits.ADFM=1; // result is in bits 9...0
/* see the data sheet description:
1 = Right justified. Six Most Significant bits of ADRESH are set to ‘0’ when the conversion result is loaded.
0 = Left justified. Six Least Significant bits of ADRESL are set to ‘0’ when the conversion result is loaded.
*/
ADCCON1bits.AFNREF=0; //VREF- is GND
ADCCON1bits.AFPREF=3; //VREF+ connected to FVR module (2.048V)
//... here further ADC initialisations ...
ADCCON0bits.ADCON=1; //enable ADC

Reading the ADC result in mV
Code: [Select]
uint16_t adcres=(ADCRESH<<8)|ADCRESL; // ADC Counts
adcres<<=1; // now you have mV units

int16_t temp=(int16_t)adcres;
temp-=500; // offset 500mV for 0°C
// now you have units of 0.1°C
temp/=10; // now you have °C
 
The following users thanked this post: djsb

Online Ian.M

  • Super Contributor
  • ***
  • Posts: 14084
Re: MCP9700A ADC reading to Temp conversion without floats-How?
« Reply #6 on: February 15, 2025, 10:57:42 am »
Fchk's proposal is technically neat as it avoids any scaling (other than by a power of 2) so without any arbitrary multiplication or division is  computationally extremely light (important for a slowish 8 bit MCU without a hardware multiplier), but there is one major fly in the ointment: The PIC16F1847 FVR is an essentially uncalibrated piece of crap, with accuracy* of -8%,+6% when used as the ADC reference.   The ADC module's worst case inherent accuracy is about +/-1% midrange (its actually expressed as +/- LSB counts so the percentage varies with the reading) and  as your MCP9700A has an accuracy of 2%, if all the tolerances stack up unfavourably your measured temperature could have 11% error#, so you cant even trust the tens digit of the result!   Also there is a lack of data for the FVR's stability with respect to temperature and time, so its hard to anticipate how bad the error is likely to be and how much drift you may expect.  Also, as there is no scaling you cant even calibrate out the error, though you can adjust the offset to improve the accuracy at a single temperature.

As 1% 3.3V LDO regulators are readily available and reasonably affordable, using Vdd as the reference with appropriate scaling will give you worst case +/-4% temperature accuracy.  You can probably improve this by a factor of two or more with calibration, as both the slope and offset constants can be trimmed (and probably stored in the MCU's EEPROM), at the cost of the scaling possibly optimising less well as the multiplication is by a variable not a compile-time constant.

* Extract from PIC16(L)F1847 datasheet DS20001942G, TABLE 30-1: SUPPLY VOLTAGE attached.

# It is of course more complicated than that - the MCP9700A is specified as +/-2°C (max.), 0°C to +70°C, and the ADC similarly specifies its accuracy in LSB counts so expressing the error as a percentage (of what?, possibly the sensor's rated operating range) isn't that useful.  You'd need to do the math and work out the worst case error band in °C at several temperatures of interest to see if its really as bad as it seems to be.
 
The following users thanked this post: djsb

Offline fchk

  • Frequent Contributor
  • **
  • Posts: 392
  • Country: de
 
The following users thanked this post: djsb

Online Ian.M

  • Super Contributor
  • ***
  • Posts: 14084
Re: MCP9700A ADC reading to Temp conversion without floats-How?
« Reply #8 on: February 15, 2025, 12:28:28 pm »
The poor accuracy and annoying scaling make the use of low-cost analog temperature sensor ICs with a MCU hard to justify.  You can get digital temperature sensor ICs with direct readout in °C at a comparable price point  with the same or greater accuracy as the analog sensor and don't have to deal with other sources of error.  The down-side is of course digital signalling which may be unattractive due to EMI and less suitable for long-distance remote sensing.
 
The following users thanked this post: djsb


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf