Constantin Legras
Constantin Legras

Reputation: 3

Equation seems to be outputting wrong value in embedded C (stm32)

Hope you're having a nice day.

I'm encountering a weird issue on my side. I am working on embedded C code on an STM32 F103 C8T6 micro controller on a custom BMS PCB, but I am having some issue with the code that calculates the actual temperature from the thermistor ADC value.

Through excel, we have determined that the equation we need to use to calculate the temperature in Celsius from the ADC value is: y = -0.5022x^5 + 6.665x^4 - 35.123x^3 + 92.559x^2 - 144.22x + 166.76.

So, in my code I have the following lines, with temp[i] being the raw ADC value and realTemp[i] being the converted value:

realTemp[i] = (double)(temp[i] / 10000);
realTemp[i] = -0.5022 * realTemp[i]*realTemp[i]*realTemp[i]*realTemp[i]*realTemp[i] + 6.665 * realTemp[i]*realTemp[i]*realTemp[i]*realTemp[i] - 35.123 * realTemp[i]*realTemp[i]*realTemp[i] + 92.559 * realTemp[i]*realTemp[i] - 144.22 * realTemp[i] + 166.76;

I am not using the pow function from math.h as it has given us issues in the past.

The values we are getting in our temp[i] variable are the following: 35480, 35496, 35393, 35480. When using these values with our function in excel, we are getting the correct output, between 25.3 and 25.5 Celsius, however the C code listed above is outputting 36 in the realTemp array. I am not sure about the decimal values, but I don't care about them because the value is typecast to a uint16 a few lines later to be transmitted over a CAN bus.

Upvotes: 0

Views: 316

Answers (3)

David
David

Reputation: 162

As others have said, you are doing integer division then casting the result to a double - you need to do the division itself as a double.

Your code will be big and very slow on the micro-controller in question. This might not be an issue, assuming that temperature values don't usually change very often, so slow code could be fine for you.

You also need to be careful with high-degree polynomials - they can easily be unstable, especially if you try to extrapolate them to very high or low temperatures. This is a particularly risky if you decide to make the code faster by switching to a float.

A better method of this kind of thing is usually a lookup table (which can be big but is simpler to implement), or with a linear spline (which has smaller footprint but a bit more complex to implement).

Upvotes: 0

ThongDT
ThongDT

Reputation: 189

The answer by Chux is correct, I just wanted to explain more why this works.

temp[i] is uint16, therefore the formula temp[i] / 10000 is integer division, and result will be the floor of (temp[i] / 10000). Thus, the final conversion to double is performed on a value which is floored already.

By converting 10000 to 10000.0, it means that the division of an integer with a float/double will perform floating division. By this, the result will be similar to what you expected.

Upvotes: 0

chux
chux

Reputation: 153557

Use floating point division, not integer division.

// Integer division ------v-------------v       
// realTemp[i] = (double)(temp[i] / 10000);
realTemp[i] = temp[i] / 10000.0;

Upvotes: 4

Related Questions