# C18 basic question

Started by 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!

```