Explain logic gates, adders, flip-flops and registers, and set, clear and read GPIO bits with bit masks.
Why it matters
Almost every electrical product now has a microcontroller in it: chargers, inverters, motor drives, protective relays, meters, sensors. Electrical engineers write firmware, read logic analyser traces and design the interface between digital pins and real loads. Underneath all of it are a few gates and the idea of a register: a row of bits, each one wired to something.
Logic levels and gates
Digital circuits treat a voltage as 1 (high) or 0 (low). A 3.3 V CMOS input typically reads anything above about 2 V as 1 and below about 0.8 V as 0; the band in between is undefined. Mixing 5 V and 3.3 V parts needs care: a 5 V output can damage a 3.3 V input that isn't "5 V tolerant", so you use a level shifter or a divider.
The basic gates and their truth tables:
A B | AND OR XOR NAND
0 0 | 0 0 0 1
0 1 | 0 1 1 1
1 0 | 0 1 1 1
1 1 | 1 1 0 0
NOT: 0 -> 1, 1 -> 0
NAND (and NOR) is universal: any logic function can be built from NAND gates alone. Inside a chip, each gate is a few CMOS transistors used as the switches from the previous lessons.
Combinational logic: adding numbers
A half adder adds two bits: sum = A XOR B, carry = A AND B. A full adder also takes a carry in:
sum = A XOR B XOR Cin
Cout = (A AND B) OR (Cin AND (A XOR B))
Chain four full adders, passing each carry to the next, and you have a 4-bit ripple-carry adder. 0101 (5) + 0011 (3) gives 1000 (8). The output depends only on the present inputs, which is what "combinational" means.
Sequential logic: memory
A flip-flop stores one bit. A D flip-flop copies its D input to its Q output on each rising clock edge and holds it until the next edge. A register is a row of flip-flops sharing a clock, for example 8 or 32 bits. Counters, shift registers and the state machines in every controller are built from registers plus combinational logic. The clock sets the pace: the logic between registers must settle within one clock period.
Inside a microcontroller
A microcontroller (MCU) puts a CPU, flash memory for the program, RAM, and peripherals on one chip: GPIO, timers, PWM, ADCs, UART, SPI and I2C. Peripherals are controlled through memory-mapped registers: writing a number to a particular address changes the hardware.
GPIO (general-purpose input/output) pins are usually configured with registers like:
- a direction register (bit = 1 for output),
- an output register (bit = 1 drives the pin high),
- an input register (read the pin's level).
You change one pin without disturbing the others with bit masks:
REG |= (1 << 5); // set bit 5 (pin 5 high)
REG &= ~(1 << 5); // clear bit 5 (pin 5 low)
int on = (REG >> 5) & 1; // read bit 5
REG ^= (1 << 5); // toggle bit 5
Arduino's digitalWrite(pin, HIGH) does exactly this behind the scenes.
Worked example. A port register holds 0b00010010 (pins 1 and 4 high). Setting bit 0 gives 0b00010011 (19); then clearing bit 4 gives 0b00000011 (3). Reading bit 1 gives 1.
Pins meet the real world
- Current limits: a GPIO pin can source or sink only a few to about 20 mA, and the whole port has a total limit. Use a transistor (previous lesson) for anything bigger.
- Pull-up and pull-down resistors keep an input from floating when a button is open. Many MCUs have internal pull-ups you enable in a register.
- Debouncing: mechanical switches bounce for a few milliseconds, so firmware waits or samples repeatedly before accepting a change.
- Interrupts let the CPU react to an edge immediately instead of polling.
Simulate it
Wokwi (https://wokwi.com) simulates Arduino and other boards in the browser, with LEDs, buttons and a logic analyser. Logic simulators such as Logisim Evolution let you build adders from gates. No hardware is needed.
Common mistakes
- Writing the whole register (
REG = 1 << 5) and accidentally turning every other pin off. - Forgetting the NOT when clearing:
REG &= (1 << 5)clears everything except bit 5. - Floating inputs with no pull-up, giving random readings.
- Driving loads straight from a pin beyond its current rating.
- Connecting 5 V signals to 3.3 V-only inputs.
Recap
- Gates implement Boolean logic; NAND is universal; full adders chain into multi-bit adders.
- Flip-flops and registers store state on clock edges.
- MCUs control peripherals through registers; use
|=,&= ~and shifts to change single bits.
Try it
Open the Do tab to build a full adder, a 4-bit adder and GPIO bit helpers in JavaScript.