Using union in Embedded Project

Thread Starter

MTech1

Joined Feb 15, 2023
147
I know what a Union is in C language and I can write programs for Union and structure on my computer. The compiler allocates memory based on the largest Union member. Union members share the same memory space. I believe if memory is limited, using Union instead of a structure is a good idea.

Have you used Union in an embedded program before? What were your needs? In what cases would you prefer using Union in your program?
 

geekoftheweek

Joined Oct 6, 2013
1,094
Copied from pic18f4550.h for SDCC... more or less the whole file is a bunch of unions

Code:
extern __at(0x0F6C) __sfr USTAT;

typedef union
  {
  struct
    {
    unsigned                    : 1;
    unsigned PPBI               : 1;
    unsigned DIR                : 1;
    unsigned ENDP0              : 1;
    unsigned ENDP1              : 1;
    unsigned ENDP2              : 1;
    unsigned ENDP3              : 1;
    unsigned                    : 1;
    };

  struct
    {
    unsigned                    : 3;
    unsigned ENDP               : 4;
    unsigned                    : 1;
    };
  } __USTATbits_t
 

geekoftheweek

Joined Oct 6, 2013
1,094
I believe if memory is limited, using Union instead of a structure is a good idea.
Only in the sense that a union is a group of bits of the same size that are defined differently. If you have structures of the same size you could just as easily cast them to a different structure and it would be essentially the same as using a union.

Edit...
Unless I am missing something about how compilers work there would be no advantage of one over the other
 

nsaspook

Joined Aug 27, 2009
12,273
https://www.allaboutcircuits.com/te...in-c-language-for-packing-and-unpacking-data/
In a previous article, we discussed that the original application of unions had been creating a shared memory area for mutually exclusive variables. However, over the time, the programmers have widely used unions for a completely different application: extracting smaller parts of data from a larger data object. In this article, we’ll look at this particular application of unions in greater detail.
 

WBahn

Joined Mar 31, 2012
29,494
I know what a Union is in C language and I can write programs for Union and structure on my computer. The compiler allocates memory based on the largest Union member. Union members share the same memory space. I believe if memory is limited, using Union instead of a structure is a good idea.

Have you used Union in an embedded program before? What were your needs? In what cases would you prefer using Union in your program?
This reads like you might be thinking that you can start arbitrary variables in a union in order to save space.

While this is possible, it is critical to keep in mind that the union only has one thing stored in it at a time, so if you store something in myunion.bob and then store something in myunion.fred, the value you stored in bob is gone.
 

Ian0

Joined Aug 7, 2020
8,941
If you copy the 8 bytes of received data from the CAN peripheral into a buffer, then you can use a union to address that buffer as either 8 bytes, 4 half-words, or 2 words.
Similarly, I have 64 control parameters which are sent to the control board via CAN.
To get them from the CAN peripheral into memory, the are put into an array.
To use them later, each with its own name, they are a structure.
I make a union of the structure and the union, so that I can save them easily as an array from the CAN peripheral, yet address the same data using
understandable names as a structure
 

Papabravo

Joined Feb 24, 2006
20,600
The only one that I have found useful is to define how bytes in a word are organized on an 8-bit only machine

C:
typedef union
{
    unsigned int    w ;
    unsigned char    b[2] ;
}    UNSIGNED_WORD ;

#define    high    b[0]
#define    low      b[1]
 

Thread Starter

MTech1

Joined Feb 15, 2023
147
For you: Show us how you've used a Union in your program?
Here is Simple union c program

C:
#include <stdio.h>

// Defining a union named ExampleUnion
union ExampleUnion
{
    int integerValue;
    char charValue;
    float floatValue;
};

int main()
{
    union ExampleUnion v;

    v.integerValue = 60;
    v.charValue = 'A';
    v.floatValue = 101.23;

    printf("v.integerValue size = %d bytes\n", sizeof(v.integerValue));
    printf("v.charValue size = %d bytes\n", sizeof(v.charValue));
    printf("v.floatValue size = %d bytes\n", sizeof(v.floatValue));
    printf("Size of v = %d bytes\n", sizeof(v));

    return 0;
}
 

Thread Starter

MTech1

Joined Feb 15, 2023
147
I know my example is not good fit for union That's reason I asked question when union can be helpful, and I gave one reason in mind. When our memory is limited and we need to access only one member at a time, using a union could be really useful.
 

nsaspook

Joined Aug 27, 2009
12,273
Here is Simple union c program

Here's an actual union usage case for a PIC18 K42 controller used as a network communications tester for local checks off-line from the master host server.
It's for a FAB communications protocol SECS-II with a multi-byte bit control structure and possible data blocks that talks to processing tools
https://en.wikipedia.org/wiki/SECS/GEM
The SECS/GEM is the semiconductor's equipment interface protocol for equipment-to-host data communications. In an automated fab, the interface can start and stop equipment processing, collect measurement data, change variables and select recipes for products. The SECS (SEMI Equipment Communications Standard)/GEM (Generic Equipment Model) standards do all this in a defined way.

Developed by the SEMI (Semiconductor Equipment and Materials International) organization,[1] the standards define a common set of equipment behaviour and communications capabilities.
https://en.wikipedia.org/wiki/SECS-II
Usage
The SECS-II defines the message structure between equipment and host. Most of the SEMI E5 standard is a large library of possible messages – a few of which have redundant functionality with different message structures. Most equipment support only a restricted subset of these messages. Some equipments define custom SECS-II messages that are not part of the SEMI E5 standard.

Messages
Only a subset of the possible messages is actually required by the GEM standard. Some SECS-II message transactions may be initiated by only the host. Other SECS-II message transactions may be initiated only by an equipment. A few message transactions may be initiated by either the host or equipment. In order for a SECS-II message to be valid, it must be used by the correct party and have the correct message format (the SECS-II message structure defined by E5). The host and equipment can agree to support custom messages to implement custom features whose format is not defined in SEMI E5, but this is highly discouraged when standard message is sufficient.

The SECS-II messages are organized into categories called streams that are identified by an integer between 0 and 255. Each stream category contains specific messages, or functions, also identified by an integer between 0 and 255. A primary message is an odd-numbered function. A secondary message is the corresponding even numbered function. A request for information and the corresponding data transmission is an example of such an activity. In most transmissions when either the host or equipment sends a primary message, the response is the corresponding secondary message.

Unless the reply bit is clear, a primary message should always be responded to with the complementary secondary message. For most SECS-II messages, a secondary reply message is required. For example, if the host sends an S1, F1 (stream 1, function 1) message to request 'Are you there?', then equipment will send a reply S1, F2 message to indicate 'I am here'. Each SECS-II message exchange has a unique transaction ID number. The standards allow message interleaving where there is more than one open, concurrent transaction.

The SECS-II standard also defines lists of allowed data types including ASCII, binary, boolean, 4 and 8 byte floating points, signed and unsigned integers of byte length 1, 2, 4, or 8 and a List; a container for other data elements including other lists.

SECS-II messages are sent as structured binary data. It is a very efficient means to package information across a network without wasting bandwidth. When using the SECS-I standard, RS-232 serial communication, the message size is limited to 7995148 bytes (about 8 MB). When using the HSMS standard, TCP/IP network communication, the maximum message size is limited to 4294967295 bytes (about 4.3 GB). The structure of each standard SECS-II message is defined by the SEMI E5 SECS-II standard. A message can be a simple data element, such as a binary response or an ASCII string. A message can also be a complex list structure with multiple levels of lists in the hierarchy. The SECS-II standard limits a single element within a SECS-II message to 16777215 bytes (about 16.5 MB).
Header for structures and unions
C:
    typedef struct block10_type {  // <<<<<<<<<<<<<<<<<<< structured binary data
        uint32_t systemb;
        uint8_t bidl;
        uint8_t bidh : 7;
        uint8_t ebit : 1;
        uint8_t function;
        uint8_t stream : 7;
        uint8_t wbit : 1;
        uint8_t didl;
        uint8_t didh : 7;
        uint8_t rbit : 1;
    } block10_type;

    typedef union block10 { // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  UNION
        uint8_t b[sizeof(block10_type)]; // <<<<<<<<<<<<<<<<<<<<<< a stream of types for a bi-directional communications port serial or network
        block10_type block; //  <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  the protocol binary bit structure of those bytes
    } block10;

    typedef struct header10 {
        uint16_t checksum;
        block10 block;
        uint8_t length;
    } header10;

    typedef struct header12 {
        uint16_t checksum;
        uint8_t data[2];
        block10 block;
        uint8_t length;
    } header12;

    typedef struct header13 {
        uint16_t checksum;
        uint8_t data[3];
        block10 block;
        uint8_t length;
    } header13;

    typedef struct header254 {
        uint16_t checksum;
        uint8_t data[244];
        block10 block;
        uint8_t length;
    } header254;
We can then define a series of commands for various functions.
C:
header10 H10[] = {
    { // S1F1 send 'are you there?' from host to equipment
        .length = 10,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 1,
        .block.block.stream = 1,
        .block.block.function = 1,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
    },
    { // all stream and function header receive buffer from equipment
        .length = 10,
    },
    { // S1F0 send 'ABORT' from host
        .length = 10,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 0,
        .block.block.stream = 1,
        .block.block.function = 0,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
    },
    { // S1F1 send 'are you there?' from equipment to host
        .length = 10,
        .block.block.rbit = 1,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 1,
        .block.block.stream = 1,
        .block.block.function = 1,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
    },
    { // S2F17 send 'date and time request?' from host
        .length = 10,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 1,
        .block.block.stream = 2,
        .block.block.function = 17,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
    },
    { // S1F15 send 'request off-line ' from host
        .length = 10,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 1,
        .block.block.stream = 1,
        .block.block.function = 15,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
    },
    { // S1F17 send 'request on-line ' from host
        .length = 10,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 1,
        .block.block.stream = 1,
        .block.block.function = 17,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
    },
};

header12 H12[] = {
    { // S1F2 send 'yes, we are here ' from host
        .length = 12,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 0,
        .block.block.stream = 1,
        .block.block.function = 2,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
        .data[1] = 1,
        .data[0] = 0,
    },
    { // S1F13 send 'online request ' from host to equipment
        .length = 12,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 1,
        .block.block.stream = 1,
        .block.block.function = 13,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
        .data[1] = 1,
        .data[0] = 0,
    },
};

header13 H13[] = {
    { // S6F12 send 'online' reply from host
        .length = 13,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 0,
        .block.block.stream = 6,
        .block.block.function = 12,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
        .data[2] = 0x21,
        .data[1] = 0x01,
        .data[0] = 0x00,
    },
    { // S10F2 send 'terminal' reply from host
        .length = 13,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 0,
        .block.block.stream = 10,
        .block.block.function = 2,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
        .data[2] = 0x21,
        .data[1] = 0x01,
        .data[0] = 0x00,
    },
    { // S5F2 send 'Alarm Report Ack' reply from host
        .length = 13,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 0,
        .block.block.stream = 5,
        .block.block.function = 2,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
        .data[2] = 0x21,
        .data[1] = 0x01,
        .data[0] = 0x00,
    },
    { // S2F26 send 'loopback' reply from host
        .length = 13,
        .block.block.rbit = 0,
        .block.block.didh = 0,
        .block.block.didl = 0,
        .block.block.wbit = 0,
        .block.block.stream = 2,
        .block.block.function = 26,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
        .data[2] = 0x21,
        .data[1] = 0x01,
        .data[0] = 0xff, // binary data
    },
};

header254 H254[] = {
    { // general message buffer for parsing
        .length = 254,
        .block.block.rbit = 1,
        .block.block.didh = 0,
        .block.block.didl = 0xEF,
        .block.block.wbit = 1,
        .block.block.stream = 10,
        .block.block.function = 3,
        .block.block.ebit = 1,
        .block.block.bidh = 0,
        .block.block.bidl = 1,
        .block.block.systemb = 1,
        .checksum = 0x2019,
        .data[1] = 0x19,
        .data[0] = 0x57,
    },
};
A communications I/O function code fragment the reads from a serial port and transfers bytes into the command structure union H10[1] using pointers and indexes
C:
    uint8_t rxData;
    static uint8_t rxData_l = 0, retry = RTY, *b_block, d = 1;

        b_block = (uint8_t*) & H254[0]; // <<<<<<<<<<<<<<<< a structured command buffer

            if (UART1_is_rx_ready()) {
                rxData = UART1_Read();
                if (rxData_l == 0) { // start header reads
                    r_block.length = rxData; // header+message bytes
                    run_checksum(0, true);
                    rxData_l++;
                    b_block[sizeof(header254) - rxData_l] = rxData; // buffer the message
                } else {
                    /*
                     * skip possible message data
                     */
                    if (rxData_l <= sizeof(block10)) // save header only
                        H10[1].block.b[sizeof(block10) - rxData_l] = rxData; // <<<<<<<<<<<<<<<<<<< start moving received data into H10[1] union as a block of bytes

                    if (d <= 16) {
                        if (rxData_l == sizeof(block10) + d) { // save possible data format codes
                            V.response.ack[d - 1] = rxData;
                            d++;
                        }
                    }

                    if (rxData_l <= r_block.length) // generate checksum from data stream
                        V.r_checksum = run_checksum(rxData, false);

                    if (rxData_l == r_block.length + 1) // checksum high byte
                        H10[1].checksum = (uint16_t) rxData << 8; // <<<<<<<<<<<<<<<<<<<<<<< start checking protocol values from H10[1] union as a structure
                    if (rxData_l == r_block.length + 2) // checksum low byte
                        H10[1].checksum += rxData;

                    rxData_l++;
                    b_block[sizeof(header254) - rxData_l] = rxData;
                    if (rxData_l > (r_block.length + 2)) { // end of total data stream
                        if (V.r_checksum == H10[1].checksum) {
                            *r_link = LINK_STATE_ACK;
                            DEBUG1_SetHigh();
                        } else { // bad checksum
                            while (UART1_is_rx_ready()) // dump receive buffer of bad data
                                rxData = UART1_Read();
                            WaitMs(T1); // inter-character timeout
                            V.error = LINK_ERROR_CHECKSUM;
                            V.checksum_error++;
                            V.all_errors++;
                            V.failed_receive = 2;
                            *r_link = LINK_STATE_NAK;
                        }
                    }
                }
            }
Using unions I can format (as a source software abstraction) complex protocol structures into the same blocks of memory used to send/receiver those messages a simple sequence of bytes over a communications channel.

The test device working to control vacuum load-locks on a 200mm wafer processing tool.

https://forum.allaboutcircuits.com/threads/secs-ii-host-using-a-pic18f57k42.157503/post-1395023
 
Last edited:
Top