aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/pages/embassy_in_the_wild.adoc2
-rw-r--r--embassy-stm32/CHANGELOG.md3
-rw-r--r--embassy-stm32/build.rs21
-rw-r--r--embassy-stm32/src/adc/g4.rs359
-rw-r--r--embassy-stm32/src/adc/injected.rs44
-rw-r--r--embassy-stm32/src/adc/ringbuffered.rs15
-rw-r--r--embassy-stm32/src/backup_sram.rs28
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/rcc/bd.rs26
-rw-r--r--embassy-stm32/src/rcc/mod.rs3
-rw-r--r--embassy-stm32/src/timer/complementary_pwm.rs12
-rw-r--r--embassy-stm32/src/timer/low_level.rs10
-rw-r--r--examples/stm32g4/Cargo.toml13
-rw-r--r--examples/stm32g4/src/bin/adc_dma.rs4
-rw-r--r--examples/stm32g4/src/bin/adc_injected_and_regular.rs154
-rw-r--r--examples/stm32h5/src/bin/backup_sram.rs31
16 files changed, 703 insertions, 24 deletions
diff --git a/docs/pages/embassy_in_the_wild.adoc b/docs/pages/embassy_in_the_wild.adoc
index b0a16fc4c..0792130eb 100644
--- a/docs/pages/embassy_in_the_wild.adoc
+++ b/docs/pages/embassy_in_the_wild.adoc
@@ -6,6 +6,8 @@ _newer entries at the top_
6 6
7* link:https://github.com/thataquarel/protovolt[ProtoV MINI: A USB-C mini lab power supply] 7* link:https://github.com/thataquarel/protovolt[ProtoV MINI: A USB-C mini lab power supply]
8** A dual-channel USB PD powered breadboard power supply based on the RP2040, running embedded graphics. Open-source schematics and firmware. 8** A dual-channel USB PD powered breadboard power supply based on the RP2040, running embedded graphics. Open-source schematics and firmware.
9* link:https://github.com/Dawson-HEP/opentrig/[Opentrig: A particle physics trigger and data acquisition system]
10** Digital event trigger with threshold, data acquisition system designed to interface with AIDA-2020 TLU systems, tested at the DESY II Test Beam Facility. Based on the RP2040, and Embassy's async event handling.
9* link:https://github.com/1-rafael-1/air-quality-monitor[Air Quality Monitor] 11* link:https://github.com/1-rafael-1/air-quality-monitor[Air Quality Monitor]
10** Air Quality Monitor based on rp2350 board, ens160 and aht21 sensors and ssd1306 display. Code and 3D printable enclosure included. 12** Air Quality Monitor based on rp2350 board, ens160 and aht21 sensors and ssd1306 display. Code and 3D printable enclosure included.
11* link:https://github.com/CarlKCarlK/clock[Embassy Clock: Layered, modular bare-metal clock with emulation] 13* link:https://github.com/CarlKCarlK/clock[Embassy Clock: Layered, modular bare-metal clock with emulation]
diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md
index df1d909b7..91785143f 100644
--- a/embassy-stm32/CHANGELOG.md
+++ b/embassy-stm32/CHANGELOG.md
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7 7
8## Unreleased - ReleaseDate 8## Unreleased - ReleaseDate
9 9
10- feat: Add support for injected ADC measurements for g4 ([#4840](https://github.com/embassy-rs/embassy/pull/4840))
11- feat: Implement into_ring_buffered for g4 ([#4840](https://github.com/embassy-rs/embassy/pull/4840))
10- feat: stm32/hrtim add new_chx_with_config to provide pin configuration 12- feat: stm32/hrtim add new_chx_with_config to provide pin configuration
11- fix flash erase on L4 & L5 13- fix flash erase on L4 & L5
12- fix: Fixed STM32H5 builds requiring time feature 14- fix: Fixed STM32H5 builds requiring time feature
@@ -37,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
37- feat: timer: add ability to set master mode 39- feat: timer: add ability to set master mode
38- fix: sdmmc: don't wait for DBCKEND flag on sdmmc_v2 devices as it never fires (Fixes #4723) 40- fix: sdmmc: don't wait for DBCKEND flag on sdmmc_v2 devices as it never fires (Fixes #4723)
39- fix: usart: fix race condition in ringbuffered usart 41- fix: usart: fix race condition in ringbuffered usart
42- feat: Add backup_sram::init() for H5 devices to access BKPSRAM
40- feat: Add I2C MultiMaster (Slave) support for I2C v1 43- feat: Add I2C MultiMaster (Slave) support for I2C v1
41- feat: stm32/fdcan: add ability to control automatic recovery from bus off ([#4821](https://github.com/embassy-rs/embassy/pull/4821)) 44- feat: stm32/fdcan: add ability to control automatic recovery from bus off ([#4821](https://github.com/embassy-rs/embassy/pull/4821))
42- low-power: update rtc api to allow reconfig 45- low-power: update rtc api to allow reconfig
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index eea1acf68..2800c73ac 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -114,6 +114,8 @@ fn main() {
114 } 114 }
115 }; 115 };
116 116
117 let has_bkpsram = memory.iter().any(|m| m.name == "BKPSRAM");
118
117 // ======== 119 // ========
118 // Generate singletons 120 // Generate singletons
119 121
@@ -124,6 +126,13 @@ fn main() {
124 singletons.push(p.name.to_string()); 126 singletons.push(p.name.to_string());
125 } 127 }
126 128
129 cfgs.declare("backup_sram");
130
131 if has_bkpsram {
132 singletons.push("BKPSRAM".to_string());
133 cfgs.enable("backup_sram")
134 }
135
127 // generate one singleton per peripheral (with many exceptions...) 136 // generate one singleton per peripheral (with many exceptions...)
128 for p in METADATA.peripherals { 137 for p in METADATA.peripherals {
129 if let Some(r) = &p.registers { 138 if let Some(r) = &p.registers {
@@ -1985,6 +1994,18 @@ fn main() {
1985 )); 1994 ));
1986 1995
1987 // ======== 1996 // ========
1997 // Generate backup sram constants
1998 if let Some(m) = memory.iter().find(|m| m.name == "BKPSRAM") {
1999 let bkpsram_base = m.address as usize;
2000 let bkpsram_size = m.size as usize;
2001
2002 g.extend(quote!(
2003 pub const BKPSRAM_BASE: usize = #bkpsram_base;
2004 pub const BKPSRAM_SIZE: usize = #bkpsram_size;
2005 ));
2006 }
2007
2008 // ========
1988 // Generate flash constants 2009 // Generate flash constants
1989 2010
1990 let flash_regions: Vec<&MemoryRegion> = memory 2011 let flash_regions: Vec<&MemoryRegion> = memory
diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs
index 5098aadd8..3767820cf 100644
--- a/embassy-stm32/src/adc/g4.rs
+++ b/embassy-stm32/src/adc/g4.rs
@@ -1,11 +1,14 @@
1use core::mem;
2
1#[allow(unused)] 3#[allow(unused)]
2#[cfg(stm32h7)] 4#[cfg(stm32h7)]
3use pac::adc::vals::{Adcaldif, Difsel, Exten}; 5use pac::adc::vals::{Adcaldif, Difsel, Exten};
4#[allow(unused)] 6#[allow(unused)]
5#[cfg(stm32g4)] 7#[cfg(stm32g4)]
6use pac::adc::vals::{Adcaldif, Difsel, Exten, Rovsm, Trovs}; 8pub use pac::adc::vals::{Adcaldif, Difsel, Exten, Rovsm, Trovs};
7use pac::adccommon::vals::Presc; 9pub use pac::adccommon::vals::Presc;
8use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; 10pub use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen};
11pub use stm32_metapac::adccommon::vals::Dual;
9 12
10use super::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, blocking_delay_us}; 13use super::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, blocking_delay_us};
11use crate::adc::SealedAdcChannel; 14use crate::adc::SealedAdcChannel;
@@ -13,11 +16,19 @@ use crate::dma::Transfer;
13use crate::time::Hertz; 16use crate::time::Hertz;
14use crate::{Peri, pac, rcc}; 17use crate::{Peri, pac, rcc};
15 18
19mod ringbuffered;
20pub use ringbuffered::RingBufferedAdc;
21
22mod injected;
23pub use injected::InjectedAdc;
24
16/// Default VREF voltage used for sample conversion to millivolts. 25/// Default VREF voltage used for sample conversion to millivolts.
17pub const VREF_DEFAULT_MV: u32 = 3300; 26pub const VREF_DEFAULT_MV: u32 = 3300;
18/// VREF voltage used for factory calibration of VREFINTCAL register. 27/// VREF voltage used for factory calibration of VREFINTCAL register.
19pub const VREF_CALIB_MV: u32 = 3300; 28pub const VREF_CALIB_MV: u32 = 3300;
20 29
30const NR_INJECTED_RANKS: usize = 4;
31
21/// Max single ADC operation clock frequency 32/// Max single ADC operation clock frequency
22#[cfg(stm32g4)] 33#[cfg(stm32g4)]
23const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); 34const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60);
@@ -120,6 +131,24 @@ impl Prescaler {
120 } 131 }
121} 132}
122 133
134// Trigger source for ADC conversions¨
135#[derive(Copy, Clone)]
136pub struct ConversionTrigger {
137 // See Table 166 and 167 in RM0440 Rev 9 for ADC1/2 External triggers
138 // Note that Injected and Regular channels uses different mappings
139 pub channel: u8,
140 pub edge: Exten,
141}
142
143// Conversion mode for regular ADC channels
144#[derive(Copy, Clone)]
145pub enum RegularConversionMode {
146 // Samples as fast as possible
147 Continuous,
148 // Sample at rate determined by external trigger
149 Triggered(ConversionTrigger),
150}
151
123impl<'d, T: Instance> Adc<'d, T> { 152impl<'d, T: Instance> Adc<'d, T> {
124 /// Create a new ADC driver. 153 /// Create a new ADC driver.
125 pub fn new(adc: Peri<'d, T>) -> Self { 154 pub fn new(adc: Peri<'d, T>) -> Self {
@@ -357,7 +386,29 @@ impl<'d, T: Instance> Adc<'d, T> {
357 self.read_channel(channel) 386 self.read_channel(channel)
358 } 387 }
359 388
360 /// Read one or multiple ADC channels using DMA. 389 /// Start regular adc conversion
390 pub(super) fn start() {
391 T::regs().cr().modify(|reg| {
392 reg.set_adstart(true);
393 });
394 }
395
396 /// Stop regular conversions
397 pub(super) fn stop() {
398 Self::stop_regular_conversions();
399 }
400
401 /// Teardown method for stopping regular ADC conversions
402 pub(super) fn teardown_adc() {
403 Self::stop_regular_conversions();
404
405 // Disable dma control
406 T::regs().cfgr().modify(|reg| {
407 reg.set_dmaen(Dmaen::DISABLE);
408 });
409 }
410
411 /// Read one or multiple ADC regular channels using DMA.
361 /// 412 ///
362 /// `sequence` iterator and `readings` must have the same length. 413 /// `sequence` iterator and `readings` must have the same length.
363 /// 414 ///
@@ -382,6 +433,9 @@ impl<'d, T: Instance> Adc<'d, T> {
382 /// .await; 433 /// .await;
383 /// defmt::info!("measurements: {}", measurements); 434 /// defmt::info!("measurements: {}", measurements);
384 /// ``` 435 /// ```
436 ///
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`
385 pub async fn read( 439 pub async fn read(
386 &mut self, 440 &mut self,
387 rx_dma: Peri<'_, impl RxDma<T>>, 441 rx_dma: Peri<'_, impl RxDma<T>>,
@@ -399,18 +453,16 @@ impl<'d, T: Instance> Adc<'d, T> {
399 ); 453 );
400 454
401 // Ensure no conversions are ongoing and ADC is enabled. 455 // Ensure no conversions are ongoing and ADC is enabled.
402 Self::cancel_conversions(); 456 Self::stop_regular_conversions();
403 self.enable(); 457 self.enable();
404 458
405 // Set sequence length 459 // Set sequence length
406 T::regs().sqr1().modify(|w| { 460 T::regs().sqr1().modify(|w| {
407 w.set_l(sequence.len() as u8 - 1); 461 w.set_l(sequence.len() as u8 - 1);
408 }); 462 });
409
410 // Configure channels and ranks 463 // Configure channels and ranks
411 for (_i, (channel, sample_time)) in sequence.enumerate() { 464 for (_i, (channel, sample_time)) in sequence.enumerate() {
412 Self::configure_channel(channel, sample_time); 465 Self::configure_channel(channel, sample_time);
413
414 match _i { 466 match _i {
415 0..=3 => { 467 0..=3 => {
416 T::regs().sqr1().modify(|w| { 468 T::regs().sqr1().modify(|w| {
@@ -469,7 +521,7 @@ impl<'d, T: Instance> Adc<'d, T> {
469 transfer.await; 521 transfer.await;
470 522
471 // Ensure conversions are finished. 523 // Ensure conversions are finished.
472 Self::cancel_conversions(); 524 Self::stop_regular_conversions();
473 525
474 // Reset configuration. 526 // Reset configuration.
475 T::regs().cfgr().modify(|reg| { 527 T::regs().cfgr().modify(|reg| {
@@ -477,6 +529,277 @@ impl<'d, T: Instance> Adc<'d, T> {
477 }); 529 });
478 } 530 }
479 531
532 /// Set external trigger for regular conversion sequence
533 fn set_regular_conversion_trigger(&mut self, trigger: ConversionTrigger) {
534 T::regs().cfgr().modify(|r| {
535 r.set_extsel(trigger.channel);
536 r.set_exten(trigger.edge);
537 });
538 // Regular conversions uses DMA so no need to generate interrupt
539 T::regs().ier().modify(|r| r.set_eosie(false));
540 }
541
542 // Dual ADC mode selection
543 pub fn configure_dual_mode(&mut self, val: Dual) {
544 T::common_regs().ccr().modify(|reg| {
545 reg.set_dual(val);
546 })
547 }
548
549 /// Configures the ADC to use a DMA ring buffer for continuous data acquisition.
550 ///
551 /// Use the [`read`] method to retrieve measurements from the DMA ring buffer. The read buffer
552 /// should be exactly half the size of `dma_buf`. When using triggered mode, it is recommended
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`.
561 ///
562 /// # Parameters
563 /// - `dma`: The DMA peripheral used to transfer ADC data into the buffer.
564 /// - `dma_buf`: The buffer where DMA stores ADC samples.
565 /// - `regular_sequence`: Sequence of channels and sample times for regular ADC conversions.
566 /// - `regular_conversion_mode`: Mode for regular conversions (continuous or triggered).
567 ///
568 /// # Returns
569 /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling.
570 pub fn into_ring_buffered<'a>(
571 mut self,
572 dma: Peri<'a, impl RxDma<T>>,
573 dma_buf: &'a mut [u16],
574 sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>,
575 mode: RegularConversionMode,
576 ) -> RingBufferedAdc<'a, T> {
577 assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);
578 assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty");
579 assert!(
580 sequence.len() <= 16,
581 "Asynchronous read sequence cannot be more than 16 in length"
582 );
583 // reset conversions and enable the adc
584 Self::stop_regular_conversions();
585 self.enable();
586
587 //adc side setup
588
589 // Set sequence length
590 T::regs().sqr1().modify(|w| {
591 w.set_l(sequence.len() as u8 - 1);
592 });
593
594 // Configure channels and ranks
595 for (_i, (mut channel, sample_time)) in sequence.enumerate() {
596 Self::configure_channel(&mut channel, sample_time);
597
598 match _i {
599 0..=3 => {
600 T::regs().sqr1().modify(|w| {
601 w.set_sq(_i, channel.channel());
602 });
603 }
604 4..=8 => {
605 T::regs().sqr2().modify(|w| {
606 w.set_sq(_i - 4, channel.channel());
607 });
608 }
609 9..=13 => {
610 T::regs().sqr3().modify(|w| {
611 w.set_sq(_i - 9, channel.channel());
612 });
613 }
614 14..=15 => {
615 T::regs().sqr4().modify(|w| {
616 w.set_sq(_i - 14, channel.channel());
617 });
618 }
619 _ => unreachable!(),
620 }
621 }
622
623 // Clear overrun flag before starting transfer.
624 T::regs().isr().modify(|reg| {
625 reg.set_ovr(true);
626 });
627
628 T::regs().cfgr().modify(|reg| {
629 reg.set_discen(false); // Convert all channels for each trigger
630 reg.set_dmacfg(Dmacfg::CIRCULAR);
631 reg.set_dmaen(Dmaen::ENABLE);
632 });
633
634 match mode {
635 RegularConversionMode::Continuous => {
636 T::regs().cfgr().modify(|reg| {
637 reg.set_cont(true);
638 });
639 }
640 RegularConversionMode::Triggered(trigger) => {
641 T::regs().cfgr().modify(|r| {
642 r.set_cont(false); // New trigger is neede for each sample to be read
643 });
644 self.set_regular_conversion_trigger(trigger);
645 }
646 }
647
648 mem::forget(self);
649
650 RingBufferedAdc::new(dma, dma_buf)
651 }
652
653 /// Configures the ADC for injected conversions.
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>(
681 mut self,
682 sequence: [(AnyAdcChannel<T>, SampleTime); N],
683 trigger: ConversionTrigger,
684 interrupt: bool,
685 ) -> InjectedAdc<T, N> {
686 assert!(N != 0, "Read sequence cannot be empty");
687 assert!(
688 N <= NR_INJECTED_RANKS,
689 "Read sequence cannot be more than {} in length",
690 NR_INJECTED_RANKS
691 );
692
693 Self::stop_regular_conversions();
694 self.enable();
695
696 T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1));
697
698 for (n, (mut channel, sample_time)) in sequence.into_iter().enumerate() {
699 Self::configure_channel(&mut channel, sample_time);
700
701 let idx = match n {
702 0..=3 => n,
703 4..=8 => n - 4,
704 9..=13 => n - 9,
705 14..=15 => n - 14,
706 _ => unreachable!(),
707 };
708
709 T::regs().jsqr().modify(|w| w.set_jsq(idx, channel.channel()));
710 }
711
712 T::regs().cfgr().modify(|reg| reg.set_jdiscen(false));
713
714 self.set_injected_conversion_trigger(trigger);
715 self.enable_injected_eos_interrupt(interrupt);
716 Self::start_injected_conversions();
717
718 InjectedAdc::new(sequence) // InjectedAdc<'a, T, N> now borrows the channels
719 }
720
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>(
745 self,
746 dma: Peri<'a, impl RxDma<T>>,
747 dma_buf: &'a mut [u16],
748 regular_sequence: impl ExactSizeIterator<Item = (AnyAdcChannel<T>, SampleTime)>,
749 regular_conversion_mode: RegularConversionMode,
750 injected_sequence: [(AnyAdcChannel<T>, SampleTime); N],
751 injected_trigger: ConversionTrigger,
752 injected_interrupt: bool,
753 ) -> (RingBufferedAdc<'a, T>, InjectedAdc<T, N>) {
754 unsafe {
755 (
756 Self {
757 adc: self.adc.clone_unchecked(),
758 sample_time: self.sample_time,
759 }
760 .into_ring_buffered(dma, dma_buf, regular_sequence, regular_conversion_mode),
761 Self {
762 adc: self.adc.clone_unchecked(),
763 sample_time: self.sample_time,
764 }
765 .setup_injected_conversions(injected_sequence, injected_trigger, injected_interrupt),
766 )
767 }
768 }
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
782 /// Start injected ADC conversion
783 pub(super) fn start_injected_conversions() {
784 T::regs().cr().modify(|reg| {
785 reg.set_jadstart(true);
786 });
787 }
788
789 /// Set external trigger for injected conversion sequence
790 /// Possible trigger values are seen in Table 167 in RM0440 Rev 9
791 fn set_injected_conversion_trigger(&mut self, trigger: ConversionTrigger) {
792 T::regs().jsqr().modify(|r| {
793 r.set_jextsel(trigger.channel);
794 r.set_jexten(trigger.edge);
795 });
796 }
797
798 /// Enable end of injected sequence interrupt
799 fn enable_injected_eos_interrupt(&mut self, enable: bool) {
800 T::regs().ier().modify(|r| r.set_jeosie(enable));
801 }
802
480 fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { 803 fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) {
481 // Configure channel 804 // Configure channel
482 Self::set_channel_sample_time(channel.channel(), sample_time); 805 Self::set_channel_sample_time(channel.channel(), sample_time);
@@ -509,16 +832,34 @@ impl<'d, T: Instance> Adc<'d, T> {
509 } 832 }
510 } 833 }
511 834
512 fn cancel_conversions() { 835 // Stop regular conversions
836 fn stop_regular_conversions() {
513 if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { 837 if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() {
514 T::regs().cr().modify(|reg| { 838 T::regs().cr().modify(|reg| {
515 reg.set_adstp(Adstp::STOP); 839 reg.set_adstp(Adstp::STOP);
516 }); 840 });
841 // The software must poll ADSTART until the bit is reset before assuming the
842 // ADC is completely stopped
517 while T::regs().cr().read().adstart() {} 843 while T::regs().cr().read().adstart() {}
518 } 844 }
519 } 845 }
520} 846}
521 847
848impl<T: Instance, const N: usize> InjectedAdc<T, N> {
849 /// Read sampled data from all injected ADC injected ranks
850 /// Clear the JEOS flag to allow a new injected sequence
851 pub(super) fn read_injected_data() -> [u16; N] {
852 let mut data = [0u16; N];
853 for i in 0..N {
854 data[i] = T::regs().jdr(i).read().jdata();
855 }
856
857 // Clear JEOS by writing 1
858 T::regs().isr().modify(|r| r.set_jeos(true));
859 data
860 }
861}
862
522/// Implemented for ADCs that have a Temperature channel 863/// Implemented for ADCs that have a Temperature channel
523pub trait TemperatureChannel { 864pub trait TemperatureChannel {
524 const CHANNEL: u8; 865 const CHANNEL: u8;
diff --git a/embassy-stm32/src/adc/injected.rs b/embassy-stm32/src/adc/injected.rs
new file mode 100644
index 000000000..0e4fe5847
--- /dev/null
+++ b/embassy-stm32/src/adc/injected.rs
@@ -0,0 +1,44 @@
1use core::marker::PhantomData;
2use core::sync::atomic::{Ordering, compiler_fence};
3
4#[allow(unused_imports)]
5use embassy_hal_internal::Peri;
6
7use super::{AnyAdcChannel, SampleTime};
8use crate::adc::Adc;
9#[allow(unused_imports)]
10use crate::adc::Instance;
11
12/// Injected ADC sequence with owned channels.
13pub struct InjectedAdc<T: Instance, const N: usize> {
14 _channels: [(AnyAdcChannel<T>, SampleTime); N],
15 _phantom: PhantomData<T>,
16}
17
18impl<T: Instance, const N: usize> InjectedAdc<T, N> {
19 pub(crate) fn new(channels: [(AnyAdcChannel<T>, SampleTime); N]) -> Self {
20 Self {
21 _channels: channels,
22 _phantom: PhantomData,
23 }
24 }
25
26 pub fn stop_injected_conversions(&mut self) {
27 Adc::<T>::stop_injected_conversions()
28 }
29
30 pub fn start_injected_conversions(&mut self) {
31 Adc::<T>::start_injected_conversions()
32 }
33
34 pub fn read_injected_samples(&mut self) -> [u16; N] {
35 InjectedAdc::<T, N>::read_injected_data()
36 }
37}
38
39impl<T: Instance, const N: usize> Drop for InjectedAdc<T, N> {
40 fn drop(&mut self) {
41 Adc::<T>::teardown_adc();
42 compiler_fence(Ordering::SeqCst);
43 }
44}
diff --git a/embassy-stm32/src/adc/ringbuffered.rs b/embassy-stm32/src/adc/ringbuffered.rs
index 1f384efb5..971c8195c 100644
--- a/embassy-stm32/src/adc/ringbuffered.rs
+++ b/embassy-stm32/src/adc/ringbuffered.rs
@@ -63,14 +63,17 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> {
63 /// Reads measurements from the DMA ring buffer. 63 /// Reads measurements from the DMA ring buffer.
64 /// 64 ///
65 /// This method fills the provided `measurements` array with ADC readings from the DMA buffer. 65 /// This method fills the provided `measurements` array with ADC readings from the DMA buffer.
66 /// The length of the `measurements` array should be exactly half of the DMA buffer length. Because interrupts are only generated if half or full DMA transfer completes. 66 /// The length of the `measurements` array should be exactly half of the DMA buffer length.
67 /// Because interrupts are only generated if half or full DMA transfer completes.
67 /// 68 ///
68 /// Each call to `read` will populate the `measurements` array in the same order as the channels defined with `sequence`. 69 /// Each call to `read` will populate the `measurements` array in the same order as the channels
69 /// There will be many sequences worth of measurements in this array because it only returns if at least half of the DMA buffer is filled. 70 /// defined with `sequence`. There will be many sequences worth of measurements in this array
70 /// For example if 2 channels are sampled `measurements` contain: `[sq0 sq1 sq0 sq1 sq0 sq1 ..]`. 71 /// because it only returns if at least half of the DMA buffer is filled. For example if 2
72 /// channels are sampled `measurements` contain: `[sq0 sq1 sq0 sq1 sq0 sq1 ..]`.
71 /// 73 ///
72 /// Note that the ADC Datarate can be very fast, it is suggested to use DMA mode inside tightly running tasks 74 /// Note that the ADC Datarate can be very fast, it is suggested to use DMA mode inside tightly
73 /// Otherwise, you'll see constant Overrun errors occuring, this means that you're sampling too quickly for the task to handle, and you may need to increase the buffer size. 75 /// running tasks. Otherwise, you'll see constant Overrun errors occurring, this means that
76 /// you're sampling too quickly for the task to handle, and you may need to increase the buffer size.
74 /// Example: 77 /// Example:
75 /// ```rust,ignore 78 /// ```rust,ignore
76 /// const DMA_BUF_LEN: usize = 120; 79 /// const DMA_BUF_LEN: usize = 120;
diff --git a/embassy-stm32/src/backup_sram.rs b/embassy-stm32/src/backup_sram.rs
new file mode 100644
index 000000000..31b373c6c
--- /dev/null
+++ b/embassy-stm32/src/backup_sram.rs
@@ -0,0 +1,28 @@
1//! Battary backed SRAM
2
3use core::slice;
4
5use embassy_hal_internal::Peri;
6
7use crate::_generated::{BKPSRAM_BASE, BKPSRAM_SIZE};
8use crate::peripherals::BKPSRAM;
9
10/// Struct used to initilize backup sram
11pub struct BackupMemory {}
12
13impl BackupMemory {
14 /// Setup battery backed sram
15 ///
16 /// Returns slice to sram and whether the sram was retained
17 pub fn new(_backup_sram: Peri<'static, BKPSRAM>) -> (&'static mut [u8], bool) {
18 // Assert bksram has been enabled in rcc
19 assert!(crate::pac::PWR.bdcr().read().bren() == crate::pac::pwr::vals::Retention::PRESERVED);
20
21 unsafe {
22 (
23 slice::from_raw_parts_mut(BKPSRAM_BASE as *mut u8, BKPSRAM_SIZE),
24 critical_section::with(|_| crate::rcc::BKSRAM_RETAINED),
25 )
26 }
27 }
28}
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 85606e7de..e08ab30e6 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -54,6 +54,8 @@ pub mod timer;
54 54
55#[cfg(adc)] 55#[cfg(adc)]
56pub mod adc; 56pub mod adc;
57#[cfg(backup_sram)]
58pub mod backup_sram;
57#[cfg(can)] 59#[cfg(can)]
58pub mod can; 60pub mod can;
59// FIXME: Cordic driver cause stm32u5a5zj crash 61// FIXME: Cordic driver cause stm32u5a5zj crash
diff --git a/embassy-stm32/src/rcc/bd.rs b/embassy-stm32/src/rcc/bd.rs
index 3b2a10581..5b367c043 100644
--- a/embassy-stm32/src/rcc/bd.rs
+++ b/embassy-stm32/src/rcc/bd.rs
@@ -1,6 +1,8 @@
1use core::sync::atomic::{Ordering, compiler_fence}; 1use core::sync::atomic::{Ordering, compiler_fence};
2 2
3use crate::pac::common::{RW, Reg}; 3use crate::pac::common::{RW, Reg};
4#[cfg(backup_sram)]
5use crate::pac::pwr::vals::Retention;
4pub use crate::pac::rcc::vals::Rtcsel as RtcClockSource; 6pub use crate::pac::rcc::vals::Rtcsel as RtcClockSource;
5use crate::time::Hertz; 7use crate::time::Hertz;
6 8
@@ -89,6 +91,8 @@ pub struct LsConfig {
89 pub rtc: RtcClockSource, 91 pub rtc: RtcClockSource,
90 pub lsi: bool, 92 pub lsi: bool,
91 pub lse: Option<LseConfig>, 93 pub lse: Option<LseConfig>,
94 #[cfg(backup_sram)]
95 pub enable_backup_sram: bool,
92} 96}
93 97
94impl LsConfig { 98impl LsConfig {
@@ -113,6 +117,8 @@ impl LsConfig {
113 peripherals_clocked: false, 117 peripherals_clocked: false,
114 }), 118 }),
115 lsi: false, 119 lsi: false,
120 #[cfg(backup_sram)]
121 enable_backup_sram: false,
116 } 122 }
117 } 123 }
118 124
@@ -121,6 +127,8 @@ impl LsConfig {
121 rtc: RtcClockSource::LSI, 127 rtc: RtcClockSource::LSI,
122 lsi: true, 128 lsi: true,
123 lse: None, 129 lse: None,
130 #[cfg(backup_sram)]
131 enable_backup_sram: false,
124 } 132 }
125 } 133 }
126 134
@@ -129,6 +137,8 @@ impl LsConfig {
129 rtc: RtcClockSource::DISABLE, 137 rtc: RtcClockSource::DISABLE,
130 lsi: false, 138 lsi: false,
131 lse: None, 139 lse: None,
140 #[cfg(backup_sram)]
141 enable_backup_sram: false,
132 } 142 }
133 } 143 }
134} 144}
@@ -193,6 +203,22 @@ impl LsConfig {
193 while !csr.read().lsi1rdy() {} 203 while !csr.read().lsi1rdy() {}
194 } 204 }
195 205
206 // Enable backup regulator for peristent battery backed sram
207 #[cfg(backup_sram)]
208 {
209 unsafe { super::BKSRAM_RETAINED = crate::pac::PWR.bdcr().read().bren() == Retention::PRESERVED };
210
211 crate::pac::PWR.bdcr().modify(|w| {
212 w.set_bren(match self.enable_backup_sram {
213 true => Retention::PRESERVED,
214 false => Retention::LOST,
215 });
216 });
217
218 // Wait for backup regulator voltage to stabilize
219 while self.enable_backup_sram && !crate::pac::PWR.bdsr().read().brrdy() {}
220 }
221
196 // backup domain configuration (LSEON, RTCEN, RTCSEL) is kept across resets. 222 // backup domain configuration (LSEON, RTCEN, RTCSEL) is kept across resets.
197 // once set, changing it requires a backup domain reset. 223 // once set, changing it requires a backup domain reset.
198 // first check if the configuration matches what we want. 224 // first check if the configuration matches what we want.
diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs
index c817dd4b7..01fa3a475 100644
--- a/embassy-stm32/src/rcc/mod.rs
+++ b/embassy-stm32/src/rcc/mod.rs
@@ -48,6 +48,9 @@ pub(crate) static mut REFCOUNT_STOP1: u32 = 0;
48/// May be read without a critical section 48/// May be read without a critical section
49pub(crate) static mut REFCOUNT_STOP2: u32 = 0; 49pub(crate) static mut REFCOUNT_STOP2: u32 = 0;
50 50
51#[cfg(backup_sram)]
52pub(crate) static mut BKSRAM_RETAINED: bool = false;
53
51#[cfg(not(feature = "_dual-core"))] 54#[cfg(not(feature = "_dual-core"))]
52/// Frozen clock frequencies 55/// Frozen clock frequencies
53/// 56///
diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs
index 75a83629c..9a56a41fb 100644
--- a/embassy-stm32/src/timer/complementary_pwm.rs
+++ b/embassy-stm32/src/timer/complementary_pwm.rs
@@ -2,7 +2,7 @@
2 2
3use core::marker::PhantomData; 3use core::marker::PhantomData;
4 4
5pub use stm32_metapac::timer::vals::{Ckd, Ossi, Ossr}; 5pub use stm32_metapac::timer::vals::{Ckd, Mms2, Ossi, Ossr};
6 6
7use super::low_level::{CountingMode, OutputPolarity, Timer}; 7use super::low_level::{CountingMode, OutputPolarity, Timer};
8use super::simple_pwm::PwmPin; 8use super::simple_pwm::PwmPin;
@@ -136,6 +136,16 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> {
136 self.inner.get_moe() 136 self.inner.get_moe()
137 } 137 }
138 138
139 /// Set Master Slave Mode 2
140 pub fn set_mms2(&mut self, mms2: Mms2) {
141 self.inner.set_mms2_selection(mms2);
142 }
143
144 /// Set Repetition Counter
145 pub fn set_repetition_counter(&mut self, val: u16) {
146 self.inner.set_repetition_counter(val);
147 }
148
139 /// Enable the given channel. 149 /// Enable the given channel.
140 pub fn enable(&mut self, channel: Channel) { 150 pub fn enable(&mut self, channel: Channel) {
141 self.inner.enable_channel(channel, true); 151 self.inner.enable_channel(channel, true);
diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs
index 82645887e..0122fe4f7 100644
--- a/embassy-stm32/src/timer/low_level.rs
+++ b/embassy-stm32/src/timer/low_level.rs
@@ -814,6 +814,16 @@ impl<'d, T: AdvancedInstance4Channel> Timer<'d, T> {
814 self.regs_advanced().cr2().modify(|w| w.set_oisn(channel.index(), val)); 814 self.regs_advanced().cr2().modify(|w| w.set_oisn(channel.index(), val));
815 } 815 }
816 816
817 /// Set master mode selection 2
818 pub fn set_mms2_selection(&self, mms2: vals::Mms2) {
819 self.regs_advanced().cr2().modify(|w| w.set_mms2(mms2));
820 }
821
822 /// Set repetition counter
823 pub fn set_repetition_counter(&self, val: u16) {
824 self.regs_advanced().rcr().modify(|w| w.set_rep(val));
825 }
826
817 /// Trigger software break 1 or 2 827 /// Trigger software break 1 or 2
818 /// Setting this bit generates a break event. This bit is automatically cleared by the hardware. 828 /// Setting this bit generates a break event. This bit is automatically cleared by the hardware.
819 pub fn trigger_software_break(&self, n: usize) { 829 pub fn trigger_software_break(&self, n: usize) {
diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml
index 6fd282d6d..8bbeb594c 100644
--- a/examples/stm32g4/Cargo.toml
+++ b/examples/stm32g4/Cargo.toml
@@ -7,12 +7,12 @@ 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.
10embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } 10embassy-stm32 = { path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] }
11embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } 11embassy-sync = { path = "../../embassy-sync", features = ["defmt"] }
12embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } 12embassy-executor = { path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
13embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } 13embassy-time = { path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
14embassy-usb = { version = "0.5.1", path = "../../embassy-usb", features = ["defmt"] } 14embassy-usb = { path = "../../embassy-usb", features = ["defmt"] }
15embassy-futures = { version = "0.1.2", path = "../../embassy-futures" } 15embassy-futures = { path = "../../embassy-futures" }
16usbd-hid = "0.8.1" 16usbd-hid = "0.8.1"
17 17
18defmt = "1.0.1" 18defmt = "1.0.1"
@@ -25,6 +25,7 @@ embedded-can = { version = "0.4" }
25panic-probe = { version = "1.0.0", features = ["print-defmt"] } 25panic-probe = { version = "1.0.0", features = ["print-defmt"] }
26heapless = { version = "0.8", default-features = false } 26heapless = { version = "0.8", default-features = false }
27static_cell = "2.0.0" 27static_cell = "2.0.0"
28critical-section = "1.1"
28 29
29[profile.release] 30[profile.release]
30debug = 2 31debug = 2
diff --git a/examples/stm32g4/src/bin/adc_dma.rs b/examples/stm32g4/src/bin/adc_dma.rs
index a82067049..ef8b0c3c2 100644
--- a/examples/stm32g4/src/bin/adc_dma.rs
+++ b/examples/stm32g4/src/bin/adc_dma.rs
@@ -12,7 +12,7 @@ static mut DMA_BUF: [u16; 2] = [0; 2];
12 12
13#[embassy_executor::main] 13#[embassy_executor::main]
14async fn main(_spawner: Spawner) { 14async fn main(_spawner: Spawner) {
15 let mut read_buffer = unsafe { &mut DMA_BUF[..] }; 15 let read_buffer = unsafe { &mut DMA_BUF[..] };
16 16
17 let mut config = Config::default(); 17 let mut config = Config::default();
18 { 18 {
@@ -47,7 +47,7 @@ async fn main(_spawner: Spawner) {
47 (&mut pa0, SampleTime::CYCLES247_5), 47 (&mut pa0, SampleTime::CYCLES247_5),
48 ] 48 ]
49 .into_iter(), 49 .into_iter(),
50 &mut read_buffer, 50 read_buffer,
51 ) 51 )
52 .await; 52 .await;
53 53
diff --git a/examples/stm32g4/src/bin/adc_injected_and_regular.rs b/examples/stm32g4/src/bin/adc_injected_and_regular.rs
new file mode 100644
index 000000000..3ae2ff064
--- /dev/null
+++ b/examples/stm32g4/src/bin/adc_injected_and_regular.rs
@@ -0,0 +1,154 @@
1//! adc injected and regular conversions
2//!
3//! This example both regular and injected ADC conversions at the same time
4//! p:pa0 n:pa2
5
6#![no_std]
7#![no_main]
8
9use core::cell::RefCell;
10
11use defmt::info;
12use embassy_stm32::adc::{
13 Adc, AdcChannel as _, ConversionTrigger, Exten, InjectedAdc, RegularConversionMode, SampleTime,
14};
15use embassy_stm32::interrupt::typelevel::{ADC1_2, Interrupt};
16use embassy_stm32::peripherals::ADC1;
17use embassy_stm32::time::Hertz;
18use embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, Mms2};
19use embassy_stm32::timer::low_level::CountingMode;
20use embassy_stm32::{Config, interrupt};
21use embassy_sync::blocking_mutex::CriticalSectionMutex;
22use {defmt_rtt as _, panic_probe as _};
23
24static ADC1_HANDLE: CriticalSectionMutex<RefCell<Option<InjectedAdc<ADC1, 1>>>> =
25 CriticalSectionMutex::new(RefCell::new(None));
26
27/// This example showcases how to use both regular ADC conversions with DMA and injected ADC
28/// conversions with ADC interrupt simultaneously. Both conversion types can be configured with
29/// different triggers and thanks to DMA it is possible to use the measurements in different task
30/// without needing to access the ADC peripheral.
31///
32/// If you don't need both regular and injected conversions the example code can easily be reworked
33/// to only include one of the ADC conversion types.
34#[embassy_executor::main]
35async fn main(_spawner: embassy_executor::Spawner) {
36 // See Table 166 and 167 in RM0440 Rev 9 for ADC1/2 External triggers
37 // Note: Regular and Injected channels use different tables!!
38 const ADC1_INJECTED_TRIGGER_TIM1_TRGO2: u8 = 8;
39 const ADC1_REGULAR_TRIGGER_TIM1_TRGO2: u8 = 10;
40
41 // --- RCC config ---
42 let mut config = Config::default();
43 {
44 use embassy_stm32::rcc::*;
45 config.rcc.pll = Some(Pll {
46 source: PllSource::HSI,
47 prediv: PllPreDiv::DIV4,
48 mul: PllMul::MUL85,
49 divp: None,
50 divq: None,
51 divr: Some(PllRDiv::DIV2),
52 });
53 config.rcc.mux.adc12sel = mux::Adcsel::SYS;
54 config.rcc.sys = Sysclk::PLL1_R;
55 }
56 let p = embassy_stm32::init(config);
57
58 // In this example we use tim1_trgo2 event to trigger the ADC conversions
59 let tim1 = p.TIM1;
60 let pwm_freq = 1;
61 let mut pwm = ComplementaryPwm::new(
62 tim1,
63 None,
64 None,
65 None,
66 None,
67 None,
68 None,
69 None,
70 None,
71 Hertz::hz(pwm_freq),
72 CountingMode::EdgeAlignedUp,
73 );
74 pwm.set_master_output_enable(false);
75 // Mms2 is used to configure which timer event that is connected to tim1_trgo2.
76 // In this case we use the update event of the timer.
77 pwm.set_mms2(Mms2::UPDATE);
78
79 // Configure regular conversions with DMA
80 let adc1 = Adc::new(p.ADC1);
81
82 let vrefint_channel = adc1.enable_vrefint().degrade_adc();
83 let pa0 = p.PC1.degrade_adc();
84 let regular_sequence = [
85 (vrefint_channel, SampleTime::CYCLES247_5),
86 (pa0, SampleTime::CYCLES247_5),
87 ]
88 .into_iter();
89
90 // Configurations of Injected ADC measurements
91 let pa2 = p.PA2.degrade_adc();
92 let injected_sequence = [(pa2, SampleTime::CYCLES247_5)];
93
94 // Configure DMA for retrieving regular ADC measurements
95 let dma1_ch1 = p.DMA1_CH1;
96 // Using buffer of double size means the half-full interrupts will generate at the expected rate
97 let mut readings = [0u16; 4];
98
99 let injected_trigger = ConversionTrigger {
100 channel: ADC1_INJECTED_TRIGGER_TIM1_TRGO2,
101 edge: Exten::RISING_EDGE,
102 };
103 let regular_trigger = ConversionTrigger {
104 channel: ADC1_REGULAR_TRIGGER_TIM1_TRGO2,
105 edge: Exten::RISING_EDGE,
106 };
107
108 let (mut ring_buffered_adc, injected_adc) = adc1.into_ring_buffered_and_injected(
109 dma1_ch1,
110 &mut readings,
111 regular_sequence,
112 RegularConversionMode::Triggered(regular_trigger),
113 injected_sequence,
114 injected_trigger,
115 true,
116 );
117
118 // Store ADC globally to allow access from ADC interrupt
119 critical_section::with(|cs| {
120 ADC1_HANDLE.borrow(cs).replace(Some(injected_adc));
121 });
122 // Enable interrupt for ADC1_2
123 unsafe { ADC1_2::enable() };
124
125 // Main loop for reading regular ADC measurements periodically
126 let mut data = [0u16; 2];
127 loop {
128 {
129 match ring_buffered_adc.read(&mut data).await {
130 Ok(n) => {
131 defmt::info!("Regular ADC reading, VrefInt: {}, PA0: {}", data[0], data[1]);
132 defmt::info!("Remaining samples: {}", n,);
133 }
134 Err(e) => {
135 defmt::error!("DMA error: {:?}", e);
136 ring_buffered_adc.clear();
137 }
138 }
139 }
140 }
141}
142
143/// Use ADC1_2 interrupt to retrieve injected ADC measurements
144/// Interrupt must be unsafe as hardware can invoke it any-time. Critical sections ensure safety
145/// within the interrupt.
146#[interrupt]
147unsafe fn ADC1_2() {
148 critical_section::with(|cs| {
149 if let Some(injected_adc) = ADC1_HANDLE.borrow(cs).borrow_mut().as_mut() {
150 let injected_data = injected_adc.read_injected_samples();
151 info!("Injected reading of PA2: {}", injected_data[0]);
152 }
153 });
154}
diff --git a/examples/stm32h5/src/bin/backup_sram.rs b/examples/stm32h5/src/bin/backup_sram.rs
new file mode 100644
index 000000000..f8db1853e
--- /dev/null
+++ b/examples/stm32h5/src/bin/backup_sram.rs
@@ -0,0 +1,31 @@
1#![no_std]
2#![no_main]
3
4use defmt::*;
5use embassy_executor::Spawner;
6use embassy_stm32::Config;
7use embassy_stm32::backup_sram::BackupMemory;
8use embassy_time::Timer;
9use {defmt_rtt as _, panic_probe as _};
10
11#[embassy_executor::main]
12async fn main(_spawner: Spawner) {
13 let mut config = Config::default();
14 config.rcc.ls.enable_backup_sram = true;
15
16 let p = embassy_stm32::init(config);
17 info!("Started!");
18
19 let (bytes, status) = BackupMemory::new(p.BKPSRAM);
20
21 match status {
22 false => info!("BKPSRAM just enabled"),
23 true => info!("BKPSRAM already enabled"),
24 }
25
26 loop {
27 info!("byte0: {}", bytes[0]);
28 bytes[0] = bytes[0].wrapping_add(1);
29 Timer::after_millis(500).await;
30 }
31}