aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/src/adc/g4.rs85
-rw-r--r--embassy-stm32/src/adc/injected.rs36
-rw-r--r--examples/stm32g4/src/bin/adc_injected_and_regular.rs38
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};
17mod ringbuffered; 17mod ringbuffered;
18pub use ringbuffered::RingBufferedAdc; 18pub use ringbuffered::RingBufferedAdc;
19 19
20mod injected;
21pub use injected::InjectedAdc;
22
20/// Default VREF voltage used for sample conversion to millivolts. 23/// Default VREF voltage used for sample conversion to millivolts.
21pub const VREF_DEFAULT_MV: u32 = 3300; 24pub 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
133pub 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
141pub enum RegularConversionMode {
142 // Samples as fast as possible
143 Continuous,
144 // Sample at rate determined by external trigger
145 Triggered(ConversionTrigger),
146}
147
129impl<'d, T: Instance> Adc<'d, T> { 148impl<'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 @@
1use core::marker::PhantomData;
2use core::sync::atomic::{Ordering, compiler_fence};
3
4#[allow(unused_imports)]
5use embassy_hal_internal::Peri;
6
7use crate::adc::Adc;
8#[allow(unused_imports)]
9use crate::adc::Instance;
10
11const NR_INJECTED_RANKS: usize = 4;
12
13pub struct InjectedAdc<T: Instance> {
14 _phantom: PhantomData<T>,
15}
16
17impl<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
30impl<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 @@
9use core::cell::RefCell; 9use core::cell::RefCell;
10 10
11use defmt::info; 11use defmt::info;
12use embassy_stm32::adc::{Adc, AdcChannel as _, Exten, SampleTime}; 12use embassy_stm32::adc::{Adc, AdcChannel as _, Exten, SampleTime, ConversionTrigger, RegularConversionMode};
13use embassy_stm32::interrupt::typelevel::{ADC1_2, Interrupt}; 13use embassy_stm32::interrupt::typelevel::{ADC1_2, Interrupt};
14use embassy_stm32::peripherals::ADC1; 14use embassy_stm32::peripherals::ADC1;
15use embassy_stm32::time::Hertz; 15use embassy_stm32::time::Hertz;
@@ -17,9 +17,10 @@ use embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, Mms2};
17use embassy_stm32::timer::low_level::CountingMode; 17use embassy_stm32::timer::low_level::CountingMode;
18use embassy_stm32::{Config, interrupt}; 18use embassy_stm32::{Config, interrupt};
19use embassy_sync::blocking_mutex::CriticalSectionMutex; 19use embassy_sync::blocking_mutex::CriticalSectionMutex;
20use embassy_stm32::adc::InjectedAdc;
20use {critical_section, defmt_rtt as _, panic_probe as _}; 21use {critical_section, defmt_rtt as _, panic_probe as _};
21 22
22static ADC1_HANDLE: CriticalSectionMutex<RefCell<Option<Adc<'static, ADC1>>>> = 23static 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]
137unsafe fn ADC1_2() { 139unsafe 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 });