← Back to Embedded C series
🔌
8051
8051 · Prereq: lesson 7

08. GPIO Input — Reading Buttons/Switches

Lesson 7 sent signals out; now we read them in. The 8051's quirk: its ports are quasi-bidirectional, so you must write 1 to a pin before reading it as input. We read a P3.2 button to control a P1.0 LED, and implement edge-detection that toggles on each press. Contact bounce (chatter) is covered in lesson 21.

GPIOinputbuttonquasi-bidirectionalpull-upedge detect
Duration
~1 hour
Level
📊 Beginner
Prerequisite
🎯 Lesson 7
OUTCOME
Write 1 before reading on a quasi-bidirectional port, and distinguish mirroring from edge detection (1→0) to react only at the press instant.

What you'll learn

  • 1Explain why you write 1 before reading on a quasi-bidirectional port
  • 2Read an input pin via sbit and branch
  • 3Understand pull-up/active-low buttons (1 idle, 0 pressed)
  • 4Mirror button state onto an LED
  • 5Detect edges (1→0) to catch only the press instant

Introduction

Reading digital signals from buttons·switches·sensors so the program reacts. Understanding the 8051 quasi-bidirectional-port trap is the heart of this lesson.

Key concepts

1) Quasi-bidirectional — write 1 before reading

8051 pins share an output latch and input buffer. With 0 latched, the pin is strongly held Low and external High won't read. Write 1 to "release" pins you'll use as input.

c
sbit BTN = P3 ^ 2;
BTN = 1;            /* prepare input: pin to 1 (weak pull-up) */
if (BTN == 0) { }   /* now external 0 reads */

2) Pull-up + active-low button

StatePin
not pressed1 (pull-up)
pressed0 (GND)

So "pressed" is `BTN == 0`.

3) Mirroring vs edge detection

c
if (prev_btn == 1 && btn == 0) {   /* falling edge = just pressed */
    led = !led;                    /* toggle (once) */
}
prev_btn = btn;
ℹ️

Mirroring reacts while held (level); edge detection reacts only at the press instant. "Once per press" needs edge detection. Bounce is handled by debouncing in lesson 21.

Core example

c
#include <reg52.h>
sbit BTN = P3 ^ 2;   /* input */
sbit LED = P1 ^ 0;   /* output (active-low) */
void main(void) {
    BTN = 1;                 /* prepare input */
    while (1) {
        if (BTN == 0) LED = 0;   /* pressed → LED ON */
        else          LED = 1;   /* released → LED OFF */
    }
}

Simulator: click the P3.2 bit in the Port 3 window to "press" the button and watch P1.0 follow in Port 1.

Common mistakes

Q. The input always reads the same value.

A. The quasi-bidirectional trap. If you ever wrote output 0 to that pin, input is blocked. Write BTN = 1; before reading.

Q. One press toggles the LED several times.

A. A too-fast loop mistakes "while held" for many toggles → use edge detection. If it's chatter, you need debouncing (lesson 21).

Q. if (P3 & 0x04 == 0) looks wrong.

A. Precedence trap: == binds before &, so 0x04==0 is first. Parenthesize: if ((P3 & 0x04) == 0), or use sbit: if (BTN == 0).

Summary

  • A quasi-bidirectional port needs a 1 written before reading as input
  • Pull-up + active-low button: 1 idle, 0 pressed — test BTN == 0
  • Mirroring follows the current state; edge detection catches the press instant
  • Edge detection compares previous and current state (prev==1 && cur==0)
  • Contact bounce is solved by debouncing (lesson 21)

Exercises

  1. Write edge-detection code that moves the lit LED one step per press
  2. Predict the toggle result for {1,1,0,0,1,0} by hand, then verify in pc_test
  3. Read two buttons (P3.2/P3.3) to control two LEDs independently
Example code / lecture materials

All lecture materials and example code (with homework and answers) are openly available on GitHub.

View on GitHub ↗