A better way to access STM32 registers

MrChips

Joined Oct 2, 2009
34,807
I understand the conventions and practices used by professional engineers and the various pros and cons involved.

As for code breaking that is no more a risk for bitfields than it is for predefined offsets, one simply uses a typedefs header appropriate for the device, that is just as the explicit offset constants are defined per-device, so too can bitfields.

Clearly the typedefs I've defined so far pertain to STM32F407, for a different device I'd like define a slightly different set.

I'm doing this for personal edification and to gain a better understanding of the MCUs, this isn't my job, I don't have any customers.
I am not sure what is your point.

When I use, for example,

RCC_MCO1Config(RCC_MCO1Source_PLLCLK, RCC_MCO1Div_4);

I understand exactly what it is doing. I don't need to know what bit in what register is being altered.
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,762
I am not sure what is your point.
I already explained my point:

I'm doing this for personal edification and to gain a better understanding of the MCUs, this isn't my job, I don't have any customers.

When I use, for example,

RCC_MCO1Config(RCC_MCO1Source_PLLCLK, RCC_MCO1Div_4);

I understand exactly what it is doing. I don't need to know what bit in what register is being altered.
That's absolutely fine, I understand there is a large API like that, I don't dispute that. But there is a plethora of such functions and often one needs to look at the source code to see not only what they do but also what they don't do. This makes it rather difficult to learn about and understand the device - IMHO anyway.

Besides it's not so much the API I'm addressing but rather the way these APIs are written. If we study the source code for this example you gave it is far from obvious what register fields are being cleared and/or set. Here's that register which by pure coincidence I defined the other day:

Code:
typedef union
{
    struct
    {
        // LSB at top, MSB at bottom
        bit SW : 2;
        bit SWS : 2;
        bit HPRE : 4;
        bit UNUSED2 : 2;
        bit PPRE1 : 3;
        bit PPRE2 : 3;
        bit RTCPRE : 5;
        bit MCO1 : 2;
        bit I2SSC : 1;
        bit MCO1_PRE : 3;
        bit MCO2_PRE : 3;
        bit MCO2 : 2;
    };
    uint32_t ALLBITS;
} RCC_CFGR_Reg, * RCC_CFGR_Reg_ptr;
If an API function was written that set/unset the relevant fields via that typedef we'd see immediately and exactly what it's doing and be able to map that directly to the chip's documentation.

But from the function as it stands we cannot, all we know is that it uses a rather cryptic constant to clear some bits as part of it's operation.

Code:
#define CFGR_MCO1_RESET_MASK      ((uint32_t)0xF89FFFFF)
Whereas this makes its crystal clear which fields in the register are being cleared:

Code:
reg.MC01_PRE = 0;
reg.MC01 = 0;
I can look at that source code and it maps directly and exactly to the chip's documentation and I'll argue with anybody that the bit field code is easier to understand and this is a trivial example. Are there disadvantages to this? yes there are pros and cons like any design decision and in my case the added clarity is helping me understand these peripherals much more readily than using the masks and shits and so on.

Finally you said "I don't need to know what bit in what register is being altered" but one likely does want to know what fields in the register are being manipulated because the documentation is written in terms of those fields.
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,762
In fact I just realized I have equivalent code in a simple demo:
Code:
    ahb1_ptr->RCC.CFGR.MCO1_PRE = MCOPRE(5);
    ahb1_ptr->RCC.CFGR.I2SSC = I2SSRC(PLLI2S);
    ahb1_ptr->RCC.CFGR.MCO1 = MCO1(PLL);
That's a bitfield version of this code from a book:
Code:
RCC->CFGR &= ~0x07E00000;
RCC->CFGR |=  0x07600000;
Regarding readability, and understanding by a novice I rest my case.
 

MrChips

Joined Oct 2, 2009
34,807
We are beating a dead horse.

If I want to know what bits are affected, I look it up in the device User Guide.

If I want to modify a bit manually, I go into debug and access the register in question. I don't need to know the exact bit position because debug lists all the bits and registers of the peripheral using the defined labels. I can modify the register bits on the fly. There is no need to recompiled the program.

STM32F407 RCC register.jpg
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,762
We are beating a dead horse.
I don't think I'm doing that at all, I'm simply disagreeing with your dismissal of what is clearly an alternative way of expressing code that offers greater readability.

If I want to know what bits are affected, I look it up in the device User Guide.

If I want to modify a bit manually, I go into debug and access the register in question. I don't need to know the exact bit position because debug lists all the bits and registers of the peripheral using the defined labels. I can modify the register bits on the fly. There is no need to recompiled the program.

View attachment 315162
I don't see how that invalidates my argument that the source code is more readable and corelates better with the device's reference manual. Glancing at this alone:

Code:
RCC->CFGR &= ~0x07E00000;
RCC->CFGR |=  0x07600000;
Does not reveal that it's doing this:
Code:
    ahb1_ptr->RCC.CFGR.MCO1_PRE = MCOPRE(5);
    ahb1_ptr->RCC.CFGR.I2SSC = I2SSRC(PLLI2S);
    ahb1_ptr->RCC.CFGR.MCO1 = MCO1(PLL);
That's all I'm arguing here, not arguing that the established way the code is written by professionals is wrong, only that if clarity and readability are the most important things then the bitfields design is the better way.

Perhaps we should just agree to disagree on this last point and move on.
 

MrChips

Joined Oct 2, 2009
34,807
Different strokes for different folks.

Some people are accustomed to program computers by flipping toggle switches.
Some people like to solder wires on a protoboard.
Some people like to use a graphical user interface.

To each their own.
 

Thread Starter

ApacheKid

Joined Jan 12, 2015
1,762
This might qualify as the most horrifying example of C I've seen so far in my career!
C:
SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
This code is setting the interrupt priority for the SysTick timer interrupt. The SHP register set (three 32-bit registers) is itself a non-uniform byte array (or can be treated as such), although this is frightening code I am learning a lot...

There's also a small error in the documentation of the SHP registers - let's see if anyone can spot it:

1707863974851.png
 
Last edited:
Top