This project implements a single-channel digital oscilloscope using a Raspberry Pi Pico board based on the RP2040 microcontroller. The oscilloscope samples an external analog signal with the Pico's internal 12-bit ADC, detects a configurable trigger event, and displays the captured waveform on a 2.4 inch ILI9341 TFT display connected through SPI.
The user can adjust the timebase, trigger threshold, trigger edge and display mode using four push buttons and one potentiometer. The second RP2040 core is used as an independent PWM function generator for generating local test signals. The sampled waveform can also be streamed over USB CDC as comma-separated sample values for optional logging on a PC.
The goal of the project is to build a compact embedded measurement tool and to exercise several microcontroller peripherals at the same time: ADC, SPI, DMA-driven display transfers, PWM, GPIO input handling, multicore execution and USB serial communication.
The oscilloscope is centered around the Raspberry Pi Pico. The analog input signal first passes through a protection and scaling circuit before reaching ADC0 on GP26. The trigger threshold is read from a potentiometer connected to ADC1 on GP27.
Core 0 handles the oscilloscope user interface and acquisition pipeline:
Core 1 runs independently as a simple PWM-based signal generator on GP14. During testing, this output can be routed through an RC low-pass filter and connected back to the oscilloscope input.
Signal flow:
[Probe input]
|
[Voltage divider and ADC protection]
|
[GP26 / ADC0]
|
[DMA sample buffer]
|
[Trigger detection and waveform processing]
|
[SPI display rendering on ILI9341]
[GP27 / ADC1] <- potentiometer for trigger level
[GP2-GP5] <- buttons for user control
[GP22 GPIO] -> display backlight enable
[USB CDC] -> sample export to PC
[GP14 PWM] -> optional function generator output
The hardware is built around the Raspberry Pi Pico, an SPI TFT display, a protected ADC input stage, a potentiometer, four push buttons and a PWM test output.
| Component | Role |
|---|---|
| Raspberry Pi Pico | Main controller, ADC acquisition, display rendering, USB communication and PWM generation |
| ILI9341 2.4 inch TFT display | Waveform and UI display, connected through SPI0 |
| 10k potentiometer | Trigger threshold adjustment, read by ADC1 |
| 2 x 100k resistors | Input voltage divider, divides the probe input by 2 |
| 2 x 100 ohm resistors | ADC input series protection and PWM output series resistor |
| Small capacitor, about 100pF to 1nF | Optional ADC input noise filtering |
| 100nF capacitors | Supply, display and ADC reference decoupling |
| 4 x tactile push buttons | Timebase, trigger edge and display mode controls |
| Probe/header input | External signal input and ground reference |
| Display Pin | Pico Pin | Notes |
|---|---|---|
| GND | GND | Common ground |
| VCC | 3.3V | Display supply |
| CLK | GP18, pin 24 | SPI0 clock |
| MOSI / SDA | GP19, pin 25 | SPI0 TX |
| RESET / RES | GP20, pin 26 | Active low reset |
| DC | GP21, pin 27 | Data/command select |
| LED / BLK | GP22, pin 29 | Backlight enable |
| MISO / SDO | Not connected | Not used by the firmware |
The display module used for the final implementation does not expose a chip-select pin. The firmware therefore uses the display as the only SPI device and does not drive a display CS line. Touch controller pins, if present on other display modules, are not used.
The oscilloscope input uses a 2:1 voltage divider before the Pico ADC. This allows input voltages up to about 6.6V to be mapped into the Pico ADC's 0V to 3.3V range.
INPUT SIGNAL
|
R1 100k
|
+---- node A ---- R3 100R ---- GP26 / ADC0
|
R2 100k
|
GND
The optional ADC filter capacitor should be small. In the final circuit, a 100pF capacitor was used because a larger capacitor visibly rounded fast square waves.
| Potentiometer Pin | Connection |
|---|---|
| Left terminal | 3.3V |
| Right terminal | GND |
| Wiper | GP27 / ADC1, pin 32 |
Each button is connected between the GPIO pin and GND. Internal pull-up resistors are enabled in software.
| Button | Pico GPIO | Pico Pin | Function |
|---|---|---|---|
| Button 1 | GP2 | pin 4 | Timebase increase |
| Button 2 | GP3 | pin 5 | Timebase decrease |
| Button 3 | GP4 | pin 6 | Toggle trigger edge |
| Button 4 | GP5 | pin 7 | Cycle display mode |
| Signal | Connection |
|---|---|
| PWM source | Pico GP14, pin 19 |
| Series resistor | 100 ohm |
| Output terminal | GEN_OUT |
| Optional filter capacitor | From GEN_OUT to GND |
For testing, GEN_OUT can be connected to the oscilloscope input header. This jumper is only used during tests and is not a permanent connection in the main input circuit.
100nF capacitors are placed between 3.3V and GND near the Pico and near the display module. These capacitors reduce local supply noise caused by fast digital switching, especially during SPI display updates. A separate 100nF capacitor is placed between ADC_VREF and AGND to decouple the ADC voltage reference.
The firmware is organized around the two RP2040 cores.
Core 0 performs the oscilloscope acquisition and display tasks. The ADC is sampled into a RAM buffer. The software searches for a trigger crossing based on the threshold read from the potentiometer and the selected edge direction. The selected waveform window is then converted from ADC values to display pixel coordinates and rendered on the ILI9341 display.
The display is updated over SPI0. The frame buffer is transferred to the display using DMA, which reduces the time spent waiting for SPI transfers and keeps the interface more responsive. The display backlight is enabled from GP22.
The four buttons are read as digital inputs with internal pull-ups. Software debouncing is used to avoid repeated false events. The buttons modify the timebase, trigger edge and display mode.
Core 1 runs the signal generator. It configures PWM on GP14 and updates the PWM output independently from the acquisition code. This makes it possible to test the oscilloscope without requiring an external signal generator.
USB CDC is used to stream sample data to a connected PC. The data can be sent as comma-separated values, allowing it to be saved or plotted with common serial tools.
Main software modules:
The firmware is implemented in a single C source file for the Raspberry Pi Pico SDK:
The code contains the ILI9341 display driver, ADC acquisition, trigger detection, waveform rendering, button handling, potentiometer reading, USB CDC sample streaming and the second-core PWM function generator.