Skip to content

RPi Pico 2W — Using ADC

Pico 2 ADC overview can be found in the datasheet on page 1067.

The ADC runs on a 48 MHz clock and a conversion takes 96 cycles. This gives a maximum of 500000 samples per 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 the hardware_adc library to CMakeLists.txt for ADC support.

target_link_libraries(my_project pico_stdlib hardware_adc)

Now the steps are:

  • Initialize 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);

The ADC 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 its result register. However, there is no function in the SDK that can read the result register (except for 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 there doesn’t seem to be 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 came from.

So, the hardware seems very capable, but the SDK is currently lacking features to use it fully.