This is an old revision of the document!
The 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 a local test signal. The sampled waveform can also be streamed over USB CDC to a PC for logging and analysis.
The goal of the project is to build a compact embedded measurement tool and to exercise several microcontroller peripherals at the same time: ADC, DMA, SPI, PWM, GPIO interrupts, multicore execution and USB serial communication.
The oscilloscope is centered around the Raspberry Pi Pico. The analog input signal first passes through a simple protection and scaling circuit before reaching ADC0 on GP26. The ADC samples the signal into a memory buffer using DMA, which reduces CPU load and allows stable periodic acquisition.
Core 0 handles the oscilloscope user interface and acquisition pipeline:
Core 1 runs independently as a simple PWM-based signal generator on GP22. 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
[GP12 PWM] -> display backlight brightness
[USB CDC] -> sample export to PC
[GP22 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 SPI1 |
| 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 |
| 1nF capacitor | ADC input filtering |
| 4 x 100nF capacitors | Pico supply decoupling, display supply decoupling, PWM output filtering and ADC_VREF 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 |
|---|---|---|
| VCC | 3.3V, pin 36 | Display supply |
| GND | GND, pin 38 | Common ground |
| CS | GP13, pin 17 | SPI1 chip select |
| RESET | GP14, pin 19 | Active low reset |
| DC | GP15, pin 20 | Data/command select |
| MOSI / SDA | GP11, pin 15 | SPI1 TX |
| SCK / CLK | GP10, pin 14 | SPI1 clock |
| LED | GP12, pin 16 | PWM backlight control |
Touch controller pins, if present on the display module, 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 ADC filter capacitor is 1nF. A larger 100nF capacitor would create a very low cutoff frequency with the 100k/100k input divider and would visibly attenuate useful oscilloscope signals.
| 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 GP22, pin 29 |
| Series resistor | R5, 100 ohm |
| Output terminal | GEN_OUT |
| Output filter capacitor | C4, 100nF 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, C5, is placed between Pico pin 35, ADC_VREF, and GND 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 configured to sample ADC0 at a fixed rate. Samples are transferred into a RAM buffer using DMA. After a buffer is filled, 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 SPI1. Static UI elements such as the grid, voltage scale, time scale and trigger marker are drawn separately from the waveform when possible, reducing redraw work. The display backlight is controlled with PWM on GP12.
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 GP22 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 expected result is a working oscilloscope interface showing a live waveform on the ILI9341 display. The user can change the horizontal scale, adjust the trigger threshold with the potentiometer, switch between rising and falling edge triggering, and freeze or change the display mode using the buttons.
The internal PWM generator output can be connected to the input stage during testing to verify acquisition, triggering and rendering without an external signal generator.
This project combines multiple RP2040 hardware features into one embedded measurement device. The main challenges are stable high-speed ADC acquisition, efficient display rendering, and reliable triggering. The final implementation demonstrates how DMA and multicore execution can be used to reduce CPU load and keep the user interface responsive while continuously acquiring data.
The firmware is developed as a Raspberry Pi Pico SDK project. The repository contains the acquisition code, display driver integration, button handling, trigger logic, PWM generator and USB CDC serial export.