Forums

C18 basic question

Started by Dennis September 12, 2011
Hi Guys, I'm pushing along learning C (using MPLAB + C18 on an 18LF14K22).

I've what I think is a pretty basic problem.....

I'm reading a 14 bit result back from a I2C sensor and have managed to get a 
sensible result stored in a variable called Data[], I've defined this as 
follows:


unsigned char Data[2];  //Data from I2C sensor

Data[0] ends up holding the high byte, Data[0] the low byte.




I need to used this in the following calculation where "Data" is the 14 bit 
raw sensor data consisting of Data[0]*16 + Data[1]:

Temp = 17572L*Data/65536 - 4685; //calc temperature using raw sensor data




What is the correct way to achieve this. It seems messy to convert the 
Data[] values using arithmetic. Is there a nice way ?

thanks
D.


[snip...]

> > Data[0] ends up holding the high byte, Data[0] the low byte. >
SORRY! the above line should have ended with "Data[1] holds the low byte" [snip....]
> > > thanks > D. > >
On 12/09/2011 4:40 PM, Dennis wrote:
> [snip...] > >> >> Data[0] ends up holding the high byte, Data[0] the low byte. >> > > SORRY! the above line should have ended with "Data[1] holds the low byte" > > [snip....] > >> >> >> thanks >> D. >> >>
There are many ways to skin this cat... One way to do it (use with caution as it's non-portable) is the concept of 'unions' which effectively overlays different data types so you input as one type (eg 2 byte array) and output as another (eg 16 bit integer). Its a pretty straightforward & I'm sure you'll find plenty of info on the interweb... HTH Chris.
> I need to used this in the following calculation where "Data" is the 14 bit > raw sensor data consisting of Data[0]*16 + Data[1]:
You probably mean Data[0] * 256 + Data[1] instead, or (Data[0] << 8) + Data[1], if your compiler isn't smart enough.
> > Temp = 17572L*Data/65536 - 4685; //calc temperature using raw sensor data > > What is the correct way to achieve this. It seems messy to convert the > Data[] values using arithmetic. Is there a nice way ?
I wouldn't worry about it. Just use multiply or shift, like above. The performance will likely be determined by your long multiply and divide anyway.
On Mon, 12 Sep 2011 09:16:27 +0200, Arlet Ottens wrote:

>> I need to used this in the following calculation where "Data" is the 14 >> bit raw sensor data consisting of Data[0]*16 + Data[1]: > > You probably mean Data[0] * 256 + Data[1] instead, or (Data[0] << 8) + > Data[1], if your compiler isn't smart enough. >
C18 may not automatically cast to integer type for the computation, so it's best to force it to be explicit: (((unsigned int)Data[0]) << 8) + Data[1], or if the right header is in there, (((uint32_t)Data[0]) << 8) + Data[1].
> >> Temp = 17572L*Data/65536 - 4685; //calc temperature using raw sensor >> data >> >> What is the correct way to achieve this. It seems messy to convert the >> Data[] values using arithmetic. Is there a nice way ? > > I wouldn't worry about it. Just use multiply or shift, like above. The > performance will likely be determined by your long multiply and divide > anyway.
Note that the above calculations will have problems with underflow -- any 16-bit unsigned, divided by 65536, will equal 0 with a large remainder. Since the compiler doesn't enforce order per the standard, you need to: Temp = (17572L*Data)/65536 - 4685 Note that I would do: Temp = (17572L * (long int)Data)/65536L - 4685L just to make sure that the compiler didn't get too clever on me. Consider also that temperatures vary slowly: if you have enough memory, you may well have the time to do everything in floating point after you collect the data. That's your design decision to make, however. -- Tim Wescott Control system and signal processing consulting www.wescottdesign.com
On 09/12/2011 09:31 AM, Tim wrote:
> On Mon, 12 Sep 2011 09:16:27 +0200, Arlet Ottens wrote: > >>> I need to used this in the following calculation where "Data" is the 14 >>> bit raw sensor data consisting of Data[0]*16 + Data[1]: >> >> You probably mean Data[0] * 256 + Data[1] instead, or (Data[0]<< 8) + >> Data[1], if your compiler isn't smart enough. >> > C18 may not automatically cast to integer type for the computation, so > it's best to force it to be explicit:
According to the user guide, if you use the -Oi command line option, the C18 compiler will follow standard integer promotion rules. For a novice programmer, I'd recommend enabling this option. I think Microchip should have made that the default, and implemented a command line option to deviate from the standard, or made their compiler smart enough to recognize where this optimization would be safe.
"Chris" <c@b.a> wrote in message 
news:D%hbq.2259$7r4.1344@viwinnwfe02.internal.bigpond.com...
> On 12/09/2011 4:40 PM, Dennis wrote: >> [snip...] >> >>> >>> Data[0] ends up holding the high byte, Data[0] the low byte. >>> >> >> SORRY! the above line should have ended with "Data[1] holds the low byte" >> >> [snip....] >> >>> >>> >>> thanks >>> D. >>> >>> > > There are many ways to skin this cat... > > One way to do it (use with caution as it's non-portable) is the concept of > 'unions' which effectively overlays different data types so you input as > one type (eg 2 byte array) and output as another (eg 16 bit integer). > > Its a pretty straightforward & I'm sure you'll find plenty of info on the > interweb... > > HTH > Chris. >
Thanks Chris - I'll go and look into unions.
"Arlet Ottens" <usenet+5@c-scape.nl> wrote in message 
news:4e6db1cb$0$2515$e4fe514c@news2.news.xs4all.nl...
> >> I need to used this in the following calculation where "Data" is the 14 >> bit >> raw sensor data consisting of Data[0]*16 + Data[1]: > > You probably mean Data[0] * 256 + Data[1] instead, or (Data[0] << 8) + > Data[1], if your compiler isn't smart enough. >
Ah - yes I did mean *256.
>> >> Temp = 17572L*Data/65536 - 4685; //calc temperature using raw sensor data >> >> What is the correct way to achieve this. It seems messy to convert the >> Data[] values using arithmetic. Is there a nice way ? > > I wouldn't worry about it. Just use multiply or shift, like above. The > performance will likely be determined by your long multiply and divide > anyway.
I'll try it that way. Thanks
"Arlet Ottens" <usenet+5@c-scape.nl> wrote in message 
news:4e6db854$0$2479$e4fe514c@news2.news.xs4all.nl...
> On 09/12/2011 09:31 AM, Tim wrote: >> On Mon, 12 Sep 2011 09:16:27 +0200, Arlet Ottens wrote: >> >>>> I need to used this in the following calculation where "Data" is the 14 >>>> bit raw sensor data consisting of Data[0]*16 + Data[1]: >>> >>> You probably mean Data[0] * 256 + Data[1] instead, or (Data[0]<< 8) + >>> Data[1], if your compiler isn't smart enough. >>> >> C18 may not automatically cast to integer type for the computation, so >> it's best to force it to be explicit: > > According to the user guide, if you use the -Oi command line option, the > C18 compiler will follow standard integer promotion rules. For a novice > programmer, I'd recommend enabling this option. > > I think Microchip should have made that the default, and implemented a > command line option to deviate from the standard, or made their compiler > smart enough to recognize where this optimization would be safe.
Thanks Arlet - I will do that. The more traps for the novice I can remove the better!
"Tim" <tim@seemywebsite.please> wrote in message 
news:19OdnQX0Q7vDKPDTnZ2dnUVZ_tKdnZ2d@web-ster.com...
> On Mon, 12 Sep 2011 09:16:27 +0200, Arlet Ottens wrote: > >>> I need to used this in the following calculation where "Data" is the 14 >>> bit raw sensor data consisting of Data[0]*16 + Data[1]: >> >> You probably mean Data[0] * 256 + Data[1] instead, or (Data[0] << 8) + >> Data[1], if your compiler isn't smart enough. >> > C18 may not automatically cast to integer type for the computation, so > it's best to force it to be explicit: > > (((unsigned int)Data[0]) << 8) + Data[1], or if the right header is in > there, > (((uint32_t)Data[0]) << 8) + Data[1]. > >> >>> Temp = 17572L*Data/65536 - 4685; //calc temperature using raw sensor >>> data >>> >>> What is the correct way to achieve this. It seems messy to convert the >>> Data[] values using arithmetic. Is there a nice way ? >> >> I wouldn't worry about it. Just use multiply or shift, like above. The >> performance will likely be determined by your long multiply and divide >> anyway. > > Note that the above calculations will have problems with underflow -- any > 16-bit unsigned, divided by 65536, will equal 0 with a large remainder. > Since the compiler doesn't enforce order per the standard, you need to: > > Temp = (17572L*Data)/65536 - 4685 > > Note that I would do: > > Temp = (17572L * (long int)Data)/65536L - 4685L > > just to make sure that the compiler didn't get too clever on me. > > Consider also that temperatures vary slowly: if you have enough memory, > you may well have the time to do everything in floating point after you > collect the data. That's your design decision to make, however. > > -- > Tim Wescott > Control system and signal processing consulting > www.wescottdesign.com
Thanks Tim. I wasn't aware of the issue of the potential overflow. At some stage I was going to try and write some test code to try and simulate all possible values and export the result from MPLAB to check for error modes I was not aware of. I probably could use floating point - maybe that is the next step. Readings are likely to be taken on intevals of at least 10s of seconds so time is not really an issue!