Practical Operations for Nested Structures and Unions in Embedded C Programming

Practical Operations for Nested Structures and Unions in Embedded C Programming

Content

Structures and unions are composite types in the C language, and we often use structures. However, some beginners may not use unions frequently and may even feel unfamiliar with them. Let’s take a brief look at unions:

The keyword for defining a union in C is union.

The general form for defining a union type is:

union union_name
{
    member_list
};

The member list contains several members, and the general form of a member is: type_specifier member_name. The number of bytes it occupies is equal to the number of bytes occupied by the largest data type among the members.

Next, let’s explore some practical techniques for using structures and unions together in C language and embedded systems.

1. Managing Different Data Types

Example code:

enum DATA_PKG_TYPE
{
    DATA_PKG1 = 1,
    DATA_PKG2,
    DATA_PKG3    
};

struct data_pkg1
{
    // ...
};

struct data_pkg2
{
    // ...
};

struct data_pkg3
{
    // ...
};

struct data_pkg
{
    enum DATA_PKG_TYPE data_pkg_type;
    union 
    {
       struct data_pkg1 data_pkg1_info;
       struct data_pkg2 data_pkg2_info;
       struct data_pkg3 data_pkg3_info;
    }data_pkg_info;
};

Here, we have placed struct data_pkg1, struct data_pkg2, and struct data_pkg3 into the struct data_pkg for management, establishing a one-to-one correspondence between data_pkg_type and the three structures in the union. We can select which structure’s data to use based on data_pkg_type.

When packaging data, we first assign a value to data_pkg_type to determine the type of data packet, and then assign values to the corresponding structure in the union; during data parsing, we use data_pkg_type to select which set of data to parse.

Consider what would happen if we nested another union inside the existing union? Would it become more complex? In the past, I thought that the more nested it was, the more complicated it would become, and I resisted such nesting. However, after seeing the design by my colleague Yuying (WeChat public account: Yuying Talks about Microcontrollers), I was amazed! This is a brilliant way to manage complex system data clearly. Let’s take a look at his design (a rough diagram):

Practical Operations for Nested Structures and Unions in Embedded C Programming

We can see that the leftmost and rightmost parts establish a one-to-one correspondence. We have many modules and a lot of data, but in such a design, it appears very clear and easy to maintain.

2. Register and Status Variable Encapsulation

Let’s take a look at how TI encapsulates registers:

Practical Operations for Nested Structures and Unions in Embedded C Programming

All registers are encapsulated as union types, where the members include a 32bit integer and a structure that reflects the bit fields. This allows direct manipulation of certain bits of the registers. For example, to set both bits [1:0] of the GPAQSEL1 register for pin PA0 to 1, we can easily set it by manipulating just two bits:

GpioCtrlRegs.GPAQSEL1.bit.GPIO0 = 3

Or directly manipulate the entire register:

GpioCtrlRegs.GPAQSEL1.all |= 0x03

If you are not working at the chip manufacturer, register encapsulation may seem distant. However, we can learn to use this method and apply it in our actual application development.

Now let’s look at a practical application: managing some status variables.

Example code:

union sys_status
{
   uint32 all_status;
   struct 
   {
      bool status1:  1; // FALSE / TRUE
      bool status2:  1; // 
      bool status3:  1; // 
      bool status4:  1; // 
      bool status5:  1; // 
      bool status6:  1; // 
      bool status7:  1; // 
      bool status8:  1; // 
      bool status9:  1; // 
      bool status10: 1; // 
      // ...
   }bit;
};

I remember a friend in the group asked how to manage dozens of status variables in the system. The above example is a good management method.

3. Data Combination/Splitting, Endianness

(1) Verifying Endianness

#include <stdio.h>

typedef unsigned int  uint32_t;
typedef unsigned char uint8_t;

union bit32_data
{
    uint32_t data;
    struct 
    {
        uint8_t byte0;
        uint8_t byte1;
        uint8_t byte2;
        uint8_t byte3;
    }byte;
};

int main(void)
{
    union bit32_data num;
    
    num.data = 0x12345678;
 
    if (0x78 == num.byte.byte0)
    {
      printf("Little endian\n");
    }
    else if (0x78 == num.byte.byte3)
    {
      printf("Big endian\n");
    }else{}

    return 0;
}

Running Result:

Practical Operations for Nested Structures and Unions in Embedded C Programming

(2) Data Combination and Splitting

Before combining and splitting data, we first need to confirm the endianness of the current platform. For example, the platform I am using is little-endian.

① Splitting 0x12345678 into 0x78, 0x56, 0x34, 0x12:

#include <stdio.h>

typedef unsigned int  uint32_t;
typedef unsigned char uint8_t;

union bit32_data
{
    uint32_t data;
    struct 
    {
        uint8_t byte0;
        uint8_t byte1;
        uint8_t byte2;
        uint8_t byte3;
    }byte;
};

int main(void)
{
    union bit32_data num;
    
    num.data = 0x12345678;

    printf("byte0 = 0x%x\n", num.byte.byte0);
    printf("byte1 = 0x%x\n", num.byte.byte1);
    printf("byte2 = 0x%x\n", num.byte.byte2);
    printf("byte3 = 0x%x\n", num.byte.byte3);

    return 0;
}

Running Result:

Practical Operations for Nested Structures and Unions in Embedded C Programming

② Combining 0x78, 0x56, 0x34, 0x12 into 0x12345678:

#include <stdio.h>

typedef unsigned int  uint32_t;
typedef unsigned char uint8_t;

union bit32_data
{
    uint32_t data;
    struct 
    {
        uint8_t byte0;
        uint8_t byte1;
        uint8_t byte2;
        uint8_t byte3;
    }byte;
};

int main(void)
{
    union bit32_data num;
    
    num.byte.byte0 = 0x78;
    num.byte.byte1 = 0x56;
    num.byte.byte2 = 0x34;
    num.byte.byte3 = 0x12;

    printf("num.data = 0x%x\n", num.data);

    return 0;
}

Running Result:

Practical Operations for Nested Structures and Unions in Embedded C Programming

However, there is a better method for data combination and splitting: bitwise operations. Due to space limitations, I will not provide the code here.

4. Structures & Buffers

#define BUF_SIZE 16
union protocol_data
{
 uint8_t data_buffer[BUF_SIZE];
 struct 
 {
  uint8_t data1;
  uint8_t data2;
  uint8_t data3;
  uint8_t data4;
  // ...
 }data_info;
};

This application is very common and is used for custom communication protocols. The contents of the struct can be designed to be very simple, such as containing only useful data, or designed to be complex, including protocol headers, lengths, valid data, checksums, etc.

In any case, the process of packaging and sending is fill structure -> send data_buffer; conversely, the process of receiving and parsing data is receive data into data_buffer -> use structure data.

5. Transmitting Floating Point Data

union f_data 
{
 float f;
 struct
 {
  unsigned char byte[4];
 };
}

Similarly, this method can be used for transmitting floating-point numbers. For more specific details, there are many resources available online. Interested readers can experiment and verify this themselves.

Author: WeChat Public Account: Embedded Miscellaneous

Copyright belongs to the original author or platform, for learning reference and academic research only. If there is any infringement, please contact for deletion~ Thank you.

Finally

That’s all for today’s sharing. If you found it helpful, please remember to give a like~Bug’s unique and permanent embedded technology knowledge platform~Practical Operations for Nested Structures and Unions in Embedded C ProgrammingRecommended Collections Click the blue text to jumpMCU Advanced Collection Practical Operations for Nested Structures and Unions in Embedded C ProgrammingEmbedded C Language Advanced Collection Practical Operations for Nested Structures and Unions in Embedded C Programming“Bug Says” Collection Practical Operations for Nested Structures and Unions in Embedded C ProgrammingCollection | Comprehensive Programming of Linux ApplicationsCollection | Learn Some Networking KnowledgeCollection | Handwritten C LanguageCollection | Handwritten C++ LanguageCollection | Experience SharingCollection | Power Control TechnologyCollection | From Microcontrollers to LinuxPractical Operations for Nested Structures and Unions in Embedded C Programming

Leave a Comment