All projects
02 · Embedded Systems

Digital Oscilloscope

Low-cost STM32 oscilloscope that streams ADC samples to a Python GUI for live plotting, frequency readout, and FFT.

STM32 ADC DMA UART Python PyQtGraph

Highlights

  • DMA moves ADC samples into ping-pong buffers: the half- and full-complete interrupts flag each half, and the main loop sends it over UART while the other half fills, keeping the ISRs short.
  • Sized the buffers and interrupt period from throughput math (100 samples every ~10 ms at 921,600 baud), then halved transfer time to ~2 ms by sending 16-bit half-words instead of 32-bit values.
  • Python GUI (PyQtGraph + pyserial) reads and processes serial data on a background thread while the main thread redraws every 20 ms, with frequency readout, FFT, and waveform export.

Specs

Board
STM32L476RG (NUCLEO-L476RG), Arm Cortex-M4 at 80 MHz
ADC
12-bit, one ADC per channel, continuous conversion into DMA
Sampling rate
~105 kS/s single channel (2 MHz ADC clock, 6.5-cycle sample + 12.5-cycle conversion) · ~5.2 kS/s per channel in two-channel mode
Buffering
Circular DMA ping-pong buffers with half- and full-complete interrupts: 1,024 samples (half fills every ~4.8 ms) single channel · 100 samples per ADC (~10 ms per half) two-channel
Link to PC
UART at 921,600 baud with samples packed as 16-bit half-words (~2 ms per 100-sample transfer); USB CDC also tested for more throughput
Display software
Python, PyQtGraph, and pyserial; background reader thread; plots refresh every 20 ms with frequency readout, FFT, and waveform export; up to 4 COM ports
Test signal
~160 Hz sine wave from an ESP32, compared against a bench oscilloscope

Gallery

Select any image or video to view it full size.

Fig. 1 Single-channel demo: a ~160 Hz sine wave from an ESP32, plotted live by the Python GUI (left) next to a bench oscilloscope (right).