2

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.

Community
  • 1
  • 1
Ahmad Magrabi
  • 59
  • 1
  • 6
  • 2
    you will find that a chip vendor will re-use peripherals from chip to chip but not necessarily use the same base address. so a base plus offset can sometimes be more readable. but you can just as easily use the full address and change the upper portion. Also as you have shown there may be multiple gpio ports, the offsets are often the same (depends on vendor) per port but the port bases are different. may or may not increase readability. – old_timer May 19 '19 at 17:44
  • 1
    readability is in the eye of the beholder. At the end of the day one persons easy to read code is another persons nightmare. – old_timer May 19 '19 at 17:45
  • 1
    It's also easier to reuse code when you only need to change the base address. Also, often there are several identical or very similar peripherals at different addresses, then you can use the same code with a different offset for all of them. – starblue May 23 '19 at 12:03

4 Answers4

3

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.

Yoni Newman
  • 185
  • 13
  • I don't buy your arguments. The alternative is to provide absolute addresses in a header file, and if something is changed, only that header is changed and the names remain the same. If you write register definitions all over your application code, then the absolute addresses are the least of your problems - the root of the problem in that case is the non-existent program design. MCU applications either provide a "register map" header with everything in the MCU, or (far less commonly) provide a header with a map for the specific hardware peripheral only. – Lundin May 20 '19 at 10:55
  • Actually it is defined in the STM header another way. You guys try to reinvent the wheel – 0___________ May 20 '19 at 10:58
3

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.

Vinci
  • 1,382
  • 10
  • 12
0

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.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • No one sane does it this way. Use STM provided .h files. – 0___________ May 20 '19 at 11:00
  • @P__J__ This is how it is very commonly done in professional products, especially mission-critical ones. ST (and others) have too poor quality of register maps and libraries to be used, they don't even follow industry standard MISRA-C. Regardless of that, you can use the above method even if you use manufacturer headers. – Lundin May 20 '19 at 14:04
  • @P__J__ As an example, I'm working on a non-critical product right now where the manufacturer's bloatware drivers (Atmel ASF) were too crappily written to be used in the real world. And so I ended up coding the drivers myself, but using the register typedef provided by the manufacturer. So I can still use multiple identical peripherals with the same driver, even though I didn't write the register definitions. – Lundin May 20 '19 at 14:04
0
  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;
0___________
  • 60,014
  • 4
  • 34
  • 74
  • 2
    ...except re-inventers of the wheel that have to follow industry standards. The header you linked is most likely neither C standard nor MISRA-C compatible. – Lundin May 20 '19 at 14:09
  • @Lundin of course :) STM headers are not industry standard for the STM uCs. BTW HAL drivers (based on the STM32-CMSIS) are fully MISRA 2004 compliant. – 0___________ May 20 '19 at 14:29
  • CMSIS is pushing this fad of horrible code. ARM is not in a position right now for that gamble. A note to the reader, avoid structs across compile domains, and worse illegal use of unions not shown here, but part of the fad. – old_timer May 20 '19 at 14:33
  • A note to the reader - do not listen. Use your mmicro manufacturer headers. – 0___________ May 20 '19 at 14:36
  • I haven't used the STM32 in person, but I have the header here. Took me 50 lines to find the first MISRA-C:2004 violation (code placed before pre-processor #includes). Then comes the casts from integers to struct pointers, MISRA-C:2004 violation. If someone told you it was compliant, you were either scammed or they didn't tell you their list of deviations. – Lundin May 20 '19 at 14:45
  • Though in all fairness, the STM32 header looks a whole lot better than what these vendor-provided register headers usually look like. I can't speak for the rest of the HAL library though. – Lundin May 20 '19 at 14:49