Module rp2040_hal::adc

source ·
Expand description

Analog-Digital Converter (ADC)

See Chapter 4 Section 9 of the datasheet for more details

§Usage

Capture ADC reading from a pin:

// Embedded HAL 1.0.0 doesn't have an ADC trait, so use the one from 0.2
use embedded_hal_0_2::adc::OneShot;
use rp2040_hal::{adc::Adc, adc::AdcPin, gpio::Pins, pac, Sio};
let mut peripherals = pac::Peripherals::take().unwrap();
let sio = Sio::new(peripherals.SIO);
let pins = Pins::new(peripherals.IO_BANK0, peripherals.PADS_BANK0, sio.gpio_bank0, &mut peripherals.RESETS);
// Enable adc
let mut adc = Adc::new(peripherals.ADC, &mut peripherals.RESETS);
// Configure one of the pins as an ADC input
let mut adc_pin_0 = AdcPin::new(pins.gpio26.into_floating_input()).unwrap();
// Read the ADC counts from the ADC channel
let pin_adc_counts: u16 = adc.read(&mut adc_pin_0).unwrap();

Capture ADC reading from temperature sensor. Note that this needs conversion to be a real-world temperature.

// Embedded HAL 1.0.0 doesn't have an ADC trait, so use the one from 0.2
use embedded_hal_0_2::adc::OneShot;
use rp2040_hal::{adc::Adc, gpio::Pins, pac, Sio};
let mut peripherals = pac::Peripherals::take().unwrap();
let sio = Sio::new(peripherals.SIO);
let pins = Pins::new(peripherals.IO_BANK0, peripherals.PADS_BANK0, sio.gpio_bank0, &mut peripherals.RESETS);
// Enable adc
let mut adc = Adc::new(peripherals.ADC, &mut peripherals.RESETS);
// Enable the temperature sensor
let mut temperature_sensor = adc.take_temp_sensor().unwrap();
// Read the ADC counts from the ADC channel
let temperature_adc_counts: u16 = adc.read(&mut temperature_sensor).unwrap();

See examples/adc.rs and pimoroni_pico_explorer_showcase.rs for more complete examples

§Free running mode with FIFO

In free-running mode the ADC automatically captures samples in regular intervals. The samples are written to a FIFO, from which they can be retrieved.

use rp2040_hal::{adc::Adc, gpio::Pins, pac, Sio};
let mut peripherals = pac::Peripherals::take().unwrap();
let sio = Sio::new(peripherals.SIO);
let pins = Pins::new(peripherals.IO_BANK0, peripherals.PADS_BANK0, sio.gpio_bank0, &mut peripherals.RESETS);
// Enable adc
let mut adc = Adc::new(peripherals.ADC, &mut peripherals.RESETS);
// Enable the temperature sensor
let mut temperature_sensor = adc.take_temp_sensor().unwrap();

// Configure & start capturing to the fifo:
let mut fifo = adc.build_fifo()
  .clock_divider(0, 0) // sample as fast as possible (500ksps. This is the default)
  .set_channel(&mut temperature_sensor)
  .start();

loop {
  if fifo.len() > 0 {
    // Read one captured ADC sample from the FIFO:
    let temperature_adc_counts: u16 = fifo.read();
  }
}

See examples/adc_fifo_poll.rs for a more complete example.

§Using DMA

When the ADC is in free-running mode, it’s possible to use DMA to transfer data from the FIFO elsewhere, without having to read the FIFO manually.

This requires a number of steps:

  1. Build an AdcFifo, with DMA enabled (AdcFifoBuilder::enable_dma)
  2. Use AdcFifoBuilder::prepare instead of AdcFifoBuilder::start, so that the FIFO is created in paused state
  3. Start a DMA transfer (dma::single_buffer::Transfer, dma::double_buffer::Transfer, …), using the AdcFifo::dma_read_target as the source (from parameter)
  4. Finally unpause the FIFO by calling AdcFifo::resume, to start capturing

Example:

use rp2040_hal::{adc::Adc, gpio::Pins, pac, Sio, dma::{single_buffer, DMAExt}};
use cortex_m::singleton;
let mut peripherals = pac::Peripherals::take().unwrap();
let sio = Sio::new(peripherals.SIO);
let pins = Pins::new(peripherals.IO_BANK0, peripherals.PADS_BANK0, sio.gpio_bank0, &mut peripherals.RESETS);
let dma = peripherals.DMA.split(&mut peripherals.RESETS);
// Enable adc
let mut adc = Adc::new(peripherals.ADC, &mut peripherals.RESETS);
// Enable the temperature sensor
let mut temperature_sensor = adc.take_temp_sensor().unwrap();

// Configure & start capturing to the fifo:
let mut fifo = adc.build_fifo()
  .clock_divider(0, 0) // sample as fast as possible (500ksps. This is the default)
  .set_channel(&mut temperature_sensor)
  .enable_dma()
  .prepare();

// Set up a buffer, where the samples should be written:
let buf = singleton!(: [u16; 500] = [0; 500]).unwrap();

// Start DMA transfer
let transfer = single_buffer::Config::new(dma.ch0, fifo.dma_read_target(), buf).start();

// Resume the FIFO to start capturing
fifo.resume();

// Wait for the transfer to complete:
let (ch, adc_read_target, buf) = transfer.wait();

// do something with `buf` (it now contains 500 samples read from the ADC)
//...

//! See examples/adc_fifo_dma.rs for a more complete example.

§Free running mode without FIFO

While free-running mode is usually used in combination with a FIFO, there are use cases where it can be used without. For example, if you want to be able to get the latest available sample at any point in time, and without waiting 96 ADC clock cycles (2µs).

In this case, you can just enable free-running mode on it’s own. The ADC will continuously do ADC conversions. The ones not read will just be discarded, but it’s always possible to read the latest value, without additional delay:

use rp2040_hal::{adc::Adc, adc::AdcPin, gpio::Pins, pac, Sio};
// Embedded HAL 1.0.0 doesn't have an ADC trait, so use the one from 0.2
use embedded_hal_0_2::adc::OneShot;
let mut peripherals = pac::Peripherals::take().unwrap();
let sio = Sio::new(peripherals.SIO);
let pins = Pins::new(peripherals.IO_BANK0, peripherals.PADS_BANK0, sio.gpio_bank0, &mut peripherals.RESETS);
// Enable adc
let mut adc = Adc::new(peripherals.ADC, &mut peripherals.RESETS);
// Configure one of the pins as an ADC input
let mut adc_pin_0 = AdcPin::new(pins.gpio26.into_floating_input()).unwrap();
// Enable free-running mode
adc.free_running(&adc_pin_0);
// Read the ADC counts from the ADC channel whenever necessary
loop {
   let pin_adc_counts: u16 = adc.read_single();
   // Do time critical stuff
}

Structs§

Traits§

  • Trait for entities that can be used as ADC channels.