Reserved structure member in CMSIS device peripheral access file - arm

Why in the CMSIS device file "TM4C123GH6PM.h" there are reserved structure members declared in some of the peripheral's structures for example in TIMER0_Type structure there are two reserved members.
does it have something to do with the microcontroller itself or is it concerned with word alignment and how the structure is going to be allocated in the memory?
typedef struct { /*!< TIMER0 Structure */
__IO uint32_t CFG; /*!< GPTM Configuration */
__IO uint32_t TAMR; /*!< GPTM Timer A Mode */
__IO uint32_t TBMR; /*!< GPTM Timer B Mode */
__IO uint32_t CTL; /*!< GPTM Control */
__IO uint32_t SYNC; /*!< GPTM Synchronize */
__I uint32_t RESERVED;
__IO uint32_t IMR; /*!< GPTM Interrupt Mask */
__IO uint32_t RIS; /*!< GPTM Raw Interrupt Status */
__IO uint32_t MIS; /*!< GPTM Masked Interrupt Status */
__O uint32_t ICR; /*!< GPTM Interrupt Clear */
__IO uint32_t TAILR; /*!< GPTM Timer A Interval Load */
__IO uint32_t TBILR; /*!< GPTM Timer B Interval Load */
__IO uint32_t TAMATCHR; /*!< GPTM Timer A Match */
__IO uint32_t TBMATCHR; /*!< GPTM Timer B Match */
__IO uint32_t TAPR; /*!< GPTM Timer A Prescale */
__IO uint32_t TBPR; /*!< GPTM Timer B Prescale */
__IO uint32_t TAPMR; /*!< GPTM TimerA Prescale Match */
__IO uint32_t TBPMR; /*!< GPTM TimerB Prescale Match */
__IO uint32_t TAR; /*!< GPTM Timer A */
__IO uint32_t TBR; /*!< GPTM Timer B */
__IO uint32_t TAV; /*!< GPTM Timer A Value */
__IO uint32_t TBV; /*!< GPTM Timer B Value */
__IO uint32_t RTCPD; /*!< GPTM RTC Predivide */
__IO uint32_t TAPS; /*!< GPTM Timer A Prescale Snapshot */
__IO uint32_t TBPS; /*!< GPTM Timer B Prescale Snapshot */
__IO uint32_t TAPV; /*!< GPTM Timer A Prescale Value */
__IO uint32_t TBPV; /*!< GPTM Timer B Prescale Value */
__I uint32_t RESERVED1[981];
__IO uint32_t PP; /*!< GPTM Peripheral Properties */
} TIMER0_Type;

On that particular part there is a 981 word gap between TIMER0_TBPV and TIMER0_PP, and a 1 word (32bit) gap between TIMER0_SYNC and TIMER0_IMR. From the datasheet:
The "reserved" fields simply force alignment with the hardware register map for which the type is used as an overlay. You certainly should not instantiate an object of this type in memory!

These structs are defined for easier access to memory mapped peripheral registers.
Each register has a fixed absolute address. The struct layout needs to be carefully designed such that the base address of the struct plus the offset of the member within the struct results in the register address.
As the register addresses are only partially contiguous, gaps without registers must be filled with dummy struct members, named RESERVEDxx.

They are a 1:1 map with the hardware. Say that you have only two registers:
A # 0x1000
B # 0x1004
You could define this struct:
struct {
u32* Ar;
u32* Br;
}regs;
And then assign manually the addresses:
struct regs myRegs;
myRegs.Ar = (u32*)0x1000;
myRegs.Br = (u32*)0x1004;
But doing this is error prone and also very long to do. What you can do, and what CMSIS does, is to define a struct with datatypes instead of pointers, knowing that the compiler will treat such structure as contiguous elements in the memory and will do pointer access for you.
struct {
u32 Ar;
u32 Br;
}regs;
struct regs* myRegsPtr = (struct regs*) 0x1000;
Now myRegsPtr holds the base address of the hardware register structure. Since now the C structure mimics the HW layout, accessing a member of the structure does exactly the pointer arithmetic you need for accessing the register you requested.
myRegs->Br; //This does 0x1000 + sizeof(all members before Br), reaching the correct offset.
Now back to your question. When we look at the datasheet we see this particular HW structure:
Can you spot the jump of 981*4 bytes? That is where you RESERVED area in your struct come from. In order to access the last register, the compiler must know that there are this amount of bytes to "jump".

Related

is it possible to make a structure pointer points to another structure pointer with different types

the following structure is a structure of registers with type timer_t
typedef struct { /*!< TIMER0 Structure */
uint32_t CFG; /*!< GPTM Configuration */
uint32_t TAMR; /*!< GPTM Timer A Mode */
uint32_t TBMR; /*!< GPTM Timer B Mode */
uint32_t CTL; /*!< GPTM Control */
uint32_t SYNC; /*!< GPTM Synchronize */
uint32_t RESERVED;
uint32_t IMR; /*!< GPTM Interrupt Mask */
uint32_t RIS; /*!< GPTM Raw Interrupt Status */
uint32_t MIS; /*!< GPTM Masked Interrupt Status */
uint32_t ICR; /*!< GPTM Interrupt Clear */
uint32_t TAILR; /*!< GPTM Timer A Interval Load */
uint32_t TBILR; /*!< GPTM Timer B Interval Load */
uint32_t TBMATCHR; /*!< GPTM Timer B Match */
uint32_t TAPR; /*!< GPTM Timer A Prescale */
uint32_t TBPR; /*!< GPTM Timer B Prescale */
uint32_t TAPMR; /*!< GPTM TimerA Prescale Match */
uint32_t TBPMR; /*!< GPTM TimerB Prescale Match */
uint32_t TAR; /*!< GPTM Timer A */
uint32_t TBR; /*!< GPTM Timer B */
uint32_t TAV; /*!< GPTM Timer A Value */
uint32_t TBV; /*!< GPTM Timer B Value */
uint32_t RTCPD; /*!< GPTM RTC Predivide */
uint32_t TAPS; /*!< GPTM Timer A Prescale Snapshot */
uint32_t TBPS; /*!< GPTM Timer B Prescale Snapshot */
uint32_t TAPV; /*!< GPTM Timer A Prescale Value */
uint32_t TBPV; /*!< GPTM Timer B Prescale Value */
uint32_t RESERVED1[981];
uint32_t PP; /*!< GPTM Peripheral Properties */
}timer_t;
and this #define TIMER0 ((timer_t *) TIMER0_BASE) is used to dereference them so I can write on them as TIMER0_BASE is the base address of the used module. now I want to declare another bit field with type timerAModeBitField_t;for every register something to be like this
typedef struct timerAModeBitField{
uint32_t timerAMode : 2;
uint32_t timerACaptMode : 1;
uint32_t timeraPWMMode : 1;
uint32_t timerACountDir : 1;
uint32_t timerAMatchIntEn : 1;
uint32_t timerAWaitOnTrig : 1;
uint32_t timerASnapShotMode : 1;
uint32_t timerAIntervalLoad : 1;
}timerAModeBitField_t;
so I can directly write to their specific bits is it possible to implement something like that so I can first dereference the register and then dereference the bit field TIMER0->TAMR->timerAmode = 0x04
In portable C, the layout of bit-fields is unspecified. So you can't use bit-fields to describe a specific layout in portable C.
However, if you're writing code for a specific architecture, your C implementation may have additional guarantees. On Arm platforms, in particular, the Arm Architecture Procedure Call Standard (AAPCS) applies. It has a rule for bit-fields, and specifically for bit-fields in C and C++. If the bit-field description were provided by your vendor and the peripheral was intended to be used on an Arm processor, the AAPCS guarantees that you're both making the same assumptions on the data layout, so your code will use the intended data layout.
However using the same data layout is not sufficient! You also have to ensure that the compiler will do the right thing when a field is written in one way (say, through the uint32_t) and then read back in the other way (say, through the bit-field). C compilers are generally allowed to assume that when memory is accessed through different types, it's different memory. This allows compilers to optimize code better.
There are several exceptions which I won't go into here, but in general you can't assume that writing memory in one way and reading it back in some other way will give consistent results. Accessing the same memory in different ways is called aliasing. What is the strict aliasing rule? has a very good explanation of how this applies to C. Be careful: violating the aliasing rules can often result in code that fails only at certain levels of optimization, and only in certain contexts (e.g. depending on the number of registers that the compiler had available for a particular block of code), so this can be hard to debug. In particular, beware that casts can often get around compiler warnings, but not against aliasing bugs: casts just tell the compiler to stop complaining about broken code, they don't make the code less broken.
One way to let the compiler know that you're accessing the same memory through different types is to put those types in a union. (This is guaranteed in C11, and not officially guaranteed but widely implemented in C99.) That is:
typedef union {
uint32_t value;
timerAModeBitField bits;
} timerAModeUnion;
typedef struct {
…
timerAModeUnion TAMR;
…
} timer_t;
Then you can reliably do things like bulk-write TIMER0->TAMR.value and read a specific bit-field via TIMER0->TAMR.bits.timerAmode.
You can use the (inner) structure as the data type of an element of another (outer) structure, like this:
typedef struct timerAModeBitField {
uint32_t timerAMode : 2;
uint32_t timerACaptMode : 1;
uint32_t timeraPWMMode : 1;
uint32_t timerACountDir : 1;
uint32_t timerAMatchIntEn : 1;
uint32_t timerAWaitOnTrig : 1;
uint32_t timerASnapShotMode : 1;
uint32_t timerAIntervalLoad : 1;
uint32_t padding : 23;
} timerAModeBitField_t;
typedef struct { /*!< TIMER0 Structure */
uint32_t CFG; /*!< GPTM Configuration */
timerAModeBitField_t TAMR; /*!< GPTM Timer A Mode */
uint32_t TBMR; /*!< GPTM Timer B Mode */
uint32_t CTL; /*!< GPTM Control */
uint32_t SYNC; /*!< GPTM Synchronize */
uint32_t RESERVED;
uint32_t IMR; /*!< GPTM Interrupt Mask */
uint32_t RIS; /*!< GPTM Raw Interrupt Status */
uint32_t MIS; /*!< GPTM Masked Interrupt Status */
uint32_t ICR; /*!< GPTM Interrupt Clear */
uint32_t TAILR; /*!< GPTM Timer A Interval Load */
uint32_t TBILR; /*!< GPTM Timer B Interval Load */
uint32_t TBMATCHR; /*!< GPTM Timer B Match */
uint32_t TAPR; /*!< GPTM Timer A Prescale */
uint32_t TBPR; /*!< GPTM Timer B Prescale */
uint32_t TAPMR; /*!< GPTM TimerA Prescale Match */
uint32_t TBPMR; /*!< GPTM TimerB Prescale Match */
uint32_t TAR; /*!< GPTM Timer A */
uint32_t TBR; /*!< GPTM Timer B */
uint32_t TAV; /*!< GPTM Timer A Value */
uint32_t TBV; /*!< GPTM Timer B Value */
uint32_t RTCPD; /*!< GPTM RTC Predivide */
uint32_t TAPS; /*!< GPTM Timer A Prescale Snapshot */
uint32_t TBPS; /*!< GPTM Timer B Prescale Snapshot */
uint32_t TAPV; /*!< GPTM Timer A Prescale Value */
uint32_t TBPV; /*!< GPTM Timer B Prescale Value */
uint32_t RESERVED1[981];
uint32_t PP; /*!< GPTM Peripheral Properties */
} timer_t;
Then you can access mode bits in this way:
TIMER0->TAMR.timerAmode = 2;
Note: You should not assign 0x04 to a bitfield of width 2, the width of the value is too big. Possible values range from 0 to 3.
EDIT:
Let's minize the example:
typedef union {
struct {
unsigned int mode: 2;
unsigned int padding: 30;
};
uint32_t value;
} control_t;
typedef struct {
control_t control;
uint32_t stuff;
} module_t;
You can always obtain the address of an element of a structure:
module_t module;
control_t* mode_p = &module.control;
mode_p->mode = 3; // write only to 2 bits
uint32_t v = mode_p->value; // read all 32 bits
Via the union you can use either the complete control register or conveniently access just some bits:
module.control.value = 0x12345678;
module.control.mode = 2;
Don't fall into the trap to think too much from a machine code perspective. Use the abstraction of the higher level language to gain better source control by the compiler. Avoid casts as much as possible, since they tell the compiler to think, "The programmer is always right, who am I to doubt her?" Even in cases where your code has errors.
Caution: In fact it is not defined by the C standard how exactly bitfields are allocated. However, compilers do this in a reproducable way, it might well be documented. Make sure you guard your code with a check on the compiler you validate for your usage.
You could try this approach using unnamed members:
#include <stdint.h>
typedef struct {
...
union {
struct {
uint32_t timerAMode : 2;
uint32_t timerACaptMode : 1;
uint32_t timeraPWMMode : 1;
uint32_t timerACountDir : 1;
uint32_t timerAMatchIntEn : 1;
uint32_t timerAWaitOnTrig : 1;
uint32_t timerASnapShotMode : 1;
uint32_t timerAIntervalLoad : 1;
};
uint32_t TAMR;
};
...
} timer_t;
#define TIMER0_BASE (0x40001000)
volatile timer_t *TIMER0 = (timer_t*) TIMER0_BASE;
int main(void)
{
TIMER0->TAMR = 0x1234;
TIMER0->timerAMode = 2;
}
By using unnamend members you can access members of the union/struct directly without adding a field name for that union.
This allows accessing TAMR at once as well as the separate fields of it.

CMSIS - Peripheral Definitions - Structs With Unions Syntax?

I've moving over from 8 bit bare-metal registers, and having to learn some new C Kungfu to wrap my head around the CMSIS Core approach.
I have a snippet of code here from a Peripheral Access Layer from a ARM Cortex M vendor. They create this SN_WDT_TYPE structure, which you can use to set watch-dog timer registers using their notation.
Why do they use the unions? I haven't seen this kind of syntax before.
If you use unions to create structures like that, do you they go several layers deep with pointers? Memory management with the unions? Is there some C syntax thing I'm missing here?
This might be CMSIS specific, does anyone know what the ": 1" is doing in those struct declarations...? I know the __IO ties back to some CMSIS definition of read/write.
HELPFUL STUFF I FOUND AFTER COMMENTS & ANSWERS:
ARM'S CMSCIS PERIPHERAL NAMING CONVENTION -- This example code doesn't seem to confirm too gracefully...
ARM'S BITFIELD COMPILER NOTES ON STRUCTS & UNIONS
/**
* #brief Watchdog Timer (SN_WDT)
*/
typedef struct { /*!< SN_WDT Structure */
union {
__IO uint32_t CFG; /*!< Offset:0x00 WDT Configuration Register */
struct {
__IO uint32_t WDTEN : 1; /*!< WDT enable */
__IO uint32_t WDTIE : 1; /*!< WDT interrupt enable */
__IO uint32_t WDTINT : 1; /*!< WDT interrupt flag */
uint32_t : 13;
__O uint32_t WDKEY : 16; /*!< Watchdog register key */
} CFG_b; /*!< BitSize */
};
union {
__IO uint32_t CLKSOURCE; /*!< Offset:0x04 WDT Clock Source Register */
struct {
__IO uint32_t CLKSOURCE : 2; /*!< WDT clock source */
uint32_t : 14;
__O uint32_t WDKEY : 16; /*!< Watchdog register key */
} CLKSOURCE_b; /*!< BitSize */
};
union {
__IO uint32_t TC; /*!< Offset:0x08 WDT Timer Constant Register */
struct {
__IO uint32_t TC : 8; /*!< Watchdog timer constant reload value */
uint32_t : 8;
__O uint32_t WDKEY : 16; /*!< Watchdog register key */
} TC_b; /*!< BitSize */
};
union {
__O uint32_t FEED; /*!< Offset:0x0C WDT Feed Register */
struct {
__O uint32_t FV : 16; /*!< Watchdog feed value */
__O uint32_t WDKEY : 16; /*!< Watchdog register key */
} FEED_b; /*!< BitSize */
};
} SN_WDT_Type;
The union allows you to access the hardware register as either a 32 bit word or as the bit fields contained in the register. Both representations have their uses. Perhaps you are missing the bit field syntax as it is not used in most application level coding. Bit field layout is compiler specific, but CMSIS headers are built to work with the intended compilers.

How compiler knows if a pointer address in C is port mapped or RAM?

I know that port mapped IO are accessed thorugh in/out CPU instructions and memory (& memory mapped registers) are accessed through load/store CPU instructions (similar to memory). But, with a pointer in C code, how compiler knows if a address is port mapped IO register or memory and then inserts the correct CPU instructions?
Example:
uint16_t const* uart_reg = 0x8000c000;
uint16_t const* ram_addr = 0x4000c000;
*uart_reg = 0x1;
*ram_addr = 0x12;
If you have non-memory-mapped I/O, you're not going to be able to access it from C as if it were memory.
You must use some platform-specific trickery to get the proper I/O instructions. This is a pain, and might be one reason why modern hardware seems to favor memory-mapped I/O.
C compilers for platforms with port-mapped I/O had to include this, see for instance the inp() function in the old Turbo C++ for DOS.
Two steps:
Install the correct toolchain for your target hardware.
Use provided header files with all the necessary properly dome definitions
For example if you want to program ARM STM uC bare metal gcc compiler - use arm-none-eabi-gcc and tools. Use header CMSIS header files provided by STM & ARM (usually they are already included if you choose a "ready made" toolchain)
Example declarations and definitions (only for ADC1):
#define FLASH_BASE ((uint32_t)0x08000000U) /*!< FLASH base address in the alias region */
#define CCMDATARAM_BASE ((uint32_t)0x10000000U) /*!< CCM(core coupled memory) data RAM base address in the alias region */
#define SRAM_BASE ((uint32_t)0x20000000U) /*!< SRAM base address in the alias region */
#define PERIPH_BASE ((uint32_t)0x40000000U) /*!< Peripheral base address in the alias region */
#define SRAM_BB_BASE ((uint32_t)0x22000000U) /*!< SRAM base address in the bit-band region */
#define PERIPH_BB_BASE ((uint32_t)0x42000000U) /*!< Peripheral base address in the bit-band region */
#define AHB3PERIPH_BASE (PERIPH_BASE + 0x10000000U)
#define ADC1_BASE (AHB3PERIPH_BASE + 0x00000000U)
#define ADC1 ((ADC_TypeDef *) ADC1_BASE)
#ifdef __cplusplus
#define __I volatile /*!< Defines 'read only' permissions */
#else
#define __I volatile const /*!< Defines 'read only' permissions */
#endif
#define __O volatile /*!< Defines 'write only' permissions */
#define __IO volatile /*!< Defines 'read / write' permissions */
/* following defines should be used for structure members */
#define __IM volatile const /*! Defines 'read only' structure member permissions */
#define __OM volatile /*! Defines 'write only' structure member permissions */
#define __IOM volatile /*! Defines 'read / write' structure member permissions */
typedef struct
{
__IO uint32_t ISR; /*!< ADC Interrupt and Status Register, Address offset: 0x00 */
__IO uint32_t IER; /*!< ADC Interrupt Enable Register, Address offset: 0x04 */
__IO uint32_t CR; /*!< ADC control register, Address offset: 0x08 */
__IO uint32_t CFGR; /*!< ADC Configuration register, Address offset: 0x0C */
uint32_t RESERVED0; /*!< Reserved, 0x010 */
__IO uint32_t SMPR1; /*!< ADC sample time register 1, Address offset: 0x14 */
__IO uint32_t SMPR2; /*!< ADC sample time register 2, Address offset: 0x18 */
uint32_t RESERVED1; /*!< Reserved, 0x01C */
__IO uint32_t TR1; /*!< ADC watchdog threshold register 1, Address offset: 0x20 */
__IO uint32_t TR2; /*!< ADC watchdog threshold register 2, Address offset: 0x24 */
__IO uint32_t TR3; /*!< ADC watchdog threshold register 3, Address offset: 0x28 */
uint32_t RESERVED2; /*!< Reserved, 0x02C */
__IO uint32_t SQR1; /*!< ADC regular sequence register 1, Address offset: 0x30 */
__IO uint32_t SQR2; /*!< ADC regular sequence register 2, Address offset: 0x34 */
__IO uint32_t SQR3; /*!< ADC regular sequence register 3, Address offset: 0x38 */
__IO uint32_t SQR4; /*!< ADC regular sequence register 4, Address offset: 0x3C */
__IO uint32_t DR; /*!< ADC regular data register, Address offset: 0x40 */
uint32_t RESERVED3; /*!< Reserved, 0x044 */
uint32_t RESERVED4; /*!< Reserved, 0x048 */
__IO uint32_t JSQR; /*!< ADC injected sequence register, Address offset: 0x4C */
uint32_t RESERVED5[4]; /*!< Reserved, 0x050 - 0x05C */
__IO uint32_t OFR1; /*!< ADC offset register 1, Address offset: 0x60 */
__IO uint32_t OFR2; /*!< ADC offset register 2, Address offset: 0x64 */
__IO uint32_t OFR3; /*!< ADC offset register 3, Address offset: 0x68 */
__IO uint32_t OFR4; /*!< ADC offset register 4, Address offset: 0x6C */
uint32_t RESERVED6[4]; /*!< Reserved, 0x070 - 0x07C */
__IO uint32_t JDR1; /*!< ADC injected data register 1, Address offset: 0x80 */
__IO uint32_t JDR2; /*!< ADC injected data register 2, Address offset: 0x84 */
__IO uint32_t JDR3; /*!< ADC injected data register 3, Address offset: 0x88 */
__IO uint32_t JDR4; /*!< ADC injected data register 4, Address offset: 0x8C */
uint32_t RESERVED7[4]; /*!< Reserved, 0x090 - 0x09C */
__IO uint32_t AWD2CR; /*!< ADC Analog Watchdog 2 Configuration Register, Address offset: 0xA0 */
__IO uint32_t AWD3CR; /*!< ADC Analog Watchdog 3 Configuration Register, Address offset: 0xA4 */
uint32_t RESERVED8; /*!< Reserved, 0x0A8 */
uint32_t RESERVED9; /*!< Reserved, 0x0AC */
__IO uint32_t DIFSEL; /*!< ADC Differential Mode Selection Register, Address offset: 0xB0 */
__IO uint32_t CALFACT; /*!< ADC Calibration Factors, Address offset: 0xB4 */
} ADC_TypeDef;
and it expands to: ((ADC_TypeDef *) ((((uint32_t)0x40000000U) + 0x10000000U) + 0x00000000U))
It is not worth to do it manually.

Why pointer of type CAN_TypeDef is being used in following code?

I am trying to understand the code, my worry is why in below code "CAN_TypeDef" is being used, can I simply use pointer of type integer,what will be the adverse consequences of integer pointer ?
The below code belongs to CAN(controller area network) library code of stm32f discovery board
in Filename can.c
void CAN_setup (uint32_t ctrl) {
CAN_TypeDef *pCAN = (ctrl == 1) ? CAN1 : CAN2;
uint32_t brp;
When I went to definition of CAN_Typedef it throws me here.
typedef struct
{
__IO uint32_t MCR; /*!< CAN master control register, Address offset: 0x00 */
__IO uint32_t MSR; /*!< CAN master status register, Address offset: 0x04 */
__IO uint32_t TSR; /*!< CAN transmit status register, Address offset: 0x08 */
__IO uint32_t RF0R; /*!< CAN receive FIFO 0 register, Address offset: 0x0C */
__IO uint32_t RF1R; /*!< CAN receive FIFO 1 register, Address offset: 0x10 */
__IO uint32_t IER; /*!< CAN interrupt enable register, Address offset: 0x14 */
__IO uint32_t ESR; /*!< CAN error status register, Address offset: 0x18 */
__IO uint32_t BTR; /*!< CAN bit timing register, Address offset: 0x1C */
uint32_t RESERVED0[88]; /*!< Reserved, 0x020 - 0x17F */
CAN_TxMailBox_TypeDef sTxMailBox[3]; /*!< CAN Tx MailBox, Address offset: 0x180 - 0x1AC */
CAN_FIFOMailBox_TypeDef sFIFOMailBox[2]; /*!< CAN FIFO MailBox, Address offset: 0x1B0 - 0x1CC */
uint32_t RESERVED1[12]; /*!< Reserved, 0x1D0 - 0x1FF */
__IO uint32_t FMR; /*!< CAN filter master register, Address offset: 0x200 */
__IO uint32_t FM1R; /*!< CAN filter mode register, Address offset: 0x204 */
uint32_t RESERVED2; /*!< Reserved, 0x208 */
__IO uint32_t FS1R; /*!< CAN filter scale register, Address offset: 0x20C */
uint32_t RESERVED3; /*!< Reserved, 0x210 */
__IO uint32_t FFA1R; /*!< CAN filter FIFO assignment register, Address offset: 0x214 */
uint32_t RESERVED4; /*!< Reserved, 0x218 */
__IO uint32_t FA1R; /*!< CAN filter activation register, Address offset: 0x21C */
uint32_t RESERVED5[8]; /*!< Reserved, 0x220-0x23F */
CAN_FilterRegister_TypeDef sFilterRegister[28]; /*!< CAN Filter Register, Address offset: 0x240-0x31C */
} CAN_TypeDef;
I don't understand why typedef .. I have base address of CAN and offset of Different registers, I didn't find any relevance of above code for my purpose.
I am trying to apply this way
/*=========================CAN BASE ADDRESS=============== */
#define CAN1_BASE 0x40006800
#define CAN2_BASE 0x40006400
/==========================================================/
#define CAN1_MCR (CAN1_BASE+ 0x00)
#define CAN2_MCR (CAN2_BASE+ 0x00) // 0x00 is offset for MCR
#define DEMO(X) (*(unsigned int*)(X)) // will use this to type cast and deference ,accessing the register.
DEMO(CAN1_MCR) = (CAN_MCR_INRQ | CAN_MCR_NART ); // CAN_MCR_INRQ and CAN_MCR_NART has hexadecimal vale pointing to specific bit in MCR register
Yes... You can use an integer pointer and go ahead with the method you use.
But you will miss on "Code Readability" and "Structured Code".
With the typedef struct, it is pretty easy to access the registers and code is easy to read/understand.
Nothing else!!
The key word is reuse. Saying
#define CAN1_MCR (CAN1_BASE+ 0x00)
#define CAN2_MCR (CAN2_BASE+ 0x00)
means that you'd have to define the layout twice (with all the double maintenance problems). The structure approach allows to define it once, and use the same definition for each bus.

Peripheral definition

Could someone please explain the following construction to a beginner:
typedef struct
{
__IO uint32_t CTRL; /**< Control Register */
__IO uint32_t CNT; /**< Counter Value Register */
__IO uint32_t COMP0; /**< Compare Value Register 0 */
__IO uint32_t COMP1; /**< Compare Value Register 1 */
__I uint32_t IF; /**< Interrupt Flag Register */
__IO uint32_t IFS; /**< Interrupt Flag Set Register */
__IO uint32_t IFC; /**< Interrupt Flag Clear Register */
__IO uint32_t IEN; /**< Interrupt Enable Register */
__IO uint32_t FREEZE; /**< Freeze Register */
__I uint32_t SYNCBUSY; /**< Synchronization Busy Register */
} RTC_TypeDef;
#define RTC_BASE (0x40080000UL)
#define RTC ((RTC_TypeDef *) RTC_BASE)
especially the last line
Why are the brackets so unusual? What does the * mean? Pointer or multiply operator ?
Thanks
Why are the brackets so unusual? What does the * mean? Pointer or
multiply operator ?
* here is a pointer not multiplication operator.
There is nothing unusual here about the macro. It is just type casting the address to be of type struct RTC_TypeDef
Whenever you will encounter a single or multiple * after a datatype (even a user defined datatype), its a pointer.
What you are calling a constructions:
typedef struct
{
__IO uint32_t CTRL; /**< Control Register */
__IO uint32_t CNT; /**< Counter Value Register */
__IO uint32_t COMP0; /**< Compare Value Register 0 */
__IO uint32_t COMP1; /**< Compare Value Register 1 */
__I uint32_t IF; /**< Interrupt Flag Register */
__IO uint32_t IFS; /**< Interrupt Flag Set Register */
__IO uint32_t IFC; /**< Interrupt Flag Clear Register */
__IO uint32_t IEN; /**< Interrupt Enable Register */
__IO uint32_t FREEZE; /**< Freeze Register */
__I uint32_t SYNCBUSY; /**< Synchronization Busy Register */
} RTC_TypeDef;
is a structure and programmatically called a struct, a user-defined datatype. struct is keyword in C. And so is typedef. To keep it simple as of now, I'll say, typedef here is specifically used to have the convenience to write RTC_TypeDef; instead of having the need to write struct stRTC for example.
RTC_BASE is a macro. So, before compilation pre-processor will replace RTC_BASE with the value (0x40080000UL), again, it is for convenience and readability.
This last line:
#define RTC ((RTC_TypeDef *) RTC_BASE)
also is a macro defined. Which is nothing but a pointer of the RTC_Typedef type, pointing to whatever is located at the address (0x40080000UL)
So, before the compilation the pre-processor will replace all instances of RTC with ((RTC_TypeDef *)0x40080000UL).
If you increment RTC pointer, it will increment by sizeof(RTC_Typedef).
The structure definition is pretty straight forward, variables are encapsulated in a structure. That structure can then be referenced as RTC_TypeDef because you use typedef.
typedef struct
{
__IO uint32_t CTRL; /**< Control Register */
__IO uint32_t CNT; /**< Counter Value Register */
__IO uint32_t COMP0; /**< Compare Value Register 0 */
__IO uint32_t COMP1; /**< Compare Value Register 1 */
__I uint32_t IF; /**< Interrupt Flag Register */
__IO uint32_t IFS; /**< Interrupt Flag Set Register */
__IO uint32_t IFC; /**< Interrupt Flag Clear Register */
__IO uint32_t IEN; /**< Interrupt Enable Register */
__IO uint32_t FREEZE; /**< Freeze Register */
__I uint32_t SYNCBUSY; /**< Synchronization Busy Register */
} RTC_TypeDef; /** #} */
The following line means every time you write RTC_BASE, the compiler will replace that line with (0x40080000UL)
#define RTC_BASE (0x40080000UL)
And finally, the following line can be broken into two parts:
(RTB_TypeDef *)RTC_BASE
Which means (RTB_TypeDef *)(0x40080000UL) - basically tells the compiler to treat memory address 0x40080000UL as a pointer to RTB_TypeDef
and the extra parenthesis are there to denote a single variable as a compund expression RTC.
From that point on, whenever the compiler sees RTC, it will replace it with (RTB_TypeDef *)(0x40080000UL), basically a convenient way to mark the beginning of this structure in memory

Resources