MCP9700A ADC reading to Temp conversion without floats-How? - Page 1
Forum section: electronics / general

author: djsb
author_rank: Super Contributor
timestamp: February 14, 2025, 09:46:41 pm
content: 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 farCode:void my_function(void){setup_oscillator(OSC_8MHZ); //Set clock speedsetup_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 PIC16F1847const float LSB_FIVE = 0.0048875; //Using 10 bit ADC of PIC16F1847const 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.

author: Ian.M
author_rank: Super Contributor
timestamp: February 14, 2025, 11:25:10 pm
content: For 3.3V Vdd, try:Code:temperature=(((adc_value-155)*165+127)/2)>>8;

author: mikerj
author_rank: Super Contributor
timestamp: February 15, 2025, 12:04:17 am
content: 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:int16_t temperature;temperature = ((adc*40038)/8192)-500;Fixed point - 3 fractional bitsCode: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!

author: fchk
author_rank: Frequent Contributor
timestamp: February 15, 2025, 08:59:57 am
content: Quote from: djsb on February 14, 2025, 09:46:41 pmThe 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.

author: djsb
author_rank: Super Contributor
timestamp: February 15, 2025, 09:18:18 am
content: Quote from: fchk on February 15, 2025, 08:59:57 amThanks. 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?

author: fchk
author_rank: Frequent Contributor
timestamp: February 15, 2025, 09:42:41 am
content: ok.FVR SetupCode:FVRCON=0;FVRCONbits.ADFVR=2; // ADC FVR=2.048VFVRCONbits.FVREN=1; // Enable FVRwhile (!FVRCONbits.FVRRDY); // wait untio FVR is ready and voltage is stableADC SetupCode: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 GNDADCCON1bits.AFPREF=3; //VREF+ connected to FVR module (2.048V)//... here further ADC initialisations ...ADCCON0bits.ADCON=1; //enable ADCReading the ADC result in mVCode:uint16_t adcres=(ADCRESH<<8)|ADCRESL; // ADC Countsadcres<<=1; // now you have mV unitsint16_t temp=(int16_t)adcres;temp-=500; // offset 500mV for 0°C// now you have units of 0.1°Ctemp/=10; // now you have °C

author: Ian.M
author_rank: Super Contributor
timestamp: February 15, 2025, 10:57:42 am
content: 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.

author: fchk
author_rank: Frequent Contributor
timestamp: February 15, 2025, 11:33:18 am
content: ok, I haven't checked this. If you need something better, use this on the external VREF+https://www.ti.com/lit/ds/symlink/lm4040-n.pdfhttps://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP1501-Data-Sheet-DS20005474F.pdffchk

author: Ian.M
author_rank: Super Contributor
timestamp: February 15, 2025, 12:28:28 pm
content: 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.