Skip to content

Read from the ADC

Pico 2 ADC overview can be found in the datasheet https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf on page 1067.

The ADC runs on a 48 MHz clock and a conversion takes 96 cycles. This gives a maximum of 500000 samples pr. second.

On the Pico 2, the ADC has 5 channels, which can sample inputs from GPIO-26, GPIO-27, GPIO-28, GPIO-29 and from the built in temperature sensor.

Channel Connection
0 GPIO[26]
1 GPIO[27]
2 GPIO[28]
3 GPIO[29]
4 Temperature Sensor

First, add hardware_adc library to the CMakeList.txt for PWM support.

target_link_libraries(my_project pico_stdlib hardware_adc)

Now the steps are: - Intialize the ADC. - Configure the GPIO pin to sample. - Configure the ADC to sample the correct GPIO. - Configure the sample rate.

    adc_init();

    // Make sure GPIO is high-impedance, no pullups etc
    adc_gpio_init(26);

    // Select ADC input 0 (GPIO26)
    adc_select_input(0);

And then continuously read from the ADC, with code similar to the code in the Pico examples repository:

    // 12-bit conversion, assume max value == ADC_VREF == 3.3 V
    const float conversion_factor = 3.3f / (1 << 12);
    uint16_t result = adc_read(); // Trigger conversion and wait for result
    printf("Raw value: 0x%03x, voltage: %f V\n", result, result * conversion_factor);

Interrupts, FIFO and freerunning mode

I can't find anything in the SDK on how to configure an IRQ callback. Maybe it is not supported yet - 20250911.

It has a 'DIV' register, which can be used to control how often to initiate a new sampling. From the datasheet:
"Set DIV.INT to a positive value n to trigger the ADC once per n + 1 cycles. The ADC ignores this if a conversion is currently in progress, so generally n will be ≥ 96. For example, setting DIV.INT to 47999 runs the ADC at 1 kS/s, if running from a 48 MHz clock. The pacing timer supports fractional-rate division (first order delta sigma). When setting DIV.FRAC to a non-zero value, the ADC starts a new conversion once per 1 + INT + FRAC/256 cycles on average, by changing the sample interval between INT + 1 and INT + 2."

The function void adc_set_clkdiv(float clkdiv) from adc.h can be used to set the DIV register. The function void adc_run(bool run) from adc.h starts the ADC as 'freerunning', which means that it keeps sampling and placing the sampled value in it's result register. However, there is no function in the SDK, which can read the result register (except for the adc_read(), which starts a new conversion..). An option would be to configure use of the FIFO, but it looks like the FIFO would have to be polled from application code, because I can't find an option to configure a callback or interrupt on the FIFO.
The ADC can also sample the 5 channels in round-robin mode, but it will place the results in the same FIFO with no indication of which channel the result comes from.
So, the HW seems very capable, but the SDK is lacking features to use the HW properly.