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 optimizationuint32_t *: pointer to 32-bit register0x40021000: 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
constfor 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
volatileto prevent compiler optimizations. - Use the correct pointer type matching the register size (e.g.,
uint8_t,uint16_t,uint32_t). - Declare the pointer as
constif 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.