To locate, the Device header file. The location is : C:\Keil_v5\ARM\Pack\Keil\STM32F4xx_DFP\2.13.0\Drivers\CMSIS\Device\ST\STM32F4xx\Include
ARM makes CMSIS, so it has its own design guidelines, so the header files are CMSIS compatible as they use some macros that are defined in CMSIS header file. It contains the description of all the peripherals. Each peripherals is defined as C-Data structure and each of its registers are defined as volatile. Volatile because, these values can be changed without user doing so, so every-time you want to read its values, the compiler should check its current value and return. No optimization on any access related to these because compilers usually do such optimizations to make your code fast.
The developers use reference manual to code these files. For instance, the peripherals hanging to the APB1 bus have certain base addresses as we saw in the ref manual.
As i t can be seen, the first peripheral is TIM2 whose base address is 0x40000000. Similarly, the header file also declares the same address as shown.
Now, to understand this basic defination : ******************. For example, when i want to use any peripheral, that means i want to access or program the register peripherals. For that, we must know what is its base address. To program the Date register of the RTC, i must know where this register lies. To not to go back and forth to the reference manual, we have defined a structure for the RTC Registers and linked it with the base address of the RTC.
Let's do this linking in the following program.
https://github.com/manvendra88/STM32/blob/master/understanding_headerfile.c . Well commented for understanding why typecasting is needed. \
But, thanks to the header file, all this is done in the header file so you just have to include it. This is important to understand that by type-casting statement, the address of your pointer becomes same as the base-address of the peripheral that you are referring to. This can be digested using the following C-Program.
https://github.com/manvendra88/STM32/blob/master/typecating.c
This shows that when you define a structure pointer to an address. All the variables defined inside it are also declared with an offset which is equal to the register size.
Another question could be why we need to define another pointer for this? Because, it allows switching between different Registers. For example, ADC_Typedef *pADC can be used for pADC = ADC1 or for pADC=ADC2. It is a bad practice to confine it to one peripheral.