How to Display Numbers on 4-Digit 7-Segment Display with Raspberry Pi Pico in Wokwi - Complete Step-by-Step Tutorial
4-Digit 7-Segment Display
with Raspberry Pi Pico
Master multiplexing and PIO control to drive a 4-digit display — simulate it instantly in your browser, no hardware required.
📋 Table of Contents
Project Overview
This tutorial shows you how to connect and program a 4-digit 7-segment display (Common Cathode) with the Raspberry Pi Pico using the Wokwi online simulator. You'll use the PIO (Programmable I/O) hardware to drive all four digits efficiently with minimal CPU involvement.
Multiplexing
Drive 4 digits with just 12 GPIO pins using time-division multiplexing
PIO Control
Hardware state machine keeps display refreshed without blocking the CPU
Wokwi Sim
Simulate everything online — no breadboard or components needed
Counter Demo
Watch an incrementing counter from 0000–9999 update every 200ms
How 7-Segment Displays Work
A 7-segment display contains 7 LED segments (A–G) plus a decimal point (DP). By turning specific segments on or off, you can display digits 0–9 and many letters.
Segment layout: A=Top, B=Top-Right, C=Bottom-Right, D=Bottom, E=Bottom-Left, F=Top-Left, G=Middle, DP=Decimal Point
Common Cathode vs Common Anode
| Type | Common Pin | Segment ON when GPIO = | Used in this tutorial? |
|---|---|---|---|
| Common Cathode | GND | HIGH | ✅ Yes |
| Common Anode | VCC (3.3V) | LOW | ❌ No |
Common Cathode displays are simpler to control — a HIGH GPIO signal turns a segment ON. This is what the Wokwi wokwi-7segment component uses by default.
Understanding Multiplexing
A 4-digit display has 8 segment pins (A–G, DP) and 4 digit-select pins (D1–D4). Only one digit is active at a time, but switching happens faster than 50 Hz — so all four digits appear lit simultaneously to the human eye.
Enable Digit 1
Pull D1 HIGH, set segment pins to show the thousands digit, then disable.
Enable Digit 2
Pull D2 HIGH, set segment pins to show the hundreds digit, then disable.
Enable Digit 3
Pull D3 HIGH, set segment pins to show the tens digit, then disable.
Enable Digit 4
Pull D4 HIGH, set segment pins to show the units digit, then disable.
Repeat Continuously
The PIO state machine loops this at 2000 Hz — no CPU intervention needed.
Multiplexing below 50 Hz causes visible flickering. The PIO clock runs at 2000 Hz, giving each digit 500 Hz — well above the flicker threshold.
GPIO Pin Assignment
Connect your 4-digit 7-segment display to the Raspberry Pi Pico using the following pin mapping:
🟢 Segment Pins (GP2 – GP9)
| Segment | Position | Pico GPIO |
|---|---|---|
| A | Top | GP2 |
| B | Top Right | GP3 |
| C | Bottom Right | GP4 |
| D | Bottom | GP5 |
| E | Bottom Left | GP6 |
| F | Top Left | GP7 |
| G | Middle | GP8 |
| DP | Decimal Point | GP9 |
🔵 Digit Select Pins (GP10 – GP13)
| Digit | Position | Pico GPIO |
|---|---|---|
| D1 | Leftmost digit | GP10 |
| D2 | Second digit | GP11 |
| D3 | Third digit | GP12 |
| D4 | Rightmost digit | GP13 |
In Wokwi simulation, resistors are optional. For real hardware, add 220Ω–330Ω resistors in series with each segment pin to protect the LEDs.
diagram.json
Paste this into your diagram.json file in the Wokwi editor. It places the Pico and 4-digit display, then wires all 12 connections automatically.
{
"version": 1,
"author": "Uri Shaked",
"editor": "wokwi",
"parts": [
{
"type": "wokwi-pi-pico",
"id": "pico",
"top": 65.16,
"left": -27.77,
"rotate": 90,
"hide": false,
"attrs": { "env": "arduino-community" }
},
{
"type": "wokwi-7segment",
"id": "sevseg1",
"top": -21.97,
"left": -85.69,
"rotate": 0,
"hide": false,
"attrs": { "digits": "4" }
}
],
"connections": [
[ "pico:GP0", "$serialMonitor:RX", "", [] ],
[ "pico:GP1", "$serialMonitor:TX", "", [] ],
[ "sevseg1:A", "pico:GP2", "green", [ "v-21", "h145", "v115", "h-68" ] ],
[ "sevseg1:B", "pico:GP3", "green", [ "v-15", "h100", "v103", "h-65" ] ],
[ "sevseg1:C", "pico:GP4", "green", [ "v16", "h37" ] ],
[ "sevseg1:D", "pico:GP5", "green", [ "v22", "h53" ] ],
[ "sevseg1:E", "pico:GP6", "green", [ "v32", "h42" ] ],
[ "sevseg1:F", "pico:GP7", "green", [ "v-13", "h-96", "v116", "h104" ] ],
[ "sevseg1:G", "pico:GP8", "green", [ "v11", "h1" ] ],
[ "sevseg1:DP", "pico:GP9", "green", [ "v7", "h-3" ] ],
[ "sevseg1:DIG1","pico:GP10", "blue", [ "v-19", "h-84", "v128", "h81" ] ],
[ "sevseg1:DIG2","pico:GP11", "blue", [ "v-28", "h-121","v144", "h80" ] ],
[ "sevseg1:DIG3","pico:GP12", "blue", [ "v-39", "h-139","v162", "h79" ] ],
[ "sevseg1:DIG4","pico:GP13", "blue", [ "v66", "h-82" ] ]
]
}
segment.pio.h
This auto-generated PIO header file contains the state machine program that drives all four digits via hardware — freeing up the CPU for other tasks.
// -------------------------------------------------- // // This file is autogenerated by pioasm; do not edit! // // -------------------------------------------------- // #pragma once #if !PICO_NO_HARDWARE #include "hardware/pio.h" #endif #define segment_wrap_target 0 #define segment_wrap 5 static const uint16_t segment_program_instructions[] = { // .wrap_target 0x8080, // 0: pull noblock side 0 0xa027, // 1: mov x, osr side 0 0x6208, // 2: out pins, 8 side 1 0x6408, // 3: out pins, 8 side 2 0x6808, // 4: out pins, 8 side 4 0x7008, // 5: out pins, 8 side 8 // .wrap }; #if !PICO_NO_HARDWARE static const struct pio_program segment_program = { .instructions = segment_program_instructions, .length = 6, .origin = -1, }; static inline pio_sm_config segment_program_get_default_config(uint offset) { pio_sm_config c = pio_get_default_sm_config(); sm_config_set_wrap(&c, offset + segment_wrap_target, offset + segment_wrap); sm_config_set_sideset(&c, 4, false, false); return c; } #include "hardware/clocks.h" static inline void segment_program_init(PIO pio, uint sm, uint offset, uint first_segment_pin, uint first_digit_pin) { const int segment_pins = 8; const int digit_pins = 4; pio_sm_config c = segment_program_get_default_config(offset); sm_config_set_out_pins(&c, first_segment_pin, segment_pins); sm_config_set_sideset_pins(&c, first_digit_pin); sm_config_set_out_shift(&c, false, false, 32); // Shift left // Attach all segment pins to PIO for (int pin = first_segment_pin; pin < first_segment_pin + segment_pins; pin++) pio_gpio_init(pio, pin); // Attach all digit select pins to PIO for (int pin = first_digit_pin; pin < first_digit_pin + digit_pins; pin++) pio_gpio_init(pio, pin); // Set all as outputs pio_sm_set_consecutive_pindirs(pio, sm, first_segment_pin, segment_pins, true); pio_sm_set_consecutive_pindirs(pio, sm, first_digit_pin, digit_pins, true); // Run at 2000 Hz float div = (float)clock_get_hz(clk_sys) / 2000; sm_config_set_clkdiv(&c, div); pio_sm_init(pio, sm, offset, &c); pio_sm_set_enabled(pio, sm, true); } #endif
Full C++ Code
This is the main Arduino sketch. It initialises the PIO state machine and increments a counter displayed on all four digits every 200 ms.
/** * Pi Pico PIO driving a 4-digit seven segment display example. * Copyright (C) 2021, Uri Shaked */ #include "segment.pio.h" // Segment bit patterns for digits 0–9 (Common Anode encoding) uint8_t digits[] = { 0b11000000, // 0 0b11111001, // 1 0b10100100, // 2 0b10110000, // 3 0b10011001, // 4 0b10010010, // 5 0b10000010, // 6 0b11111000, // 7 0b10000000, // 8 0b10011000, // 9 }; const uint8_t first_segment_pin = 2; // GP2 = Segment A const uint8_t first_digit_pin = 10; // GP10 = Digit 1 void setup() { Serial1.begin(115200); Serial1.println("Raspberry Pi Pico PIO 7-Segment Example"); // Load the PIO program and start the state machine auto offset = pio_add_program(pio0, &segment_program); segment_program_init(pio0, 0, offset, first_segment_pin, first_digit_pin); } /** * Display any 0–9999 value across all four digits. * Packs each digit's segment byte into a single 32-bit word for the PIO FIFO. */ void displayNumber(uint value) { pio_sm_put(pio0, 0, digits[value / 1000 % 10] << 24 | // Thousands digits[value / 100 % 10] << 16 | // Hundreds digits[value / 10 % 10] << 8 | // Tens digits[value % 10] // Units ); } int i = 0; void loop() { displayNumber(i++); delay(200); }
Step-by-Step Instructions
Follow these steps to get the simulation running in under 5 minutes:
Open Wokwi
Go to wokwi.com and create a new project. Select Raspberry Pi Pico as your board with the Arduino Community environment.
Add the 7-Segment Display
Click the "+" button in the parts panel and search for wokwi-7segment. Add it to the canvas and set its digits attribute to 4.
Paste diagram.json
Click on the diagram.json tab in Wokwi and replace its content with the JSON from Section 5 above. This auto-wires all 12 connections.
Add segment.pio.h
Create a new file named segment.pio.h in your project and paste in the full PIO header from Section 6. This is required for the C++ sketch to compile.
Paste the Main Sketch
In the main sketch.ino editor, paste the C++ code from Section 7. Ensure the #include "segment.pio.h" line is at the top.
Run the Simulation
Click the green ▶ Play button. You should see the display counting up from 0000, updating every 200 ms with zero flickering.
Expected Output
The display counts from 0000 → 0001 → 0002 → … → 9999, updating every 200 ms. No flickering should be visible thanks to the 2000 Hz PIO refresh rate.
| Demo Mode | Display Output |
|---|---|
| Incrementing Counter | 0000 → 0001 → 0002 … |
| Decimal Display | 00.0 → 01.0 … |
| Countdown Timer | 0010 → 0009 → … → 0000 |
| Static Number | 1234 (fixed) |
Applications
Once you've mastered this technique, you can apply it to a wide range of embedded projects:
Comments
Post a Comment