C programming - how to split int(16 bits) to 2 char(8bit)

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

I have unsigned int(16 bits) variable and 2 char(8 bit) variables. Char1 is byte1 of int variable and char2 is byte2 of int variable.
How can I split int to 2 chars, and vice versa - how can I make int from 2 chars.
I am using ICC AVR.

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

unsigned short MyShort;
unsigned char Char1; // lower byte
unsigned char Char2; // upper byte

// Split short into two char

Char1 = MyShort & 0xFF;
Char2 = MyShort >> 8;

// merge two char into short

MyShort = (Char2 << 8) | Char1;

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

This sounds like a union ...

typedef union {
  short    int_var;
  char[2]  ch1ch2;
} COMBO;

COMBO   var1;

var1.int_var = 35;        // set int value to something
var1.ch1ch2[0] = 5;      // set first byte to something
var1.ch1ch2[1] = 9;      // set second byte to something

Dean 94TT
"Life is just one damn thing after another" Elbert Hubbard (1856 - 1915)

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

GedasL wrote:
I have unsigned int(16 bits) variable and 2 char(8 bit) variables. Char1 is byte1 of int variable and char2 is byte2 of int variable.
How can I split int to 2 chars, and vice versa - how can I make int from 2 chars.
I am using ICC AVR.

A UNION is the most elegant solution and designed to perform just this task.

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)
}

//Sy

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

While the union solution will work just fine (I won't quite agree with Sy about it being "designed to perform just this task" :) ), the purists could argue that it is not portable based on endianess (big-endian [Moto], little-endian[Intel], referring to the byte order of multiple-byte binary numbers in memory storage).

So the totally-portable C solution is presented as a combination of a right-shift 8 bits for the high byte (which preserves the sign if you are careful), and a mask for the low byte. This usually takes more code & cycles than the union but, as mentioned, it is more portable across different architectures.

Another efficient way, non-portable and dipping into the bowels of the power of C (a sharp knife cuts both ways) is to use a cast on the address of the multi-bit integer into a series of char (8-bit) pointers. Example:

unsigned char low_byte;
unsigned char high_byte;
unsigned int the_value;
...
the_value = 1234;
low_byte = * ((unsigned char *)&the_value);
high_byte = * ((unsigned char *)((&the_value)+1));

or a char-sized pointer could be used the same way. [I'd probably use the pointer method for repetitive uses within an app and/or many-byte variables.]

unsigned char low_byte;
unsigned char high_byte;
unsigned int the_value;
unsigned char *the_ptr;
...
the_value = 1234;
the_ptr = * ((unsigned char *)&the_value);
low_byte = *the_ptr;
the_ptr++;
high_byte = *the_ptr;

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

theusch wrote:
While the union solution will work just fine (I won't quite agree with Sy about it being "designed to perform just this task" :) ), the purists could argue that it is not portable based on endianess (big-endian [Moto], little-endian[Intel], referring to the byte order of multiple-byte binary numbers in memory storage).
Lee

I agree with Lee. Unions are not very portable. The size of int is compilier (processor) dependent. I've recently used a compiler where the size of char was 32-bits (TI Code Composer for the TMS320C30).

Also, if I remember the ANSI C standard (I don't have a copy at home), union implementation is compilier dependent - the storage areas don't have to overlap. That said, I've never seen a compilier where this was the case.

Don

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

the bset way:

it only compile in ONe line asm code !

#define LowB(x) (*((unsigned char*)  &(##x)+1)) 
#define HighB(x) (*((unsigned char*) &(##x)+0)) 

for ex for low byte use LowB(x)

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

maziar wrote:
the bset way:

it only compile in ONe line asm code !

#define LowB(x) (*((unsigned char*)  &(##x)+1)) 
#define HighB(x) (*((unsigned char*) &(##x)+0)) 

for ex for low byte use LowB(x)

Here we go with the endian problem again.

The AVR register pairs are laid out little-endian (low byte first). The CodeVision C compiler, and AFAIK ImageCraft, gcc, & IAR also use little-endian. With any of those compilers masiar's macros are backwards.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

i am one old 8015 keil user (i am newbie in avr)

i f you think this change +1 to 0
& 0 to +1 ..

if this is true old avr c programmer reply this :

keil & IAr have diffrent byte order in int ?

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

If you ONLY access multi-byte items with your own macros, you can use whatever byte order you choose as long as you are consistent with each item.

Try a simple experiment with your '8051 (or other) code:

unsigned int a;
unsigned int b;
unsigned int c;

a = 1;
b = 2;
c = a + b;

and examine the code produced by the compiler. The simple experiment should pretty much answer the question, as the compiler is in all probability consistent in all multi-byte operations. See if the '3' ends up in the lower RAM address or the higher.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Endianness aside, the pointer trick requires that the 16-bit value be in storage (I'm pretty
sure the optimizer isn't allowed to recognize this case), while the shifting method doesn't.
(Indeed, GCC generates storage-spills for the pointer method at all optimization levels.)

Moreover, for the AVR, since the two halves need to be in separate registers, the
shifting method doesn't actually require any shift/and instructions. (GCC doesn't in
fact generate any shifts/ands at any optimization level.)

In short, the shifting method is small/fast, portable, avoids endianness entirely, and is
a well-known idiom for most readers. 'S got my vote.