The pitfalls of register code
Register code fails in a small number of ways, over and over.
Most of the failures look like a dead peripheral rather than a wrong value.
Each one has a symptom you can recognise and a fix of one line.
Try register pitfalls in the bit editor
The safe idiom: clear the field, then set it. Edit the expression or select a bit to flip it.
7
6
5
4
3
2
1
0
0xB3
hex · 8 bits
- binary
- 0b10110011
- value
- 179
Bit width
Shift
Register pitfalls step by step
Change the low four bits of 0xB6 to 3 and keep the rest:
- The mask for the field is 0x0F.
- Clear the field first: 0xB6 & ~0x0F gives 0xB0.
- Set the new value: 0xB0 | 0x03 gives 0xB3.
- An OR alone gives 0xBF, because the old bits stay.
Ten register mistakes, with the symptom and the fix
| Mistake | What you see | Fix |
|---|---|---|
| reg = value | Other bits of the register go to 0 | reg |= value |
| OR without a clear | An old field value stays behind | reg &= ~mask first |
| Bit n for a 2-bit field | The wrong pin changes | Use 2n for the position |
| 1 << 31 | Undefined behaviour on a signed int | Write 1u << 31 |
| Shift by the width | Undefined behaviour, not 0 | Guard the shift amount |
| No volatile | The compiler drops the read | Declare the pointer volatile |
| Read, change, write | An interrupt loses a change | Use a W1TS register or a lock |
| x & mask == 0 | The test always fails | Write (x & mask) == 0 |
| Signed right shift | A negative value keeps its sign | Shift an unsigned type |
| A full write to MODER | The debug port stops | Keep the bits of the debug pins |
Where register pitfalls is used
A dead peripheral after a register write is almost always one of these ten.
A code generator and a language model both produce field offsets that look right and are not.
A value that reads back wrong is faster to check against the bits than against the datasheet.
Register pitfalls: points to note
- A wrong bit position gives a program that runs with a dead pin, which hides the cause.
- A clobbered field often works until a second peripheral needs the bits you cleared.
- A read, change and write sequence fails once in a thousand runs, which reads as random.
- Paste the value you wrote into the editor above and compare it against the field you meant.
The full bit editor
The full editor holds many values, evaluates expressions across them, and reads live values from a serial port.
Open this value in the full editorQuestions about register pitfalls
- Why did my other pins go dead?
- An assignment writes every bit. Use |= to set bits and &= ~mask to clear them.
- Why does the pin next to mine change?
- A field of two bits sits at the position 2n, so pin 5 uses bit 10 and not bit 5.
- Why does my register read back as 0?
- A pointer without volatile lets the compiler drop the read. Declare it volatile.