Bits and registers in C
Set, clear, toggle and test single bits. Every peripheral is configured this way.
Explanation
Every peripheral is controlled through registers: memory locations in which each bit has a meaning. Bit 5 of one register might enable a pin; bit 3 of another might signal that a byte has arrived. For this reason, embedded C often changes one bit and leaves the others unchanged. There are exactly 4 idioms for this, and they are worth learning by heart.
First, make a mask with a 1 in the required position: (1u << n). The u makes the constant unsigned, which avoids problems when n is 31. Set a bit with OR: reg |= (1u << 3). Clear it with AND NOT: reg &= ~(1u << 3). Toggle it with XOR: reg ^= (1u << 3). Test it with AND: if (reg & (1u << 3)) is true when bit 3 is set.
The compound forms |=, &= and ^= read the register, change one bit and write it back, so the other bits keep their values. Plain assignment (reg = 0x08) overwrites all the other bits, which is a common bug.
Hexadecimal notation shows bits clearly. Each hex digit is 4 bits: 0x0F is 0000 1111 and 0x28 is 0010 1000, so in 0x28 bits 5 and 3 are set. When in doubt, write the number out in binary.
Some registers hold a small number in a few bits, for example a 3-bit field in bits 4 to 6. To read it, shift the field down to bit 0, then mask off everything above it: (r >> 4) & 0x7. To write it, clear the field first, then OR in the new value shifted into place.
Key points
- Mask: (1u << n). Set: |=. Clear: &= ~. Toggle: ^=. Test: & inside an if.
- The compound operators keep the other bits. Plain = overwrites the whole register.
- One hex digit is 4 bits. 0x28 is 0010 1000.
- Read a field with (r >> shift) & mask.
Check your understanding
4 questions on this lesson. An explanation follows each answer.
Sign in to answer the questions. Your answers count towards your level.
You can read the lesson and the project without an account.
Project
Binary counter on LEDs
What you need
- Your board and breadboard.
- 4 LEDs with 4 resistors, on 4 pins.
Steps
- 1Write 4 small functions or macros: bit_set, bit_clear, bit_toggle and bit_test, each taking a value and a bit number. Test them on your computer, or by printing, before you use the hardware.
- 2Keep a uint8_t counter. Once a second, add 1 and show its low 4 bits on the 4 LEDs using bit_test.
- 3Open the datasheet or reference manual of your chip and find the register that sets the output level of the port your LEDs are on. Switch one LED on and off by writing to that register directly, without the library call.
Done when
The LEDs count from 0000 to 1111 and wrap around, and at least one LED is driven by a register write that you wrote yourself.