diff options
| -rw-r--r-- | embassy-stm32/src/adc/g4.rs | 85 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/injected.rs | 36 | ||||
| -rw-r--r-- | examples/stm32g4/src/bin/adc_injected_and_regular.rs | 38 |
3 files changed, 131 insertions, 28 deletions
diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 613fe921b..f60a14a72 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs | |||
| @@ -17,6 +17,9 @@ use crate::{Peri, pac, rcc}; | |||
| 17 | mod ringbuffered; | 17 | mod ringbuffered; |
| 18 | pub use ringbuffered::RingBufferedAdc; | 18 | pub use ringbuffered::RingBufferedAdc; |
| 19 | 19 | ||
| 20 | mod injected; | ||
| 21 | pub use injected::InjectedAdc; | ||
| 22 | |||
| 20 | /// Default VREF voltage used for sample conversion to millivolts. | 23 | /// Default VREF voltage used for sample conversion to millivolts. |
| 21 | pub const VREF_DEFAULT_MV: u32 = 3300; | 24 | pub const VREF_DEFAULT_MV: u32 = 3300; |
| 22 | /// VREF voltage used for factory calibration of VREFINTCAL register. | 25 | /// VREF voltage used for factory calibration of VREFINTCAL register. |
| @@ -126,6 +129,22 @@ impl Prescaler { | |||
| 126 | } | 129 | } |
| 127 | } | 130 | } |
| 128 | 131 | ||
| 132 | // Trigger source for ADC conversions | ||
| 133 | pub struct ConversionTrigger { | ||
| 134 | // See Table 166 and 167 in RM0440 Rev 9 for ADC1/2 External triggers | ||
| 135 | // Note that Injected and Regular channels uses different mappings | ||
| 136 | pub channel: u8, | ||
| 137 | pub edge: Exten, | ||
| 138 | } | ||
| 139 | |||
| 140 | // Conversion mode for regular ADC channels | ||
| 141 | pub enum RegularConversionMode { | ||
| 142 | // Samples as fast as possible | ||
| 143 | Continuous, | ||
| 144 | // Sample at rate determined by external trigger | ||
| 145 | Triggered(ConversionTrigger), | ||
| 146 | } | ||
| 147 | |||
| 129 | impl<'d, T: Instance> Adc<'d, T> { | 148 | impl<'d, T: Instance> Adc<'d, T> { |
| 130 | /// Create a new ADC driver. | 149 | /// Create a new ADC driver. |
| 131 | pub fn new(adc: Peri<'d, T>) -> Self { | 150 | pub fn new(adc: Peri<'d, T>) -> Self { |
| @@ -508,11 +527,10 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 508 | } | 527 | } |
| 509 | 528 | ||
| 510 | /// Set external trigger for regular conversion sequence | 529 | /// Set external trigger for regular conversion sequence |
| 511 | /// Possible trigger values are seen in Table 166 in RM0440 Rev 9 | 530 | pub fn set_regular_conversion_trigger(&mut self, trigger: ConversionTrigger) { |
| 512 | pub fn set_regular_conversion_trigger(&mut self, trigger: u8, edge: Exten) { | ||
| 513 | T::regs().cfgr().modify(|r| { | 531 | T::regs().cfgr().modify(|r| { |
| 514 | r.set_extsel(trigger); | 532 | r.set_extsel(trigger.channel); |
| 515 | r.set_exten(edge); | 533 | r.set_exten(trigger.edge); |
| 516 | }); | 534 | }); |
| 517 | // Regular conversions uses DMA so no need to generate interrupt | 535 | // Regular conversions uses DMA so no need to generate interrupt |
| 518 | T::regs().ier().modify(|r| r.set_eosie(false)); | 536 | T::regs().ier().modify(|r| r.set_eosie(false)); |
| @@ -542,6 +560,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 542 | dma: Peri<'a, impl RxDma<T>>, | 560 | dma: Peri<'a, impl RxDma<T>>, |
| 543 | dma_buf: &'a mut [u16], | 561 | dma_buf: &'a mut [u16], |
| 544 | sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<T>, SampleTime)>, | 562 | sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<T>, SampleTime)>, |
| 563 | mode: RegularConversionMode, | ||
| 545 | ) -> RingBufferedAdc<'a, T> { | 564 | ) -> RingBufferedAdc<'a, T> { |
| 546 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | 565 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); |
| 547 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); | 566 | assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); |
| @@ -596,11 +615,24 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 596 | 615 | ||
| 597 | T::regs().cfgr().modify(|reg| { | 616 | T::regs().cfgr().modify(|reg| { |
| 598 | reg.set_discen(false); // Convert all channels for each trigger | 617 | reg.set_discen(false); // Convert all channels for each trigger |
| 599 | reg.set_cont(false); // New trigger is neede for each sample to be read | ||
| 600 | reg.set_dmacfg(Dmacfg::CIRCULAR); | 618 | reg.set_dmacfg(Dmacfg::CIRCULAR); |
| 601 | reg.set_dmaen(Dmaen::ENABLE); | 619 | reg.set_dmaen(Dmaen::ENABLE); |
| 602 | }); | 620 | }); |
| 603 | 621 | ||
| 622 | match mode { | ||
| 623 | RegularConversionMode::Continuous => { | ||
| 624 | T::regs().cfgr().modify(|reg| { | ||
| 625 | reg.set_cont(true); | ||
| 626 | }); | ||
| 627 | }, | ||
| 628 | RegularConversionMode::Triggered(trigger) => { | ||
| 629 | T::regs().cfgr().modify(|r| { | ||
| 630 | r.set_cont(false); // New trigger is neede for each sample to be read | ||
| 631 | }); | ||
| 632 | self.set_regular_conversion_trigger(trigger); | ||
| 633 | } | ||
| 634 | } | ||
| 635 | |||
| 604 | RingBufferedAdc::new(dma, dma_buf) | 636 | RingBufferedAdc::new(dma, dma_buf) |
| 605 | } | 637 | } |
| 606 | 638 | ||
| @@ -608,7 +640,8 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 608 | pub fn configure_injected_sequence<'a>( | 640 | pub fn configure_injected_sequence<'a>( |
| 609 | &mut self, | 641 | &mut self, |
| 610 | sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<T>, SampleTime)>, | 642 | sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<T>, SampleTime)>, |
| 611 | ) { | 643 | trigger: ConversionTrigger, |
| 644 | ) -> InjectedAdc<T> { | ||
| 612 | assert!(sequence.len() != 0, "Read sequence cannot be empty"); | 645 | assert!(sequence.len() != 0, "Read sequence cannot be empty"); |
| 613 | assert!( | 646 | assert!( |
| 614 | sequence.len() <= NR_INJECTED_RANKS, | 647 | sequence.len() <= NR_INJECTED_RANKS, |
| @@ -656,6 +689,35 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 656 | T::regs().cfgr().modify(|reg| { | 689 | T::regs().cfgr().modify(|reg| { |
| 657 | reg.set_jdiscen(false); // Will convert all channels for each trigger | 690 | reg.set_jdiscen(false); // Will convert all channels for each trigger |
| 658 | }); | 691 | }); |
| 692 | |||
| 693 | self.set_injected_conversion_trigger(trigger); | ||
| 694 | self.enable_injected_eos_interrupt(true); | ||
| 695 | |||
| 696 | self.start_injected_conversion(); | ||
| 697 | InjectedAdc::new() | ||
| 698 | } | ||
| 699 | |||
| 700 | pub fn into_regular_ringbuffered_and_injected_interrupts<'a>( | ||
| 701 | &mut self, | ||
| 702 | dma: Peri<'a, impl RxDma<T>>, | ||
| 703 | dma_buf: &'a mut [u16], | ||
| 704 | regular_sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<T>, SampleTime)>, | ||
| 705 | regular_conversion_mode: RegularConversionMode, | ||
| 706 | injected_sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<T>, SampleTime)>, | ||
| 707 | injected_trigger: ConversionTrigger, | ||
| 708 | ) -> (RingBufferedAdc<'a, T>, InjectedAdc<T>) { | ||
| 709 | ( | ||
| 710 | self.into_ring_buffered( | ||
| 711 | dma, | ||
| 712 | dma_buf, | ||
| 713 | regular_sequence, | ||
| 714 | regular_conversion_mode, | ||
| 715 | ), | ||
| 716 | self.configure_injected_sequence( | ||
| 717 | injected_sequence, | ||
| 718 | injected_trigger, | ||
| 719 | ), | ||
| 720 | ) | ||
| 659 | } | 721 | } |
| 660 | 722 | ||
| 661 | /// Start injected ADC conversion | 723 | /// Start injected ADC conversion |
| @@ -667,10 +729,10 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 667 | 729 | ||
| 668 | /// Set external trigger for injected conversion sequence | 730 | /// Set external trigger for injected conversion sequence |
| 669 | /// Possible trigger values are seen in Table 167 in RM0440 Rev 9 | 731 | /// Possible trigger values are seen in Table 167 in RM0440 Rev 9 |
| 670 | pub fn set_injected_conversion_trigger(&mut self, trigger: u8, edge: Exten) { | 732 | pub fn set_injected_conversion_trigger(&mut self, trigger: ConversionTrigger) { |
| 671 | T::regs().jsqr().modify(|r| { | 733 | T::regs().jsqr().modify(|r| { |
| 672 | r.set_jextsel(trigger); | 734 | r.set_jextsel(trigger.channel); |
| 673 | r.set_jexten(edge); | 735 | r.set_jexten(trigger.edge); |
| 674 | }); | 736 | }); |
| 675 | } | 737 | } |
| 676 | 738 | ||
| @@ -681,7 +743,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 681 | 743 | ||
| 682 | /// Read sampled data from all injected ADC injected ranks | 744 | /// Read sampled data from all injected ADC injected ranks |
| 683 | /// Clear the JEOS flag to allow a new injected sequence | 745 | /// Clear the JEOS flag to allow a new injected sequence |
| 684 | pub fn clear_injected_eos(&mut self) -> [u16; NR_INJECTED_RANKS] { | 746 | pub(super) fn read_injected_samples() -> [u16; NR_INJECTED_RANKS] { |
| 685 | let mut data = [0u16; NR_INJECTED_RANKS]; | 747 | let mut data = [0u16; NR_INJECTED_RANKS]; |
| 686 | for i in 0..NR_INJECTED_RANKS { | 748 | for i in 0..NR_INJECTED_RANKS { |
| 687 | data[i] = T::regs().jdr(i).read().jdata(); | 749 | data[i] = T::regs().jdr(i).read().jdata(); |
| @@ -732,6 +794,9 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 732 | }); | 794 | }); |
| 733 | while T::regs().cr().read().adstart() {} | 795 | while T::regs().cr().read().adstart() {} |
| 734 | } | 796 | } |
| 797 | } | ||
| 798 | |||
| 799 | pub(super) fn stop_injected_conversions() { | ||
| 735 | // Cancel injected conversions | 800 | // Cancel injected conversions |
| 736 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | 801 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 737 | T::regs().cr().modify(|reg| { | 802 | T::regs().cr().modify(|reg| { |
diff --git a/embassy-stm32/src/adc/injected.rs b/embassy-stm32/src/adc/injected.rs new file mode 100644 index 000000000..0efc47759 --- /dev/null +++ b/embassy-stm32/src/adc/injected.rs | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | use core::sync::atomic::{Ordering, compiler_fence}; | ||
| 3 | |||
| 4 | #[allow(unused_imports)] | ||
| 5 | use embassy_hal_internal::Peri; | ||
| 6 | |||
| 7 | use crate::adc::Adc; | ||
| 8 | #[allow(unused_imports)] | ||
| 9 | use crate::adc::Instance; | ||
| 10 | |||
| 11 | const NR_INJECTED_RANKS: usize = 4; | ||
| 12 | |||
| 13 | pub struct InjectedAdc<T: Instance> { | ||
| 14 | _phantom: PhantomData<T>, | ||
| 15 | } | ||
| 16 | |||
| 17 | impl<T: Instance> InjectedAdc<T> { | ||
| 18 | pub(crate) fn new() -> Self { | ||
| 19 | Self { | ||
| 20 | _phantom: PhantomData, | ||
| 21 | } | ||
| 22 | } | ||
| 23 | |||
| 24 | pub fn read_injected_samples(&mut self) -> [u16; NR_INJECTED_RANKS] { | ||
| 25 | Adc::<T>::read_injected_samples() | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | |||
| 30 | impl<T: Instance> Drop for InjectedAdc<T> { | ||
| 31 | fn drop(&mut self) { | ||
| 32 | Adc::<T>::teardown_adc(); | ||
| 33 | |||
| 34 | compiler_fence(Ordering::SeqCst); | ||
| 35 | } | ||
| 36 | } | ||
diff --git a/examples/stm32g4/src/bin/adc_injected_and_regular.rs b/examples/stm32g4/src/bin/adc_injected_and_regular.rs index c2985e1d6..85e01dbf3 100644 --- a/examples/stm32g4/src/bin/adc_injected_and_regular.rs +++ b/examples/stm32g4/src/bin/adc_injected_and_regular.rs | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | use core::cell::RefCell; | 9 | use core::cell::RefCell; |
| 10 | 10 | ||
| 11 | use defmt::info; | 11 | use defmt::info; |
| 12 | use embassy_stm32::adc::{Adc, AdcChannel as _, Exten, SampleTime}; | 12 | use embassy_stm32::adc::{Adc, AdcChannel as _, Exten, SampleTime, ConversionTrigger, RegularConversionMode}; |
| 13 | use embassy_stm32::interrupt::typelevel::{ADC1_2, Interrupt}; | 13 | use embassy_stm32::interrupt::typelevel::{ADC1_2, Interrupt}; |
| 14 | use embassy_stm32::peripherals::ADC1; | 14 | use embassy_stm32::peripherals::ADC1; |
| 15 | use embassy_stm32::time::Hertz; | 15 | use embassy_stm32::time::Hertz; |
| @@ -17,9 +17,10 @@ use embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, Mms2}; | |||
| 17 | use embassy_stm32::timer::low_level::CountingMode; | 17 | use embassy_stm32::timer::low_level::CountingMode; |
| 18 | use embassy_stm32::{Config, interrupt}; | 18 | use embassy_stm32::{Config, interrupt}; |
| 19 | use embassy_sync::blocking_mutex::CriticalSectionMutex; | 19 | use embassy_sync::blocking_mutex::CriticalSectionMutex; |
| 20 | use embassy_stm32::adc::InjectedAdc; | ||
| 20 | use {critical_section, defmt_rtt as _, panic_probe as _}; | 21 | use {critical_section, defmt_rtt as _, panic_probe as _}; |
| 21 | 22 | ||
| 22 | static ADC1_HANDLE: CriticalSectionMutex<RefCell<Option<Adc<'static, ADC1>>>> = | 23 | static ADC1_HANDLE: CriticalSectionMutex<RefCell<Option<InjectedAdc<ADC1>>>> = |
| 23 | CriticalSectionMutex::new(RefCell::new(None)); | 24 | CriticalSectionMutex::new(RefCell::new(None)); |
| 24 | 25 | ||
| 25 | /// This example showcases how to use both regular ADC conversions with DMA and injected ADC | 26 | /// This example showcases how to use both regular ADC conversions with DMA and injected ADC |
| @@ -85,29 +86,30 @@ async fn main(_spawner: embassy_executor::Spawner) { | |||
| 85 | ] | 86 | ] |
| 86 | .into_iter(); | 87 | .into_iter(); |
| 87 | 88 | ||
| 89 | // Configurations of Injected ADC measurements | ||
| 90 | let mut pa2 = p.PA2.degrade_adc(); | ||
| 91 | let injected_sequence = [(&mut pa2, SampleTime::CYCLES247_5)].into_iter(); | ||
| 92 | |||
| 88 | // Configure DMA for retrieving regular ADC measurements | 93 | // Configure DMA for retrieving regular ADC measurements |
| 89 | let dma1_ch1 = p.DMA1_CH1; | 94 | let dma1_ch1 = p.DMA1_CH1; |
| 90 | // Using buffer of double size means the half-full interrupts will generate at the expected rate | 95 | // Using buffer of double size means the half-full interrupts will generate at the expected rate |
| 91 | let mut readings = [0u16; 4]; | 96 | let mut readings = [0u16; 4]; |
| 92 | let mut ring_buffered_adc = adc1.into_ring_buffered(dma1_ch1, &mut readings, regular_sequence); | ||
| 93 | |||
| 94 | // Configurations of Injected ADC measurements | ||
| 95 | let mut pa2 = p.PA2.degrade_adc(); | ||
| 96 | let injected_seq = [(&mut pa2, SampleTime::CYCLES247_5)].into_iter(); | ||
| 97 | adc1.configure_injected_sequence(injected_seq); | ||
| 98 | 97 | ||
| 99 | adc1.set_regular_conversion_trigger(ADC1_REGULAR_TRIGGER_TIM1_TRGO2, Exten::RISING_EDGE); | 98 | let injected_trigger = ConversionTrigger { channel: ADC1_INJECTED_TRIGGER_TIM1_TRGO2, edge: Exten::RISING_EDGE }; |
| 100 | adc1.set_injected_conversion_trigger(ADC1_INJECTED_TRIGGER_TIM1_TRGO2, Exten::RISING_EDGE); | 99 | let regular_trigger = ConversionTrigger { channel: ADC1_REGULAR_TRIGGER_TIM1_TRGO2, edge: Exten::RISING_EDGE }; |
| 101 | 100 | ||
| 102 | // ADC must be started after all configurations are completed | 101 | let (mut ring_buffered_adc, injected_adc) = adc1.into_regular_ringbuffered_and_injected_interrupts( |
| 103 | adc1.start_injected_conversion(); | 102 | dma1_ch1, |
| 104 | 103 | &mut readings, | |
| 105 | // Enable interrupt at end of injected ADC conversion | 104 | regular_sequence, |
| 106 | adc1.enable_injected_eos_interrupt(true); | 105 | RegularConversionMode::Triggered(regular_trigger), |
| 106 | injected_sequence, | ||
| 107 | injected_trigger, | ||
| 108 | ); | ||
| 107 | 109 | ||
| 108 | // Store ADC globally to allow access from ADC interrupt | 110 | // Store ADC globally to allow access from ADC interrupt |
| 109 | critical_section::with(|cs| { | 111 | critical_section::with(|cs| { |
| 110 | ADC1_HANDLE.borrow(cs).replace(Some(adc1)); | 112 | ADC1_HANDLE.borrow(cs).replace(Some(injected_adc)); |
| 111 | }); | 113 | }); |
| 112 | // Enable interrupt for ADC1_2 | 114 | // Enable interrupt for ADC1_2 |
| 113 | unsafe { ADC1_2::enable() }; | 115 | unsafe { ADC1_2::enable() }; |
| @@ -136,8 +138,8 @@ async fn main(_spawner: embassy_executor::Spawner) { | |||
| 136 | #[interrupt] | 138 | #[interrupt] |
| 137 | unsafe fn ADC1_2() { | 139 | unsafe fn ADC1_2() { |
| 138 | critical_section::with(|cs| { | 140 | critical_section::with(|cs| { |
| 139 | if let Some(adc) = ADC1_HANDLE.borrow(cs).borrow_mut().as_mut() { | 141 | if let Some(injected_adc) = ADC1_HANDLE.borrow(cs).borrow_mut().as_mut() { |
| 140 | let injected_data = adc.clear_injected_eos(); | 142 | let injected_data = injected_adc.read_injected_samples(); |
| 141 | info!("Injected reading of PA2: {}", injected_data[0]); | 143 | info!("Injected reading of PA2: {}", injected_data[0]); |
| 142 | } | 144 | } |
| 143 | }); | 145 | }); |
