Macros, Base addresses and API functions

So Iím a little confused with this code I am wading through. Itís the CMSIS level for the 32-bit zero gecko from Silabs.

Basically they have a series of defines, for the base address of each peripheral. Followed by a macro, which contains a pointer to a struct of ***_TypeDef as the first parameter (these typedef structs are declared in repsective peripheral header files), and then the defined base addresses as a second paramater. Defines as follows:

At the start of my learning, this was naturally all a bit confusing. (I made a post earlier in the year about these structs. It was made clear to me that the size of each register and thus the calculated offset, was defined by size of the data type. Very cool!)

Then after having stumbled upon the syntax for writing to a register i.e.

Code:

USART1→ROUTE (= / |= / &=) PREDEFINED_MASK_EXAMPLE;

I then learned about the arrow operator (something I personally feel should be called the deference [not de-reference] operator) and why we use it as opposed to the dot operator: because I am accessing a member of a struct via a pointer. Again, very cool. Woot.

So all of that makes sense. I call a macro, access a member via a pointer and the base address is passed to the macro. Then magic.

What doesnít make sense is two things:

1. In the macros, how does the second parameter i.e. base address, actually become associated with the struct. Is this something that is inherent to macros??

2. The same question plays out, in a slightly different form, in the following function which is defined in the silabs api for this chip.

I understand that the first parameter is a pointer to a struct of USART_TypeDef and that we are writing to an instance of that struct.

What I am struggling to see is where the appropriate peripheral base addresses are being passed to this instance of said struct. For all I can see itís merely a random instance of a struct, just like any other struct, without association to any real address space register.

The passing of the struct as the first parameter (and base address as second parameter), to the macro is only relevant when I call that macro directly.. yes?
Again,how does this base address become associated with the struct inside the macro and how then does the same thing happen to the instance of the struct, that is created inside the above mentioned function?

All the macros are doing is substituting the memory address the compiler knows about with &myVar, with a physical address on your board such as 0x4000C400UL.

> I understand that the first parameter is a pointer to a struct of USART_TypeDef and that we are writing to an instance of that struct.
Given your macros, I expect that the first parameter will always be USART1 when programming for this particular device.
As in

The whole point of the macros, base register pointers and wrapper functions like USART_SpiTransfer() is to create a much nicer programming environment where 99% of the code simply doesn't care that a USART exists at address 0x4000C400UL. If that address were to change, it would be a simple 1-line code change and recompilation, and the rest of the code carries on in happy ignorance of the underlying hardware.

[/COLOR]Do you understand this bit of convolution as being the same asCode:
[COLOR=white !important]?

1

myVar.TXDATA = 0;

[/COLOR]All the macros are doing is substituting the memory address the compiler knows about with &myVar, with a physical address on your board such as 0x4000C400UL.

Yes I understand that macros are simple text replacement. And I understand that the dot operator and arrow operator are essentially the same. As mentioned the only reason we use the arrow operator, is because I am accessing a member of a struct via a pointer.

Otherwise, without the pointer, I could simply access the member like this:

Code:

Code:

myVar.TXDATA = 0;

I understand all of this, the peripherals like USART, CMU etc. are all memory mapped to registers in address space. Totally solid on that concept.

When I use that macro i.e.

Code:

USART1->ROUTE = BITMASK_EXAMPLE;

I am simply writing to that address space.

My question was more 'what are the exact mechanics for associating that base address with the struct pointer' (below). Having done a little more digging around, I have found that the following macro contains a simple cast:

The nested brackets confused me, I thought it was some sort of nested bracket magic (or something). But apparently it's just like any other casting operation.

So that's solved; it's casting the USART1_BASE macro as a pointer of type USART_TypeDef, to that address. Super cool trick!!! Please correct me if I'm not 100% in my understanding on that one.

> I understand that the first parameter is a pointer to a struct of USART_TypeDef and that we are writing to an instance of that struct.Given your macros, I expect that the first parameter will always be USART1 when programming for this particular device.As inCode:
[COLOR=white !important]?

1

uint8_t rxByte = USART_SpiTransfer(USART1, '\n');

[/COLOR]Presumably on other supported chipsets, there may be a Code:
[COLOR=white !important]?

[/COLOR]The whole point of the macros, base register pointers and wrapper functions like USART_SpiTransfer() is to create a much nicer programming environment where 99% of the code simply doesn't care that a USART exists at address 0x4000C400UL. If that address were to change, it would be a simple 1-line code change and recompilation, and the rest of the code carries on in happy ignorance of the underlying hardware.

AAHHHH!!!! Of course!! There are matching function calls! derrrr.... In my intense study and staring at the code, I completely forgot about the part where I actually call the function! haha.. I need to pass the macro USART1 to this function as the first parameter (which would be just like writing the base address, cast as USART1_TypeDef*) and then the arrow operator, inside the function, takes care of the rest. Oh I feel so silly.