How to Write a (GPIO_reg.h) File for STM32F401 GPIO

Introduction

When you’re working close to the hardware, you need a way to talk to registers without memorizing their hex addresses. That’s where the reg.h file comes in!!! a small header that connects register addresses from the datasheet to something you can use in C.

 

1. Where to Start

Everything begins with the reference manual for the STM32F401. Two places in it matter the most:

  • Memory map → gives you the starting address for each peripheral.
  • Peripheral section → shows the registers, their offsets, and their bit fields.

 

Examples:

[1] GPIO Driver (Step 1, Get the Base Address)

·         GPIOA Base address: 0x40020000

·         GPIOB Base address: 0x40020400

·         GPIOC Base address: 0x40020800

 

 

·         Registers:

    • MODER:    0x00
    • OTYPER:   0x04
    • OSPEEDR:  0x08
    • PUDR:     0x0C
    • IDR:      0x10
    • ODR:      0x14
    • BSRR:     0x18
    • LCKR:     0x1C
    • AFRL:     0x20
    • AFRH:     0x24

 


2. Build the Struct

In C, we can make a struct that lines up exactly with the hardware layout.

typedef struct

{

    u32 MODER;      // 0x00

    u32 OTYPER;     // 0x04

    u32 OSPEEDR;    // 0x08

    u32 PUPDR;      // 0x0C

    u32 IDR;        // 0x10

    u32 ODR;        // 0x14

    u32 BSRR;       // 0x18

    u32 LCKR;       // 0x1C

    u32 AFRL;       // 0x20

    u32 AFRH;       // 0x24

} GPIO_MemoryMap_t;

  • The order must match the manual — even unused spots need to be accounted for.

 

3. Base Addresses

#define GPIOA_BASE_ADDRESS    0x40020000

#define GPIOB_BASE_ADDRESS    0x40020400

#define GPIOC_BASE_ADDRESS    0x40020800

 

4. Pointers to the Struct

We link the address to the struct type:

#define GPIOA  ((volatile GPIO_MemoryMap_t*) (GPIOA_BASE_ADDRESS))

#define GPIOB  ((volatile GPIO_MemoryMap_t*) (GPIOB_BASE_ADDRESS))

#define GPIOC  ((volatile GPIO_MemoryMap_t*) (GPIOC_BASE_ADDRESS))

volatile tells the compiler this value can change outside the program (e.g., hardware writes to it)

 

Now to access any register you can do it as simple as possible:

GPIOA -> ODR

 

5. Same Steps for Any Peripheral

The process doesn’t change for USART, SPI, I2C, NVIC, or anything else:

  1. Get base address.
  2. List registers and offsets.
  3. Make struct in correct order.
  4. Add reserved spaces if needed.
  5. Define pointer macros.

6. Things That Break Your Code

  • Wrong register order                      every value read/written is off.
  • Skipping volatile                            compiler might remove your register access.
  • Wrong base address                       you’re talking to the wrong hardware block.
  • Forgetting reserved spots               struct alignment goes wrong.

 

7. Wrap-Up

Once you understand this, you can make a reg.h for anything on the STM32F401.
The idea is always the same:

  • Look up base address.
  • Copy the register layout.
  • Turn it into a struct.
  • Point a macro at it.

 

By: Salma Hisham


Comments

Popular posts from this blog

How to Write a Driver in C – Simple Steps

ARM vs AVR Microcontrollers: Which One Should You Choose?