Why does STMCubeMX use redundancy when defining bits?

Viewed 63

I was looking at how bits are defined in the header file generated by STM32CubeMX and this is what I found:

#define RCC_AHB1LPENR_GPIOALPEN_Pos        (0U)                                
#define RCC_AHB1LPENR_GPIOALPEN_Msk        (0x1UL << RCC_AHB1LPENR_GPIOALPEN_Pos) /*!< 0x00000001*/
#define RCC_AHB1LPENR_GPIOALPEN            RCC_AHB1LPENR_GPIOALPEN_Msk

What is the reason for this redundancy? Wouldn't this

#define RCC_AHB1LPENR_GPIOALPEN_Pos        (0U)                                
#define RCC_AHB1LPENR_GPIOALPEN            (0x1UL << RCC_AHB1LPENR_GPIOALPEN_Pos) /*!< 0x00000001*/

or this

#define RCC_AHB1LPENR_GPIOALPEN            (0x1UL << 0U) /*!< 0x00000001*/

work in the exact same way? I tried Search Text > Workspace (Ctrl + Alt + G), and position nor mask are used anywhere else in the header file...

3 Answers

They are handy notations for different situations. You have bits of the register, their mask and their definition as positions.

Example:
Imagine you have a field of 4 bits in a 32-bit register, bits 2-5. For example, they set some specific parameter up. Then there will be a bit definition:

#define RCC_EXAMPLEREGISTER_EXAMPLESETTING (0x3C) // 0000..00111100

Now, the mask definition actually is redundant as follows from your code quote:

#define RCC_AHB1LPENR_GPIOALPEN            RCC_AHB1LPENR_GPIOALPEN_Msk

I suspect the purpose of this is so that when you read the code and you see that it's mask, you immediately know what that value represents. RCC_AHB1LPENR_GPIOALPEN can be not very descriptive as to what value it contains unless you already know how this set of definitions works. Personally, I never ever use mask and haven't actually seen it very much in use.

Position is a completely different story. Let's get back to my example of a field of more than one bit (actually, this holds perfectly well for 1 bit definition like in your example as well). In my case, the bits of my field start at position 2, so

#define RCC_EXAMPLEREGISTER_EXAMPLESETTING_Pos (2U)

It's very handy in certain expressions. It eliminates the need to manually shift the desired value you want to place into those bits. For example, let's say I still have this 4-bit-wide field of bits 2-5, and I want to place number "9" into there. If you don't have a position definition, you will have to calculate number 9 shifted into position 2 and write that thing into the register. Except that you will have to actually look up in the documentation every time what position you need to shift it in. For every field for every register. It's tedious. Instead, I will do the following:

RCC->EXAMPLEREGISTER |= (0x09 << RCC_EXAMPLEREGISTER_EXAMPLESETTING_Pos);    

Notice how I just need the name of the field, but I don't need to know what exactly position the bits are in. I don't need to look it up, it's already done for us. I just know that EXAMPLESETTING bits are in that register and I don't even open the reference manual to see what position they're in. Saves a lot of time if you need to do it for every register and every field in there.

Also, the compiler will make sure to optimize it, after all, it's compiler's job. It's a fixed number shifted by fixed number bits, so this will not create any overhead, there will be no computation of that during execution if it's not a variable. And if it is, then you will have to do some shifts in the program anyway, either before writing into the register or on the same line as you write the register if you use _Pos.

The *_Pos constant is needed for shifting your own values into the correct bitfield.

The name of the bitfield without _Msk is used by most code for historical reasons. The name of the bitfield with _Msk is a newer idea introduced to try to give more consistency.

There are some named constants defined which are bitfield values with other than all bits set, these do not have the _Msk suffix, but append another word to the register and bitfield name to idicate the purpose of the value.

*_Pos definitions becomes especially useful when the setting/field in question is not a single bit but a collection of two or more bits, like GPIOx_MODER bits for example.

For single bit settings/fields, using the mask directly is easier and *_Pos definitions aren't needed.

Related