π Home | π Embedded Systems | π§° Development Toolbox | π Training Courses | π Documents
π― In embedded C programming, struct and union are essential user-defined data types that enable efficient management of related data. A struct (structure) allows grouping variables of different types under a single name, making it easier to organize and access complex data, such as sensor readings or device configurations. Each member of a struct occupies its own memory space, enabling simultaneous storage of multiple values.
π‘ A union, on the other hand, provides a way to store different data types in the same memory location. All members of a union share the same memory, so only one value can be stored at a time. This is particularly useful in memory-constrained embedded systems where optimizing resource usage is critical.
π‘ Both struct and union play a vital role in embedded applications, offering flexibility, improved code readability, and efficient memory management for handling diverse data requirements.
π Here are the key points of the struct data type in C:
Hereβs a typical use case for using the struct data type in embedded systems:
π Structs are commonly used to group related data for hardware peripherals, such as sensors or communication modules. For example, you can define a struct to store all relevant information about a sensor, including its ID, measurement value, status, and configuration settings. This approach simplifies data management, improves code readability, and makes it easier to pass complex data between functions or modules in embedded applications.
struct
keyword followed by the structure name and its member definitions. For example:
struct Sensor {
int id;
float value;
char status;
};
struct Sensor sensor1 = {1, 23.5, 'A'};
.
) operator. For example:
sensor1.value = 25.0;
sensor1.status = 'B';
π Here are the key points of the union
data type in C:
.
) operator.Hereβs a typical use case for using the union data type in embedded systems:
π Unions are commonly used when a variable may need to store different types of data at different times, such as protocol packets, hardware registers, or interpreting raw data from peripherals. For example, a union can be used to access the same memory as an integer or a byte array, depending on the applicationβs requirements. This approach helps save memory and simplifies data handling in embedded applications.
union
keyword followed by the union name and its member definitions. For example:
union Data {
int intValue;
char byteArray[4];
};
union Data data1;
data1.intValue = 100;
.
) operator. For example:
data1.byteArray[0] = 10;
[!CAUTION] This updates the
byteArray[0]
member of the data1 union variable. Note that modifying one member will overwrite the value of other members, since all share the same memory.
π Source code: demo project
π The size of a struct is typically the sum of the sizes of all its members, plus any padding added for alignment. For a union, its size is determined by its largest member, since all members share the same memory space.
// Example: struct data type for a rectangle shape
typedef struct {
uint16_t width_u16;
uint16_t height_u16;
uint32_t area_u32;
} Rectangle_st;
// Example: union data type for register access
typedef struct {
uint32_t bit0 : 1;
uint32_t bit1 : 1;
uint32_t bit2 : 1;
uint32_t bit3 : 1;
uint32_t reserved : 28;
} RegisterBits_st; // Access individual bits
typedef union {
uint32_t register_u32; // Access the entire register
RegisterBits_st bits_st; // Access individual bits
uint8_t registerBytes_u8[4]; // Access as bytes
uint16_t registerHalfWords_u16[2]; // Access as half-words
} RegisterType_un;
[!TIP] Using typedef before struct and union lets you create a type alias for the structure or union. This makes your code cleaner and easier to read, as you can use the alias directly without repeatedly writing struct or union before variable declarations. For example, with
typedef struct { ... } RegisterBits_st;
, you can declare variables asRegisterBits_st reg;
instead ofstruct RegisterBits_st reg;
. This is especially useful in embedded systems where code clarity and brevity are important.
π Actual size of struct and union data types in embedded system memory:
2
(width_u16) + 2
(height_u16) + 4
(area_u32) = 8
bytes4
(register_u32), 4
(bits_st), 4
(registerBytes_u8[4]), 4
(registerHalfWords_u16[2])) = 4
bytesπ You can initialize a struct variable when you declare it, assigning values to its members immediately.
π After a struct variable has been declared, you can update its members at any time using the dot operator.
π You can modify any member of a union variable after it has been declared. However, updating one member will immediately affect the values of all other members, since they share the same memory space.
Set the entire register value:
Set individual bit values:
Update union member value as bytes:
Update union member value as half-words:
[!IMPORTANT] The order of struct members can impact the overall size of a struct because of memory alignment and padding. Grouping members of similar or decreasing size together helps minimize padding and improves memory efficiency.
[!IMPORTANT] The amount of padding bytes inserted depends on the microcontrollerβs architecture and its memory alignment requirements.
π The following demonstration shows how padding bytes are used for alignment in the 32-bit STM32F103C6 microcontroller and offers tips for optimizing memory usage.
π Padding bytes may be inserted if struct members are not arranged efficiently. For example, 2 bytes of padding can be added after a uint16_t
member to align the next member on a 32-bit boundary. The uint32_t
member naturally fits the 32-bit memory layout without requiring extra padding.
typedef struct {
uint16_t width_u16;
uint32_t area_u32;
uint16_t height_u16;
} Rectangle_st;
The padding bytes are visible in the microcontrollerβs memory layout:
[!TIP] One effective way to reduce padding bytes is to arrange struct members from largest to smallest, or to group them according to the bit-width supported by the microcontroller.
typedef struct {
uint16_t width_u16;
uint16_t height_u16;
uint32_t area_u32;
} Rectangle_st;
[!TIP] Another method is to use compiler-specific features, such as the
packed
attribute in the ARM compiler, to force members to be allocated consecutively without padding. Similar options are available in other compilers.
typedef struct __attribute__((packed)) {
uint16_t width_u16;
uint32_t area_u32;
uint16_t height_u16;
} Rectangle_st;
π You can use this demo project to experiment further and deepen your understanding of struct and union data types.
π¨ Development Boards: STM32F103 Blue Pill Development Board
π§ Tools: Keil uVision
π My Repositories
If you have any thing would like to discuss or cooperate with me, please donβt hesitate to contact me via:
π Home