Parsing unsigned long int into four char types

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

Need to send an unsigned long int via SPI.
To do that need to break it into four bytes.

Can the long be type cast into a char and assigned to a char variable?
This assumes a series of right shifts ( >>8 ) for subsequent bytes.

What is the recognised ( smart) way of extracting the four bytes?

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

Overlay it with a uint8_t singlechar[4] array?

Einstein was right: "Two things are unlimited: the universe and the human stupidity. But i'm not quite sure about the former..."

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

Do You mean


uint8_t singlechar[3];
unsigned long int * var;
var = singlechar;
*var = longvalue;

is this what you mean by overlay?

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

Well, your approach would "overlay" the data during runtime, i was more thinking of a union structure, like:

union {
  unsigned long int MyInt;
  unsigned char MyChar[4];
} MyData;

MyData.MyInt = 12345678;
for (i=0;i<4;i++)
    printf("MyData.MyChar[percent_d] = percent_02X", i, MyData.MyChar[i]);

...with percent_ being replaced with the actual percent sign

Einstein was right: "Two things are unlimited: the universe and the human stupidity. But i'm not quite sure about the former..."

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

DO1THL,

I was aware of the union as an abstract data type but had not had the opportunity in the past to use it.
An interesting way to assign data type to a collection of bits.

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

Glad to see i could help. Actually, since the license upgrade some years ago i've evolved into DL2THL but didn't find a way to change name without losing identity (the star ranking :-) ).

unions have been invented for the sole purpose of accessing a set of bits as totally unrelated data types. It quite common to i.e. fill a byte array from a sensor, and lateron you access that data as x, y and z coordinates or similar. Or you read a block of bytes from disk, then divide them logically by overlaying them with a more complex structure of data types.

Einstein was right: "Two things are unlimited: the universe and the human stupidity. But i'm not quite sure about the former..."

Last Edited: Tue. Sep 17, 2013 - 11:36 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I obtained my engineering degree in '77.
In the final Year we were given the option of either becoming familiar with micro processors of the day ( in my case motorola 6800 and coding in assembler ) or getting familiar with UNIX ( and C ) on a mainframe.
The generation decided to go with micros. Damned if You do , damned if You dont.

It was in 2001-2 that I decided to do something about it and eventually did a post grad diploma in IT.
So I did a semester's worth of Java and a semester's worth of C.
Just enough to be dangerous.

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

DO1THL wrote:
unions have been invented for the sole purpose of accessing a set of bits as totally unrelated data types.
No, it's just a common misuse of unions.
K&R wrote:
This is the purpose of a union - a single variable that can legitimately hold any of one of several types.
K&R also wrote:
It is the programmer's responsibility to keep track of which type is currently stored in a union; the results are implementation-dependent if something is stored as one type and extracted as another.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
uint8_t spi(uint8_t c);

void spi_block(void *block, uint16_t size)
{
    uint8_t *p = (uint8_t *)block;
    while (size--) {
        spi(*p++);
    }
}

You could then simply use:

    int32_t longvalue;
    ...
    spi_block(&longvalue, sizeof(longvalue));

Note that you can 'serialise' any data like this. However you can only 'serialise' between similar targets. e.g. little-endian at both sides.

If you are serialising a 'struct', both sides must use similar padding and alignment as well as endianness.

Personally, I am happier with portable arrangements. e.g. an ascii string will work fine between different architectures.

Untested. I would probably use a more general universal function e.g.

bool spi_block_transfer(void *txbuf, void *rxbuf, uint16_t size);

David.

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

snigelen, actually i don't see the slightest difference between my definition of a union's usage and that of good old K&R.

Or what else did you try to describe with the word "misuse"?

Einstein was right: "Two things are unlimited: the universe and the human stupidity. But i'm not quite sure about the former..."

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

My interpretation of what you wrote

Quote:
unions have been invented for the sole purpose of accessing a set of bits as totally unrelated data types.
is that unions was invented so you can write a value as one datatype and read it as another. Maybe you didn't mean that?

I only wanted to show with my quotes that this is not why unions was invented. You can use it to store different datatype at different times. But there is nothing in the language that say what should happen if you write it as one type and read it back as another. Therefore I called it a "misuse" if you use unions that way.

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

Unions are perfect for internal structures.

Unions are fine for kludging known architectures and compiler models between foreign systems.

Having used M68000 CPUs in the past, I am very conscious of endian-ness. Also note that ARMs can be used as both big-end and little-endian.

I know that your present project is for an AVR. One day, you may want to port it to a different chip.

David.

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

I know about the "implementation dependent" thing when casting a union onto buffered data and access it in different ways but, seriously, has anyone ever come across an implementation where the obvious thing did not work?

I suppose the "real" solution is what was mentioned in the first post - shifts. I'm guessing the compiler is going to do that pretty efficiently.

EDIT: I tried this:

#include 
#include 

union {
	uint32_t longvalue;
	uint8_t bytes[4];
} both;

void use_byte(uint8_t n) {
	PORTB = n;
}

int main(void)
{
	uint8_t * p;
	both.longvalue = random();
	
	asm("nop");

	use_byte(both.longvalue & 0x000000FF);
	use_byte((both.longvalue & 0x0000FF00) >> 8);
	use_byte((both.longvalue & 0x00FF0000) >> 16);
	use_byte((both.longvalue & 0xFF000000) >> 24);
	
	asm("nop");
	
	p = (uint8_t *)&both.longvalue;
	use_byte(*p++);
	use_byte(*p++);
	use_byte(*p++);
	use_byte(*p++);
	
	asm("nop");

	use_byte(both.bytes[0]);
	use_byte(both.bytes[1]);
	use_byte(both.bytes[2]);
	use_byte(both.bytes[3]);
}

and got this:

main:
//==> {
/* prologue: function */
/* frame size = 0 */
/* stack size = 0 */
.L__stack_usage = 0
//==> 	both.longvalue = random();
	call random
	sts both,r22
	sts both+1,r23
	sts both+2,r24
	sts both+3,r25
//==> 	asm("nop");
/* #APP */
 ;  18 ".././test.c" 1
	nop
 ;  0 "" 2
/* #NOAPP */
//==> 	PORTB = n;
	out 0x18,r22
//==> 	use_byte((both.longvalue & 0x0000FF00) >> 8);
	lds r24,both
	lds r25,both+1
	lds r26,both+2
	lds r27,both+3
//==> 	PORTB = n;
	out 0x18,r25
	out 0x18,r26
//==> 	use_byte((both.longvalue & 0xFF000000) >> 24);
	mov r24,r27
	clr r25
	clr r26
	clr r27
//==> 	PORTB = n;
	out 0x18,r24
//==> 	asm("nop");
/* #APP */
 ;  25 ".././test.c" 1
	nop
 ;  0 "" 2
//==> 	use_byte(*p++);
/* #NOAPP */
	ldi r30,lo8(both)
	ldi r31,hi8(both)
	ld r24,Z
//==> 	PORTB = n;
	out 0x18,r24
//==> 	use_byte(*p++);
	ldd r25,Z+1
//==> 	PORTB = n;
	out 0x18,r25
//==> 	use_byte(*p++);
	ldd r25,Z+2
//==> 	PORTB = n;
	out 0x18,r25
//==> 	use_byte(*p++);
	ldd r25,Z+3
//==> 	PORTB = n;
	out 0x18,r25
//==> 	asm("nop");
/* #APP */
 ;  33 ".././test.c" 1
	nop
 ;  0 "" 2
/* #NOAPP */
//==> 	PORTB = n;
	out 0x18,r24
//==> 	use_byte(both.bytes[1]);
	lds r24,both+1
//==> 	PORTB = n;
	out 0x18,r24
//==> 	use_byte(both.bytes[2]);
	lds r24,both+2
//==> 	PORTB = n;
	out 0x18,r24
//==> 	use_byte(both.bytes[3]);
	lds r24,both+3
//==> 	PORTB = n;
	out 0x18,r24
//==> }
	ldi r24,0
	ldi r25,0
	ret

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

Fifty ways to leave your lover. Or rather, an evolving sequence of five ways to extract your bytes. Sketchy, untested. First one is Cliffs first. Last two will likely degenerate to do the shifting at runtime.

I like the third for its readability, and reduction of redundance.

use_byte(both.longvalue & 0x000000FF); 
use_byte((both.longvalue & 0x0000FF00) >> 8); 
use_byte((both.longvalue & 0x00FF0000) >> 16); 
use_byte((both.longvalue & 0xFF000000) >> 24); 

-------------------------------------------------------

#define BYTE_MASK 0x000000FF

use_byte(both.longvalue & BYTE_MASK); 
use_byte((both.longvalue & BYTE_MASK<<8) >> 8); 
use_byte((both.longvalue & BYTE_MASK<<16) >> 16); 
use_byte((both.longvalue & BYTE_MASK<<24) >> 24); 

-------------------------------------------------------

#define BYTE_MASK 0x000000FF
#define USE_BYTE(longvalue, bytenumber) use_byte((longvalue & BYTE_MASK<<(bytenumber*8)) >> (bytenumber*8))

USE_BYTE(both.longvalue, 0); 
USE_BYTE(both.longvalue, 1); 
USE_BYTE(both.longvalue, 2); 
USE_BYTE(both.longvalue, 3); 

-------------------------------------------------------
#define BYTE_MASK 0x000000FF

for (int byteNumber = 0; i < 4; byteNumber++)
{
   use_byte((both.longvalue & BYTE_MASK<<(byteNumber*8)) >> (byteNumber*8)); 
}

-------------------------------------------------------

#define BYTE_MASK 0x000000FF
#define USE_BYTE(longvalue, bytenumber) use_byte((longvalue & BYTE_MASK<<(bytenumber*8)) >> (bytenumber*8))

for (int byteNumber = 0; i < 4; byteNumber++)
{
   USE_BYTE(both.longvalue, 0); 
}

In C++ you could of-course "easily" get to the extremely beautiful

longValue.bytes[0];
longValue.bytes[1];
longValue.bytes[2];
longValue.bytes[3];

:wink:

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
use_byte(both.longvalue & 0x000000FF); 
use_byte((both.longvalue & 0x0000FF00) >> 8); 
use_byte((both.longvalue & 0x00FF0000) >> 16); 
use_byte((both.longvalue & 0xFF000000) >> 24); 

No real need for the masking, you could just do this:

use_byte(both.longvalue); 
use_byte(both.longvalue >> 8); 
use_byte(both.longvalue >> 16); 
use_byte(both.longvalue >> 24); 

Regards,
Steve A.

The Board helps those that help themselves.

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

You can tell whether a machine is big or little-endian at runtime. And then serialise if compatible with the target destination.
Personally, I would use Koshchi's method which is both efficient and portable. (having first checked the target's data sheet)

Many SPI chips require their data MSB first in a big-endian sequence. e.g. memory chips, TFT display chips, ...

David.

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

Quote:

No real need for the masking, you could just do this:

I did but the code generated was different (and horrendous).

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

Thank You all for Your advice, I did in the end settle on a variant of mask and shift approach.
This way processor endiannes did not come into consideration.

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

clawson wrote:
Quote:

No real need for the masking, you could just do this:

I did but the code generated was different (and horrendous).

What happens if you reverse the shift and mask operations? I like to keep all my masks all the same for consistency and clarity (and to remove redundancy between the mask and the shift amount) like this:

use_byte((both.longvalue >> 0) & 0xFF);
use_byte((both.longvalue >> 8) & 0xFF);
use_byte((both.longvalue >> 16) & 0xFF);
use_byte((both.longvalue >> 24) & 0xFF); 

I'm not in a position at the moment to compile the above code but I imagine the generated code should be the same as masking before shifting.

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

EDIT: it was a silly question

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

Quote:

What happens if you reverse the shift and mask operations?

This:

int main(void)
{
	both.longvalue = random();

#ifdef TESTONE	
	asm("nop");
	
	use_byte(both.longvalue & 0x000000FF);
	use_byte((both.longvalue & 0x0000FF00) >> 8);
	use_byte((both.longvalue & 0x00FF0000) >> 16);
	use_byte((both.longvalue & 0xFF000000) >> 24);

#else
	asm("nop");

	use_byte((both.longvalue >>  0) & 0xFF);
	use_byte((both.longvalue >>  8) & 0xFF);
	use_byte((both.longvalue >> 16) & 0xFF);
	use_byte((both.longvalue >> 24) & 0xFF);
#endif	
}

yields this (TESTONE):

	nop
 ;  0 "" 2
/* #NOAPP */
//==> 	PORTB = n;
	out 0x18,r22
//==> 	use_byte((both.longvalue & 0x0000FF00) >> 8);
	lds r24,both
	lds r25,both+1
	lds r26,both+2
	lds r27,both+3
//==> 	PORTB = n;
	out 0x18,r25
	out 0x18,r26
//==> 	use_byte((both.longvalue & 0xFF000000) >> 24);
	mov r24,r27
	clr r25
	clr r26
	clr r27
//==> 	PORTB = n;
	out 0x18,r24

and this (!TESTONE):

	nop
 ;  0 "" 2
/* #NOAPP */
//==> 	PORTB = n;
	out 0x18,r22
//==> 	use_byte((both.longvalue >>  8) & 0xFF);
	lds r24,both
	lds r25,both+1
	lds r26,both+2
	lds r27,both+3
	mov r20,r25
	mov r21,r26
	mov r22,r27
	clr r23
//==> 	PORTB = n;
	out 0x18,r20
//==> 	use_byte((both.longvalue >> 16) & 0xFF);
	movw r20,r26
	clr r22
	clr r23
//==> 	PORTB = n;
	out 0x18,r20
//==> 	use_byte((both.longvalue >> 24) & 0xFF);
	mov r24,r27
	clr r25
	clr r26
	clr r27
//==> 	PORTB = n;
	out 0x18,r24

The latter generating a load of pointless MOV's and CLR's. This is what I saw before and why I arrived at the code I did as (with -Os building) it appeared to generate the "tightest" code.

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

What to use depends on what you value.
To code for maintenance, just shift.
If efficient code is important enough, mask before shifting
and put in a comment stating the reason for the masks.
It seems to me that the union and the pointer have no advantage over the latter in either clarity or efficiency.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

Again, wish there is a way to "flag" important threads (on a user by user basis).

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

Quote:
I did but the code generated was different (and horrendous).
The OP asked how to do it, not how to create efficient code ;)

Regards,
Steve A.

The Board helps those that help themselves.

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

I liked the elegance of the union but somehow handraulically shifting and masking made me think the resulting code might be smaller.

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

ignoramus wrote:
I liked the elegance of the union but somehow handraulically shifting and masking made me think the resulting code might be smaller.
With union, one would normally have to think about endianness and sizeof(integer).
Doing it mathematically allows one to know one has the right sequence of octets without such considerations.
Dealing with machines for which an octet is not a byte is left as an exercise for the reader.
Masking is mathematically unnecessary and requires more work of the abstract machine,
hence masking requires a comment.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

Minor nitpick: thread title is misleading. Decoding, or extracting, perhaps, but not really "parsing". Also, "char" further implies some string/character processing, which is not the case.

"Extracting Bytes of a Quadlet" or some such.

C: i = "told you so";

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

Ah yes... extracting octets from a quadlet.. sheer poetry

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

Quote:

Ah yes... extracting octets from a quadlet.. sheer poetry
If you're looking for quality poetry, well, you're not going to find it around this board!

C: i = "told you so";

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

Quote:

If you're looking for quality poetry, well, you're not going to find it around this board!

Have you not heard of Chuck "Bard" Baird then?

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

Quote:

... hence masking requires a comment.

As does unmasking...

Quote:
After a year-long court battle, [Clayton] Moore lost the right to wear the [Lone Ranger] mask in 1979, a move that devastated both him and his fans. Moore was quoted as saying, “It felt like a slap in the face.”

http://mentalfloss.com/article/1...

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

clawson wrote:
Quote:

If you're looking for quality poetry, well, you're not going to find it around this board!

Have you not heard of Chuck "Bard" Baird then?
lol

C: i = "told you so";