aboutsummaryrefslogtreecommitdiff
path: root/examples/stm32g4/src/bin/adc_injected_and_regular.rs
diff options
context:
space:
mode:
Diffstat (limited to 'examples/stm32g4/src/bin/adc_injected_and_regular.rs')
-rw-r--r--examples/stm32g4/src/bin/adc_injected_and_regular.rs154
1 files changed, 154 insertions, 0 deletions
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..1e97fa925
--- /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, Default::default());
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}