A chip is controlled by numbers
A register is a small store of bits inside a chip, and a microcontroller is controlled by reading and writing them. Once you can read a register in binary and change one bit, you can switch one light on without touching the other seven.
A washing machine, a car key and a thermostat all work this way. There is no screen. There are only numbers that mean something.
By the end of this lesson
Note
I can read and set bits in a register.
Key words
| Word | What it means | Example |
|---|---|---|
| Microcontroller | a tiny computer on one chip, built to control one device | |
| Register | a small numbered store inside the chip | |
| Bit | one binary digit, a 0 or a 1 | |
| Mask | a number that picks out the bits to change | 0b00000100 |
| Hexadecimal | counting in sixteens, written with 0x in front | 0x0A |
The cells students fill in are left blank in this preview.
This chip is a simulator
Important
The lab runs a made-up chip written in Python. It is not a real board. Real chips have other register names, more registers and many more rules. What carries over is the idea: a chip is controlled through registers.
A register is one byte of switches
In this lab every register is 8 bits wide. That is one byte, so it holds a whole number from 0 to 255.
Each bit is a switch with a number. Bit 0 is on the right. Bit 7 is on the left. The simulated chip has seven registers. For example PORT holds what the output pins are told to do, and DDR says which pins are outputs.
Worked example: PORT holds 0b00001010
| Bit number | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| Worth | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
| Value | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |
| Adds up to | 8 | 2 |
The cells students fill in are left blank in this preview.
List the lab folder
docker exec jupyter lsShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Print every register after a reset
docker exec jupyter python3 steps/registers.pyShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Add the bits that are 1
Bits 3 and 1 are on, so the number is 8 + 2 = 10. Written in hexadecimal it is 0x0A, because 10 is the digit A when you count in sixteens.
Engineers write registers in binary when the bits matter and in hexadecimal when they want something short.
A mask changes only the bits you choose
To switch on one bit you must not write a whole new number. That would switch off every other bit. Use a mask instead: a number with a 1 only in the place you care about.
Bit 3 has the mask 1 << 3, which is 0b00001000. The symbol << moves a 1 to the left, three places.
Worked example: four operations on 0b00001010
| Job | Python | Result | What changed |
|---|---|---|---|
| Set bit 0 | value | (1 << 0) | 0b00001011 | Bit 0 became 1 |
| Clear bit 1 | value & ~(1 << 1) | 0b00001000 | Bit 1 became 0 |
| Toggle bit 3 | value ^ (1 << 3) | 0b00000010 | Bit 3 flipped to 0 |
| Read bit 3 | (value >> 3) & 1 | 1 | Nothing, it only looks |
The cells students fill in are left blank in this preview.
OR sets, AND with NOT clears, XOR toggles
The symbol | is OR. A bit that is 1 in either number stays 1, so OR with a mask sets the bit. The symbol & is AND. It keeps a bit only if both numbers have it, so AND with the opposite of a mask clears the bit. The symbol ^ is XOR. It flips a bit, so a second XOR undoes the first.
Watch a mask set, clear and toggle
docker exec jupyter python3 steps/bits.pyShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
What the bits script prints
It starts PORT at 0b00001010. Setting bit 0 gives 0b00001011. Clearing bit 1 gives 0b00001001. Toggling bit 3 gives 0b00000001, and toggling it again brings back 0b00001001.
Each time, only the chosen bit moved. That is the whole point of a mask.
A common mistake
Warning
Many people think that to switch on bit 0 you write the number 1 to the register. That writes 0b00000001 and switches every other bit off. Read the register first, combine it with a mask, then write the result back. Write a whole number only when you mean to change all eight bits.
Try a mask yourself
port = 0b00101000 # a register value
port = port | (1 << 1) # set bit 1
print(format(port, "08b"))
# Change the line above to clear bit 5, then press Run.
port = port & ~(1 << 5)
print(format(port, "08b"), (port >> 3) & 1)