aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob <[email protected]>2025-11-04 19:55:09 +0100
committerJakob <[email protected]>2025-11-04 19:55:09 +0100
commit8184bb809b65281cfcf0035e40c7c215d6b9aeda (patch)
tree46fbc20ff00150a6eb40ac0c87a214e5fac63b6a
parentdda429ce6642deaa490f8737e1373e2e2ba79655 (diff)
Implement into_ring_buffered for g4. Add methods for configuring injected sampling for g4.
-rw-r--r--embassy-stm32/src/adc/g4.rs235
-rw-r--r--embassy-stm32/src/adc/ringbuffered.rs15
-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/config.toml2
-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.rs144
8 files changed, 411 insertions, 24 deletions
diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs
index 5098aadd8..d8aaaba55 100644
--- a/embassy-stm32/src/adc/g4.rs
+++ b/embassy-stm32/src/adc/g4.rs
@@ -3,9 +3,10 @@
3use pac::adc::vals::{Adcaldif, Difsel, Exten}; 3use pac::adc::vals::{Adcaldif, Difsel, Exten};
4#[allow(unused)] 4#[allow(unused)]
5#[cfg(stm32g4)] 5#[cfg(stm32g4)]
6use pac::adc::vals::{Adcaldif, Difsel, Exten, Rovsm, Trovs}; 6pub use pac::adc::vals::{Adcaldif, Difsel, Exten, Rovsm, Trovs};
7use pac::adccommon::vals::Presc; 7pub use pac::adccommon::vals::Presc;
8use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; 8pub use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen};
9pub use stm32_metapac::adccommon::vals::Dual;
9 10
10use super::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, blocking_delay_us}; 11use super::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, blocking_delay_us};
11use crate::adc::SealedAdcChannel; 12use crate::adc::SealedAdcChannel;
@@ -13,11 +14,16 @@ use crate::dma::Transfer;
13use crate::time::Hertz; 14use crate::time::Hertz;
14use crate::{Peri, pac, rcc}; 15use crate::{Peri, pac, rcc};
15 16
17mod ringbuffered;
18pub use ringbuffered::RingBufferedAdc;
19
16/// Default VREF voltage used for sample conversion to millivolts. 20/// Default VREF voltage used for sample conversion to millivolts.
17pub const VREF_DEFAULT_MV: u32 = 3300; 21pub const VREF_DEFAULT_MV: u32 = 3300;
18/// VREF voltage used for factory calibration of VREFINTCAL register. 22/// VREF voltage used for factory calibration of VREFINTCAL register.
19pub const VREF_CALIB_MV: u32 = 3300; 23pub const VREF_CALIB_MV: u32 = 3300;
20 24
25const NR_INJECTED_RANKS: usize = 4;
26
21/// Max single ADC operation clock frequency 27/// Max single ADC operation clock frequency
22#[cfg(stm32g4)] 28#[cfg(stm32g4)]
23const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); 29const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60);
@@ -357,7 +363,31 @@ impl<'d, T: Instance> Adc<'d, T> {
357 self.read_channel(channel) 363 self.read_channel(channel)
358 } 364 }
359 365
360 /// Read one or multiple ADC channels using DMA. 366 pub(super) fn start() {
367 // Start adc conversion
368 T::regs().cr().modify(|reg| {
369 reg.set_adstart(true);
370 });
371 }
372
373 pub(super) fn stop() {
374 // Stop adc conversion
375 if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() {
376 T::regs().cr().modify(|reg| {
377 reg.set_adstp(Adstp::STOP);
378 });
379 while T::regs().cr().read().adstart() {}
380 }
381 }
382
383 pub(super) fn teardown_adc() {
384 //disable dma control
385 T::regs().cfgr().modify(|reg| {
386 reg.set_dmaen(Dmaen::DISABLE);
387 });
388 }
389
390 /// Read one or multiple ADC regular channels using DMA.
361 /// 391 ///
362 /// `sequence` iterator and `readings` must have the same length. 392 /// `sequence` iterator and `readings` must have the same length.
363 /// 393 ///
@@ -382,6 +412,8 @@ impl<'d, T: Instance> Adc<'d, T> {
382 /// .await; 412 /// .await;
383 /// defmt::info!("measurements: {}", measurements); 413 /// defmt::info!("measurements: {}", measurements);
384 /// ``` 414 /// ```
415 ///
416 /// Note: This is not very efficient as the ADC needs to be reconfigured for each read.
385 pub async fn read( 417 pub async fn read(
386 &mut self, 418 &mut self,
387 rx_dma: Peri<'_, impl RxDma<T>>, 419 rx_dma: Peri<'_, impl RxDma<T>>,
@@ -397,8 +429,6 @@ impl<'d, T: Instance> Adc<'d, T> {
397 sequence.len() <= 16, 429 sequence.len() <= 16,
398 "Asynchronous read sequence cannot be more than 16 in length" 430 "Asynchronous read sequence cannot be more than 16 in length"
399 ); 431 );
400
401 // Ensure no conversions are ongoing and ADC is enabled.
402 Self::cancel_conversions(); 432 Self::cancel_conversions();
403 self.enable(); 433 self.enable();
404 434
@@ -406,11 +436,9 @@ impl<'d, T: Instance> Adc<'d, T> {
406 T::regs().sqr1().modify(|w| { 436 T::regs().sqr1().modify(|w| {
407 w.set_l(sequence.len() as u8 - 1); 437 w.set_l(sequence.len() as u8 - 1);
408 }); 438 });
409
410 // Configure channels and ranks 439 // Configure channels and ranks
411 for (_i, (channel, sample_time)) in sequence.enumerate() { 440 for (_i, (channel, sample_time)) in sequence.enumerate() {
412 Self::configure_channel(channel, sample_time); 441 Self::configure_channel(channel, sample_time);
413
414 match _i { 442 match _i {
415 0..=3 => { 443 0..=3 => {
416 T::regs().sqr1().modify(|w| { 444 T::regs().sqr1().modify(|w| {
@@ -477,6 +505,189 @@ impl<'d, T: Instance> Adc<'d, T> {
477 }); 505 });
478 } 506 }
479 507
508 /// Set external trigger for regular conversion sequence
509 /// Possible trigger values are seen in Table 166 in RM0440 Rev 9
510 pub fn set_regular_conversion_trigger(&mut self, trigger: u8, edge: Exten) {
511 T::regs().cfgr().modify(|r| {
512 r.set_extsel(trigger);
513 r.set_exten(edge);
514 });
515 // Regular conversions uses DMA so no need to generate interrupt
516 T::regs().ier().modify(|r| r.set_eosie(false));
517 }
518
519 // Dual ADC mode selection
520 pub fn configure_dual_mode(&mut self, val: Dual) {
521 T::common_regs().ccr().modify(|reg| {
522 reg.set_dual(val);
523 })
524 }
525
526 /// Configure a sequence of injected channels
527 pub fn configure_injected_sequence<'a>(
528 &mut self,
529 sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<T>, SampleTime)>,
530 ) {
531 assert!(sequence.len() != 0, "Read sequence cannot be empty");
532 assert!(
533 sequence.len() <= NR_INJECTED_RANKS,
534 "Read sequence cannot be more than 4 in length"
535 );
536
537 // Ensure no conversions are ongoing and ADC is enabled.
538 Self::cancel_conversions();
539 self.enable();
540
541 // Set sequence length
542 T::regs().jsqr().modify(|w| {
543 w.set_jl(sequence.len() as u8 - 1);
544 });
545
546 // Configure channels and ranks
547 for (n, (channel, sample_time)) in sequence.enumerate() {
548 Self::configure_channel(channel, sample_time);
549
550 match n {
551 0..=3 => {
552 T::regs().jsqr().modify(|w| {
553 w.set_jsq(n, channel.channel());
554 });
555 }
556 4..=8 => {
557 T::regs().jsqr().modify(|w| {
558 w.set_jsq(n - 4, channel.channel());
559 });
560 }
561 9..=13 => {
562 T::regs().jsqr().modify(|w| {
563 w.set_jsq(n - 9, channel.channel());
564 });
565 }
566 14..=15 => {
567 T::regs().jsqr().modify(|w| {
568 w.set_jsq(n - 14, channel.channel());
569 });
570 }
571 _ => unreachable!(),
572 }
573 }
574
575 T::regs().cfgr().modify(|reg| {
576 reg.set_jdiscen(false); // Will convert all channels for each trigger
577 });
578 }
579
580 /// Configures the ADC to use a DMA ring buffer for continuous data acquisition.
581 ///
582 /// The `dma_buf` should be large enough to prevent DMA buffer overrun.
583 /// The length of the `dma_buf` should be a multiple of the ADC channel count.
584 /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements.
585 ///
586 /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length.
587 /// It is critical to call `read` frequently to prevent DMA buffer overrun.
588 ///
589 /// [`read`]: #method.read
590 pub fn into_ring_buffered<'a>(
591 &mut self,
592 dma: Peri<'a, impl RxDma<T>>,
593 dma_buf: &'a mut [u16],
594 sequence: impl ExactSizeIterator<Item = (&'a mut AnyAdcChannel<T>, SampleTime)>,
595 ) -> RingBufferedAdc<'a, T> {
596 assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF);
597 assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty");
598 assert!(
599 sequence.len() <= 16,
600 "Asynchronous read sequence cannot be more than 16 in length"
601 );
602 // reset conversions and enable the adc
603 Self::cancel_conversions();
604 self.enable();
605
606 //adc side setup
607
608 // Set sequence length
609 T::regs().sqr1().modify(|w| {
610 w.set_l(sequence.len() as u8 - 1);
611 });
612
613 // Configure channels and ranks
614 for (_i, (channel, sample_time)) in sequence.enumerate() {
615 Self::configure_channel(channel, sample_time);
616
617 match _i {
618 0..=3 => {
619 T::regs().sqr1().modify(|w| {
620 w.set_sq(_i, channel.channel());
621 });
622 }
623 4..=8 => {
624 T::regs().sqr2().modify(|w| {
625 w.set_sq(_i - 4, channel.channel());
626 });
627 }
628 9..=13 => {
629 T::regs().sqr3().modify(|w| {
630 w.set_sq(_i - 9, channel.channel());
631 });
632 }
633 14..=15 => {
634 T::regs().sqr4().modify(|w| {
635 w.set_sq(_i - 14, channel.channel());
636 });
637 }
638 _ => unreachable!(),
639 }
640 }
641
642 // Clear overrun flag before starting transfer.
643 T::regs().isr().modify(|reg| {
644 reg.set_ovr(true);
645 });
646
647 T::regs().cfgr().modify(|reg| {
648 reg.set_discen(false); // Convert all channels for each trigger
649 reg.set_cont(false); // New trigger is neede for each sample to be read
650 reg.set_dmacfg(Dmacfg::CIRCULAR);
651 reg.set_dmaen(Dmaen::ENABLE);
652 });
653
654 RingBufferedAdc::new(dma, dma_buf)
655 }
656
657 /// Start injected ADC conversion
658 pub fn start_injected_conversion(&mut self) {
659 T::regs().cr().modify(|reg| {
660 reg.set_jadstart(true);
661 });
662 }
663
664 /// Set external trigger for injected conversion sequence
665 /// Possible trigger values are seen in Table 167 in RM0440 Rev 9
666 pub fn set_injected_conversion_trigger(&mut self, trigger: u8, edge: Exten) {
667 T::regs().jsqr().modify(|r| {
668 r.set_jextsel(trigger);
669 r.set_jexten(edge);
670 });
671 }
672
673 /// Enable end of injected sequence interrupt
674 pub fn enable_injected_eos_interrupt(&mut self, enable: bool) {
675 T::regs().ier().modify(|r| r.set_jeosie(enable));
676 }
677
678 /// Read sampled data from all injected ADC injected ranks
679 /// Clear the JEOS flag to allow a new injected sequence
680 pub fn clear_injected_eos(&mut self) -> [u16; NR_INJECTED_RANKS] {
681 let mut data = [0u16; NR_INJECTED_RANKS];
682 for i in 0..NR_INJECTED_RANKS {
683 data[i] = T::regs().jdr(i).read().jdata();
684 }
685
686 // Clear JEOS by writing 1
687 T::regs().isr().modify(|r| r.set_jeos(true));
688 data
689 }
690
480 fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) { 691 fn configure_channel(channel: &mut impl AdcChannel<T>, sample_time: SampleTime) {
481 // Configure channel 692 // Configure channel
482 Self::set_channel_sample_time(channel.channel(), sample_time); 693 Self::set_channel_sample_time(channel.channel(), sample_time);
@@ -510,12 +721,20 @@ impl<'d, T: Instance> Adc<'d, T> {
510 } 721 }
511 722
512 fn cancel_conversions() { 723 fn cancel_conversions() {
724 // Cancel regular conversions
513 if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { 725 if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() {
514 T::regs().cr().modify(|reg| { 726 T::regs().cr().modify(|reg| {
515 reg.set_adstp(Adstp::STOP); 727 reg.set_adstp(Adstp::STOP);
516 }); 728 });
517 while T::regs().cr().read().adstart() {} 729 while T::regs().cr().read().adstart() {}
518 } 730 }
731 // Cancel injected conversions
732 if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() {
733 T::regs().cr().modify(|reg| {
734 reg.set_jadstp(Adstp::STOP);
735 });
736 while T::regs().cr().read().jadstart() {}
737 }
519 } 738 }
520} 739}
521 740
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/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/config.toml b/examples/stm32g4/.cargo/config.toml
index d28ad069e..de3e5718e 100644
--- a/examples/stm32g4/.cargo/config.toml
+++ b/examples/stm32g4/.cargo/config.toml
@@ -6,4 +6,4 @@ runner = "probe-rs run --chip STM32G484VETx"
6target = "thumbv7em-none-eabi" 6target = "thumbv7em-none-eabi"
7 7
8[env] 8[env]
9DEFMT_LOG = "trace" 9DEFMT_LOG = "trace" \ No newline at end of file
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..5db1a4fa0
--- /dev/null
+++ b/examples/stm32g4/src/bin/adc_injected_and_regular.rs
@@ -0,0 +1,144 @@
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::{Adc, AdcChannel as _, Exten, RxDma, SampleTime};
13use embassy_stm32::interrupt::typelevel::{ADC1_2, Interrupt};
14use embassy_stm32::peripherals::ADC1;
15use embassy_stm32::time::Hertz;
16use embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, Mms2};
17use embassy_stm32::timer::low_level::CountingMode;
18use embassy_stm32::{Config, interrupt};
19use embassy_sync::blocking_mutex::CriticalSectionMutex;
20use {critical_section, defmt_rtt as _, panic_probe as _};
21
22static ADC1_HANDLE: CriticalSectionMutex<RefCell<Option<Adc<'static, ADC1>>>> =
23 CriticalSectionMutex::new(RefCell::new(None));
24
25/// This example showcases how to use both regular ADC conversions with DMA and injected ADC
26/// conversions with ADC interrupt simultaneously. Both conversion types can be configured with
27/// different triggers and thanks to DMA it is possible to use the measurements in different task
28/// without needing to access the ADC peripheral.
29///
30/// If you don't need both regular and injected conversions the example code can easily be reworked
31/// to only include one of the ADC conversion types.
32#[embassy_executor::main]
33async fn main(_spawner: embassy_executor::Spawner) {
34 // See Table 166 and 167 in RM0440 Rev 9 for ADC1/2 External triggers
35 // Note: Regular and Injected channels use different tables!!
36 const ADC1_INJECTED_TRIGGER_TIM1_TRGO2: u8 = 8;
37 const ADC1_REGULAR_TRIGGER_TIM1_TRGO2: u8 = 10;
38
39 // --- RCC config ---
40 let mut config = Config::default();
41 {
42 use embassy_stm32::rcc::*;
43 config.rcc.pll = Some(Pll {
44 source: PllSource::HSI,
45 prediv: PllPreDiv::DIV4,
46 mul: PllMul::MUL85,
47 divp: None,
48 divq: None,
49 divr: Some(PllRDiv::DIV2),
50 });
51 config.rcc.mux.adc12sel = mux::Adcsel::SYS;
52 config.rcc.sys = Sysclk::PLL1_R;
53 }
54 let p = embassy_stm32::init(config);
55
56 // In this example we use tim1_trgo2 event to trigger the ADC conversions
57 let tim1 = p.TIM1;
58 let pwm_freq = 1;
59 let mut pwm = ComplementaryPwm::new(
60 tim1,
61 None,
62 None,
63 None,
64 None,
65 None,
66 None,
67 None,
68 None,
69 Hertz::hz(pwm_freq),
70 CountingMode::EdgeAlignedUp,
71 );
72 pwm.set_master_output_enable(false);
73 // Mms2 is used to configure which timer event that is connected to tim1_trgo2.
74 // In this case we use the update event of the timer.
75 pwm.set_mms2(Mms2::UPDATE);
76
77 // Configure regular conversions with DMA
78 let mut adc1 = Adc::new(p.ADC1);
79
80 let mut vrefint_channel = adc1.enable_vrefint().degrade_adc();
81 let mut pa0 = p.PC1.degrade_adc();
82 let regular_sequence = [
83 (&mut vrefint_channel, SampleTime::CYCLES247_5),
84 (&mut pa0, SampleTime::CYCLES247_5),
85 ]
86 .into_iter();
87
88 // Configure DMA for retrieving regular ADC measurements
89 let dma1_ch1 = p.DMA1_CH1;
90 // Using buffer of double size means the half-full interrupts will generate at the expected rate
91 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
99 adc1.set_regular_conversion_trigger(ADC1_REGULAR_TRIGGER_TIM1_TRGO2, Exten::RISING_EDGE);
100 adc1.set_injected_conversion_trigger(ADC1_INJECTED_TRIGGER_TIM1_TRGO2, Exten::RISING_EDGE);
101
102 // ADC must be started after all configurations are completed
103 adc1.start_injected_conversion();
104
105 // Enable interrupt at end of injected ADC conversion
106 adc1.enable_injected_eos_interrupt(true);
107
108 // Store ADC globally to allow access from ADC interrupt
109 critical_section::with(|cs| {
110 ADC1_HANDLE.borrow(cs).replace(Some(adc1));
111 });
112 // Enable interrupt for ADC1_2
113 unsafe { ADC1_2::enable() };
114
115 // Main loop for reading regular ADC measurements periodically
116 let mut data = [0u16; 2];
117 loop {
118 {
119 match ring_buffered_adc.read(&mut data).await {
120 Ok(n) => {
121 defmt::info!("Regular ADC reading, VrefInt: {}, PA0: {}", data[0], data[1]);
122 defmt::info!("Remaining samples: {}", n,);
123 }
124 Err(e) => {
125 defmt::error!("DMA error: {:?}", e);
126 ring_buffered_adc.clear();
127 }
128 }
129 }
130 }
131}
132
133/// Use ADC1_2 interrupt to retrieve injected ADC measurements
134/// Interrupt must be unsafe as hardware can invoke it any-time. Critical sections ensure safety
135/// within the interrupt.
136#[interrupt]
137unsafe fn ADC1_2() {
138 critical_section::with(|cs| {
139 if let Some(adc) = ADC1_HANDLE.borrow(cs).borrow_mut().as_mut() {
140 let injected_data = adc.clear_injected_eos();
141 info!("Injected reading of PA2: {}", injected_data[0]);
142 }
143 });
144}