diff --git a/examples/adc.rs b/examples/adc.rs index 8b1f821e..61dfe5f8 100644 --- a/examples/adc.rs +++ b/examples/adc.rs @@ -5,7 +5,7 @@ use panic_rtt_target as _; use cortex_m_rt::entry; use rtt_target::{rprint, rprintln}; -use stm32l4xx_hal::{adc::ADC, delay::Delay, pac, prelude::*}; +use stm32l4xx_hal::{adc::config, adc::ADC, delay::Delay, pac, prelude::*}; #[entry] fn main() -> ! { @@ -28,6 +28,7 @@ fn main() -> ! { &mut rcc.ahb2, &mut rcc.ccipr, &mut delay, + config::ExternalTriggerConfig::default(), ); let mut gpioc = dp.GPIOC.split(&mut rcc.ahb2); diff --git a/examples/adc_dma.rs b/examples/adc_dma.rs index 8e9d8016..71a39312 100644 --- a/examples/adc_dma.rs +++ b/examples/adc_dma.rs @@ -4,7 +4,7 @@ use panic_rtt_target as _; use rtt_target::{rprintln, rtt_init_print}; use stm32l4xx_hal::{ - adc::{DmaMode, SampleTime, Sequence, ADC}, + adc::{config, DmaMode, SampleTime, Sequence, ADC}, delay::DelayCM, dma::{dma1, RxDma, Transfer, W}, prelude::*, @@ -60,6 +60,7 @@ const APP: () = { &mut rcc.ahb2, &mut rcc.ccipr, &mut delay, + config::ExternalTriggerConfig::default(), ); let mut temp_pin = adc.enable_temperature(&mut delay); @@ -71,7 +72,7 @@ const APP: () = { adc.configure_sequence(&mut temp_pin, Sequence::Three, SampleTime::Cycles640_5); // Heapless boxes also work very well as buffers for DMA transfers - let transfer = Transfer::from_adc(adc, dma1_channel, MEMORY, DmaMode::Oneshot, true); + let transfer = Transfer::from_adc(adc, dma1_channel, MEMORY, DmaMode::Oneshot, true, false); init::LateResources { transfer: Some(transfer), @@ -96,6 +97,7 @@ const APP: () = { buffer, DmaMode::Oneshot, true, + false, )); } } diff --git a/examples/adc_dma_cont.rs b/examples/adc_dma_cont.rs new file mode 100644 index 00000000..b9bfb869 --- /dev/null +++ b/examples/adc_dma_cont.rs @@ -0,0 +1,103 @@ +#![no_main] +#![no_std] + +use panic_rtt_target as _; +use rtt_target::{rprintln, rtt_init_print}; +use stm32l4xx_hal::{ + adc::{config, DmaMode, SampleTime, Sequence, ADC}, + delay::DelayCM, + dma::{dma1, RxDma, Transfer, W}, + prelude::*, +}; + +use rtic::app; + +const SEQUENCE_LEN: usize = 8; + +#[app(device = stm32l4xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)] +const APP: () = { + // RTIC app is written in here! + + struct Resources { + transfer: Option>>, + } + + #[init] + fn init(cx: init::Context) -> init::LateResources { + let MEMORY = { + static mut MEMORY: [u16; SEQUENCE_LEN] = [0u16; SEQUENCE_LEN]; + unsafe { &mut MEMORY } + }; + + rtt_init_print!(); + + rprintln!("Hello from init!"); + + let cp = cx.core; + let mut dcb = cp.DCB; + let mut dwt = cp.DWT; + + dcb.enable_trace(); + dwt.enable_cycle_counter(); + + let pac = cx.device; + + let mut rcc = pac.RCC.constrain(); + let mut flash = pac.FLASH.constrain(); + let mut pwr = pac.PWR.constrain(&mut rcc.apb1r1); + let dma_channels = pac.DMA1.split(&mut rcc.ahb1); + + // + // Initialize the clocks + // + let clocks = rcc.cfgr.sysclk(80.MHz()).freeze(&mut flash.acr, &mut pwr); + + let mut delay = DelayCM::new(clocks); + + let mut adc = ADC::new( + pac.ADC1, + pac.ADC_COMMON, + &mut rcc.ahb2, + &mut rcc.ccipr, + &mut delay, + config::ExternalTriggerConfig::default(), + ); + + let dma1_channel = dma_channels.1; + + let mut gpioa = pac.GPIOA.split(&mut rcc.ahb2); + let mut a1 = gpioa.pa0.into_analog(&mut gpioa.moder, &mut gpioa.pupdr); + + adc.configure_sequence(&mut a1, Sequence::One, SampleTime::Cycles12_5); + + // Heapless boxes also work very well as buffers for DMA transfers + let transfer = Transfer::from_adc(adc, dma1_channel, MEMORY, DmaMode::Oneshot, true, true); + + init::LateResources { + transfer: Some(transfer), + } + } + + #[idle] + fn idle(_cx: idle::Context) -> ! { + loop { + cortex_m::asm::nop(); + } + } + + #[task(binds = DMA1_CH1, resources = [transfer])] + fn dma1_interrupt(cx: dma1_interrupt::Context) { + let transfer = cx.resources.transfer; + if let Some(transfer_val) = transfer.take() { + let (buffer, rx_dma) = transfer_val.wait(); + rprintln!("DMA measurements: {:?}", buffer); + *transfer = Some(Transfer::from_adc_dma( + rx_dma, + buffer, + DmaMode::Oneshot, + true, + true, + )); + } + } +}; diff --git a/examples/adc_dma_trigger_tim2.rs b/examples/adc_dma_trigger_tim2.rs new file mode 100644 index 00000000..d053b0ae --- /dev/null +++ b/examples/adc_dma_trigger_tim2.rs @@ -0,0 +1,121 @@ +#![no_main] +#![no_std] + +use panic_rtt_target as _; +use rtt_target::{rprintln, rtt_init_print}; +use stm32l4xx_hal::{ + adc::{config, DmaMode, SampleTime, Sequence, ADC}, + delay::DelayCM, + dma::{dma1, RxDma, Transfer, W}, + pac::TIM2, + prelude::*, + timer::{Event, MasterMode, Timer}, +}; + +use rtic::app; + +const SEQUENCE_LEN: usize = 8; + +#[app(device = stm32l4xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)] +const APP: () = { + // RTIC app is written in here! + + struct Resources { + transfer: Option>>, + timer: Timer, + } + + #[init] + fn init(cx: init::Context) -> init::LateResources { + let MEMORY = { + static mut MEMORY: [u16; SEQUENCE_LEN] = [0u16; SEQUENCE_LEN]; + unsafe { &mut MEMORY } + }; + + rtt_init_print!(); + + rprintln!("Hello from init!"); + + let cp = cx.core; + let mut dcb = cp.DCB; + let mut dwt = cp.DWT; + + dcb.enable_trace(); + dwt.enable_cycle_counter(); + + let pac = cx.device; + + let mut rcc = pac.RCC.constrain(); + let mut flash = pac.FLASH.constrain(); + let mut pwr = pac.PWR.constrain(&mut rcc.apb1r1); + let dma_channels = pac.DMA1.split(&mut rcc.ahb1); + + // + // Initialize the clocks + // + let clocks = rcc.cfgr.sysclk(80.MHz()).freeze(&mut flash.acr, &mut pwr); + + let mut delay = DelayCM::new(clocks); + + let mut adc = ADC::new( + pac.ADC1, + pac.ADC_COMMON, + &mut rcc.ahb2, + &mut rcc.ccipr, + &mut delay, + config::ExternalTriggerConfig( + config::TriggerMode::RisingEdge, + config::ExternalTrigger::Tim2TRGO, + ), + ); + + let mut timer_adc_trg = Timer::tim2(pac.TIM2, 1.Hz(), clocks, &mut rcc.apb1r1); + timer_adc_trg.master_mode(MasterMode::Update); + timer_adc_trg.listen(Event::TimeOut); + + let dma1_channel = dma_channels.1; + + let mut gpioa = pac.GPIOA.split(&mut rcc.ahb2); + let mut a1 = gpioa.pa0.into_analog(&mut gpioa.moder, &mut gpioa.pupdr); + + adc.configure_sequence(&mut a1, Sequence::One, SampleTime::Cycles12_5); + + // Heapless boxes also work very well as buffers for DMA transfers + let transfer = Transfer::from_adc(adc, dma1_channel, MEMORY, DmaMode::Oneshot, true, false); + + init::LateResources { + transfer: Some(transfer), + timer: timer_adc_trg, + } + } + + #[idle] + fn idle(_cx: idle::Context) -> ! { + loop { + cortex_m::asm::nop(); + } + } + + #[task(binds = DMA1_CH1, resources = [transfer])] + fn dma1_interrupt(cx: dma1_interrupt::Context) { + let transfer = cx.resources.transfer; + if let Some(transfer_val) = transfer.take() { + let (buffer, rx_dma) = transfer_val.wait(); + rprintln!("DMA measurements: {:?}", buffer); + *transfer = Some(Transfer::from_adc_dma( + rx_dma, + buffer, + DmaMode::Oneshot, + true, + false, + )); + } + } + + #[task(binds = TIM2, resources = [timer])] + fn tim2_interrupt(cx: tim2_interrupt::Context) { + let timer = cx.resources.timer; + timer.clear_interrupt(stm32l4xx_hal::timer::Event::TimeOut); + rprintln!("TIM2 int - ADC trigger"); + } +}; diff --git a/src/adc.rs b/src/adc.rs index 4d23c136..af23adb9 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -129,6 +129,7 @@ impl ADC { ahb: &mut AHB2, ccipr: &mut CCIPR, delay: &mut impl DelayUs, + external_trigger: config::ExternalTriggerConfig, ) -> Self { // Enable peripheral ADC1::enable(ahb); @@ -183,6 +184,8 @@ impl ADC { s.calibrate(&mut vref); + s.set_external_trigger(external_trigger); + s.common.ccr.modify(|_, w| w.vrefen().clear_bit()); s } @@ -389,6 +392,13 @@ impl ADC { self.adc.cr.modify(|_, w| w.adstart().set_bit()); } + pub fn start_cont_conversion(&mut self) { + self.enable(); + self.enable_continous(); + self.clear_end_flags(); + self.adc.cr.modify(|_, w| w.adstart().set_bit()); + } + pub fn is_converting(&self) -> bool { self.adc.cr.read().adstart().bit_is_set() } @@ -426,6 +436,14 @@ impl ADC { } } + pub fn enable_continous(&mut self) { + self.adc.cfgr.modify(|_, w| w.cont().set_bit()); + } + + pub fn disable_continous(&mut self) { + self.adc.cfgr.modify(|_, w| w.cont().clear_bit()); + } + pub fn is_enabled(&self) -> bool { self.adc.cr.read().aden().bit_is_set() } @@ -433,6 +451,16 @@ impl ADC { pub fn disable(&mut self) { self.adc.cr.modify(|_, w| w.addis().set_bit()); } + + /// Sets which external trigger to use and if it is disabled, rising, falling or both + pub fn set_external_trigger(&mut self, ext_trg_conf: config::ExternalTriggerConfig) { + self.adc.cfgr.modify(|_, w| unsafe { + w.exten() + .bits(ext_trg_conf.0.into()) + .extsel() + .bits(ext_trg_conf.1.into()) + }); + } } impl OneShot for ADC @@ -491,9 +519,17 @@ where buffer: BUFFER, dma_mode: DmaMode, transfer_complete_interrupt: bool, + continuous_conversion: bool, ) -> Self { let (adc, channel) = dma.split(); - Transfer::from_adc(adc, channel, buffer, dma_mode, transfer_complete_interrupt) + Transfer::from_adc( + adc, + channel, + buffer, + dma_mode, + transfer_complete_interrupt, + continuous_conversion, + ) } /// Initiate a new DMA transfer from an ADC. @@ -508,6 +544,7 @@ where buffer: BUFFER, dma_mode: DmaMode, transfer_complete_interrupt: bool, + continuous_conversion: bool, ) -> Self { assert!(dma_mode != DmaMode::Disabled); @@ -555,7 +592,11 @@ where atomic::compiler_fence(Ordering::Release); channel.start(); - adc.start_conversion(); + if continuous_conversion == false { + adc.start_conversion(); + } else { + adc.start_cont_conversion(); + } Transfer::w( buffer, @@ -643,6 +684,87 @@ pub trait Channel: EmbeddedHalChannel { fn set_sample_time(&mut self, adc: &ADC1, sample_time: SampleTime); } +/// Contains types related to ADC configuration +pub mod config { + /// Possible external triggers the ADC can listen to + #[derive(Debug, Clone, Copy)] + pub enum ExternalTrigger { + /// TIM1 compare channel 1 + Tim1CC1, + /// TIM1 compare channel 2 + Tim1CC2, + /// TIM1 compare channel 3 + Tim1CC3, + /// TIM2 compare channel 2 + Tim2CC2, + /// TIM3 trigger out + Tim3TRGO, + /// External interupt line 11 + Exti11, + /// TIM1 trigger out + Tim1TRGO, + /// TIM1 trigger out 2 + Tim1TRGO2, + /// TIM2 trigger out + Tim2TRGO, + /// TIM6 trigger out + Tim6TRGO, + /// TIM15 trigger out + Tim15TRGO, + } + + impl From for u8 { + fn from(et: ExternalTrigger) -> u8 { + match et { + ExternalTrigger::Tim1CC1 => 0b0000, // EXT0 + ExternalTrigger::Tim1CC2 => 0b0001, // EXT1 + ExternalTrigger::Tim1CC3 => 0b0010, // EXT2 + ExternalTrigger::Tim2CC2 => 0b0011, // EXT3 + ExternalTrigger::Tim3TRGO => 0b0100, // EXT4 + ExternalTrigger::Exti11 => 0b0110, // EXT6 + ExternalTrigger::Tim1TRGO => 0b1001, // EXT9 + ExternalTrigger::Tim1TRGO2 => 0b1010, // EXT10 + ExternalTrigger::Tim2TRGO => 0b1011, // EXT11 + ExternalTrigger::Tim6TRGO => 0b1101, // EXT13 + ExternalTrigger::Tim15TRGO => 0b1110, // EXT14 + } + } + } + + /// Possible trigger modes + #[derive(Debug, Clone, Copy)] + pub enum TriggerMode { + /// Don't listen to external trigger + Disabled, + /// Listen for rising edges of external trigger + RisingEdge, + /// Listen for falling edges of external trigger + FallingEdge, + /// Listen for both rising and falling edges of external trigger + BothEdges, + } + + impl From for u8 { + fn from(tm: TriggerMode) -> u8 { + match tm { + TriggerMode::Disabled => 0b00, + TriggerMode::RisingEdge => 0b01, + TriggerMode::FallingEdge => 0b10, + TriggerMode::BothEdges => 0b11, + } + } + } + + #[derive(Debug)] + pub struct ExternalTriggerConfig(pub TriggerMode, pub ExternalTrigger); + + impl Default for ExternalTriggerConfig { + fn default() -> Self { + Self(TriggerMode::Disabled, ExternalTrigger::Tim1CC1) + } + } +} + macro_rules! adc_pins { ( $( diff --git a/src/timer.rs b/src/timer.rs index 6e47acf7..d7b4f697 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -80,6 +80,33 @@ pub enum Event { TimeOut, } +/// Master mode types +pub enum MasterMode { + Reset = 0, + Enable = 1, + Update = 2, + ComparePulse = 3, + CompareOC1REF = 4, + CompareOC2REF = 5, + CompareOC3REF = 6, + CompareOC4REF = 7, +} + +impl Into for MasterMode { + fn into(self) -> u8 { + match self { + MasterMode::Reset => 0, + MasterMode::Enable => 1, + MasterMode::Update => 2, + MasterMode::ComparePulse => 3, + MasterMode::CompareOC1REF => 4, + MasterMode::CompareOC2REF => 5, + MasterMode::CompareOC3REF => 6, + MasterMode::CompareOC4REF => 7, + } + } +} + macro_rules! hal { ($($TIM:ident: ($tim:ident, $frname:ident, $apb:ident, $width:ident, $timclk:ident),)+) => { $( @@ -280,6 +307,21 @@ macro_rules! hal { } } +macro_rules! master_mode { + ($($TIM:ident,)+) => { + $( + impl Timer<$TIM> { + // NOTE(allow) `w.mms().bits()` is unsafe for TIM6 but sage for TIM2 due to + // some SVD omission. + #[allow(unused_unsafe)] + pub fn master_mode(&mut self, mode: MasterMode) { + self.tim.cr2.modify(|_, w| unsafe { w.mms().bits(mode.into()) }); + } + } + )+ + } +} + hal! { TIM2: (tim2, free_running_tim2, APB1R1, u32, timclk1), TIM6: (tim6, free_running_tim6, APB1R1, u16, timclk1), @@ -288,6 +330,9 @@ hal! { TIM16: (tim16, free_running_tim16, APB2, u16, timclk2), } +// no impl for TIM1, TIM7, TIM8, TIM15 +master_mode!(TIM2, TIM6,); + #[cfg(any( // feature = "stm32l451", feature = "stm32l452",