Skip to content

Instantly share code, notes, and snippets.

@chmanie
Last active July 17, 2022 13:57
Show Gist options
  • Save chmanie/d897d9be6e85c872673b6f010dfff7b0 to your computer and use it in GitHub Desktop.
Save chmanie/d897d9be6e85c872673b6f010dfff7b0 to your computer and use it in GitHub Desktop.
RP2040 ADC over DMA Round-Robin in Rust
/**
* Configure DMA to copy ADC FIFO round-robin values into memory
*
* The strategy is as follows:
* - Have a static array which is the DMA target address, holding 3 u16 values
* - Enable DMA with a data-size of halfword (16bits), increase the write address after each transfer and wrap at 8 bytes
* - Start the ADC according to datasheet section 4.9.2.5 (round robin with FIFO enabled and DREQ threshold 1)
*
* The result will be an array with 16bit values of ADC results where the first bit indicates an error if set
*/
use rp2040_hal::{
pac::{ADC, DMA},
};
static ADC_VALS: [u16; 4] = [0; 4];
pub fn setup_adc_dma(adc: ADC, dma: DMA) {
// ---- Configure DMA ----
// Set DMA channel 0 read address to the fifo register address
dma.ch[0]
.ch_read_addr
.write(|w| unsafe { w.bits(adc.fifo.as_ptr() as u32) });
// Get raw pointer to ADC_VAL array
let dest: *const [u16; 4] = &ADC_VALS;
let dest_addr = dest as usize;
// Set write address to ADC_VAL address
dma.ch[0]
.ch_write_addr
.write(|w| unsafe { w.bits(dest_addr as u32) });
dma.ch[0].ch_ctrl_trig.write(|w| unsafe {
w
// Select ADC as Data Request Trigger
.treq_sel()
.bits(DREQ_ADC)
// Set CTRL.DATA_SIZE to 16bit (0x1 -> SIZE_HALFWORD)
.data_size()
.bits(0x1)
// Enable CTRL.INC_WRITE to increase the destination address after each transfer
.incr_write()
.set_bit()
// Set CTRL.RING_SEL to wrap write addresses
.ring_sel()
.set_bit()
// Wrap write addresses at 8 bytes (4 values a 2 bytes)
.ring_size()
.bits(3)
// Enable DMA channel
.en()
.set_bit()
});
// Wait until ADC is read (for good measure)
while !adc.cs.read().ready().bit_is_set() {
cortex_m::asm::nop();
}
// ---- Configure ADC ----
// Set sample rate to 1kS/s
adc.div.modify(|_, w| unsafe { w.int().bits(47999) });
adc.fcs.modify(|_, w| unsafe {
w
// Enable FIFO
.en()
.set_bit()
// Set interrupt threshold to 1
.thresh()
.bits(1)
// Enable DREQ DMA transfer requests
.dreq_en()
.set_bit()
});
// // Enable Interrupt
// adc.inte.modify(|_, w| w.fifo().set_bit());
//
// unsafe {
// NVIC::unmask(Interrupt::ADC_IRQ_FIFO);
// }
adc.cs.modify(|_, w| unsafe {
w.ainsel()
// Set start channel
.bits(0)
// Sample all four channels (1111)
.rrobin()
.bits(0b1111)
// Start free running conversions
.start_many()
.set_bit()
});
// Wait until ADC is read again (for good measure)
while !adc.cs.read().ready().bit_is_set() {
cortex_m::asm::nop();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment