The Short Answer: Pico 2 ADC Sample Rate Limits
The Raspberry Pi Pico 2 (powered by the RP2350 chip) features a 12-bit Successive Approximation Register (SAR) ADC. The hardware theoretical maximum raspberry pi pico 2 adc sample rate is 500 ksps (kilosamples per second) when utilizing the C/C++ SDK with Direct Memory Access (DMA). However, your actual achievable rate depends entirely on your firmware environment:
- MicroPython (Standard Polling): ~20 to 35 ksps
- MicroPython (with rp2.DMA): ~80 to 120 ksps
- C/C++ SDK (with DMA): Up to 500 ksps
Decision Path: Which Implementation Do You Need?
| Your Target Sample Rate | Use Case | Required Stack |
|---|---|---|
| < 20 ksps | Temperature, light, slow battery monitoring | MicroPython machine.ADC |
| 20 - 100 ksps | Audio envelope tracking, motor current sensing | MicroPython with tight-loop or rp2.DMA |
| > 100 ksps | FFT analysis, high-speed oscilloscope builds | C/C++ SDK with DMA and PIO |
Hardware & Pin Mapping for RP2350 ADC
The RP2350 improves upon the original RP2040 by offering 5 ADC channels (ADC0 through ADC4) instead of 4, with better internal noise isolation. The ADC reference voltage (VREF) is tied to the 3.3V supply. Because the internal sampling capacitor is roughly 2pF, you must keep your source impedance below 10kΩ to allow the capacitor to fully charge within the sampling window, otherwise your 12-bit readings will suffer from linearity errors.
RP2350 ADC Pinout Table
| GPIO Pin | ADC Channel | Physical Pin (Pico 2) | Notes |
|---|---|---|---|
| GP26 | ADC0 | 31 | Standard external analog input |
| GP27 | ADC1 | 32 | Standard external analog input |
| GP28 | ADC2 | 34 | Standard external analog input |
| GP29 | ADC3 | 35 | Often routed to VSYS/3 on some boards |
| N/A | ADC4 | N/A | Internal temperature sensor only |
Parts List for Benchmarking
- Board: Raspberry Pi Pico 2 (Standard, RP2350 variant - avoid the 'W' if you don't need WiFi, as the RF module can inject minor ADC noise floor elevation)
- Sensor: 10kΩ Linear Potentiometer (B10K) wired as a voltage divider
- Filtering: 100nF Ceramic Capacitor (0805 or through-hole)
- Wiring: 22 AWG solid core jumper wires, half-size breadboard
MicroPython Implementation: Fast Polling Benchmark
Below is a complete, compilable MicroPython script designed for the Raspberry Pi Pico 2 (RP2350) running MicroPython v1.24 or newer. This script initializes the ADC on GP26, runs a tight polling loop, and calculates the exact achieved sample rate in real-time. It includes robust error handling for pin misconfigurations.
import machine
import time
import sys
# --- Configuration & Pin Definitions ---
ADC_PIN_NUM = 26 # GP26 maps to ADC0 on RP2350
SAMPLE_COUNT = 50000 # Number of reads for benchmarking
VREF_VOLTAGE = 3.3 # Pico 2 nominal 3.3V reference
# --- Hardware Initialization with Error Handling ---
try:
# Initialize the ADC pin
adc_pin = machine.Pin(ADC_PIN_NUM)
adc = machine.ADC(adc_pin)
except ValueError as e:
print(f"FATAL INIT ERROR: {e}")
print(f"GPIO {ADC_PIN_NUM} is not a valid ADC channel on the RP2350.")
sys.exit(1)
except OSError as e:
print(f"FATAL HARDWARE ERROR: {e}")
print("ADC peripheral failed to initialize. Check for bus contention.")
sys.exit(1)
print(f"ADC initialized on GP{ADC_PIN_NUM}. Starting {SAMPLE_COUNT} sample benchmark...")
# --- Benchmarking Loop ---
# We use time.ticks_us() for microsecond precision without overflow issues
start_time = time.ticks_us()
# Pre-allocate a dummy variable to prevent the garbage collector
# from interrupting the tight loop
val = 0
for _ in range(SAMPLE_COUNT):
val = adc.read_u16()
end_time = time.ticks_us()
# --- Calculate Metrics ---
# time.ticks_diff handles the 30-bit microsecond counter wrap-around safely
elapsed_us = time.ticks_diff(end_time, start_time)
elapsed_sec = elapsed_us / 1_000_000
if elapsed_sec > 0:
actual_sample_rate = SAMPLE_COUNT / elapsed_sec
final_voltage = (val / 65535) * VREF_VOLTAGE
print("--- Benchmark Results ---")
print(f"Total Samples: {SAMPLE_COUNT}")
print(f"Elapsed Time: {elapsed_sec:.4f} seconds")
print(f"Achieved Rate: {actual_sample_rate:.0f} Hz ({actual_sample_rate/1000:.2f} ksps)")
print(f"Final Reading: {val} raw | {final_voltage:.3f} V")
else:
print("Error: Elapsed time recorded as zero. Loop executed too fast for timer resolution.")
When you run this on a stock Pico 2 at 150MHz, expect the Achieved Rate to settle right around 28,000 to 32,000 Hz. The bottleneck here is not the ADC hardware; it is the MicroPython interpreter overhead executing the for loop and calling the C-bindings for read_u16().
Debugging: First Three Things to Check When It Fails
When working with the RP2350 ADC, silent failures (getting static numbers) or hard crashes are common. If your build fails, follow this exact diagnostic sequence.
1. You get a ValueError on initialization
Exact Error String: ValueError: Pin(20) doesn't have an ADC channel (or similar, depending on the pin you passed).
- Cause A (Most Likely): You attempted to assign a digital-only GPIO (like GP20) to the
machine.ADCobject. The RP2350 only supports ADC on GP26, GP27, GP28, and GP29. - Cause B: You are using an outdated MicroPython build (pre-v1.23) that doesn't fully map the RP2350's new ADC4 channel or pinout correctly. Flash the latest UF2 from the official MicroPython download page.
2. Your readings are stuck at 65535 or 0
Symptom: The code runs without throwing an exception, but val never changes when you turn the potentiometer.
- Cause A (Most Likely): Your voltage divider is wired incorrectly. Verify the outer pins of the potentiometer are connected to 3.3V (Pin 36) and AGND (Pin 33), not digital GND (Pin 38). Mixing AGND and DGND at the sensor creates ground loops that peg the ADC.
- Cause B: The pin is being driven by another peripheral. If you previously initialized GP26 as a digital output or PWM pin in your REPL session and didn't hard-reset the board, the pin mux is still routed to the digital block. Press the hardware RESET button on the Pico 2 before running the script.
3. You get an OSError during the read loop
Exact Error String: OSError: [Errno 5] EIO
- Cause A: I2C/SPI bus contention. If you have an I2C sensor initialized on the same GPIO bank and are polling it simultaneously without proper mutex locks (or in a dual-core setup without atomic access), the peripheral bus can lock up.
- Cause B: Brownout condition. If you are powering the Pico 2 via a weak USB hub and drawing current from the 3.3V out pin to power external op-amps, the internal VREG may brown out, causing the ADC peripheral to throw an I/O error. Measure the 3.3V pin with a multimeter; it must stay above 3.1V under load.
Extending and Simplifying Your ADC Build
Depending on your project requirements, you will either need to strip this down for battery life or scale it up for DSP (Digital Signal Processing).
How to Simplify (Low Power / Slow Logging)
If you are building a weather station or battery monitor, 30 ksps is a massive waste of power and CPU cycles.
The Fix: Put the ADC to sleep between reads. Use time.sleep_ms(1000) in your loop. Furthermore, drop the Pico 2's system clock to 48MHz using machine.freq(48_000_000). The ADC will still sample accurately at 48MHz, but your overall power draw will drop from ~25mA to under 8mA.
How to Extend (High Speed / FFT Audio)
If you need to push past the MicroPython 30 ksps ceiling to capture audio waveforms or run Fast Fourier Transforms, you must abandon standard polling.
- Step 1: Switch to the C/C++ Pico SDK. The RP2350 Datasheet details the ADC FIFO registers.
- Step 2: Configure the ADC to free-run mode by setting the
ADC_CS.ENandADC_CS.START_MANYbits. - Step 3: Chain a DMA channel to the
ADC_FIFODREQ (Data Request) signal. This allows the DMA controller to move 12-bit samples directly into SRAM without the CPU executing a single instruction per sample. - Step 4: Set the ADC clock divider to achieve your exact target rate. The formula is:
Sample Rate = 48MHz / (Clock_Divider + 1). For exactly 100 ksps, set the divider to 479.
Final Recommendation
Do not default to the C/C++ SDK unless your math explicitly requires it. For 90% of maker projects—including motor current shunt monitoring, joystick polling, and audio envelope followers—the Raspberry Pi Pico 2 running MicroPython with a tight polling loop is the correct choice. It delivers a highly stable ~30 ksps, requires zero memory management, and allows you to prototype the circuit in an afternoon. Reserve the DMA and C++ SDK toolchain exclusively for projects where you are performing real-time FFTs or capturing ultrasonic transceiver echoes.






