Skip to content

Instantly share code, notes, and snippets.

@albertmoravec
Last active January 26, 2021 20:35
Show Gist options
  • Save albertmoravec/989129440d92d1a95ef458c7ee84638b to your computer and use it in GitHub Desktop.
Save albertmoravec/989129440d92d1a95ef458c7ee84638b to your computer and use it in GitHub Desktop.
ADC DMA multiple channels
#![no_std]
#![no_main]
#[macro_use]
mod utilities;
use stm32f4xx_hal as hal;
use cortex_m::singleton;
use hal::{
adc::config::{
AdcConfig, Dma, ExternalTrigger, Resolution, SampleTime, Scan, Sequence, TriggerMode,
},
adc::Adc,
delay::Delay,
dma::config::DmaConfig,
dma::traits::{DMASet, PeriAddress},
dma::{Channel0, PeripheralToMemory, Stream0, StreamsTuple, Transfer},
gpio::gpioa::{PA0, PA1},
gpio::Analog,
pac::{ADC1, DMA2, TIM1},
prelude::*,
rcc::Clocks,
stm32,
timer::Timer,
};
use log::info;
use rtic::app;
type Buffer = &'static mut [u16; 2];
type DmaTransfer = Transfer<Stream0<DMA2>, Channel0, ADC1DMA, PeripheralToMemory, Buffer>;
pub struct ADC1DMA {
address: u32,
}
impl ADC1DMA {
fn new(address: u32) -> ADC1DMA {
ADC1DMA { address }
}
}
unsafe impl PeriAddress for ADC1DMA {
type MemSize = u16;
fn address(&self) -> u32 {
self.address
}
}
unsafe impl DMASet<Stream0<DMA2>, Channel0, PeripheralToMemory> for ADC1DMA {}
#[app(device = stm32f4xx_hal::stm32, peripherals = true)]
const APP: () = {
struct Resources {
adc: Adc<ADC1>,
timer: Timer<TIM1>,
transfer: DmaTransfer,
empty_buffer: Option<Buffer>,
delay: Delay,
}
#[init]
fn init(cx: init::Context) -> init::LateResources {
utilities::logger::init();
let core: cortex_m::Peripherals = cx.core;
let device: stm32::Peripherals = cx.device;
let rcc = device.RCC;
rcc.ahb1enr.modify(|_, w| w.gpioaen().enabled());
let clocks = rcc
.constrain()
.cfgr
.use_hse(25.mhz())
.sysclk(100.mhz())
.pclk1(50.mhz())
.pclk2(100.mhz())
.freeze();
let gpioa = device.GPIOA.split();
let pa0 = gpioa.pa0.into_analog();
let pa1 = gpioa.pa1.into_analog();
let first_buffer = singleton!(: [u16; 2] = [0; 2]).unwrap();
let second_buffer = singleton!(: [u16; 2] = [0; 2]).unwrap();
let mut adc = init_adc(device.ADC1, pa0, pa1);
let timer = init_tim(device.TIM1, clocks);
let transfer = init_dma(
device.DMA2,
ADC1DMA::new(adc.data_register_address()),
first_buffer,
);
init::LateResources {
adc,
timer,
transfer,
empty_buffer: Some(second_buffer),
delay: Delay::new(core.SYST, clocks),
}
}
#[idle(resources = [delay, transfer, empty_buffer])]
fn idle(cx: idle::Context) -> ! {
let delay = cx.resources.delay;
let mut transfer = cx.resources.transfer;
let mut empty_buffer = cx.resources.empty_buffer;
transfer.lock(|tr| tr.start(|_| {}));
loop {
let (mut val1, mut val2) = (0, 0);
empty_buffer.lock(|buf| {
let vals = buf.as_ref().unwrap();
val1 = vals[0];
val2 = vals[1];
});
info!("Value 1: {}", val1);
info!("Value 2: {}", val2);
delay.delay_ms(1000u32);
}
}
#[task(binds = DMA2_STREAM0, priority = 2, resources = [transfer, empty_buffer])]
fn dma(cx: dma::Context) {
let new = cx.resources.empty_buffer.take().unwrap();
let old = cx
.resources
.transfer
.next_transfer(new)
.map_err(|_| {})
.unwrap()
.0;
*cx.resources.empty_buffer = Some(old);
}
};
fn init_dma(dma: DMA2, adc: ADC1DMA, buffer: Buffer) -> DmaTransfer {
let stream_0 = StreamsTuple::new(dma).0;
let config = DmaConfig::default()
.transfer_complete_interrupt(true)
.memory_increment(true);
DmaTransfer::init(stream_0, adc, buffer, None, config)
}
fn init_adc(adc: ADC1, pa0: PA0<Analog>, pa1: PA1<Analog>) -> Adc<ADC1> {
let config = AdcConfig::default()
.dma(Dma::Continuous)
.external_trigger(TriggerMode::RisingEdge, ExternalTrigger::Tim_1_cc_1)
.scan(Scan::Enabled)
.resolution(Resolution::Ten);
let mut adc = Adc::adc1(adc, true, config);
adc.configure_channel(&pa0, Sequence::One, SampleTime::Cycles_480);
adc.configure_channel(&pa1, Sequence::Two, SampleTime::Cycles_480);
adc
}
fn init_tim(tim: TIM1, clocks: Clocks) -> Timer<TIM1> {
let timer = Timer::tim1(tim, 10.hz(), clocks);
// This part is taken from the ADC HAL documentation
unsafe {
let tim = &(*TIM1::ptr());
//Channel 1
//Disable the channel before configuring it
tim.ccer.modify(|_, w| w.cc1e().clear_bit());
tim.ccmr1_output().modify(|_, w| {
w
//Preload enable for channel
.oc1pe()
.set_bit()
//Set mode for channel, the default mode is "frozen" which won't work
.oc1m()
.pwm_mode1()
});
//Set the duty cycle, 0 won't work in pwm mode but might be ok in
//toggle mode or match mode
let max_duty = tim.arr.read().arr().bits() as u16;
tim.ccr1.modify(|_, w| w.ccr().bits(max_duty / 2));
//Enable the channel
tim.ccer.modify(|_, w| w.cc1e().set_bit());
//Enable the TIM main Output
tim.bdtr.modify(|_, w| w.moe().set_bit());
}
timer
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment