How does a tiny chip read a button?
Inside a washing machine, a thermostat or a game controller there is a small chip with no screen and no keyboard. It has to notice a button, switch a light on and fade a motor, all by writing and reading a few numbers. This lesson uses a made-up chip, simulated in Python, so you can see those numbers change.
By the end of this lesson
Note
I can read a register in binary and say what setting, clearing or toggling one bit does.
Key words
- Register: a small numbered store inside the chip, here 8 bits wide.
- Bit: one 0 or 1. Bit 0 is the one on the right.
- Mask: a number used to pick out or change chosen bits.
- Pin: a metal leg of the chip, high (1) or low (0).
- PWM: pulse width modulation, a pin switched on and off fast, so the share of time on sets how strong it acts.
- Interrupt: a signal that makes the chip stop and run a handler.
Starter: what you already know
Note
Answer in your head. 1. What is the binary number 1010 in ordinary numbers? 2. How many different values can 8 bits hold? 3. What does print do in Python? Answers: 10; 256, from 0 to 255; it shows a value on the screen.
Before you start
Note
Press Start the lab on the bar at the top of this lesson and wait until it says it is running. There is one container, jupyter, and the terminal reaches its Python with docker exec jupyter, which runs one command inside it. The chip is a simulator of a made-up chip, not a real board, and real chips have other registers, other names and many more rules. The JupyterLab link opens its web page, port 8888. Each Run button below types its command into your lab's terminal, one after another in the same session. To watch them, or to type your own, press Open terminal on the bar.
A chip is a handful of registers
ls lists the work folder: mcu.py is the chip, start.ipynb a notebook and steps the scripts. The first script resets the chip and prints every register in binary, hex and ordinary numbers. There are seven: DDR (which pins are outputs), PORT (what the outputs drive), PIN (what the pins read), TCCR (the timer control), TCNT (the timer count), OCR (the compare value) and IFR (the interrupt flag). After a reset all seven are 0b00000000.
docker exec jupyter lsShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
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.
A bit is changed with a mask
The script starts PORT at 0b00001010. OR with a mask sets a bit, giving 0b00001011. AND with the opposite of a mask clears one, giving 0b00001001. XOR toggles one, giving 0b00000001, and the same toggle again brings back 0b00001001. Only the chosen bit changes each time, which is the point of a mask.
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.
A pin is a bit you can see from outside
A 1 in DDR makes a pin an output. The script makes pin 0 one, writes a 1 to bit 0 of PORT, and PIN reads back 0b00000001. Then it toggles the bit once a tick for eight ticks and prints #.#.#.#., a light that blinks: # is on and . is off.
docker exec jupyter python3 steps/pins.pyShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Check
Tip
Which register decides whether a pin is an output, and what would PIN show for an output pin that PORT has set to 1? Answer: DDR, and 1, because an output pin shows what the chip drives.
PWM is a pin that is on for part of every period
The timer counts from 0 to 15 and starts again, so a period is 16 ticks. While TCNT is below OCR, pin 5 is high. The script tries OCR of 4, 8 and 12 and prints one period of each: four ticks on and 12 off is 25 percent, eight and eight is 50 percent, and 12 on and four off is 75 percent. That share is the duty cycle, and it is how a real chip can dim a light or slow a motor with nothing but on and off.
docker exec jupyter python3 steps/pwm.pyShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
A button bounces, so how you listen matters
A real button does not go from 0 to 1 once: it flickers. The first script polls, reading the pin every tick, and one press makes it see two rising edges, at ticks 2 and 4, so it counts 2 presses. The second script lets the chip raise the interrupt flag and call a handler instead. It is called twice, at ticks 2 and 4, the same two edges. Then the handler ignores a call within three ticks of the last one it kept, and one press is counted.
docker exec jupyter python3 steps/poll.pyShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
docker exec jupyter python3 steps/interrupt.pyShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
A common mistake
Warning
Many people think an interrupt cures bounce. It does not: the handler ran for every edge, twice for one press. An interrupt only means the chip need not keep asking. The bounce is still in the signal and your code has to deal with it. Remember too that this is a simulator: on a real chip the register names and bit positions are different, and the data sheet is the authority.
The same work in a notebook
Open JupyterLab from the bar and double-click start.ipynb. It builds the chip, writes registers and makes a PWM signal, one cell at a time. Click a cell and press Shift and Enter to run it. The last cell asks you to change the compare value and say what the duty cycle is now. mcu.py is short and commented: open it beside the notebook and read it.
Exit check
Tip
Answer without looking back. 1. Which operation sets a bit, and which toggles one? 2. With a period of 16 ticks and a compare value of 8, what is the duty cycle? 3. Why was one press counted twice? 4. Is this chip a real one? Answers: OR sets and XOR toggles; 50 percent; the button bounced and each rising edge counted; no, it is a simulator of a made-up chip.
For teachers
Tip
This lesson is a small example of a lesson worked in a lab: the steps and the Run buttons are the lesson, and the lab is each student's own copy. The lab is one service, JupyterLab with a short Python simulator of a made-up 8-bit chip, and nothing here marks itself: the student hands in a short script or notebook of their own that uses mcu.py, downloaded from JupyterLab, and a sentence on what the registers showed. Key learning points: 1. A register is a number whose bits each mean something. 2. A mask sets, clears or toggles chosen bits. 3. A timer and a compare value make a PWM signal. 4. A button can be polled or handled by an interrupt, and either way it bounces. Common mistake: believing an interrupt removes bounce. Equipment: the lab and a browser. Tip: ask students to predict each printed line before they press Run. Be clear that it is a simulator: real chips differ, so a data sheet is what to read for one. Copy it into your own module and change it, or build one from scratch with the Lab menu in the lesson builder.