Introduction
Design and build a 20 MHz digital storage oscilloscope with a 100 MSPS ADC, FPGA trigger, and touchscreen display. This comprehensive guide covers everything from design through implementation, testing, and deployment.
Design and build a 20 MHz digital storage oscilloscope with a 100 MSPS ADC, FPGA trigger, and touchscreen display.
Design and build a 20 MHz digital storage oscilloscope with a 100 MSPS ADC, FPGA trigger, and touchscreen display. This comprehensive guide covers everything from design through implementation, testing, and deployment.
A DSO has three main sections: Analog Front End (AFE), ADC + Trigger, and Display. AFE: scales the input signal to the ADC's full-scale range regardless of whether measuring 10mV or 100V signals. It includes input protection (diodes + resistors), a programmable attenuator (relay-switched resistor dividers: 1x, 2x, 5x, 10x...), and a wideband amplifier. ADC: converts the continuously-varying voltage to digital numbers at 100M samples per second (100 MSPS). FPGA: stores a circular buffer of samples, detects trigger conditions, and timestamps data. MCU + Display: processes captured waveforms for rendering, measurements (Vpp, frequency, RMS).
10 components required for this project.
| # | Component | Purpose | Qty |
|---|---|---|---|
| 1 | AD9288 Dual 8-bit 100 MSPS ADC | High-speed analog-to-digital conversion | x1 |
| 2 | Xilinx Spartan-7 FPGA (XC7S25) | Trigger logic, decimation, data buffer | x1 |
| 3 | STM32H743 MCU (480 MHz) | Display, USB, user interface | x1 |
| 4 | OPA657 Wideband Op-Amp | Input amplifier/attenuator stages | x2 |
| 5 | Analog input stage (BNC connectors, dividers) | 1× and 10× input scaling | x1 |
| 6 | 7-inch IPS Touchscreen (800×480) | Waveform display and UI | x1 |
| 7 | 1 MB SRAM (CY7C1041) | Waveform sample buffer | x1 |
| 8 | LDO Regulators (LT3045) + DCDC | Ultra-low-noise power rails | x1 |
| 9 | USB 2.0 interface | PC connectivity and firmware update | x1 |
| 10 | Calibration test signal generator | Built-in 1kHz square wave calibration | x1 |
Follow these 6 steps carefully.
A DSO has three main sections: Analog Front End (AFE), ADC + Trigger, and Display. AFE: scales the input signal to the ADC's full-scale range regardless of whether measuring 10mV or 100V signals. It includes input protection (diodes + resistors), a programmable attenuator (relay-switched resistor dividers: 1x, 2x, 5x, 10x...), and a wideband amplifier. ADC: converts the continuously-varying voltage to digital numbers at 100M samples per second (100 MSPS). FPGA: stores a circular buffer of samples, detects trigger conditions, and timestamps data. MCU + Display: processes captured waveforms for rendering, measurements (Vpp, frequency, RMS).
Input impedance: 1MΩ || 20pF (standard oscilloscope input). Input protection: two antiparallel Schottky diodes (BAT54S) to clamp overvoltage transients to supply rails. Attenuator ladder: precision 1% resistors in a divider network. Use relays (Omron G6K) for switching — reed relays minimize crosstalk at 20 MHz. AC/DC coupling: relay switches a series capacitor (1µF film cap) in or out of signal path. Variable offset: a DAC generates a reference voltage summed with the signal to allow Y-axis offset. Gain flatness: OPA657 has 1.6GHz GBW — flat within 1dB to 20 MHz with proper compensation.
FPGA Trigger implemented in Verilog: receive 8-bit samples from ADC at 100 MHz clock. Compare each sample with trigger threshold register. Edge detection: for rising edge trigger, check if previous sample < threshold AND current sample >= threshold. Pre-trigger memory: maintain circular buffer of last 2048 samples before trigger. When trigger fires: record post-trigger samples (up to full buffer), signal MCU via interrupt. MCU reads buffer over parallel bus. Trigger modes: rising/falling edge, pulse width, runt pulse (voltage but brief), pattern trigger (logical combination of channels).
100 MSPS ADC outputs 8-bit parallel LVDS data — 800 Mbps aggregate. PCB layout critical: controlled impedance traces (100Ω differential for LVDS pairs), matched lengths (within 5 mil / 0.127mm for timing margin), ground pours, avoid right-angle bends (causes reflections). Power supply: digital noise couples into ADC reference — use separate LDO (LT3045) for analog supply. Keep ADC analog and digital supply pins decoupled independently. Star grounding for analog and digital grounds, joined at single point.
STM32H743 renders waveform on touchscreen. Acquire N samples from FPGA buffer. Map sample values to pixel Y coordinates: pixel_y = (ADC_value - offset) × volts_per_division / screen_height_pixels. Time axis: sweep rate determines how many samples per screen division. Render as connected line segments (each sample connected to next). Vector rendering in LVGL graphics library. Auto-measurements: Vpp = max(samples) - min(samples) × calibration_factor, frequency = 1 / period (time between trigger events), RMS = sqrt(mean(samples²)) × calibration_factor.
Calibrate vertical accuracy: apply precision DC voltage (from calibrated DMM-traceable source) at each voltage range. Measure ADC code, calculate gain and offset errors. Store calibration coefficients in flash. Calibrate time base: connect internal 1kHz calibration signal, measure period — should be exactly 1ms. Adjust timing coefficients. Bandwidth test: inject swept sine from signal generator (1kHz to 20MHz), measure amplitude response — should be flat within ±3dB to 20 MHz. Document results in calibration report.
Core code for trigger_engine.v:
// FPGA Trigger Engine for DSO // Implements rising/falling edge trigger with pre/post trigger memory module trigger_engine #( parameter DATA_WIDTH = 8, parameter BUFFER_DEPTH = 4096 ) ( input wire clk, // 100 MHz ADC clock input wire rst_n, input wire [DATA_WIDTH-1:0] adc_data, // 8-bit ADC sample input wire [DATA_WIDTH-1:0] trigger_level, // Trigger threshold input wire trigger_edge, // 0=rising, 1=falling input wire [11:0] pre_trig_depth,// Pre-trigger samples output reg triggered, // Trigger event flag output reg [11:0] trig_position, // Position in buffer output reg [DATA_WIDTH-1:0] sample_buffer [0:BUFFER_DEPTH-1] ); reg [DATA_WIDTH-1:0] prev_sample; reg [11:0] write_ptr; reg pre_filling; reg [11:0] post_count; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin write_ptr <= 0; triggered <= 0; prev_sample <= 0; end else begin // Always write to circular buffer sample_buffer[write_ptr] <= adc_data; write_ptr <= write_ptr + 1; // Edge detection wire rising = (prev_sample < trigger_level) && (adc_data >= trigger_level); wire falling = (prev_sample > trigger_level) && (adc_data <= trigger_level); if (!triggered) begin if ((trigger_edge == 0 && rising) || (trigger_edge == 1 && falling)) begin trig_position <= write_ptr - pre_trig_depth; triggered <= 1; post_count <= 0; end end else begin post_count <= post_count + 1; if (post_count >= (BUFFER_DEPTH - pre_trig_depth)) begin triggered <= 0; // Notify MCU to read buffer end end prev_sample <= adc_data; end end endmodule
Test DSO (Digital Storage Oscilloscope) Design by verifying each subsystem individually before full integration.
Verify power voltages, check ground connections, use serial monitor for debug.
An interactive simulator will be available here — simulate circuits and run code in-browser without hardware.