diff options
| -rw-r--r-- | embassy-stm32/src/adc/g4.rs | 251 | ||||
| -rw-r--r-- | embassy-stm32/src/adc/injected.rs | 28 | ||||
| -rw-r--r-- | examples/stm32g4/.cargo/config.toml | 20 | ||||
| -rw-r--r-- | examples/stm32g4/Cargo.toml | 2 | ||||
| -rw-r--r-- | examples/stm32g4/src/bin/adc_injected_and_regular.rs | 17 |
5 files changed, 191 insertions, 127 deletions
diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 5a36b0999..3767820cf 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs | |||
| @@ -131,7 +131,8 @@ impl Prescaler { | |||
| 131 | } | 131 | } |
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | // Trigger source for ADC conversions | 134 | // Trigger source for ADC conversions¨ |
| 135 | #[derive(Copy, Clone)] | ||
| 135 | pub struct ConversionTrigger { | 136 | pub struct ConversionTrigger { |
| 136 | // See Table 166 and 167 in RM0440 Rev 9 for ADC1/2 External triggers | 137 | // See Table 166 and 167 in RM0440 Rev 9 for ADC1/2 External triggers |
| 137 | // Note that Injected and Regular channels uses different mappings | 138 | // Note that Injected and Regular channels uses different mappings |
| @@ -140,6 +141,7 @@ pub struct ConversionTrigger { | |||
| 140 | } | 141 | } |
| 141 | 142 | ||
| 142 | // Conversion mode for regular ADC channels | 143 | // Conversion mode for regular ADC channels |
| 144 | #[derive(Copy, Clone)] | ||
| 143 | pub enum RegularConversionMode { | 145 | pub enum RegularConversionMode { |
| 144 | // Samples as fast as possible | 146 | // Samples as fast as possible |
| 145 | Continuous, | 147 | Continuous, |
| @@ -384,25 +386,23 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 384 | self.read_channel(channel) | 386 | self.read_channel(channel) |
| 385 | } | 387 | } |
| 386 | 388 | ||
| 389 | /// Start regular adc conversion | ||
| 387 | pub(super) fn start() { | 390 | pub(super) fn start() { |
| 388 | // Start adc conversion | ||
| 389 | T::regs().cr().modify(|reg| { | 391 | T::regs().cr().modify(|reg| { |
| 390 | reg.set_adstart(true); | 392 | reg.set_adstart(true); |
| 391 | }); | 393 | }); |
| 392 | } | 394 | } |
| 393 | 395 | ||
| 396 | /// Stop regular conversions | ||
| 394 | pub(super) fn stop() { | 397 | pub(super) fn stop() { |
| 395 | // Stop adc conversion | 398 | Self::stop_regular_conversions(); |
| 396 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 397 | T::regs().cr().modify(|reg| { | ||
| 398 | reg.set_adstp(Adstp::STOP); | ||
| 399 | }); | ||
| 400 | while T::regs().cr().read().adstart() {} | ||
| 401 | } | ||
| 402 | } | 399 | } |
| 403 | 400 | ||
| 401 | /// Teardown method for stopping regular ADC conversions | ||
| 404 | pub(super) fn teardown_adc() { | 402 | pub(super) fn teardown_adc() { |
| 405 | //disable dma control | 403 | Self::stop_regular_conversions(); |
| 404 | |||
| 405 | // Disable dma control | ||
| 406 | T::regs().cfgr().modify(|reg| { | 406 | T::regs().cfgr().modify(|reg| { |
| 407 | reg.set_dmaen(Dmaen::DISABLE); | 407 | reg.set_dmaen(Dmaen::DISABLE); |
| 408 | }); | 408 | }); |
| @@ -434,7 +434,8 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 434 | /// defmt::info!("measurements: {}", measurements); | 434 | /// defmt::info!("measurements: {}", measurements); |
| 435 | /// ``` | 435 | /// ``` |
| 436 | /// | 436 | /// |
| 437 | /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. | 437 | /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use |
| 438 | /// `into_ring_buffered`, `into_ring_buffered_and_injected` | ||
| 438 | pub async fn read( | 439 | pub async fn read( |
| 439 | &mut self, | 440 | &mut self, |
| 440 | rx_dma: Peri<'_, impl RxDma<T>>, | 441 | rx_dma: Peri<'_, impl RxDma<T>>, |
| @@ -452,7 +453,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 452 | ); | 453 | ); |
| 453 | 454 | ||
| 454 | // Ensure no conversions are ongoing and ADC is enabled. | 455 | // Ensure no conversions are ongoing and ADC is enabled. |
| 455 | Self::cancel_conversions(); | 456 | Self::stop_regular_conversions(); |
| 456 | self.enable(); | 457 | self.enable(); |
| 457 | 458 | ||
| 458 | // Set sequence length | 459 | // Set sequence length |
| @@ -520,7 +521,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 520 | transfer.await; | 521 | transfer.await; |
| 521 | 522 | ||
| 522 | // Ensure conversions are finished. | 523 | // Ensure conversions are finished. |
| 523 | Self::cancel_conversions(); | 524 | Self::stop_regular_conversions(); |
| 524 | 525 | ||
| 525 | // Reset configuration. | 526 | // Reset configuration. |
| 526 | T::regs().cfgr().modify(|reg| { | 527 | T::regs().cfgr().modify(|reg| { |
| @@ -529,7 +530,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 529 | } | 530 | } |
| 530 | 531 | ||
| 531 | /// Set external trigger for regular conversion sequence | 532 | /// Set external trigger for regular conversion sequence |
| 532 | pub fn set_regular_conversion_trigger(&mut self, trigger: ConversionTrigger) { | 533 | fn set_regular_conversion_trigger(&mut self, trigger: ConversionTrigger) { |
| 533 | T::regs().cfgr().modify(|r| { | 534 | T::regs().cfgr().modify(|r| { |
| 534 | r.set_extsel(trigger.channel); | 535 | r.set_extsel(trigger.channel); |
| 535 | r.set_exten(trigger.edge); | 536 | r.set_exten(trigger.edge); |
| @@ -547,21 +548,30 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 547 | 548 | ||
| 548 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. | 549 | /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. |
| 549 | /// | 550 | /// |
| 550 | /// The `dma_buf` should be large enough to prevent DMA buffer overrun. | 551 | /// Use the [`read`] method to retrieve measurements from the DMA ring buffer. The read buffer |
| 551 | /// The length of the `dma_buf` should be a multiple of the ADC channel count. | 552 | /// should be exactly half the size of `dma_buf`. When using triggered mode, it is recommended |
| 552 | /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. | 553 | /// to configure `dma_buf` as a double buffer so that one half can be read while the other half |
| 554 | /// is being filled by the DMA, preventing data loss. The trigger period of the ADC effectively | ||
| 555 | /// defines the period at which the buffer should be read. | ||
| 556 | /// | ||
| 557 | /// If continous conversion mode is selected, the provided `dma_buf` must be large enough to prevent | ||
| 558 | /// DMA buffer overruns. Its length should be a multiple of the number of ADC channels being measured. | ||
| 559 | /// For example, if 3 channels are measured and you want to store 40 samples per channel, | ||
| 560 | /// the buffer length should be `3 * 40 = 120`. | ||
| 553 | /// | 561 | /// |
| 554 | /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer | 562 | /// # Parameters |
| 555 | /// should be exactly half of the `dma_buf` length. It is critical to call `read` frequently to | 563 | /// - `dma`: The DMA peripheral used to transfer ADC data into the buffer. |
| 556 | /// prevent DMA buffer overrun. Or configure a software trigger for the regular ADC conversions | 564 | /// - `dma_buf`: The buffer where DMA stores ADC samples. |
| 557 | /// at a fixed interval. | 565 | /// - `regular_sequence`: Sequence of channels and sample times for regular ADC conversions. |
| 566 | /// - `regular_conversion_mode`: Mode for regular conversions (continuous or triggered). | ||
| 558 | /// | 567 | /// |
| 559 | /// [`read`]: #method.read | 568 | /// # Returns |
| 569 | /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling. | ||
| 560 | pub fn into_ring_buffered<'a>( | 570 | pub fn into_ring_buffered<'a>( |
| 561 | mut self, | 571 | mut self, |
| 562 | dma: Peri<'a, impl RxDma<T>>, | 572 | dma: Peri<'a, impl RxDma<T>>, |
| 563 | dma_buf: &'a mut [u16], | 573 | dma_buf: &'a mut [u16], |
| 564 | sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<T>, SampleTime)>, | 574 | sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, |
| 565 | mode: RegularConversionMode, | 575 | mode: RegularConversionMode, |
| 566 | ) -> RingBufferedAdc<'a, T> { | 576 | ) -> RingBufferedAdc<'a, T> { |
| 567 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); | 577 | assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); |
| @@ -571,7 +581,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 571 | "Asynchronous read sequence cannot be more than 16 in length" | 581 | "Asynchronous read sequence cannot be more than 16 in length" |
| 572 | ); | 582 | ); |
| 573 | // reset conversions and enable the adc | 583 | // reset conversions and enable the adc |
| 574 | Self::cancel_conversions(); | 584 | Self::stop_regular_conversions(); |
| 575 | self.enable(); | 585 | self.enable(); |
| 576 | 586 | ||
| 577 | //adc side setup | 587 | //adc side setup |
| @@ -582,8 +592,8 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 582 | }); | 592 | }); |
| 583 | 593 | ||
| 584 | // Configure channels and ranks | 594 | // Configure channels and ranks |
| 585 | for (_i, (channel, sample_time)) in sequence.enumerate() { | 595 | for (_i, (mut channel, sample_time)) in sequence.enumerate() { |
| 586 | Self::configure_channel(channel, sample_time); | 596 | Self::configure_channel(&mut channel, sample_time); |
| 587 | 597 | ||
| 588 | match _i { | 598 | match _i { |
| 589 | 0..=3 => { | 599 | 0..=3 => { |
| @@ -640,79 +650,107 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 640 | RingBufferedAdc::new(dma, dma_buf) | 650 | RingBufferedAdc::new(dma, dma_buf) |
| 641 | } | 651 | } |
| 642 | 652 | ||
| 643 | /// Configure a sequence of injected channels | 653 | /// Configures the ADC for injected conversions. |
| 644 | pub fn configure_injected_sequence<'a>( | 654 | /// |
| 655 | /// Injected conversions are separate from the regular conversion sequence and are typically | ||
| 656 | /// triggered by software or an external event. This method sets up a fixed-length sequence of | ||
| 657 | /// injected channels with specified sample times, the trigger source, and whether the end-of-sequence | ||
| 658 | /// interrupt should be enabled. | ||
| 659 | /// | ||
| 660 | /// # Parameters | ||
| 661 | /// - `sequence`: An array of tuples containing the ADC channels and their sample times. The length | ||
| 662 | /// `N` determines the number of injected ranks to configure (maximum 4 for STM32). | ||
| 663 | /// - `trigger`: The trigger source that starts the injected conversion sequence. | ||
| 664 | /// - `interrupt`: If `true`, enables the end-of-sequence (JEOS) interrupt for injected conversions. | ||
| 665 | /// | ||
| 666 | /// # Returns | ||
| 667 | /// An `InjectedAdc<T, N>` instance that represents the configured injected sequence. The returned | ||
| 668 | /// type encodes the sequence length `N` in its type, ensuring that reads return exactly `N` samples. | ||
| 669 | /// | ||
| 670 | /// # Panics | ||
| 671 | /// This function will panic if: | ||
| 672 | /// - `sequence` is empty. | ||
| 673 | /// - `sequence` length exceeds the maximum number of injected ranks (`NR_INJECTED_RANKS`). | ||
| 674 | /// | ||
| 675 | /// # Notes | ||
| 676 | /// - Injected conversions can run independently of regular ADC conversions. | ||
| 677 | /// - The order of channels in `sequence` determines the rank order in the injected sequence. | ||
| 678 | /// - Accessing samples beyond `N` will result in a panic; use the returned type | ||
| 679 | /// `InjectedAdc<T, N>` to enforce bounds at compile time. | ||
| 680 | pub fn setup_injected_conversions<'a, const N: usize>( | ||
| 645 | mut self, | 681 | mut self, |
| 646 | sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<T>, SampleTime)>, | 682 | sequence: [(AnyAdcChannel<T>, SampleTime); N], |
| 647 | trigger: ConversionTrigger, | 683 | trigger: ConversionTrigger, |
| 648 | ) -> InjectedAdc<T> { | 684 | interrupt: bool, |
| 649 | assert!(sequence.len() != 0, "Read sequence cannot be empty"); | 685 | ) -> InjectedAdc<T, N> { |
| 686 | assert!(N != 0, "Read sequence cannot be empty"); | ||
| 650 | assert!( | 687 | assert!( |
| 651 | sequence.len() <= NR_INJECTED_RANKS, | 688 | N <= NR_INJECTED_RANKS, |
| 652 | "Read sequence cannot be more than 4 in length" | 689 | "Read sequence cannot be more than {} in length", |
| 690 | NR_INJECTED_RANKS | ||
| 653 | ); | 691 | ); |
| 654 | 692 | ||
| 655 | // Ensure no conversions are ongoing and ADC is enabled. | 693 | Self::stop_regular_conversions(); |
| 656 | Self::cancel_conversions(); | ||
| 657 | self.enable(); | 694 | self.enable(); |
| 658 | 695 | ||
| 659 | // Set sequence length | 696 | T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); |
| 660 | T::regs().jsqr().modify(|w| { | ||
| 661 | w.set_jl(sequence.len() as u8 - 1); | ||
| 662 | }); | ||
| 663 | 697 | ||
| 664 | // Configure channels and ranks | 698 | for (n, (mut channel, sample_time)) in sequence.into_iter().enumerate() { |
| 665 | for (n, (channel, sample_time)) in sequence.enumerate() { | 699 | Self::configure_channel(&mut channel, sample_time); |
| 666 | Self::configure_channel(channel, sample_time); | ||
| 667 | 700 | ||
| 668 | match n { | 701 | let idx = match n { |
| 669 | 0..=3 => { | 702 | 0..=3 => n, |
| 670 | T::regs().jsqr().modify(|w| { | 703 | 4..=8 => n - 4, |
| 671 | w.set_jsq(n, channel.channel()); | 704 | 9..=13 => n - 9, |
| 672 | }); | 705 | 14..=15 => n - 14, |
| 673 | } | ||
| 674 | 4..=8 => { | ||
| 675 | T::regs().jsqr().modify(|w| { | ||
| 676 | w.set_jsq(n - 4, channel.channel()); | ||
| 677 | }); | ||
| 678 | } | ||
| 679 | 9..=13 => { | ||
| 680 | T::regs().jsqr().modify(|w| { | ||
| 681 | w.set_jsq(n - 9, channel.channel()); | ||
| 682 | }); | ||
| 683 | } | ||
| 684 | 14..=15 => { | ||
| 685 | T::regs().jsqr().modify(|w| { | ||
| 686 | w.set_jsq(n - 14, channel.channel()); | ||
| 687 | }); | ||
| 688 | } | ||
| 689 | _ => unreachable!(), | 706 | _ => unreachable!(), |
| 690 | } | 707 | }; |
| 708 | |||
| 709 | T::regs().jsqr().modify(|w| w.set_jsq(idx, channel.channel())); | ||
| 691 | } | 710 | } |
| 692 | 711 | ||
| 693 | T::regs().cfgr().modify(|reg| { | 712 | T::regs().cfgr().modify(|reg| reg.set_jdiscen(false)); |
| 694 | reg.set_jdiscen(false); // Will convert all channels for each trigger | ||
| 695 | }); | ||
| 696 | 713 | ||
| 697 | self.set_injected_conversion_trigger(trigger); | 714 | self.set_injected_conversion_trigger(trigger); |
| 698 | self.enable_injected_eos_interrupt(true); | 715 | self.enable_injected_eos_interrupt(interrupt); |
| 699 | 716 | Self::start_injected_conversions(); | |
| 700 | self.start_injected_conversion(); | ||
| 701 | |||
| 702 | mem::forget(self); | ||
| 703 | 717 | ||
| 704 | InjectedAdc::new() | 718 | InjectedAdc::new(sequence) // InjectedAdc<'a, T, N> now borrows the channels |
| 705 | } | 719 | } |
| 706 | 720 | ||
| 707 | pub fn into_ring_buffered_and_injected<'a>( | 721 | /// Configures ADC for both regular conversions with a ring-buffered DMA and injected conversions. |
| 722 | /// | ||
| 723 | /// # Parameters | ||
| 724 | /// - `dma`: The DMA peripheral to use for the ring-buffered ADC transfers. | ||
| 725 | /// - `dma_buf`: The buffer to store DMA-transferred samples for regular conversions. | ||
| 726 | /// - `regular_sequence`: The sequence of channels and their sample times for regular conversions. | ||
| 727 | /// - `regular_conversion_mode`: The mode for regular conversions (e.g., continuous or triggered). | ||
| 728 | /// - `injected_sequence`: An array of channels and sample times for injected conversions (length `N`). | ||
| 729 | /// - `injected_trigger`: The trigger source for injected conversions. | ||
| 730 | /// - `injected_interrupt`: Whether to enable the end-of-sequence interrupt for injected conversions. | ||
| 731 | /// | ||
| 732 | /// Injected conversions are typically used with interrupts. If ADC1 and ADC2 are used in dual mode, | ||
| 733 | /// it is recommended to enable interrupts only for the ADC whose sequence takes the longest to complete. | ||
| 734 | /// | ||
| 735 | /// # Returns | ||
| 736 | /// A tuple containing: | ||
| 737 | /// 1. `RingBufferedAdc<'a, T>` — the configured ADC for regular conversions using DMA. | ||
| 738 | /// 2. `InjectedAdc<T, N>` — the configured ADC for injected conversions. | ||
| 739 | /// | ||
| 740 | /// # Safety | ||
| 741 | /// This function is `unsafe` because it clones the ADC peripheral handle unchecked. Both the | ||
| 742 | /// `RingBufferedAdc` and `InjectedAdc` take ownership of the handle and drop it independently. | ||
| 743 | /// Ensure no other code concurrently accesses the same ADC instance in a conflicting way. | ||
| 744 | pub fn into_ring_buffered_and_injected<'a, const N: usize>( | ||
| 708 | self, | 745 | self, |
| 709 | dma: Peri<'a, impl RxDma<T>>, | 746 | dma: Peri<'a, impl RxDma<T>>, |
| 710 | dma_buf: &'a mut [u16], | 747 | dma_buf: &'a mut [u16], |
| 711 | regular_sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<T>, SampleTime)>, | 748 | regular_sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>, |
| 712 | regular_conversion_mode: RegularConversionMode, | 749 | regular_conversion_mode: RegularConversionMode, |
| 713 | injected_sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<T>, SampleTime)>, | 750 | injected_sequence: [(AnyAdcChannel<T>, SampleTime); N], |
| 714 | injected_trigger: ConversionTrigger, | 751 | injected_trigger: ConversionTrigger, |
| 715 | ) -> (RingBufferedAdc<'a, T>, InjectedAdc<T>) { | 752 | injected_interrupt: bool, |
| 753 | ) -> (RingBufferedAdc<'a, T>, InjectedAdc<T, N>) { | ||
| 716 | unsafe { | 754 | unsafe { |
| 717 | ( | 755 | ( |
| 718 | Self { | 756 | Self { |
| @@ -724,13 +762,25 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 724 | adc: self.adc.clone_unchecked(), | 762 | adc: self.adc.clone_unchecked(), |
| 725 | sample_time: self.sample_time, | 763 | sample_time: self.sample_time, |
| 726 | } | 764 | } |
| 727 | .configure_injected_sequence(injected_sequence, injected_trigger), | 765 | .setup_injected_conversions(injected_sequence, injected_trigger, injected_interrupt), |
| 728 | ) | 766 | ) |
| 729 | } | 767 | } |
| 730 | } | 768 | } |
| 731 | 769 | ||
| 770 | /// Stop injected conversions | ||
| 771 | pub(super) fn stop_injected_conversions() { | ||
| 772 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | ||
| 773 | T::regs().cr().modify(|reg| { | ||
| 774 | reg.set_jadstp(Adstp::STOP); | ||
| 775 | }); | ||
| 776 | // The software must poll JADSTART until the bit is reset before assuming the | ||
| 777 | // ADC is completely stopped | ||
| 778 | while T::regs().cr().read().jadstart() {} | ||
| 779 | } | ||
| 780 | } | ||
| 781 | |||
| 732 | /// Start injected ADC conversion | 782 | /// Start injected ADC conversion |
| 733 | pub fn start_injected_conversion(&mut self) { | 783 | pub(super) fn start_injected_conversions() { |
| 734 | T::regs().cr().modify(|reg| { | 784 | T::regs().cr().modify(|reg| { |
| 735 | reg.set_jadstart(true); | 785 | reg.set_jadstart(true); |
| 736 | }); | 786 | }); |
| @@ -738,7 +788,7 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 738 | 788 | ||
| 739 | /// Set external trigger for injected conversion sequence | 789 | /// Set external trigger for injected conversion sequence |
| 740 | /// Possible trigger values are seen in Table 167 in RM0440 Rev 9 | 790 | /// Possible trigger values are seen in Table 167 in RM0440 Rev 9 |
| 741 | pub fn set_injected_conversion_trigger(&mut self, trigger: ConversionTrigger) { | 791 | fn set_injected_conversion_trigger(&mut self, trigger: ConversionTrigger) { |
| 742 | T::regs().jsqr().modify(|r| { | 792 | T::regs().jsqr().modify(|r| { |
| 743 | r.set_jextsel(trigger.channel); | 793 | r.set_jextsel(trigger.channel); |
| 744 | r.set_jexten(trigger.edge); | 794 | r.set_jexten(trigger.edge); |
| @@ -746,23 +796,10 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 746 | } | 796 | } |
| 747 | 797 | ||
| 748 | /// Enable end of injected sequence interrupt | 798 | /// Enable end of injected sequence interrupt |
| 749 | pub fn enable_injected_eos_interrupt(&mut self, enable: bool) { | 799 | fn enable_injected_eos_interrupt(&mut self, enable: bool) { |
| 750 | T::regs().ier().modify(|r| r.set_jeosie(enable)); | 800 | T::regs().ier().modify(|r| r.set_jeosie(enable)); |
| 751 | } | 801 | } |
| 752 | 802 | ||
| 753 | /// Read sampled data from all injected ADC injected ranks | ||
| 754 | /// Clear the JEOS flag to allow a new injected sequence | ||
| 755 | pub(super) fn read_injected_samples() -> [u16; NR_INJECTED_RANKS] { | ||
| 756 | let mut data = [0u16; NR_INJECTED_RANKS]; | ||
| 757 | for i in 0..NR_INJECTED_RANKS { | ||
| 758 | data[i] = T::regs().jdr(i).read().jdata(); | ||
| 759 | } | ||
| 760 | |||
| 761 | // Clear JEOS by writing 1 | ||
| 762 | T::regs().isr().modify(|r| r.set_jeos(true)); | ||
| 763 | data | ||
| 764 | } | ||
| 765 | |||
| 766 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { | 803 | fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { |
| 767 | // Configure channel | 804 | // Configure channel |
| 768 | Self::set_channel_sample_time(channel.channel(), sample_time); | 805 | Self::set_channel_sample_time(channel.channel(), sample_time); |
| @@ -795,25 +832,31 @@ impl<'d, T: Instance> Adc<'d, T> { | |||
| 795 | } | 832 | } |
| 796 | } | 833 | } |
| 797 | 834 | ||
| 798 | fn cancel_conversions() { | 835 | // Stop regular conversions |
| 799 | // Cancel regular conversions | 836 | fn stop_regular_conversions() { |
| 800 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | 837 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { |
| 801 | T::regs().cr().modify(|reg| { | 838 | T::regs().cr().modify(|reg| { |
| 802 | reg.set_adstp(Adstp::STOP); | 839 | reg.set_adstp(Adstp::STOP); |
| 803 | }); | 840 | }); |
| 841 | // The software must poll ADSTART until the bit is reset before assuming the | ||
| 842 | // ADC is completely stopped | ||
| 804 | while T::regs().cr().read().adstart() {} | 843 | while T::regs().cr().read().adstart() {} |
| 805 | } | 844 | } |
| 806 | } | 845 | } |
| 846 | } | ||
| 807 | 847 | ||
| 808 | #[allow(dead_code)] | 848 | impl<T: Instance, const N: usize> InjectedAdc<T, N> { |
| 809 | pub(super) fn stop_injected_conversions() { | 849 | /// Read sampled data from all injected ADC injected ranks |
| 810 | // Cancel injected conversions | 850 | /// Clear the JEOS flag to allow a new injected sequence |
| 811 | if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { | 851 | pub(super) fn read_injected_data() -> [u16; N] { |
| 812 | T::regs().cr().modify(|reg| { | 852 | let mut data = [0u16; N]; |
| 813 | reg.set_jadstp(Adstp::STOP); | 853 | for i in 0..N { |
| 814 | }); | 854 | data[i] = T::regs().jdr(i).read().jdata(); |
| 815 | while T::regs().cr().read().jadstart() {} | ||
| 816 | } | 855 | } |
| 856 | |||
| 857 | // Clear JEOS by writing 1 | ||
| 858 | T::regs().isr().modify(|r| r.set_jeos(true)); | ||
| 859 | data | ||
| 817 | } | 860 | } |
| 818 | } | 861 | } |
| 819 | 862 | ||
diff --git a/embassy-stm32/src/adc/injected.rs b/embassy-stm32/src/adc/injected.rs index 0efc47759..30a7cacc5 100644 --- a/embassy-stm32/src/adc/injected.rs +++ b/embassy-stm32/src/adc/injected.rs | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | use core::marker::PhantomData; | 1 | use core::marker::PhantomData; |
| 2 | use core::sync::atomic::{Ordering, compiler_fence}; | 2 | use core::sync::atomic::{Ordering, compiler_fence}; |
| 3 | 3 | ||
| 4 | use super::{AnyAdcChannel, SampleTime}; | ||
| 4 | #[allow(unused_imports)] | 5 | #[allow(unused_imports)] |
| 5 | use embassy_hal_internal::Peri; | 6 | use embassy_hal_internal::Peri; |
| 6 | 7 | ||
| @@ -8,29 +9,36 @@ use crate::adc::Adc; | |||
| 8 | #[allow(unused_imports)] | 9 | #[allow(unused_imports)] |
| 9 | use crate::adc::Instance; | 10 | use crate::adc::Instance; |
| 10 | 11 | ||
| 11 | const NR_INJECTED_RANKS: usize = 4; | 12 | /// Injected ADC sequence with owned channels. |
| 12 | 13 | pub struct InjectedAdc<T: Instance, const N: usize> { | |
| 13 | pub struct InjectedAdc<T: Instance> { | 14 | _channels: [(AnyAdcChannel<T>, SampleTime); N], |
| 14 | _phantom: PhantomData<T>, | 15 | _phantom: PhantomData<T>, |
| 15 | } | 16 | } |
| 16 | 17 | ||
| 17 | impl<T: Instance> InjectedAdc<T> { | 18 | impl<T: Instance, const N: usize> InjectedAdc<T, N> { |
| 18 | pub(crate) fn new() -> Self { | 19 | pub(crate) fn new(channels: [(AnyAdcChannel<T>, SampleTime); N]) -> Self { |
| 19 | Self { | 20 | Self { |
| 21 | _channels: channels, | ||
| 20 | _phantom: PhantomData, | 22 | _phantom: PhantomData, |
| 21 | } | 23 | } |
| 22 | } | 24 | } |
| 23 | 25 | ||
| 24 | pub fn read_injected_samples(&mut self) -> [u16; NR_INJECTED_RANKS] { | 26 | pub fn stop_injected_conversions(&mut self) { |
| 25 | Adc::<T>::read_injected_samples() | 27 | Adc::<T>::stop_injected_conversions() |
| 26 | } | 28 | } |
| 27 | } | ||
| 28 | 29 | ||
| 30 | pub fn start_injected_conversions(&mut self) { | ||
| 31 | Adc::<T>::start_injected_conversions() | ||
| 32 | } | ||
| 29 | 33 | ||
| 30 | impl<T: Instance> Drop for InjectedAdc<T> { | 34 | pub fn read_injected_samples(&mut self) -> [u16; N] { |
| 35 | InjectedAdc::<T, N>::read_injected_data() | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | impl<T: Instance, const N: usize> Drop for InjectedAdc<T, N> { | ||
| 31 | fn drop(&mut self) { | 40 | fn drop(&mut self) { |
| 32 | Adc::<T>::teardown_adc(); | 41 | Adc::<T>::teardown_adc(); |
| 33 | |||
| 34 | compiler_fence(Ordering::SeqCst); | 42 | compiler_fence(Ordering::SeqCst); |
| 35 | } | 43 | } |
| 36 | } | 44 | } |
diff --git a/examples/stm32g4/.cargo/config.toml b/examples/stm32g4/.cargo/config.toml index de3e5718e..52b5a7bc8 100644 --- a/examples/stm32g4/.cargo/config.toml +++ b/examples/stm32g4/.cargo/config.toml | |||
| @@ -1,9 +1,21 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] |
| 2 | # replace STM32G071C8Rx with your chip as listed in `probe-rs chip list` | 2 | # Change this runner as required for your MCU. |
| 3 | runner = "probe-rs run --chip STM32G484VETx" | 3 | runner = [ |
| 4 | "probe-rs", | ||
| 5 | "run", | ||
| 6 | "--chip", | ||
| 7 | "STM32G431VBTx", | ||
| 8 | "--speed", | ||
| 9 | "5000", | ||
| 10 | "--preverify", | ||
| 11 | "--log-format", | ||
| 12 | "{t} [{L}] {s}", | ||
| 13 | ] | ||
| 14 | |||
| 4 | 15 | ||
| 5 | [build] | 16 | [build] |
| 6 | target = "thumbv7em-none-eabi" | 17 | target = "thumbv7em-none-eabihf" |
| 7 | 18 | ||
| 8 | [env] | 19 | [env] |
| 9 | DEFMT_LOG = "trace" \ No newline at end of file | 20 | DEFMT_LOG = "info" |
| 21 | DEFMT_RTT_BUFFER_SIZE = "4096" | ||
diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 8bbeb594c..9089ec0d5 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml | |||
| @@ -7,7 +7,7 @@ publish = false | |||
| 7 | 7 | ||
| 8 | [dependencies] | 8 | [dependencies] |
| 9 | # Change stm32g491re to your chip name, if necessary. | 9 | # Change stm32g491re to your chip name, if necessary. |
| 10 | embassy-stm32 = { path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } | 10 | embassy-stm32 = { path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g431vb", "memory-x", "unstable-pac", "exti"] } |
| 11 | embassy-sync = { path = "../../embassy-sync", features = ["defmt"] } | 11 | embassy-sync = { path = "../../embassy-sync", features = ["defmt"] } |
| 12 | embassy-executor = { path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } | 12 | embassy-executor = { path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } |
| 13 | embassy-time = { path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | 13 | embassy-time = { path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } |
diff --git a/examples/stm32g4/src/bin/adc_injected_and_regular.rs b/examples/stm32g4/src/bin/adc_injected_and_regular.rs index d0c577b4b..c929ca3bf 100644 --- a/examples/stm32g4/src/bin/adc_injected_and_regular.rs +++ b/examples/stm32g4/src/bin/adc_injected_and_regular.rs | |||
| @@ -18,9 +18,9 @@ use embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, Mms2}; | |||
| 18 | use embassy_stm32::timer::low_level::CountingMode; | 18 | use embassy_stm32::timer::low_level::CountingMode; |
| 19 | use embassy_stm32::{Config, interrupt}; | 19 | use embassy_stm32::{Config, interrupt}; |
| 20 | use embassy_sync::blocking_mutex::CriticalSectionMutex; | 20 | use embassy_sync::blocking_mutex::CriticalSectionMutex; |
| 21 | use {critical_section, defmt_rtt as _, panic_probe as _}; | 21 | use {defmt_rtt as _, panic_probe as _}; |
| 22 | 22 | ||
| 23 | static ADC1_HANDLE: CriticalSectionMutex<RefCell<Option<InjectedAdc<ADC1>>>> = | 23 | static ADC1_HANDLE: CriticalSectionMutex<RefCell<Option<InjectedAdc<ADC1, 1>>>> = |
| 24 | CriticalSectionMutex::new(RefCell::new(None)); | 24 | CriticalSectionMutex::new(RefCell::new(None)); |
| 25 | 25 | ||
| 26 | /// 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 |
| @@ -78,17 +78,17 @@ async fn main(_spawner: embassy_executor::Spawner) { | |||
| 78 | // Configure regular conversions with DMA | 78 | // Configure regular conversions with DMA |
| 79 | let adc1 = Adc::new(p.ADC1); | 79 | let adc1 = Adc::new(p.ADC1); |
| 80 | 80 | ||
| 81 | let mut vrefint_channel = adc1.enable_vrefint().degrade_adc(); | 81 | let vrefint_channel = adc1.enable_vrefint().degrade_adc(); |
| 82 | let mut pa0 = p.PC1.degrade_adc(); | 82 | let pa0 = p.PC1.degrade_adc(); |
| 83 | let regular_sequence = [ | 83 | let regular_sequence = [ |
| 84 | (&mut vrefint_channel, SampleTime::CYCLES247_5), | 84 | (vrefint_channel, SampleTime::CYCLES247_5), |
| 85 | (&mut pa0, SampleTime::CYCLES247_5), | 85 | (pa0, SampleTime::CYCLES247_5), |
| 86 | ] | 86 | ] |
| 87 | .into_iter(); | 87 | .into_iter(); |
| 88 | 88 | ||
| 89 | // Configurations of Injected ADC measurements | 89 | // Configurations of Injected ADC measurements |
| 90 | let mut pa2 = p.PA2.degrade_adc(); | 90 | let pa2 = p.PA2.degrade_adc(); |
| 91 | let injected_sequence = [(&mut pa2, SampleTime::CYCLES247_5)].into_iter(); | 91 | let injected_sequence = [(pa2, SampleTime::CYCLES247_5)]; |
| 92 | 92 | ||
| 93 | // Configure DMA for retrieving regular ADC measurements | 93 | // Configure DMA for retrieving regular ADC measurements |
| 94 | let dma1_ch1 = p.DMA1_CH1; | 94 | let dma1_ch1 = p.DMA1_CH1; |
| @@ -111,6 +111,7 @@ async fn main(_spawner: embassy_executor::Spawner) { | |||
| 111 | RegularConversionMode::Triggered(regular_trigger), | 111 | RegularConversionMode::Triggered(regular_trigger), |
| 112 | injected_sequence, | 112 | injected_sequence, |
| 113 | injected_trigger, | 113 | injected_trigger, |
| 114 | true, | ||
| 114 | ); | 115 | ); |
| 115 | 116 | ||
| 116 | // Store ADC globally to allow access from ADC interrupt | 117 | // Store ADC globally to allow access from ADC interrupt |
