Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(adc): POC for ADC reads using DMA w/ circular buffer #1

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions examples/adc_dma_circ.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#![no_main]
#![no_std]

use core::{mem, mem::MaybeUninit};
use log::info;

use cortex_m_rt::entry;

use embedded_hal::adc::Channel;
use stm32h7xx_hal::{
adc,
delay::Delay,
dma::{
dma::{DmaConfig, StreamsTuple},
Transfer,
},
gpio, pac,
prelude::*,
stm32::ADC1,
};

#[macro_use]
mod utilities;

#[entry]
fn main() -> ! {
utilities::logger::init();
let cp = cortex_m::Peripherals::take().unwrap();
let dp = pac::Peripherals::take().unwrap();

// 1 slot because we are only using 1 channel
#[link_section = ".axisram"]
static mut BUFFER: MaybeUninit<[u16; 1]> = MaybeUninit::uninit();

let adc_buffer: &'static mut [u16; 1] = {
// Convert an uninitialised array into an array of uninitialised
let buf: &mut [MaybeUninit<u16>; 1] =
unsafe { mem::transmute(&mut BUFFER) };
// Initialise memory to valid values
for slot in buf.iter_mut() {
// Never create even a _temporary_ reference to uninitialised memory
unsafe {
slot.as_mut_ptr().write(0);
}
}
unsafe { mem::transmute(buf) }
};

// Constrain and Freeze power
info!("Setup PWR... ");
let pwr = dp.PWR.constrain();
let pwrcfg = example_power!(pwr).freeze();

// Constrain and Freeze clock
info!("Setup RCC... ");
let rcc = dp.RCC.constrain();

let ccdr = rcc
.sys_ck(400.MHz())
.pll2_p_ck(40.MHz())
.freeze(pwrcfg, &dp.SYSCFG);

info!("");
info!("stm32h7xx-hal example - ADC to Memory DMA Continuous using Circular Mode");
info!("");

let mut delay = Delay::new(cp.SYST, ccdr.clocks);

// Setup ADC
let mut adc1 = adc::Adc::adc1(
dp.ADC1,
2.MHz(),
&mut delay,
ccdr.peripheral.ADC12,
&ccdr.clocks,
)
.enable();
adc1.set_resolution(adc::Resolution::SixteenBit);
// We can't use ADC2 here because ccdr.peripheral.ADC12 has been
// consumed. See examples/adc12.rs

// Setup GPIOC
let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC);

// Configure pc4 as an analog input
let mut channel = gpioc.pc4.into_analog(); // ANALOG IN 4

let config = DmaConfig::default()
.circular_buffer(true)
.memory_increment(true);

// Setup the DMA transfer on stream 0
let streams = StreamsTuple::new(dp.DMA1, ccdr.peripheral.DMA1);
let mut transfer: Transfer<_, _, _, _, _> =
Transfer::init(streams.0, adc1, &mut adc_buffer[..], None, config);

// This closure runs right after enabling the stream
transfer.start(|adc| {
let channel = <gpio::Pin<'C', 4> as Channel<ADC1>>::channel();
adc.start_conversion_dma_circ(&[channel]); // more channels can be added to the list
});

loop {
info!("{}", unsafe {
core::ptr::read_volatile(0x2400_0000 as *const u16)
// if more channels are used, each address will be offset by 2
});

delay.delay_ms(10_u16); // slow down the loop a bit
}
}
60 changes: 60 additions & 0 deletions src/adc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,66 @@ macro_rules! adc_hal {
self.start_conversion_common(chan);
}

pub fn start_conversion_dma_circ(&mut self, chans: &[u8])
{
for chan in chans.iter() {
assert!(*chan <= 19);
}

self.rb.cfgr.modify(|_, w| unsafe { w.res().bits(self.get_resolution().into()) }); // set resolution
self.rb.cfgr.modify(|_, w| w.dmngt().bits(0b11)); // circular mode enabled
self.rb.cfgr.modify(|_, w| w.cont().set_bit().discen().clear_bit() ); // set continuous mode, unset discontinuous mode
self.rb.cfgr.modify(|_, w| w.ovrmod().overwrite()); // overwrite on overrun
self.check_conversion_conditions();
self.rb.cfgr2.modify(|_, w| w.lshift().bits(self.get_lshift().value())); // set LSHIFT[3:0]

// preselect channels
for chan in chans.iter() {
self.rb.pcsel.modify(|r, w| unsafe { w.pcsel().bits(r.pcsel().bits() | (1 << chan)) });
self.set_chan_smp(*chan);
}

// set sequence order and length
let mut seq_len = 0;
for (i, ch) in chans.iter().enumerate() {
self.set_sequence(*ch, i as u8 + 1);
seq_len += 1;
}
self.set_sequence_len(seq_len);

self.rb.cr.modify(|_, w| w.adstart().set_bit()); // start circ conversions
}

/// Select a sequence to sample, by inputting a single channel and position.
pub fn set_sequence(&mut self, chan: u8, position: u8) {
match position {
1 => self.rb.sqr1.modify(|_, w| unsafe { w.sq1().bits(chan) }),
2 => self.rb.sqr1.modify(|_, w| unsafe { w.sq2().bits(chan) }),
3 => self.rb.sqr1.modify(|_, w| unsafe { w.sq3().bits(chan) }),
4 => self.rb.sqr1.modify(|_, w| unsafe { w.sq4().bits(chan) }),
5 => self.rb.sqr2.modify(|_, w| unsafe { w.sq5().bits(chan) }),
6 => self.rb.sqr2.modify(|_, w| unsafe { w.sq6().bits(chan) }),
7 => self.rb.sqr2.modify(|_, w| unsafe { w.sq7().bits(chan) }),
8 => self.rb.sqr2.modify(|_, w| unsafe { w.sq8().bits(chan) }),
9 => self.rb.sqr2.modify(|_, w| unsafe { w.sq9().bits(chan) }),
10 => self.rb.sqr3.modify(|_, w| unsafe { w.sq10().bits(chan) }),
11 => self.rb.sqr3.modify(|_, w| unsafe { w.sq11().bits(chan) }),
12 => self.rb.sqr3.modify(|_, w| unsafe { w.sq12().bits(chan) }),
13 => self.rb.sqr3.modify(|_, w| unsafe { w.sq13().bits(chan) }),
14 => self.rb.sqr3.modify(|_, w| unsafe { w.sq14().bits(chan) }),
15 => self.rb.sqr4.modify(|_, w| unsafe { w.sq15().bits(chan) }),
16 => self.rb.sqr4.modify(|_, w| unsafe { w.sq16().bits(chan) }),
_ => panic!("Sequence out of bounds. Only 16 positions are available."),
}
}

pub fn set_sequence_len(&mut self, len: u8) {
if len - 1 >= 16 {
panic!("ADC sequence length must be in 1..=16")
}

self.rb.sqr1.modify(|_, w| w.l().bits(len - 1));
}

/// Read sample
///
Expand Down