How to Read Modify Write Register in Embedded C
To read, modify, and write a hardware register in embedded C, first read the register value using a pointer to the register address, then change the bits you want using bitwise operations, and finally write the new value back to the register using the same pointer. Use
volatile pointers to ensure the compiler does not optimize away these hardware accesses.Syntax
Use a volatile pointer to the register address to read and write the register. Use bitwise operators to modify specific bits without affecting others.
volatile uint32_t *REG = (volatile uint32_t *)0xADDRESS;— pointer to registeruint32_t val = *REG;— read register valueval |= MASK;— set bitsval &= ~MASK;— clear bits*REG = val;— write back modified value
c
volatile uint32_t *REG = (volatile uint32_t *)0x40021000; // example address uint32_t val = *REG; // read register val |= 0x01; // set bit 0 val &= ~0x02; // clear bit 1 *REG = val; // write back
Example
This example shows how to read a 32-bit register, set bit 3, clear bit 2, and write the new value back safely.
c
#include <stdint.h> #include <stdio.h> // Simulate a hardware register as a global variable volatile uint32_t REGISTER = 0x0A; // initial value 00001010b int main() { volatile uint32_t *reg = ®ISTER; uint32_t val = *reg; // read register val |= (1 << 3); // set bit 3 (bit counting from 0) val &= ~(1 << 2); // clear bit 2 *reg = val; // write back printf("Register value after modify: 0x%X\n", *reg); return 0; }
Output
Register value after modify: 0x12
Common Pitfalls
Common mistakes include:
- Not using
volatile, causing the compiler to optimize away register reads/writes. - Overwriting the entire register without preserving other bits.
- Using incorrect bit masks or shifts.
Always read the register first, modify only the bits you want, then write back.
c
/* Wrong way: overwrites all bits, losing other settings */ *REG = 0x01; // sets bit 0 but clears others /* Right way: read-modify-write preserves other bits */ uint32_t val = *REG; val |= 0x01; // set bit 0 *REG = val;
Quick Reference
| Step | Action | Code Example |
|---|---|---|
| 1 | Declare volatile pointer to register | volatile uint32_t *REG = (volatile uint32_t *)0xADDRESS; |
| 2 | Read current register value | uint32_t val = *REG; |
| 3 | Modify bits using bitwise operators | val |= MASK; // set bits val &= ~MASK; // clear bits |
| 4 | Write modified value back | *REG = val; |
Key Takeaways
Always use volatile pointers to access hardware registers to prevent compiler optimizations.
Use read-modify-write to change only specific bits without affecting others.
Use bitwise OR (|) to set bits and bitwise AND with NOT (&~) to clear bits.
Never overwrite the whole register unless you intend to reset all bits.
Test your code on real hardware or simulators to verify register changes.