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.
C18 basic question
Started by ●September 12, 2011
Reply by ●September 12, 20112011-09-12
[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. > >
Reply by ●September 12, 20112011-09-12
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.
Reply by ●September 12, 20112011-09-12
> 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.
Reply by ●September 12, 20112011-09-12
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
Reply by ●September 12, 20112011-09-12
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.
Reply by ●September 12, 20112011-09-12
"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.
Reply by ●September 12, 20112011-09-12
"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
Reply by ●September 12, 20112011-09-12
"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!
Reply by ●September 12, 20112011-09-12
"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.comThanks 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!