View previous topic :: View next topic |
Author |
Message |
andyd
Joined: 08 Mar 2007 Posts: 30
|
"Joining" ints |
Posted: Wed Apr 04, 2007 3:52 pm |
|
|
I'm sure there's some basic logical operation that could do this, but my head just won't think of it now... I need a 16 bit number, but due to the way my EEPROMs are read, I need to read it as two 8 bit numbers (the 8 MSB then the 8 LSB), how can I then "join" these to make the 16 bit number? Like strcat() but for ints basically... |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed Apr 04, 2007 4:14 pm |
|
|
...make16()
See also the chapter 'How do I write variables to EEPROM that are not a byte?' in the 'Common Questions And Answers' section of the CCS manual for another solution. |
|
|
Guest
|
|
Posted: Thu Apr 05, 2007 8:09 am |
|
|
Something like this would work. A similar, but opposite, technique can be used to break a 16-bit number into two 8-bit numbers.
(note: this has not been compiled)
int8 lsb; // low order byte
int8 msb; // high order byte
int16 result; // concatenation of the two above values
result = (int16)msb << 8; // cast msb to a 16-bit, shift it left 8 places
result += lsb // add lsb into the lower half of result |
|
|
Ttelmah Guest
|
|
Posted: Thu Apr 05, 2007 9:29 am |
|
|
There are three techniques. Which is easiest/best, depends on you.
Make8/make16. Advantage already there in the compiler. Disadvantage, is that it is 'CCS' specific.
Using shifts and masks as shown above. Pretty generic. Downside is that it is more work for the compiler (but the compiler is pretty efficient about this).
The third is the union. My favourite.
Code: |
union splitter {
int8 b[2];
int16 w;
};
union splitter val;
val.b[0] = 0x10;
val.b[1] = 0x20;
printf("%4LX/n/r",val.w);
//prints '2010' - the bytes have been combined
val.w=0x1234;
printf("%2X/n/r",val.b[0]);
//prints '34' the low byte has been selected.
|
This is a standard C construct, and allows rally quick conversions both ways.
You can even declare a structure, with 'low', and 'high' bytes, and ue this instead of the array. This can be really useful when moving code between processors where the byte oder differs, making it easy to convert.
Best Wishes |
|
|
bungee-
Joined: 27 Jun 2007 Posts: 206
|
|
Posted: Mon Apr 20, 2009 4:28 pm |
|
|
Ttelmah wrote: |
The third is the union. My favourite.
Code: |
union splitter {
int8 b[2];
int16 w;
};
union splitter val;
val.b[0] = 0x10;
val.b[1] = 0x20;
printf("%4LX/n/r",val.w);
//prints '2010' - the bytes have been combined
val.w=0x1234;
printf("%2X/n/r",val.b[0]);
//prints '34' the low byte has been selected.
|
|
Thank you for the code example above.
I have a question regarding union. Is it possible to declare it like that:
Code: |
union splitter {
int8 b1;
int8 b2;
int16 w;
};
|
And to work it the same as your code. I would put two ports instead of b1 and b2 so I could work with 16bit port :D
Thank's for the answers. |
|
|
Ttelmah Guest
|
|
Posted: Tue Apr 21, 2009 2:21 am |
|
|
No.
As posted:
Code: |
union splitter {
int8 b1;
int8 b2;
int16 w;
};
|
This gives you a 16bit memory 'location', holding 'w', and then a byte 'b1', mapped to the same location (LSB), and _another_byte 'b2', mapped to the same location_. Neither talks to the 'MSB' of the 16bit value.
A union, maps each variable in it, to the same memory location. In the original, the 2byte array, is mapped to the same location as the 16bit 'w' value, allowing you to access each byte, as the entries in the array.
If you want to use 'names', rather than an array access, the way to go, is to declare a 'new' 16bit structure. So:
Code: |
struct bytes {
int8 LSB;
int8 MSB;
};
union splitter {
struct bytes b;
int16 w
} val;
|
Then you can talk to val.w for the whole 16bit 'word', or val.b.LSB, and val.b.MSB for the two bytes.
Best Wishes |
|
|
andrewg
Joined: 17 Aug 2005 Posts: 316 Location: Perth, Western Australia
|
|
Posted: Tue Apr 21, 2009 8:00 am |
|
|
One caution for using the union technique, is if you might ever want to port to a different endian architecture (big-endian versus little-endian, which flip the order of the bytes). In that case, using explicit bit shifts is probably best, followed by the make16, since you can easily implement your own make16 on other platforms. _________________ Andrew |
|
|
Ttelmah Guest
|
|
Posted: Tue Apr 21, 2009 8:43 am |
|
|
Actually, I'd say the union/structure technique is better in this case.
All you do is something like:
Code: |
#ifdef LSBfirst
struct bytes {
int8 LSB;
int8 MSB;
};
#else
struct bytes {
int8 MSB;
int8 LSB;
};
#endif
|
and you gain a version that is portable onto any compiler offering a define int8 type, just defining 'LSBfirst', for chips where thsi is true.
Rotations can give unexpected results sometimes, when dealing with signed values in particular, on older compilers, and are sometimes coded quite inefficiently, hence my preference for the structure/union approach.
Macros ae probably the 'best' overall solution, since you can then just code to match the vagaries of your particular compliler/CPU....
Best Wishes |
|
|
John P
Joined: 17 Sep 2003 Posts: 331
|
|
Posted: Tue Apr 21, 2009 10:07 am |
|
|
A very smart friend suggested this method to me a few years ago, and since then it's the way I've always done it:
Code: |
int16 the_word;
#byte word_low = the_word
#byte word_high = the_word+1
|
If you want to see how it looks in the LST file:
MAIN.THE_WORD 00000064
MAIN.WORD_LOW 00000064
MAIN.WORD_HIGH 00000065
Yes, 8-bit variables set up using the #byte directive are usable just like any other variables. So you can read or write the full 16-bit variable, or either of its components, just as you need to. |
|
|
bungee-
Joined: 27 Jun 2007 Posts: 206
|
|
Posted: Thu Apr 23, 2009 3:59 pm |
|
|
Thank you all for the answers. I learn a lot from them. But my problem still remains. I also try to use #byte statment.
I will describe my problem. I have two ports on PIC18f4550 and I would like to use them together as 16bit port. But the trick is that I will use port B and port D, because A and C are intended for other things.
I could use make8 instructions (probably will), but I wandered if is there another way. |
|
|
rnielsen
Joined: 23 Sep 2003 Posts: 852 Location: Utah
|
|
Posted: Thu Apr 23, 2009 6:40 pm |
|
|
Code: | int8 var1;// LSB
int8 var2;// MSB
int16 var3;
var1 = input_b();
var2 = input_d();
var3 = make16(var2, var1); |
This will read portb and portd, place them into a 16-bit variable. Not much simpler, I think.
Ronald |
|
|
|