0
0
Power-electronicsHow-ToBeginner · 4 min read

How to Use Pointer to Access Hardware Registers in Embedded C

In Embedded C, you use a volatile pointer to the hardware register's memory address to read or write its value. This pointer lets you directly access the register by dereferencing it, ensuring the compiler does not optimize away these critical hardware interactions.
📐

Syntax

To access a hardware register, declare a volatile pointer to the register's fixed memory address. The volatile keyword tells the compiler the value can change anytime outside program control, preventing unwanted optimizations.

Dereference the pointer to read or write the register.

  • volatile: prevents compiler optimization
  • uint32_t *: pointer to 32-bit register
  • 0x40021000: example hardware register address
c
volatile uint32_t * const REG = (volatile uint32_t * const)0x40021000;

// Read register value
uint32_t value = *REG;

// Write register value
*REG = 0x12345678;
💻

Example

This example shows how to define a pointer to a hardware register and toggle a bit in it. It simulates turning on an LED connected to a specific bit of the register.

c
#include <stdint.h>
#include <stdio.h>

// Simulated hardware register address
volatile uint32_t simulated_register = 0;

// Pointer to the simulated register
volatile uint32_t * const REG = &simulated_register;

int main() {
    // Turn on bit 3 (e.g., LED on)
    *REG |= (1 << 3);
    printf("Register after setting bit 3: 0x%08X\n", *REG);

    // Turn off bit 3 (e.g., LED off)
    *REG &= ~(1 << 3);
    printf("Register after clearing bit 3: 0x%08X\n", *REG);

    return 0;
}
Output
Register after setting bit 3: 0x00000008 Register after clearing bit 3: 0x00000000
⚠️

Common Pitfalls

Common mistakes when using pointers for hardware registers include:

  • Not using volatile, causing the compiler to optimize away reads/writes.
  • Using incorrect pointer types or sizes, leading to wrong data access.
  • Forgetting to use const for the pointer itself if the address should not change.
  • Dereferencing invalid or wrong addresses causing undefined behavior.
c
/* Wrong: Missing volatile, may optimize away access */
uint32_t *REG = (uint32_t *)0x40021000;
*REG = 0x01; // May not actually write to hardware

/* Correct: Use volatile to prevent optimization */
volatile uint32_t * const REG = (volatile uint32_t * const)0x40021000;
*REG = 0x01; // Correct hardware access
📊

Quick Reference

Tips for safely accessing hardware registers with pointers:

  • Always use volatile to prevent compiler optimizations.
  • Use the correct pointer type matching the register size (e.g., uint8_t, uint16_t, uint32_t).
  • Declare the pointer as const if the register address is fixed.
  • Use bitwise operators to modify specific bits without affecting others.
  • Check your microcontroller's datasheet for correct register addresses and sizes.

Key Takeaways

Use a volatile pointer to the hardware register's fixed address to ensure proper access.
Dereference the pointer to read or write the register value directly.
Always match the pointer type size to the hardware register size.
Use bitwise operations to safely modify specific bits in the register.
Avoid common mistakes like missing volatile or using wrong pointer types.