05. GPIO Control - Controlling Hardware Pins¶
GPIO (General Purpose Input/Output) pins are the Raspberry Pi's interface to the physical world. Let's learn how to control LEDs, read buttons, and interact with external hardware.
What is GPIO?¶
The Raspberry Pi 4B has 40 GPIO pins on its header. These pins can be configured as: - Output: Control LEDs, relays, motors - Input: Read buttons, sensors - Alternate Functions: UART, SPI, I2C, PWM
GPIO Pin Layout (Raspberry Pi 4B)¶
Voltage Levels (Water Pressure)
Raspberry Pi GPIO operates at 3.3V.
Think of Voltage as Water Pressure: - 5V: High pressure fire hose (Arduino/USB). - 3.3V: Garden hose (Raspberry Pi GPIO).
If you connect a 5V device directly to a 3.3V pin, you burst the pipe (fry the chip). Always check voltages!
GPIO Register Overview¶
BCM2711 GPIO Registers¶
| Register | Offset | Purpose |
|---|---|---|
GPFSEL0-5 |
0x00-0x14 | Function Select (input/output/alt) |
GPSET0-1 |
0x1C-0x20 | Set Pin High |
GPCLR0-1 |
0x28-0x2C | Clear Pin Low |
GPLEV0-1 |
0x34-0x38 | Read Pin Level |
GPIO_PUP_PDN_CNTRL |
0xE4+ | Pull-up/Pull-down Control (RPi 4) |
Base address: 0xFE200000 (RPi 4)
Function Select Register (GPFSEL)¶
Each pin uses 3 bits to select its function:
| Value | Function |
|---|---|
| 000 | Input |
| 001 | Output |
| 100 | Alt Function 0 |
| 101 | Alt Function 1 |
| ... | ... |
Example: To set GPIO 21 as output:
- GPIO 21 is in GPFSEL2 (pins 20-29)
- Bit offset: (pin % 10) * 3 -> (21 % 10) * 3 = 1 * 3 = 3
We need to treat the 32-bit register like a row of mailboxes, where every 3 mailboxes belong to one pin.
block-beta
columns 10
block:Bits
bits["..."]
b29["Pin 29"]
b28["Pin 28"]
b27["Pin 27"]
b26["Pin 26"]
b25["Pin 25"]
b24["Pin 24"]
b23["Pin 23"]
b22["Pin 22"]
b21["Pin 21<br>(Bits 5-3)"]
b20["Pin 20"]
end
To configure GPIO 21, we only touch bits 3, 4, and 5.
- Clear: Set bits 3-5 to
000(wipe previous setting). - Set: Write
001(Output) to these bits.
Implementing the GPIO Driver¶
Header File: include/gpio.h¶
Driver Implementation: drivers/gpio.c¶
1. Setting Pin Function¶
2. Setting Pin High/Low¶
Write-Only Registers
GPSET and GPCLR are write-only. Write a 1 to the bit position to set/clear that pin.
3. Reading Pin State¶
LED Blinking Demo¶
Hardware Setup¶
Connect an LED to GPIO 21 (Pin 40):
graph LR
GPIO["GPIO 21 (Pin 40)"] -- "+" --> R["Result (220Ω)"]
R --> LED["LED Anode (+)"]
LED -- "-" --> GND["GND (Pin 39)"]
Software: kernel/main.c¶
Building and Testing¶
1. Build¶
2. Deploy¶
Copy kernel8.img to SD card (with firmware files).
3. Connect Hardware¶
- LED on GPIO 21 (Pin 40)
- Resistor (220Ω - 1kΩ)
- Ground (Pin 39)
4. Power On¶
The LED should blink every second!
Expected Serial Output¶
Reading Button Input (Bonus)¶
Here's how to read a button on GPIO 16:
Why Pull-up? (The Floating Problem)
Imagine a switch connected to a pin. - Pressed: Connected to GND (0V). - Released: Connected to... nothing?
If connected to "nothing" (floating), the pin acts like an antenna, picking up random noise (0, 1, 0, 1...).
The Solution: Pull-up Resistor (The Spring)
Think of the value 1 (High) as "Up" and 0 (Low) as "Down".
A Pull-up Resistor is like a weak spring pulling the pin Up (1).
- Button Released: The spring pulls the pin Up. Read:
1. - Button Pressed: You use your strength to pull it Down to GND. Read:
0.
graph TD
VCC["3.3V (VCC)"] -- "Weak Spring (Resistor)" --> Pin["GPIO Pin"]
Pin -- "Button (Open)" --> GND["GND"]
style Pin fill:#f9f,stroke:#333
style VCC fill:#dfd,stroke:#333
Complete Source Code¶
Troubleshooting¶
| Problem | Solution |
|---|---|
| LED doesn't blink | Check wiring (anode/cathode) |
| LED always on/off | Verify pin number (GPIO 21 = Pin 40) |
| No serial output | UART still working? Check baud rate |
| Dim LED | Resistor too large, try 220Ω |
GPIO Safety Tips¶
- Never exceed 3.3V on any GPIO pin
- Current limit: Max ~16mA per pin, ~50mA total
- Use resistors with LEDs (recommended: 220Ω-1kΩ)
- Short circuit protection: None! Be careful
What's Next?¶
Now that we can control GPIO, the next article covers Interrupts and Exception Handling, which will allow us to: - Respond to button presses instantly (no polling) - Handle timer interrupts for multitasking - Catch and handle CPU exceptions (e.g., data aborts)
Stay tuned!