In different hardware libraries for MCU I've seen (STM32 HAL, GD32 SPL, MDR32 SPL, LibOpenCM3), for operating with registers bits, logic operations (shifts, ands, ors, nots, etc) are used. But why not to use structs with bit fields for that? This can make code easier to read and use. The only reason I can think of is that endianness is not enforced by language standard, so it relies on compiler. But low-level code is made for specific device, so compiler settings are also known.
If so, why would one prefer to use tons of macros instead of using bunch of structs?
Simply because bit-fields are very poorly defined by the C language standard and a library using them would be non-portable. The kind of libs you mention seek to be portable to more than one compiler, meaning that bit-fields should be avoided.
But low-level code is made for specific device, so compiler settings are also known.
A specific device is most often supported by several different compilers, so not much is known about the compiler(s) unless you only intend to support a select few.
Some libs do use bit-fields (Atmel ASF for example), when they only hope to get supported by one or two compilers (in the case of ASF I think only gcc-arm-none-eabi/Microchip Studio and IAR).
Regarding readability: C programmers are expected to be able to read basic C code. If the library looks something like this (also from the mentioned Atmel ASF):
#define ADC_CTRLA_SWRST_Pos 0
#define ADC_CTRLA_SWRST (0x1u << ADC_CTRLA_SWRST_Pos)
Then a C programmer is expected to understand that this is a bit mask defined to be used when accessing the bit SWRST
in the register CTRLA
which in turn is part of the ADC peripheral. So if I want to enable this bit in my application code using this library header, I would naturally write:
ADC_CTRLA |= ADC_CTRLA_SWRST;
Nothing about this code is exotic and strange, it is beginner-level bitwise operations. The only reason anyone would simplify this further would be if they expect non-programmers to read the code, but why would anyone expect that?
(Now as it happens this particular example uses a union
between a struct bit-field and the raw register which is actually named ADC_CTRLA.reg
. So we use either bitwise operations or bit-fields in this specific case.)