two bytes, one integer......

Go To Last Post
47 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Greets Freaks!

 

I am combining a two byte ADC output into a single integer.  As I do not do much programming anymore I had to think about how to do this....and a little help from teh internet to jog the memory cells into place.

 

Two examples were:

int i = ((adc_high_byte & 0xFF) << 8) | (adc_low_byte & 0xFF);

Ok, this one looked familiar from projects past.

 

But then I see this one:

int i = (adc_high_byte << 8) + adc_low_byte;

Which in theory should give me the same result.

 

Heres the question(s)

Why do I need to AND the byte against a mask THEN shift left in the first example?  THe second one seems far simpler(thus faster).

 

Cheerio!

JIm

This topic has a solution.

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

Last Edited: Thu. Dec 10, 2020 - 02:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

are the adc_high_byte and adc_low_byte both really just single-byte variables; ie, uint8_t ?

 

If they are, then ANDing them with 0xFF has no effect - so can be omitted.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil wrote:

are the adc_high_byte and adc_low_byte both really just single-byte variables; ie, uint8_t ?

 

If they are, then ANDing them with 0xFF has no effect - so can be omitted.

Yes, they are uint8_t.  I thought the AND had no effect.  Why would you do it to begin with?

 

JIm

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgmdesign wrote:

int i = ((adc_high_byte & 0xFF) << 8) | (adc_low_byte & 0xFF);

 

This only makes sense when adc_high_byte and adc_low_byte are uint16_t variables. ANDING a uint16_t variable with 0xFF gives you the least significant byte.

Last Edited: Tue. Dec 8, 2020 - 06:34 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgmdesign wrote:
Why would you do it to begin with?

Habit?

 

Because the code was copied from somewhere where it did matter?

 

Maybe like people put in extra brackets to be sure - even thought they not strictly required?

 

can you tell that I'm just guessing ?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Heisen wrote:
This only makes sense when adc_high_byte and adc_low_byte are uint16_t variables

or larger.

 

and it would still be superfluous if the high byte(s) were not actually being used.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The ADC high and low registers can shift.  If the ADLAR bit is 1 then the ADCH will have the highest 8 bits of the 10-bit ADC result.  Your formula could result in these bits becoming bits 15-8 of a 16-bit value, instead of bits 9-2 on a 10-bit value.

 

  Use a $3 Arduino Nanohttps://www.ebay.com/itm/Good-De... that plugs into and programs through your PC USB port.  Then you can just read the ADCs with : analogRead(0);

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Simonetta wrote:
The ADC high and low registers can shift.  If the ADLAR bit is 1 then the ADCH will have the highest 8 bits of the 10-bit ADC result. 

 

Where did I say I was using the AVR's ADC?

 

I wrote:

jgmdesign wrote:
I am combining a two byte ADC output into a single integer.

 

Simonetta wrote:
Use a $3 Arduino Nanohttps://www.ebay.com/itm/Good-De... that plugs into and programs through your PC USB port.  Then you can just read the ADCs with : analogRead(0);

For what? 

 

Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

You can also do 

 

my_num = 256*highbyte + lowbyte, which gives the same result, but it's easier to understand.

 

or   

 

my_24bitter = 65535*byte2 + 256*byte1 + byte0     for 24 bits

 

As us digital guys say....don't get bit

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Tue. Dec 8, 2020 - 07:31 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jim,   if both items are uint8_t you can say:

uint16_t i = (adc_high_byte << 8) | adc_low_byte;

or equally well:

uint16_t i = (adc_high_byte << 8) + adc_low_byte;

You can go seriously wrong with signed variables because the sign bit will be extended

Unsigned is no problem.   uint8_t 0b10000000 extends to uint16_t 0b0000000010000000 i.e. 128

but int8_t 0b10000000 extends to int16_t 0b1111111110000000 i.e.  -128

 

The moral of the story is:   use unsigned variables when you when you have multi-byte values coming from the outside world.

Build the value with shifts and OR.

 

Then study the particular datasheet for this external device.    If the "bit-sequence" you have built represents a signed value you simply cast as signed.

 

David.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A union ? ;-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

clawson wrote:

A union ? ;-)

No, I'm sure Jim is strictly non-union! devil

 

Keys to wealth:

Invest for cash flow, not capital gains!

Wealth is attracted, not chased! 

Income is proportional to how many you serve!

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

avrcandies wrote:
my_num = 256*highbyte + lowbyte, ...  it's easier to understand

not so sure about "easier to understand" ?

 

By writing the shift, you show explicitly that you are just taking a block of 8 bits, and putting them into the top byte.

 

I think a multiplication rather "hides" that.

 

YMMV.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ki0bk wrote:

clawson wrote:

A union ? ;-)

No, I'm sure Jim is strictly non-union! devil

LMAO!

 

clawson wrote:

A union ? ;-)

I am always open for an education....I'll go look in my K/R when I return later tonight from pulling network cable.

 

JIm

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Since I don't know (and wouldn't bother to look it up) whether a uint8_t would be promoted before the << 8, I would cast it first for safety.

 

uint16_t i = ((uint16_t)adc_high_byte << 8) + adc_low_byte;

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The only casting I am any good at involves a rod, a reel, a fish, and my 11 year old.

Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This sound like "one country, 2 systems", can't remember where I heard that. devil

John Samperi

Ampertronics Pty. Ltd.

https://www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgmdesign wrote:
The only casting I am any good at involves a rod, a reel, a fish, and my 11 year old.
If you are intent on casting your 11 year old, cast him southwest, I'll take him! wink devil

David

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

not so sure about "easier to understand" ?

Oh certainly can be, if you are good in hex

 

say you have a two byte number:

0x0A26   (0x0A,26)   that's  10*256 +32+6 =2598 , I did it in my head in a few seconds, while you are still getting shifty with the local bits. cheeky

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Wed. Dec 9, 2020 - 03:41 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgmdesign wrote:
I am always open for an education....I'll go look in my K/R when I return later tonight from pulling network cable.

typedef union {
    uint16_t combined;
    uint8_t adcReading[2];
} joined_t;

joined_t join;

int main(void) {
    join.adcReading[0] = readADC();
    join.adcReading[1] = readADC();
    printf("combined they are %04X\n", join.combined);
}

Of course this does raise the question of "endianism". Say the first reading is 0xBC and the second if 0x19 then will "combined" be 0xBC19 or 0x19BC ? The difference between this and (reading_1 << 8) | reading_2 is that in that case you KNOW the answer will be 0xBC19 - you aren't at the mercy of how the compiler happens to choose to layout a uint16_t in memory.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Of course this does raise the question of "endianism"

That brings up a very excellent & often overlooked point! In this post, the high & low bytes were assumed to be already known, so no problemo.   Of course, when you don't know (or overlook)--that has sunk a few ships!   It's almost as bad as some chips (such as DAC's) shift in msb first, and others lsb first....just to keep us our toes.   Things seems to "work" but the results are chaotic indeed.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Where did I say I was using the AVR's ADC?

if that isn't given then we will need to know more! (signed unsigned 2.complement or ....   endian ... )

 

But with the build in ADC of AVR's the GCC compiler have a 16bit ADC read, so you should not need to think (next level : make sure it's atomic etc.)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jim is just being American !

 

As a general rule you treat all SFRs as unsigned.    And post-process the results.

e.g. you read SPI as uint8_t

e.g. you read TWI as uint8_t

 

you even read USART as uint8_t but subsequently cast as char

 

I would put money on Jim's ADC using either SPI or I2C but it might be OneWire.

 

Whatever is used he has to read the specific datasheet to see how to interpret the reading.

e.g. most SPI devices are msb first (most significant bit).     SPI bytes might be MSB first (bigendian) or Least Significant Byte first.    MSB is more common.

I2C devices are msb first

UART devices are lsb first

 

The datasheet will explain how a Sign bit might be handled.

 

Although it might be fun claiming his First Amendment rights it makes answers almost impossible.

If you quote a real part number and a real application you can get a clear answer.

 

If you choose to obfuscate,   you get a load of ifs, might, most and probablys because we can only guess.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

my_num = 256*highbyte + lowbyte, ...  it's easier to understand
not so sure about "easier to understand" ?

By writing the shift, you show explicitly that you are just taking a block of 8 bits, and putting them into the top byte.

Hopefully the compiler itself uses neither shifting nor multiplying here, since neither is needed.   In terms of knowing the numerical result, of course you can pick up a calculator & mult by 256, much easier than trying to be shifty about it.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Nowadays you just write something that is semantically correct.    Choose whatever style you are happiest with.

 

I am fairly confident that the Compiler will generate exactly the same efficient code.

There is not much point in arguing for x 256 or << 8

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's generally best to use a style which makes the programmer's intent clearest.

 

The fundamental problem with that, of course, is that different programmers may find different styles "clearer".

 

the Compiler will generate exactly the same efficient code

Indeed.

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:
if that isn't given then we will need to know more! (signed unsigned 2.complement or ....   endian ... )

 

Not really.  I could have simply wrote I needed to combine two bytes into one integer...

 

Granted, maybe I should have indicated signed or unsigned, but thats it.

 

Cheers,

Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Of course this does raise the question of "endianism". Say the first reading is 0xBC and the second if 0x19 then will "combined" be 0xBC19 or 0x19BC ? The difference between this and (reading_1 << 8) | reading_2 is that in that case you KNOW the answer will be 0xBC19 - you aren't at the mercy of how the compiler happens to choose to layout a uint16_t in memory.

 

So Cliff, what you are saying is that with the Union I wouldnt know how the data is going to be organised?  Thats a problem.

 

Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jim here is an example of using a union:

#include <stdint.h>

union
{
    struct adc_tag
    {
        uint8_t adc_low_byte;   //can swap bytes here by changing order of high/low
        uint8_t adc_high_byte;
    } adc_dat;
    uint16_t adc_val;
} adc_u;

int main(void)
{
    volatile uint16_t p;
    // Insert code

    do
    {
    /* store adc bytes into structure */
        adc_u.adc_dat.adc_low_byte = 0x55;
        adc_u.adc_dat.adc_high_byte = 0xAA;
    /* retreive adc as integer value */
        p = adc_u.adc_val; //p = 0xAA55
    }
    while(0);

    return 0;
}

Hope that helps, note by changing the order of the high/low bytes in the structure the bytes can be swapped when read as the integer value.

Jim

 

 

Keys to wealth:

Invest for cash flow, not capital gains!

Wealth is attracted, not chased! 

Income is proportional to how many you serve!

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well it's not like the Endianism switches on every last Thursday in a month that has a Y in it. It's fixed for a given CPU/compiler.

 

What's kind of nice about AVR is that I think it's all the C compilers use little Iendian which just happens to be what pretty much all the compilers on all PCs use so if in my example reading[0] goes to the low byte and reading to the high byte then you send the 16 gits to a PC and unwind it in the same way you should get the readings in the same order. But at the end of the day it's  (n << 8)|m that gives you guaranteed control.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

awneil wrote:
The fundamental problem with that, of course, is that different programmers may find different styles "clearer".

This is clearly the case.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

if you want fast C code only for AVR then make a uint8_t variable in R2 and one in R3, and then also a uint16_t in R2.

it's in general the same as a union but this way you probably avoid using loading to R25:R24 first. 

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@Cliff and ki0bk,

Unions and Structures have always been a struggle for me and both of your examples make no sense on how you get an integer from the two bytes.  In my reading on how unions are accessed only confuses me more so I think I will stick with the solution I have in posts 1 and 2, but I will do more research on Structures and unions as they are probably far more eloquent in many applications.

 

JIm

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm with you Jim, it took me more then a few minutes to get that example working correctly. 

In essence, a union says use the same memory locations for these variables (same ram location) so you store your two bytes in the structure, and read them out as an integer from the same location. 

So your code stores two bytes and reads one integer, so two ST's and one LD, but of course the compiler may just pick two adjacent registers for this!

For this use, it's overkill, where I use it is when I have a buffer of bytes from a packet of data, and use the union to access the individual bytes/words for the data it contains, makes life easy then.

Jim

 

 

Keys to wealth:

Invest for cash flow, not capital gains!

Wealth is attracted, not chased! 

Income is proportional to how many you serve!

 

Last Edited: Thu. Dec 10, 2020 - 03:19 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Surely the point of a High Level Language is to write clear simple code that you can understand (now and in years to come)

 

It needs to be semantically correct.    It needs to work.

 

Size and speed is seldom critical but correct function is very important (tm).

 

David.

Last Edited: Thu. Dec 10, 2020 - 03:30 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgmdesign wrote:
I will do more research on Structures and unions as they are probably far more eloquent in many applications.

 

I have used them to manage a messaging bitstream where a number of different messages share a common header but completely different data structures: Each message type has a structure describing it; the union is a union of all the possible messages. The reception protocol sorts out getting the message and making sure it's valid - for which it needs to know nothing about the structure contents - and then individual messages parts can be accessed using

union_name->structure_name.field_name  // when passed a pointer to the union

without having to parse each message individually. It's the same mechanism as stacking two 8-bit variables in the same 16-bit union, and has all the same issues regarding endian-ness and packing; the latter can be particularly headache inducing if the compiler generating the messages isn't the same as the one receiving them (Windows sender to IAR ARM receiver in my case) - there's a reason unions are banned in MISRA compliant code.

 

Neil

Last Edited: Thu. Dec 10, 2020 - 03:45 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


jgmdesign wrote:
Unions and Structures have always been a struggle for me and both of your examples make no sense on how you get an integer from the two bytes. 

Jim it's really simpler than you might imagine. A union is just "two types of variable on top of one another" (and before the purists tell me "no it isn't" bear with me as that is the easiest way to imagine it). So if I simply type:

union {
    int n;
    char bytes[2];
} foo;

I am creating a "combined variable" that occupies two bytes and those two bytes can be identified as either foon to write to the full 16 bit (2 byte) item in one go:

foo.n = 12345;

but if you look at 12345 in hex it is 0x3039. So in memory there is either:

location of "foo":    0x30
location of "foo" +1: 0x39

or the compiler might have chosen to store it as:

location of "foo":    0x39
location of "foo" +1: 0x30

The first would be "big endian" and the second is "little endian" (and is what AVR compilers would use).

 

But in the definition of the union we said that these two locations would either be treated as one int or two chars with names bytes[0] and bytes[1]. So we could read things back out that way. If you said:

char bar = foo.bytes[0];

then bytes[0] will be located at the same byte as the lower byte of foo.n so bar will get the value 0x39. Reading foo.bytes[1] would return 0x30 - the upper byte.

 

Or you can work this backwards - if you want to write the bytes and read the int then:

foo.bytes[0] = 0xBC;
foo.bytes[1] = 0x47;

int result = foo.n;

So after the first two writes:

foo  : 0xBC
foo+1: 0x47

now when that is read to "result" from foo.n it will read 0x47BC which is 18,364.

 

There is a little complication in what I wrote (as I wanted to keep the example simple with "char" and "int") in that I used just "int" and not "unsigned int" and by implication that means I'm using "signed it". So if my later example was:

foo.bytes[0] = 0x83;
foo.bytes[1] = 0xC2;

then the combined value is 0xC283 but that is not simply 49,795 but actually -15,741 because it is signed. My original example should really have been:

union {
    uint16_t n;
    uint8_t bytes[2];
} foo;

to make the size and signage of the components more obvious.

 

So I can do:

 

 

Or I can do:

 

 

The first combines 2 bytes into one 16 bit int. The second splits one 16 bit int into 2 bytes.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Nice work CLiff!

 

Its almost identical to what a forum search came up with here:

https://www.avrfreaks.net/forum/...

 

For converting from an Int to 2 bytes, simply reversing the order seems to reverse teh outcome.  I sort of get it.

 

BUT,

In your example:

union {
    int n;
    char bytes[2];
} foo;

you say this will convert to little Endian as this is an AVR, but in teh other thread, SY writes:

union u_type            //Setup a Union
{
  unsigned int IntVar;
  unsigned char Bytes[2];
}
 temp;                    //Assign a var name to the Union 


void main(void)
{
 temp.IntVar=65535;          //Assign a value to the Int var of the Union
 HighByte=temp.Bytes[1];  //Get the High Byte (255)
 LowByte=temp.Bytes[0];   //Get the Low Byte (255)
}

which looks like Big Endian.

 

 

Ok, so where do I put these Union(s)?  Are they part of my declarations at the top of the .C file?

 

Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgmdesign wrote:
which looks like Big Endian.
Not to me it doesn't? Apart from the fact that 65535 is a "bad example" as you wouldn't be able to tell which way round they were as both bytes would be 0xFF I see him do:

 HighByte=temp.Bytes[1];  //Get the High Byte (255)
 LowByte=temp.Bytes[0];   //Get the Low Byte (255)

which seems like the "wrong order" to me - I'd be tempted to read [0] first and [1] second but in either case:

LowByte=temp.Bytes[0];   //Get the Low Byte (255)
HighByte=temp.Bytes[1];  //Get the High Byte (255)

that is saying "low in the first of the two bytes and high in the second" which to me sounds an awful lot like "little endian".

 

Endianism is perhaps more obvious still if you trade up from 2 byte to 4 byte variables but if I write:

uint32_t foo = 0xDEADBEEF;

then there are two ways this might be stored in memory:

foo  : EF
foo+1: BE
foo+2: AD
foo+3: DE

or

foo  : DE
foo+1: AD
foo+2: BE
foo+3: EF

even reading those vertically it's easier to "see" the variable in memory when it is big endian (the latter) but this is more pronounced if you read horizontally (as you might in a debugger or hex dump program) One looks like:

foo: EF BE AD DE (little endian)

and the other as:

foo: DE AD BE EF (big endian)

The AVR (and Intel PCs) would be using little endian so the lowest byte you come to is the least significant byte. So if you positioned a byte[4] array over this [0] would be EF, [1] would be BE, [2] would be AD and [3] would be DE.

jgmdesign wrote:
Ok, so where do I put these Union(s)?  Are they part of my declarations at the top of the .C file?

as with so many things in C you have options. I personally am a HUGE fan of typedefs so while I could just use:

#include <stdio.h>
#include <stdint.h>

union {
    uint16_t n;
    uint8_t bytes[2];
} foo;

int main(void) {
    foo.n = 12345;
    etc

as I did in my examples in post #37 in reality what I personally would use is:

#include <stdio.h>
#include <stdint.h>

typedef union {
    uint16_t n;
    uint8_t bytes[2];
} joined_t;

joined_t foo;

int main(void) {
    foo.n = 12345;

That is I would start by typedef'ing a union which creates a new C type called "joined_t" so if I want to create more than one of these I could:

joined_t foo;
joined_t bar;
joined_t boo;

or, perhaps importantly if I want to pass one into a function I could use:

int processData(joined_t join) {
    if (join.bytes[0] > 0x80) {
        // something
    }
}

Oh and as for where to instantiate the variables you can go for global:

#include <stdio.h>

joined_t foo;

int main(void) {
    foo.n = 12345;
}

or you could go for local:

#include <stdio.h>

int main(void) {
    joined_t foo;

    foo.n = 12345;
}

It's up to you really. The computer science purists would say localise and limit name scope as much as possible so keep it local to the function where it is used. But maybe this thing needs to be "seen" across more of the software - in which case the choices are either global or you pass the object (or a pointer to the object?) around to anyone who needs to access it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

jgmdesign wrote:
which looks like Big Endian.
Not to me it doesn't? Apart from the fact that 65535 is a "bad example" as you wouldn't be able to tell which way round they were as both bytes would be 0xFF I see him do:

 HighByte=temp.Bytes[1];  //Get the High Byte (255)
 LowByte=temp.Bytes[0];   //Get the Low Byte (255)

which seems like the "wrong order" to me - I'd be tempted to read [0] first and [1] second but in either case:

LowByte=temp.Bytes[0];   //Get the Low Byte (255)
HighByte=temp.Bytes[1];  //Get the High Byte (255)

that is saying "low in the first of the two bytes and high in the second" which to me sounds an awful lot like "little endian".

 

Endianism is perhaps more obvious still if you trade up from 2 byte to 4 byte variables but if I write:

uint32_t foo = 0xDEADBEEF;

then there are two ways this might be stored in memory:

foo  : EF
foo+1: BE
foo+2: AD
foo+3: DE

or

foo  : DE
foo+1: AD
foo+2: BE
foo+3: EF

even reading those vertically it's easier to "see" the variable in memory when it is big endian (the latter) but this is more pronounced if you read horizontally (as you might in a debugger or hex dump program) One looks like:

foo: EF BE AD DE (little endian)

and the other as:

foo: DE AD BE EF (big endian)

The AVR (and Intel PCs) would be using little endian so the lowest byte you come to is the least significant byte. So if you positioned a byte[4] array over this [0] would be EF, [1] would be BE, [2] would be AD and [3] would be DE.

jgmdesign wrote:
Ok, so where do I put these Union(s)?  Are they part of my declarations at the top of the .C file?

as with so many things in C you have options. I personally am a HUGE fan of typedefs so while I could just use:

#include <stdio.h>
#include <stdint.h>

union {
    uint16_t n;
    uint8_t bytes[2];
} foo;

int main(void) {
    foo.n = 12345;
    etc

as I did in my examples in post #37 in reality what I personally would use is:

#include <stdio.h>
#include <stdint.h>

typedef union {
    uint16_t n;
    uint8_t bytes[2];
} joined_t;

joined_t foo;

int main(void) {
    foo.n = 12345;

That is I would start by typedef'ing a union which creates a new C type called "joined_t" so if I want to create more than one of these I could:

joined_t foo;
joined_t bar;
joined_t boo;

or, perhaps importantly if I want to pass one into a function I could use:

int processData(joined_t join) {
    if (join.bytes[0] > 0x80) {
        // something
    }
}

Oh and as for where to instantiate the variables you can go for global:

#include <stdio.h>

joined_t foo;

int main(void) {
    foo.n = 12345;
}

or you could go for local:

#include <stdio.h>

int main(void) {
    joined_t foo;

    foo.n = 12345;
}

It's up to you really. The computer science purists would say localise and limit name scope as much as possible so keep it local to the function where it is used. But maybe this thing needs to be "seen" across more of the software - in which case the choices are either global or you pass the object (or a pointer to the object?) around to anyone who needs to access it.

 

Whoa!

 

I was only wondering where do I put the union, but thanks for all the extra info for me to review....better than a textbook for sure.

 

When I ask about where do I put the Union, I men do I put the uion with all my other global variables?

 

JIm

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Aren't you making life incredibly difficult?

 

I am combining a two byte ADC output into a single integer. 

 

int i = ((adc_high_byte & 0xFF) << 8) | (adc_low_byte & 0xFF);

This does the job.   Will work on any target.   The Compiler generates suitable code.

 

Jim's time is best spent studying the ADC chip's data sheet.  e.g. to see how to determine which is adc_high_byte.

 

And in years to come,   the code will work on the future MCU (which might be bigendian)

 

I am guessing Cliff.    You never used a M68000 !!

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:
Jim's time is best spent studying the ADC chip's data sheet.  e.g. to see how to determine which is adc_high_byte.

 

Already done.  This is for something else related to the ADC which is in another thread. wink

 

Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Or you could just write assembler code.  devil  S.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Scroungre wrote:

Or you could just write assembler code.  devil  S.

 

Or MikroPascal.(do a forum search wink)

 

 

Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think it is easier and better to just say  myresult= 256*highbyte + lowbyte.  You can easily pick up your calculator & follow along, anyone can readily check the code to verify the correct results are computed, & even the poor compiler will readily work with you.

 

This is certainly a very complicated undertaking!

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

avrcandies wrote:
This is certainly a very complicated undertaking!

Sarcasm?

 

JIm

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just to say that I'm in agreement with David and avrcandies. The:

combined = (byteH << 8) | byteL;

construct is the classic way to do this. It'll work the same on all C compilers and is not sensitive to endianism issues. My post #11 suggesting union was simply saying "there's more than one way to skin a cat" (shut your ears Amelia!!)