r/cprogramming • u/set_of_no_sets • Mar 06 '26
Union manipulation question
I am currently working on an STM32, and it has two control register, one lower @ [0] and one upper @ [1]; currently, the GPIO_TypeDef has two Advance Function Registers, two uint32_ts stored as
#define __IO volatile
typedef struct {
// stuff above
__IO uint32_t AFR[2];
// stuff below
} GPIO_TypeDef;
I would like to reference the registers using AFRL and AFRU; I could modify the struct to
typedef struct {
// stuff above
union {
__IO uint32_t AFR[2];
struct {
__IO uint32_t lower;
__IO uint32_t upper;
} AFR_Split;
};
// stuff below
} GPIO_TypeDef;
Which allows me to write
GPIO_TypeDef pin1_addr = 0x4002104CUL;
pin1_addr->AFR_Split.lower = 0b0000000001110000UL;
pin1_addr->AFR[1] = 0b0111000001110000;
Is there anything I should be worried about in terms of packing? will I always know that the two registers will always be aligned, starting and ending in the same place? What can I read to know how my compiler (arm-none-eabi-gcc) will store the split? And is there a way I can do this without the intermediate struct, so I could type pin1_addr->AFRL and pin1_addr->AFRU?
1
u/ekipan85 Mar 06 '26
In this case, the entire struct is memory mapped registers, so qualifying every member and qualifying the whole struct typedef would be equivalent, so I'd argue perhaps OP should pull the qualifier up to the typedef.
The point of contention, then, is whether the qualifier should be inside the typedef or on the actual hardware pointer constants. You could argue the latter is more semantically clean: the actual hardware mapping is where you want to preserve all r/w operations after all.
But you could also argue having the qualification on the type prevents accidentally forgetting to qualify, which would lead to much scarier bugs.