Ground Embedded KO Subscribe

Notes / C Language

Bit operations: set, clear, toggle

Clear with AND, set with OR, toggle with XOR, move with shifts. The basic grammar of register control.

GROUND EMBEDDED · NOTEC Language 09Bit operations 1volatile uint32_t *ODR 2 = (uint32_t*)0x40020014; 3*ODR |= (1u << 5); 4while (1) { 5 if (flag) toggle(); 6}

One register holds several bits with different functions. The purpose of bit operations is to change only the bits you want and leave the rest as they are.

Four patterns

reg |=  (1u << n);    // set bit n
reg &= ~(1u << n);    // clear bit n
reg ^=  (1u << n);    // toggle bit n
if (reg & (1u << n))  // test bit n

Changing a multi-bit field

A field of two or more bits, such as a mode, is cleared first and then given its new value.

reg = (reg & ~(0x3u << 4)) | (mode << 4);

Shift pitfalls

  • 1 << 31 shifts a signed int and can be undefined behaviour. Use 1u.
  • Shifting a negative signed value right fills with the sign bit on some compilers.

Read-modify-write

|= is three steps: read, modify, write back. If an interrupt changes the same register in between, that change is lost. This is why registers that change bits by a write alone exist, such as BSRR on STM32.

The four basic operations

The goal is to change only the bit you want without touching the other bits in the register.

What you want Code Why it works
Set reg |= (1u << n); OR with 1 gives 1
Clear reg &= ~(1u << n); AND with 0 gives 0
Toggle reg ^= (1u << n); XOR with 1 gives the opposite
Read (reg >> n) & 1u Move the bit to the bottom and test it

Changing a field made of several bits

A field where two or more bits form a group, such as a GPIO mode, is handled in the order "clear, then write". OR alone mixes the new value with the old one.

#define MODE_POS   10u
#define MODE_MASK  (0x3u << MODE_POS)

uint32_t tmp = GPIOA->MODER;
tmp &= ~MODE_MASK;              // 1) set the field to 00
tmp |= (0x1u << MODE_POS);      // 2) write the new value 01 (output)
GPIOA->MODER = tmp;             // 3) apply in one write

The calculation is finished in a temporary and written once so that the intermediate state (00) never reaches the actual pin, even briefly.

In pictures

reg0010 1100original valuemask0010 00001u << 5result0010 1100already 1, unchangedclear0000 1100reg & ~mask
Setting bit 5 of 0x2C. OR with the mask leaves the other bits unchanged.

Problems that come up in real work

  • 1 << 31: 1 is a signed int, so shifting it 31 places is undefined behaviour. Always use 1u.
  • An interrupt between read and write: if an interrupt changes another bit of the same register while the main loop is doing reg |= A, that change is lost. This is why many MCUs provide separate write-only SET and CLEAR registers (BSRR and the like).
  • Flags cleared by writing: some status register bits are cleared by writing 1. Writing with |= can clear other flags along with it, so check datasheet markings such as rc_w1.
  • Operator precedence: reg & MASK == 0 is evaluated as reg & (MASK == 0). Use parentheses.

Check yourself

  • Which expression extracts only the upper 4 bits of the 8-bit value 0xA5?
  • If x & (x - 1) is 0, what kind of number is x?
FREE PDFGet the 3 free ebooksIf you want to keep reading this topic as one book: BLDC motor control, serial communication and sensors at book length. PDFs are in Korean.Get them free →