diff --git a/examples/adc_dma_circ.rs b/examples/adc_dma_circ.rs new file mode 100644 index 00000000..1ae14222 --- /dev/null +++ b/examples/adc_dma_circ.rs @@ -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; 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 = as Channel>::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 + } +} diff --git a/src/adc.rs b/src/adc.rs index f07391c1..b148a287 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -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 ///