what is the purpose of using offset to get register full address in micro-controller?

4.2k Views Asked by At

I'm new to embedded systems programming, and trying to make my way. Using Stellaris LM4F120 LaunchPad Evaluation Board with datasheet LM4F120H5QR Microcontroller I found to get the full address of some registers you have always to add an offset! which I don't get the importance of it as instead we can use the full address directly!

For example to configure Port F (which starts from 0x4002.5000 to 0x4002.5FFF)and it's pins (using APB bus)

  1. Activate clk to this port by setting (bit 5) to 1 in RCGCGPIO register which it's Base address is 0x400F.E000 with Offset 0x608 so full address is 0x400FE608
  2. Configure the GPIODIR reg which it's base address is 0x4002.5000 with offset 0x400 so full address is 0x4002.5400
  3. Configure the GPIODEN reg which it's base address is 0x4002.5000 with offset 0x51C so full address is 0x4002.551C
  4. Configure the GPIODATA reg which it's base address is 0x4002.5000 with 0x3FC so full address is 0x4002.50x3FC

If I can guess it would be the offset here is used to make it less prone to error as we can write it like this :

#define GPIO_PORTF_BASE 0x40025000
#define GPIO_PORTF_DATA (*((volatile unsigned long *)(GPIO_PORTF_BASE + 0x3FC)))
#define GPIO_PORTF_DIR (*((volatile unsigned long *)(GPIO_PORTF_BASE + 0x400)))
#define GPIO_PORTF_DEN (*((volatile unsigned long *)(GPIO_PORTF_BASE + 0x51C)))

Does using offset increases readability and makes it easier and unsophisticated as We only have to write the offset to get the desired register?


Update

I found that Base address has more usage than obtaining the full address of a register.

for example : GPIODATA controls 0-7 pins and it has 255 registers that can allow us to configure each pin individually and even their combination just by adding an offset to the base address e.g. If we want to configure the Red Led which is on Port F we write to the address base address 0x4002.5000 + offset 0x008 directly.

4

There are 4 best solutions below

0
Vinci On BEST ANSWER

That's because the header you copied those definitions from is auto-generated from the CMSIS System View Description format. This format is used by chip manufactures to describe the core and peripheral elements of their microprocessors in a standardized way. Usually you can download those so called ".svd" files at some repository or at the manufacturers homepage.

One of those described peripherals of the LM4F120H5QR would be the general purpose IO port F (GPIOF). The .svd file would contain an element for the port with some base-address and then a sub-element for every register the peripheral has with some offset.

2
Yoni Newman On

You could write #define GPIO_PORTF_DATA 0x400253FC which gives you the absolute address of the data register of port F. Its only a macro and its easier for you as a programmer to know that you're talking about the data register of some port.

The way I see at my work as an embedded programmer, you use offset in order to write less as possible the absolute address.

Some of the reasons that I can think about is when you find an error with the address, or you get new version of the hardware, or what ever happened that you have to write new driver with new addresses, and let assume the structre of the registers has'nt change but only the addresses, with offsets you only have to change the base address and not all the registers in your code.

3
Lundin On

The specific code you posted doesn't make much sense. But in the general case, you'd do something like this is to handle multiple hardware peripherals on the same chip:

#define PORTF 0x40025000ul
...
#define GPIO_PORT_DATA(base) (*((volatile unsigned long *)(base + 0x3FCul)))
#define GPIO_PORT_DIR(base)  (*((volatile unsigned long *)(base + 0x400ul)))
#define GPIO_PORT_DEN(base)  (*((volatile unsigned long *)(base + 0x51Cul)))

Given that all peripherals have the same memory mapping, you can now write a single driver which can handle multiple peripherals. GPIO might not be the best example, since writing abstraction layers over GPIO usually just adds clutter. But in theory we could have this driver:

void gpio_set (volatile unsigned long* port, uint8_t pin);

...

gpio_set (PORTF, 5);

Where gpio doesn't know which specific port it is dealing with, it does the same job no matter, by accessing the macros.

This is a common way to write drivers for things like SPI, UART, CAN, ADC etc where you are likely to have several identical peripherals on-chip and want the same code to handle them all, without code repetition.

The down side is a tiny bit of execution overhead, since the address must be calculated in run-time.

6
0___________ On
  1. Because documentation is written this way.

enter image description here

  1. But in the fact no one (except reinventers of the wheel) does it this way. STM CMSIS files define structures and compiler calculates the offsets itself:
      #define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)

    typedef struct
    {
      __IO uint32_t MODER;    /*!< GPIO port mode register,               Address offset: 0x00      */
      __IO uint32_t OTYPER;   /*!< GPIO port output type register,        Address offset: 0x04      */
      __IO uint32_t OSPEEDR;  /*!< GPIO port output speed register,       Address offset: 0x08      */
      __IO uint32_t PUPDR;    /*!< GPIO port pull-up/pull-down register,  Address offset: 0x0C      */
      __IO uint32_t IDR;      /*!< GPIO port input data register,         Address offset: 0x10      */
      __IO uint32_t ODR;      /*!< GPIO port output data register,        Address offset: 0x14      */
      __IO uint32_t BSRR;     /*!< GPIO port bit set/reset register,      Address offset: 0x18      */
      __IO uint32_t LCKR;     /*!< GPIO port configuration lock register, Address offset: 0x1C      */
      __IO uint32_t AFR[2];   /*!< GPIO alternate function registers,     Address offset: 0x20-0x24 */
    } GPIO_TypeDef;