Skip to content

Commit 2399f99

Browse files
committed
Explain bit operations more
1 parent 9f56501 commit 2399f99

File tree

1 file changed

+49
-0
lines changed

1 file changed

+49
-0
lines changed

README.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,55 @@ mode. For example, this snippet sets pin A3 to output mode:
164164
* (volatile uint32_t *) (0x40020000 + 0) |= 1 << 6; // Set bits 6-7 to 1
165165
```
166166

167+
Let me explain those bit operations. Our goal is to set bits 6-7, which are
168+
responsible for the pin 3 of GPIOA peripheral, to a specific value (1, in our
169+
case). This is done in two steps. First, we must clear the current value of
170+
bits 6-7, because it may hold some value already. Then we must set bits 6-7
171+
to the value we want.
172+
173+
So, first, we must set bit range 6-7 to zero. How do we set
174+
a number of bits to zero? In four steps:
175+
176+
- Get a number that has N contiguous bits set:
177+
- 1 for 1 bit: `0b1`,
178+
- 3 for 2 bits: `0b11`,
179+
- 7 for 3 bits: `0b111`,
180+
- 15 for 4 bits: `0b1111`,
181+
- and so on: generally, for N bits, the number is `2^N - 1`
182+
So, for 2 bits it is number `3`, or `0b00000000000000000000000000000011`
183+
- Shift that number left. If we need to set bits X-Y, then shift on X positions
184+
left. In our case, shift on a 6 positions left: `(3 << 6)`, or
185+
`0b00000000000000000000000011000000`
186+
- Invert the number: turn zeros to ones, and ones to zeroes:
187+
`~(3 << 6)`, or `0xb11111111111111111111111100111111`
188+
- Now, perform a "logical AND" operation of the register with our number. Bits
189+
6-7, AND-ed with 0, will give zero - that's what we want! All other bits,
190+
AND-ed with 1, will retain their current value: `REG &= ~(3 << 6)`. Retaining
191+
values of all other bits is important: we don't want to change other settings
192+
in other bit ranges!
193+
194+
So, in general, if we want to clear bits X-Y (set them to zero), do:
195+
196+
```c
197+
PERIPHERAL->REGISTER &= ~(NUMBER_WITH_N_BITS << X);
198+
```
199+
200+
And, finally, we want to set a given bit range to the value we want. We
201+
shift that value X positions left, and OR with the current value of the whole
202+
register:
203+
204+
```c
205+
PERIPHERAL->REGISTER |= VALUE << X;
206+
```
207+
208+
Now, it should be clear to you, dear reader, the meaning of these two lines,
209+
which set bits 6-7 of the GPIOA MODER register to the value of 1 (output).
210+
211+
```c
212+
* (volatile uint32_t *) (0x40020000 + 0) &= ~(3 << 6); // CLear bits 6-7
213+
* (volatile uint32_t *) (0x40020000 + 0) |= 1 << 6; // Set bits 6-7 to 1
214+
```
215+
167216
Some registers are not mapped to the MCU peripherals, but they are mapped to
168217
the ARM CPU configuration and control. For example, there is a "Reset at clock
169218
control" unit (RCC), described in section 6 of the datasheet. It describes

0 commit comments

Comments
 (0)