embassy-rs / embassy

Modern embedded framework, using Rust and async.
https://embassy.dev
Apache License 2.0
5.66k stars 790 forks source link

stm32f1 ADC example does not work #2162

Open katyo opened 1 year ago

katyo commented 1 year ago

I run ADC example on stm32f103c8 but seems it does not works as expected.

It just awaits infinitely here: https://github.com/embassy-rs/embassy/blob/94586576a06cf5869d0cdea6d678a0448bb1fdf8/examples/stm32f1/src/bin/adc.rs#L26

GDB shows me that wfe instruction was executed last but seems no events happening.

marsjo commented 9 months ago

I run into the same problem but actually only if I use the crates. If I use the dependencies from path it works fine for me. Did you fix it for you?

pfzetto commented 9 months ago

Same issue here on a stm32f103c6 (Infinite wait on adc.read()).

I run into the same problem but actually only if I use the crates. If I use the dependencies from path it works fine for me. Did you fix it for you?

Using the suggested method with 3638df789e4f498b9058bbeb27bdddab4a80bd49 I can read the adc value one time for each ADC. On the second invocation it hangs again. The output of the ADC-example for stm32f1 is:

0.000000 TRACE BDCR ok: 00008200
└─ embassy_stm32::rcc::bd::{impl#2}::init @ /home/paulz/src/embedded/embassy/embassy-stm32/src/fmt.rs:117
0.000000 DEBUG rcc: Clocks { hclk1: Some(Hertz(8000000)), pclk1: Some(Hertz(8000000)), pclk1_tim: Some(Hertz(8000000)), pclk2: Some(Hertz(8000000)), pclk2_tim: Some(Hertz(8000000)), rtc: Some(Hertz(40000)), sys: Some(Hertz(8000000)), usb: None }
└─ embassy_stm32::rcc::set_freqs @ /home/paulz/src/embedded/embassy/embassy-stm32/src/fmt.rs:130
0.000030 INFO  Hello World!
└─ embassy_stm32f1_examples::____embassy_main_task::{async_fn#0} @ src/main.rs:19

The code hangs on the first loop iteration because read got called for vref.

The issue seems to be timing related. Adding a Timer::after_ticks(1).await; to https://github.com/embassy-rs/embassy/blob/3638df789e4f498b9058bbeb27bdddab4a80bd49/embassy-stm32/src/adc/f1.rs#L126 fixed the issue xD

Edit: even a Timer::after_ticks(1).await; in L124 fixes the issue.

nuuskamummu commented 5 months ago

Just spent the evening getting stuck on the same thing. Turns out, for me it was some compiler flags. If I remove the config below, the ADC works.

[profile.dev] lto = true opt-level = 1 //doesnt matter what level, won't work.

45ninjas commented 2 months ago

ADC example functions when built without the release profile, or if opt-level = 0.

xokdvium commented 1 month ago

Actually this looks like it's caused entirely by the #3166. The interrupt simply does not get unmasked in the NVIC. I can get the example to run correctly with the patch on release builds:

diff --git a/examples/stm32f1/src/bin/adc.rs b/examples/stm32f1/src/bin/adc.rs
index 541ff159e..417730a4a 100644
--- a/examples/stm32f1/src/bin/adc.rs
+++ b/examples/stm32f1/src/bin/adc.rs
@@ -4,6 +4,8 @@
 use defmt::*;
 use embassy_executor::Spawner;
 use embassy_stm32::adc::Adc;
+use embassy_stm32::interrupt;
+use embassy_stm32::interrupt::InterruptExt;
 use embassy_stm32::peripherals::ADC1;
 use embassy_stm32::{adc, bind_interrupts};
 use embassy_time::Timer;
@@ -22,6 +24,11 @@ async fn main(_spawner: Spawner) {
     let mut pin = p.PB1;

     let mut vrefint = adc.enable_vref();
+
+    unsafe {
+        interrupt::ADC1_2.enable();
+    }
+
     let vrefint_sample = adc.read(&mut vrefint).await;
     let convert_to_millivolts = |sample| {
         // From http://www.st.com/resource/en/datasheet/CD00161566.pdf

If I drop the interrupt::ADC1_2.enable() I observe the expected hang, since no ADC1_2 handler actually gets called. I have no clue why this code sometimes works on debug builds more often than on release ones. Sorry, I'm not at all familiar with the code base so my thinking can be waaay off. But my guess is that the poll from .convert gets called since some other interrupt has occured in the meantime? Looks like that's why Timer::after_ticks(1).await helps in this case - it polls the task and the EOC flag is already set, so the Poll is ready:

if !T::regs().cr2().read().swstart() && T::regs().sr().read().eoc() {
    Poll::Ready(())
} else {

This should be an easy fix in the driver either way by actually unmasking the EOC interrupt like all other ADC drivers do. P.S. I'll work on the fix and upstreaming it. Should be trivial