Valid reason to use unions with embedded systems.

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

Unions are often used by clever programmers to create job security and non-portable programs. I've never actually had a good reason to use them in embedded systems. I've seen some arguments about this here, but never a good reason to use them. The only semi-valid thing I've seen is as a non-portable way to extract the high and low bytes on an int, but there are other ways to do that that probably don't use any more memory so I have my doubts.

Any valid reason to use unions with embedded systems?

[edit]
Okay, saw the light and wrote some instructional code located at:
https://www.avrfreaks.net/index.p...
to prove the point about the usefulness of unions in packets with variable data types.
[/edit]

Smiley

Last Edited: Mon. Dec 19, 2011 - 10:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One word (is it a word?): TCP/IP

EDIT: I went to here:

http://lxr.linux.no/#linux+v3.1.5/

and typed "union" into the search box.

Result: 3388 hits ;-)

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

Quote:
Unions are often used by clever programmers to create job security and non-portable programs.
Nonsense.

Valid reason? Say that your application or a module has several different modes of operation, with different sets of data specific to each of the modes. You then put the data specific to a mode into a structure, and put all those structures into a union. (Which you are more likely to do in an low-RAM embedded system as an alternative to, say, dynamic allocation.)

Eugene

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

Quote:
Unions are often used by clever programmers to create job security and non-portable programs.

I agree that the following is non-portable:

static inline void Serialise_le_uint32_t(uint8_t *buffer, uint32_t data)

{

    union

    {

        uint32_t uiData;

        struct

        {

            uint8_t byData0;

            uint8_t byData1;

            uint8_t byData2;

            uint8_t byData3;

        } sData;

    } uData;



    uData.uiData = data;

    buffer[0] = uData.sData.byData0;

    buffer[1] = uData.sData.byData1;

    buffer[2] = uData.sData.byData2;

    buffer[3] = uData.sData.byData3;

}

But it's in production AVR code and it compiles down to what would be expected from hand-written assembly.

I doubt this snippet of code gives me any more or less job security.

-- Damien

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

There's two reasons:

1) Whacky bit-extraction - pulling bytes from a long, that sort of thing. Supposedly the only somewhat portable way of doing it, although then you still need to deal with endianness, packing and element ordering.

2) Named access to data. You can use a union to create both a long word access to some data, as well as have named access to some elements (bitfields!). The XMEGA header files use this to provide both register access, and named access to the individual bits.

3) Saving memory. Like Eugene says, some times you just need to save on global RAM space for mutually exclusive portions of code.

Blame the programmer for bad uses of Union, not the language. Same goes for using "goto" - see the Linux kernel for a decent reason to use it (error handling).

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

I guess one more reason to use unions is the strict aliasing stuff (which I keep hearing about). On the other hand, I often see people cite a standard and say that reading a member of a union different from the one written to is undefined. You do not know who to believe ...

Eugene

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

Goto is portable at least — bit fields are not.

If bit fields are used where portability is no issue they are fin, of course. One example is representing bits of an SFR. This hardware-centric code is not portable, anyway.

Some developers like type punning

union u { float f; int i; };

int get_binary_representation (float f)
{
   union u u;
   u.f = f;
   return u.i;
}

which is not portable (but works in GCC). More reading: http://mail-index.netbsd.org/tec...

avrfreaks does not support Opera. Profile inactive.

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

Quote:

strict aliasing stuff

GAAAAAH. I hate strict aliasing; it ruins the possibility of re-casting buffers directly to struct types and the like, which I know is non-portable, but necessary when it saves you a ton of memory (RAM and FLASH) in an embedded system. Why they didn't just enable those optimizations on variables marked as "restrict" (or perhaps disable them on a converse qualifier) is beyond me.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Quote:
I hate strict aliasing
I don't ... simply because I hope to retire before I have to write an application that does not work because of strict aliasing.

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

Quote:
which is not portable (but works in GCC)
OK, here is a guy who can shed some light. Non portable - I am fine with that. But it is not "undefined", it is "implementation defined", right? Or is it really "undefined"? In one of those n1nnn.pdf files floating around, they say "if the member used to access the contents of a union object is not the same as the member last used to store a value in the object, the appropriate part of the object representation of the value is reinterpreted as an object representation in the newtype as described in 6.2.6 (a process sometimes called "type punning")". Are there other standards/documents which say that it is "undefined"?

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

I often use unions to handle the big/little-endian problem when binary data is sent between different systems.
To me, y = word_union.subcode;
is more readable than y = ( word_value >> 8 );

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

Okay, let me restructure the query. Say you have the task of teach C for embedded systems like the AVR (not large systems that can support Linux) what would be a practical and compelling use for unions that you could give as an example to these poor unwashed masses yearning to learning C? An example would be really nice.

Thxnadvc
Smiley

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

Smiley,

The implication of my "TCP/IP" was a more general thing where you are receiving "packets" of data which are between 1 and N bytes long. A byte at the start tells you what kind of packet it is. You then use this to interpret what follows in different ways. A contrived example might be:

typedef enum {
  PORTNAME = 0,
  BINARY_VALUE = 1
} pkt_t;

typedef struct {
  char name [5];
  // perhaps other bits and pieces
} port_name_t;

typedef struct {
   uint16_t port_addr;
   uint8_t port_val;
} bin_val_t;

typedef struct {
   pkt_t pkt_type;
   union { 
      port_name_t port;
      bin_val_t bin_val;
   };
} pkt_data_t;

int main(void) {
  uint8_t buffer[20];
  pkt_data_t *p_dat;
  receive(&buffer);
  p_dat = &buffer;
  if (p_dat.pkt_type == PORTNAME) {
    strcpy(portname, p_dat.port.name);
  }
  else if (p_dat.pkt_type == BINARY_VALUE) {
    *p_dat.bin_val.port_addr = p_dat.bin_val.port_val
  }
}

Totally untested - I just made it up and it is very contrived but perhaps gives some idea where a union might be used to let some part of some generic data have different interpretations cast upon it.

I really would recommend perusing Linux source(LXR makes this easy online) and seeing actual uses of union{} in a real production app - possibly the most famous and now getting on for the most widely used piece of embedded software ever.

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

I use C unions and bitfields - esp. for I/O hardware, registers. I also use unions for communications protocols where the header is constant but there are several variants of message body.

Much better than a bunch of mickey-mouse #defines or pointer casts.

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

If you are french, you will use onions and maybe leeks.

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

I sort-of stick with Cliff on this.

The example I was thinking of was an message- or event-system (e.g. on one of the larger AVRs) where a struct holds something like

- Message/source
- A union with specific data for the specific message/source

While the question was specifically mentioning C, you could of-course do this in C++ instead, replacing the union with inheritance.. :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

I use them for two reasons.
Firstly, as has already been said, they enable different modes of operation to use the same RAM space(which admittedly could probably be achieved by dynamic allocation of memory I suppose).

Secondly, I have two slightly different structures that are using for saving information, depending on a channel type, so I have a union which can hold either digital channel info, analog channel info, or simply be referred to as a block of RAM.
Almost the same as the first reason, now I come to write it out. Which seems appropriate...

Quebracho seems to be the hardest wood.

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

Smokey,

Here's a good one, from the sensor library I wrote as part of my final year project:

https://code.google.com/p/blueto...

See the definition of SensorData_t. Here each sensor gets an abstract structure entry to describe it in a global table, which serves as a common interface to each physical sensor. The sensor platform library can then easily enumerate all the sensors without having to worry about the underlying implementation. Some sensors report three axis of values while others report a single higher precision axis, so I use a union to save on RAM.

See the rest of the sensor code here:

https://code.google.com/p/blueto...

To see how it all fits together.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Joe,

I'd like to recommend you to look up and read thoroughly the comment to verse 531 in the Derek Jones book.

Jan

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

wek wrote:
Joe,

I'd like to recommend you to look up and read thoroughly the comment to verse 531 in the Derek Jones book.

Jan

Couldn't find 'verse 531', what did I miss?

Smiley

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

As I say in the edit in my OP, I've seen the light and wrote a program to illustrate how a union of arrays might be used to store data from a communication packet in an array without knowing the data type of that data until it is processed. This code is for illustration purposes only. It was tested in Pelles C and I have to assume it will work with avr-gcc. I'll write up some novice level explanations later to go along with this.

#include 

union array_t
{
  char cArray[64];
  int iArray[32];
  float fArray[16];
}myArray;

enum{CHAR, INT, FLOAT};

void show_array(void *p, int type);

int main(void) {
  int i;
  float f = 123.456;

  void *vPtr;

// Load memory with 64 char
  for(i = 0; i < 64; i++)
  {
    myArray.cArray[i] = '!' + (char)i;
  }
  // Point global void pointer to array
  vPtr = myArray.cArray;
  // Show the characters
  show_array(vPtr,CHAR);
   
// Load memory with 32 ints
  for(i = 0; i < 32; i++)
  {
    myArray.iArray[i] = i+10;
  }
  // Point global void pointer to array
  vPtr = myArray.iArray;
  // Show the characters
  show_array(vPtr,INT);

// Load memory with 16 floats
  for(i = 0; i < 16; i++)
  {
    myArray.fArray[i] = f ;
    f += 1.001;
  }
  // Point global void pointer to array
  vPtr = myArray.fArray;
  // Show the characters
  show_array(vPtr,FLOAT);

  return 0;
}

void show_array(void *p, int type)
{
  char c;
  int i,j;
  float f;
  char *cPtr;
  int *iPtr;
  float *fPtr;

  switch(type)
  {
    case CHAR:
      cPtr = p;
      printf("Show char array:\n ");
      for(i = 0; i < 64; i++)
      {
        c = (char)*(cPtr+i);
        printf("%c,",c);
      }
      printf("\n");
      break;
    case INT:
      iPtr = p;
      printf("Show int array:\n ");
      for(i = 0; i < 32; i++)
      {
        j = (int)*(iPtr+i); 
        printf("%d,",j);   
      }
      printf("\n");
      break;
     case FLOAT:
      fPtr = p;
      printf("Show float array:\n ");
      for(i = 0; i < 16; i++)
      {
        f = (float)*(fPtr+i);
        printf("%.3f,",f);
      }
      printf("\n");
      break; 
  
  }
}

Thanks for all the suggestions.

Smiley

Last Edited: Tue. Dec 20, 2011 - 12:00 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

http://www.coding-guidelines.com...

I found 531, dealing with unions. The section is a bit too large to post here. The author mentions "...implementation details, which may vary considerably between implementations...".

Other commentary has a generally positive mood w.r.t. using unions versus alternatives.

========================
General comment for Smiley's witch hunt: I guess there are embedded systems and embedded systems.

If I'm packing a Modbus RTU into a Mega48 and the union gives me the smallest (and maybe fastest) code for the necessary byte-swapping, I'm going to use it. Best practices and MISRA be damned; this is my AVR app, to fit into a Mega48, and I'll use whatever language facilities I care to, thank you very much. The same might apply to "continue;" or "short returns" or gratuitious "break;". If "do {} while()" helps, I'll use it. From time to time I might even use a goto. If I was using GCC I might even use a computed goto--horrors!

Then there are multi-member teams developing "embedded systems" apps on (compared to AVR8) "big iron". I can live with coding standards, too.

As far as I'm concerned, anything in the language is fair game. If there is no use for the construct, why has it made ith through all these language revisions? It all smacks of Fahrenheit 451. So, how many levels of pointers will you allow in your Esperanto programs?

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

Lee, I think you just missed my conversion. Anyway it the intent was to provoke comments not to burn books or witches.

Smiley.

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

smileymicros wrote:
Couldn't find 'verse 531', what did I miss?
In the cbook*.pdf file on that page, the big numbers on the pages indicate the sequential number of sentence in C99 the text on that page comments on, that's what I meant. Not exactly light bedtime reading, but that book is IMHO a very interesting even if a bit weird source of background information. Sentence 531 is part of C99 6.2.5, types, and as Lee said above, deals with the unions.

Jones (among other things) states that using union for type punning is as popular as pointers, while the fact is that in strict reading of the standard, both methods are broken. The only truly portable method as of now is to explicitly perform the conversion in the boring, "slow" way. Unions were created only to reuse space, see K&R 6.8.

All this said, I do prefer using unions for type punning because it has the potential for being systematically correct, and I hope this in some form will get codified in C1x.

Jan

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

I tested a version of this using AVRStudio 4 on an Arduino (upload with avrdude). I changed the float to long to avoid the problems with printf for floats. The project is available at:
http://code.google.com/p/avrtool...

Smiley

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

I have a data buffer for a display that is 128x64pixel, so each pixel is a bit. when doing graphical stuff I want to use uint64_t for shifting and filling data, but the data has to go out in 8bit, so here the union commes in handy, specially because I cannot shift the 64bits out but have to shift out 8 bits en then the next line 8bits and so on.

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

Useful: A union containing structures where each structure depicts the different data organization that an array of bytes might take (as in communications messages of variable length and organization but with structured data).