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

07–08. GPIO Output — Blinking LEDs · GPIO Input — Reading Buttons/Switches

Embedded's "Hello, World" is blinking an LED. What value lights it (active-low/high wiring), how long to wait (delay), and how to make patterns with multiple LEDs (bit masks) — the essence of GPIO output is all here. We make a running light on 8 LEDs at P1 and observe the pin waveform in μVision's Logic Analyzer. 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.

GPIOoutputLEDactive-lowdelaymaskinputbuttonquasi-bidirectionalpull-upedge detect
Duration
⏱ ~1 hour + ~1 hour
Level
📊 Beginner
Prerequisite
🎯 Lesson 6
OUTCOME
Light one LED with ~(1u<<pos) on active-low wiring, build a running-light pattern with while(1)+delay, and observe the waveform in the Logic Analyzer. / 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

  • 1Control LEDs via GPIO output and explain active-low/high wiring
  • 2Blink LEDs by changing the port value inside while(1)
  • 3Understand software delay loops and their imprecision
  • 4Make a running-light pattern with ~(1u<<pos)
  • 5Observe pin toggles in the Logic Analyzer
  • 6Explain why you write 1 before reading on a quasi-bidirectional port
  • 7Read an input pin via sbit and branch
  • 8Understand pull-up/active-low buttons (1 idle, 0 pressed)
  • 9Mirror button state onto an LED
  • 10Detect edges (1→0) to catch only the press instant

07. GPIO Output — Blinking LEDs

Embedded's "Hello, World" is blinking an LED. What value lights it (active-low/high wiring), how long to wait (delay), and how to make patterns with multiple LEDs (bit masks) — the essence of GPIO output is all here. We make a running light on 8 LEDs at P1 and observe the pin waveform in μVision's Logic Analyzer.

Introduction

We repeat lesson 6's port writes inside an infinite loop to make patterns. Many 8051 boards wire LEDs active-low (on when the pin is 0), so the "only the bit you want = 0" sense matters.

Key concepts

1) LED wiring — active-low vs high

TypeLED ON when
active-high (pin→LED→GND)pin = 1
active-low (VCC→LED→pin)pin = 0

8051 sinks more current when outputting 0, so boards often wire LEDs active-low. This lesson assumes active-low.

2) Light one LED — ~(1u<<pos)

c
/* active-low: only bit pos = 0, rest = 1 */
pos=0 -> ~(1<<0) = 0xFE = 11111110  (LED0 ON)
pos=3 -> ~(1<<3) = 0xF7 = 11110111  (LED3 ON)
/* sweeping pos 0->7 makes the light flow one step at a time */

3) Software delay

c
static void delay(unsigned int n) {
    unsigned int i;
    for (i = 0; i < n; i++) { }   /* imprecise! exact time → timer (lesson 9) */
}
⚠️

The compiler may optimize away an empty loop. Mark the loop variable volatile to keep it, or use a timer (lesson 9) for precise delays.

Core example

c
#include <reg52.h>
#define LED_PORT P1
static void delay(unsigned int n){ unsigned int i; for(i=0;i<n;i++){} }

void main(void) {
    unsigned char pos = 0;
    while (1) {
        LED_PORT = (unsigned char)~(1u << pos);  /* one LED ON (active-low) */
        delay(20000);
        if (++pos >= 8) pos = 0;
    }
}

The running light produces 0xFE → 0xFD → 0xFB → 0xF7 → 0xEF → 0xDF → 0xBF → 0x7F. Add P1.0–P1.7 to the Logic Analyzer to see each pin drop Low in turn (a staircase waveform).

Common mistakes

Q. I wrote 1 to the port but the LED won't light.

A. On active-low boards, 1 is OFF — write 0 to turn it on. Check the schematic if unsure (this example is active-low).

Q. The LED stays on instead of blinking.

A. No delay or too short for the eye. Increase delay, or check the toggle itself on the Logic Analyzer's time axis.

Q. My empty delay loop vanished from optimization.

A. Declare the loop variable volatile, or use a timer (lesson 9) for an exact delay.

Summary

  • The lighting value depends on wiring — active-low is ON at 0
  • Light one LED: ~(1u << pos)
  • while(1)+delay makes blinks/patterns, but software delay is imprecise
  • Control multiple LEDs by ORing masks (invert if needed)
  • Use the Logic Analyzer for waveforms, the Port window for bit values

Exercises

  1. Extend the running light into a back-and-forth (knight rider) pattern 7→0 as well
  2. Write a mask that lights two adjacent LEDs while flowing
  3. Explain and apply the change needed for an active-high board

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.

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 ↗

Comments 0

No comments yet. Be the first.

Only members can comment.

Comments with ads, abuse, personal information or illegal content are hidden or removed. Comments are shown with your nickname. Privacy Policy