I’m trying to implement ADC conversion from gpio analog input at the sample rate of 192 kHz. Then I want to send the array of samples over tcp to the client.
My question:
How to implement ADC convertion using DMA for STM32F767ZI?
There is such example for stm32f4 rtic-adc-dma
but for stm32f7 it must be done in a slightly different way depending on stm32f7 hal implementation
Already tried
- ADC in blocking mode (polling)
It works, but very slowly and main CPU used for conversion, using DMA cpu can be used for other tasks like transferring and so on. - by @JishanShaikh was suggested:
#![no_std]
#![no_main]
use core::{cell::UnsafeCell, mem::MaybeUninit};
use panic_semihosting as _;
use cortex_m_semihosting::hprintln;
use cortex_m::asm;
use cortex_m_rt::entry;
use heapless::spsc::{Queue};
use stm32f7xx_hal::{
interrupt,
adc::{
Adc, ChannelTimeSequence, StoredConfig,
},
pac::{DMA2, ADC1},
dma::{self, DMA},
gpio::{Analog, gpioa::PA3},
prelude::*,
rcc::RccExt,
};
const QSIZE: usize = 16;
const QSIZE_ADD: usize = QSIZE + 1;
static mut QUEUE: Queue<u16, QSIZE_ADD> = Queue::new();
static mut BUFFER: CircBuffer<u16, QSIZE> = CircBuffer::new();
#[entry]
fn main() -> ! {
let cp = cortex_m::Peripherals::take().unwrap();
let dp = stm32f7xx_hal::pac::Peripherals::take().unwrap();
let rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.sysclk(216.MHz()).freeze();
let dma = DMA::new(dp.DMA2);
// i was found only StoredConfig implementation in the stm32f7xx_hal
// let adc_conf = StoredConfig::default();
let mut adc = Adc::adc1(
dp.ADC1,
&mut rcc.apb2,
&clocks,
12,
true,
);
let gpio_a = dp.GPIOA.split();
let pa3 = gpio_a.pa3.into_analog();
let _circ_buffer = configure_adc_dma(dma, adc, pa3);
let mut cnt = 0_usize;
let q = unsafe {&mut QUEUE};
loop {
cortex_m::asm::wfi();
let mut buf = [0; QSIZE];
let len = q.len();
for i in 0..len {
buf[i] = q.dequeue().unwrap();
}
cnt += len;
if cnt >= QSIZE {
hprintln!("[main] buf: {:?}", buf);
cnt = 0;
}
}
}
#[interrupt]
fn DMA2_STREAM0() {
if let buffer = unsafe {&mut BUFFER} { // BUFFER.as_mut()
buffer.read(|sample, _| {
let q = unsafe {&mut QUEUE};
q.enqueue(*sample).unwrap();
});
buffer.release(1);
}
unsafe { (*DMA2::ptr()).lifcr.write(|w| w.ctcif0().set_bit()) };
}
fn configure_adc_dma(
dma: DMA<DMA2>,
adc: Adc<ADC1>,
// adc: Adc<stm32f7xx_hal::pac::ADC1>,
pa3: PA3<Analog>,
) -> &'static mut CircBuffer<u16, QSIZE> {
let buffer= unsafe {&mut BUFFER};
let mut channel = adc.channel(AdcChannel::Adc3, Default::default());
channel.enable_dma();
let mut dma_ch = dma.channel0
.mem2mem()
.circular()
// .set_memory_size(dma::MemorySize::HalfWord)
// .set_peripheral_size(dma::MemorySize::HalfWord)
.set_memory_increment(true)
.set_peripheral_increment(false)
// .set_direction(dma::Direction::PeripheralToMemory)
.set_peripheral_address(&adc.adr)
.set_memory_address(buffer.peek().unwrap())
.set_number_of_data(QSIZE as u16);
dma_ch.start();
// cortex_m::interrupt::free(|_| unsafe { BUFFER.replace(buffer) });
// cortex_m::interrupt::free(|_| unsafe { BUFFER.replace(circ_buffer) });
dma_ch.listen(dma::Event::TransferComplete);
adc.set_continuous_mode(true);
adc.cr2.modify(|_, w| w.cont().continuous());
adc.cr2.modify(|_, w| w.adon().enabled());
pa3.into_analog();
dma_ch.enable();
buffer
// circ_buffer
}
/// Some circular buffer
struct CircBuffer<T, const N: usize> {
buffer: [UnsafeCell<MaybeUninit<T>>; N],
}
impl<T, const N: usize> CircBuffer<T, N> {
const INIT: UnsafeCell<MaybeUninit<T>> = UnsafeCell::new(MaybeUninit::uninit());
pub const fn new() -> Self {
Self {
buffer: [Self::INIT; N],
}
}
pub fn read(self, callback: fn(&u16, usize)) {
(callback)(&0_u16, 0)
}
pub fn peek(&self) -> Option<&T> {
Some(unsafe { &*(self.buffer.get_unchecked(0).get() as *const T) })
}
pub fn release(self, v: usize) {}
}
Seems like stm32f7xx_hal, doesn't implements fallowing:
- adc.channel(AdcChannel::Adc3, Default::default())
- dma.channel0.mem2mem().circular() ....
- dma_ch.listen(dma::Event::TransferComplete);
- dma::Event
How can i fix it ?