This project builds ScopeBoy, a handheld, dual-core mixed-domain oscilloscope (MDO) on the Raspberry Pi Pico RP2040, featuring real-time spectral analysis and gain control.
Using the RP2040 microcontroller, we created a device that samples signals at 500 kSps. It leverages the dual-core architecture to separate signal processing (FFT math) from the user interface (drawing waveforms), ensuring a lag-free 60 FPS experience.
We wanted a portable, battery-friendly instrument for quickly debugging real-world signals (especially PWM control signals and small audio circuits) without needing a bench scope.
computeDFT()
X_k = Σ_{n=0}^{N-1} x_n · e^{-j2πkn/N}
Where:
x_n
n
X_k
k
w(n) = 0.5(1 − cos(2πn/(N−1)))
The system uses an Asymmetric Multiprocessing (AMP) model to ensure the User Interface (UI) never stutters, even when performing heavy calculations.
Create a block diagram showing:
protothreads
frame_buf
fft_output
1. Signal Processing (FFT): Software (Core 1) vs. Dedicated DSP Hardware
2. Analog Front End: Digital Zoom vs. Analog Gain
3. Display Driving: Hardware SPI vs. Bit-Banging
This project required solving several complex graphics and concurrency problems to achieve a stable 60 FPS update rate on the RP2040.
1. The "No-Fly Zone" Rendering Algorithm
One of the most persistent issues in digital oscilloscope displays is text flickering or overwriting. If the waveform is drawn across the entire screen width, it inevitably scribbles over the voltage axis labels (e.g., "3.3V", "1.65V") on the left side. Redrawing these labels every frame is computationally expensive and causes flashing.
drawWaveformFromBuffer
WAVE_MARGIN
x = MARGIN_LEFT
0
MARGIN_TOP
MARGIN_BOTTOM
2. Flicker-Free Graphics (Smart Erase)
Standard graphics libraries often rely on tft_fillScreen(BLACK) to clear the frame before redrawing. On SPI displays, this bulk pixel transfer is too slow and results in a visible "scanline" flicker.
tft_fillScreen(BLACK)
x=160
y=120
3. Dual-Core Concurrency
The RP2040 features two ARM Cortex-M0+ cores, which we leveraged to separate the real-time blocking tasks from the math-heavy processing.
multicore_launch_core1
4. Rotary Encoder
The rotary encoder generates pulses on its CLK and DT pins when rotated. The direction is determined by looking at if the pulse on CLK or DT was first. Professor Bruce had previously worked with rotary encoders, so we started by referencing his work (found here). He used a protothread and would sample the GPIO pins at a set interval. While this solution works, we wanted to use interrupts to make the driver cleaner.
5. ADC
We decided to use the ADC built into the RP2040, mostly due to time after loosing the PCBs.
The hardware design prioritizes modularity and low cost, using off-the-shelf components to create a professional-grade instrument. Figure 1 in Appendix C shows the full schematic for the breadboard version of the ScopeBoy.
Components used for input coupling, biasing, protection, and gain/trigger circuitry. See the schematic in Figure 1 for more details.
Download the full BOM CSV: Download CSV
The system uses SPI0 for the display and SPI1 for the DAC. Running them both on the same bus caused interference.
SPI0
SPI1
While the current version uses a software-simulated gain stage, the architecture is designed to support a hardware op-amp frontend.
adc_to_volt
The following exploded views illustrate the physical assembly of the ScopeBoy device:
This project stands on the shoulders of open-source libraries and hardware designs, modified for high-performance needs:
Our biggest failure was in the PCB. Not because the PCB didn't work or anything. We don't know whether the PCB worked because it wandered off (read: was stolen) over Thanksgiving break. This forced us to think on our feet and redesign the analog front end. We built a working front end, but it had significantly less bandwidth than we originally planned. We had planned for 1 MHz of bandwidth, but ultimately ended up at around 70 kHz. While it has substantially less bandwidth, the scope would still work for audio-range signals, so it serves a purpose.
We also designed an enclosure for the PCB, which we planned to 3D print. After we lost the boards, we scrapped the enclosure because the breadboard was fragile enough that it didn’t make sense to keep the system handheld. This also meant that we scrapped our plans to power it with a LiPo battery. We had the battery charger component from Adafruit, but since the board wasn’t handheld, it didn't make sense to power it.
Even though we lost the PCBs and had to redesign the front end, this allowed us to correct some design flaws in the original we had used as the base. Initially, our board had a Coax connector instead of the standard BNC connectors used by scope probes. This was because the design lacked the 1MΩ resistor and 10-22 pF capacitor used in oscilloscopes for 10x mode. So, using a coax SMA made more sense. However, that would have been annoying, since we would have had to make our own probe cable or get a coax-to-BNC connector adapter. We ordered a BNC-to-2-terminal block, which we used with the breadboard to connect standard scope probes, and put the proper resistor and capacitor in the circuit. This made connecting a scope probe easier, but it was still not a perfect solution. The scope probe's impedance wasn't matched, and 10X mode still didn't work because the signal was attenuated to the point that it was lost in the noise by the time it reached the gain stage.
The original PCB design also included a -3.3 V rail for the op-amps, allowing signals to reach negative voltages. For the breadboard design, we used the 3.3V and grounds directly from the Pico. This added more noise and, more importantly, required biasing all signals to prevent clipping at the op-amp rails. We blocked the DC bias in the original signal using a capacitor at the input, then applied our own 1.65 V bias. This meant that the scope couldn’t measure DC signals, with a minimum frequency of around 2 kHz.
handleInput
Attempted TFT Display Driver Port: Early in the project, we attempted to use AI to port an existing Arduino-based TFT library (Adafruit_ILI9341) to the Raspberry Pi Pico C SDK.
SPI.h
Adafruit_GFX.h
TFTMaster.h
Images of the scope in action:
Our project ended in a very differnt state than we originally intended. We originally intended to have a fully integrated solution, including a PCB and enclosure to make it portable. Our final design was only on a breadboard, and should not be moved much to avoid the wires on the board breaking. Still, given the circumstances, the project turned out surprisingly well. We were able to demonstrate many software features that professional oscilloscopes have, and proved that this concept is feasable. We are considering an independent study to fully realize our original vision for this project.. Here are a list of ideas that we are thinking about for a proper version:
We did not reference any standards in the making of this project. We do not believe that our design is suitable for high precision applications.
Include exactly one of the following sentences:
Final_Project.c