This post discussing about Peripheral register access. this post is helpful for who working with Hardware like implementing the device driver or implementing the IO interfaces for protocols like I2C, SPI, UART, CAN or LIN depends on the peripherals supported by microcontroller.
=======================================================================================================
Reference documents:
STM Documents: STM32Fxx series
TMC documents: Texas Instrument _tiva series
C-Concepts : Basic understand, Const-volatile
=======================================================================================================
if we visit any microcontroller Manual we can see many peripheral list like GPIO,CAN, RCC, DMA and many more. if we look into any peripheral like GPIO register or I2C Register, mentioned below details for each peripheral
A register is just a location in microcontroller memory that you can write data to or read data from. In microcontroller we can see General-purpose register, special function register (SFR) and Peripheral register. General purpose register and SFR is stored in the Register memory. peripheral registers are stored in the Peripheral Memory.
each peripheral register implemented with 32-bit long.
for the details please check your microcontroller manual "memory Map" section.
we will check How we can access those memory location in the upcoming topics.
What is Offset address? Where is the base Address?
Offset address nothing but number of memory location from base address or simply called how far from base address. Please refer peripheral memory location.
Giving an examples from STM32 and Tiva TM4C123GH6PM. what is the base address of GPIO and I2C in mentioned controller.
To find the base address of Peripheral, First step is check the Bus Architectural Overview,
For which bus the peripheral is connected?. please look into below controllers. example GPIO connected to AHB1 bus in STM but same GPIO connected to APB bus in TM controller
Second step is find the memory assigned to buses like AHB1 or APB. please refer below memory map picture.
- In STM, AHB1 bus base address is 0x40020000 where GPIO connected. here Offset value of GPIOA 0x0000 and GPIOB is 0x0400
- if we look into the Tiva then base address of APB bus is 0x40000000 and GPIOA connected address is 0x40058000 next offset value of GPIOB is 0x1000.
💬💬💬Please visit your microcontroller manual.
=======================================================================================================
Now we know the base addresses of each peripheral so our mentioned topic start now.
How we can access peripheral registers? How we can modify them?
we define the C-pointer variable with volatile for memory access.
we will define GPIO peripheral by referring Register map of GPIO peripheral from base address (AHB1 base address is 0x40020000)
#define AHB1_BASE (0x40020000u)
#define GPIOA_BASEADDR (AHB1_BASE+0x0000)
#define GPIOB_BASEADDR (AHB1_BASE+0x0400)
#define GPIOC_BASEADDR (AHB1_BASE+0x0800)
#define GPIOD_BASEADDR (AHB1_BASE+0x0C00)
#define GPIOE_BASEADDR (AHB1_BASE+0x1000)
#define GPIOF_BASEADDR (AHB1_BASE+0x1400)
#define GPIOG_BASEADDR (AHB1_BASE+0x1800)
#define GPIOH_BASEADDR (AHB1_BASE+0x1C00)
#define GPIOI_BASEADDR (AHB1_BASE+0x2000)
Lets take another example for calculating the offset and base address I2c Peripheral which is belongs to APB1 bus.
#define APB1_BASE (0x40000000) /* Note: we can identify from memory map
#define I2C1_BASEADDR (APB1_BASE+0x5400) /*Note: 0x5400 is offset value*/
#define I2C2_BASEADDR (APB1_BASE+0x5800)
#define I2C3_BASEADDR (APB1_BASE+0x5C00)
Implementation:
Please take the below code snippet:
In the device driver development using method 2 why because in method 1 we would have to define a set of pointers for each set of registers for each port hence it is lot of repetition and also tedious.
volatile unit32_t *const gpiob_idr = (uint32_t *)0x40020410; this statement meaning is we are telling to compiler that we assigning constant address (Fixed-address) to gpiob_idr but value in this location can update any time because it is volatile in nature.
In method-2, Instead of pointing of uint32 type we pointing to structure type and inside member is uint32 type hence compiler generating proper offset value of 0x04.
Pre-processor declaration:
Many of them using C-macro for defining the register address.
Method-1
#define GPIOB_IDR (*(( volatile uint32_t *)0x40020410));
#define GPIOB_ODR (*(( volatile uint32_t *)0x40020414));
#define GPIOB_SPEEDR (*(( volatile uint32_t *)0x40020408));
#define GPIOB_TYPER (*(( volatile uint32_t *)0x40020404));
while accessing, create variable and used in your code.
for example unit32_t idr = GPIOB_IDR ;
Method-2
#define GPIOA (( volatile GPIO_t * )0x40020000));
#define GPIOB (( volatile GPIO_t *)0x40020400));
#define GPIOC (( volatile GPIO_t *)0x40020800));
#define GPIOD (( volatile GPIO_t *)0x40020C00));
#define GPIOE (( volatile GPIO_t *)0x40021000));
#define GPIOF (( volatile GPIO_t *)0x40021400));
Accessing of GPIOA registers as follows.
uint8 value = ((GPIOA->IDR>>Pin_Number) & Mask_value); Mask_value=0x00000001
=======================================================================================================
Note: all vendors will provide the header file with all peripheral register structure and assigned with their base addresses so we need to use it in your driver development.
in this article just we explored the memory organization and bus architecture. we understood How we can read and write data to the specific location by defining the pointer variable.
=======================================================================================================
Thanks for Visiting the post..!!
=======================================================================================================
No comments:
Post a Comment