Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem with runtime-configurable continuous ADC acquisition with DMA #484

Open
wzab opened this issue Apr 11, 2024 · 0 comments
Open

Problem with runtime-configurable continuous ADC acquisition with DMA #484

wzab opened this issue Apr 11, 2024 · 0 comments

Comments

@wzab
Copy link

wzab commented Apr 11, 2024

I'm writing a firmware where the ADC should be runtime configured for sampling the selected set of inputs (configured as regular sequence) with the selected frequency. Data should be transferred via DMA, and the measurement should be triggered via a timer.

I have found that the current API is extremely inconvenient for that.
For with_scan_dma, I need to provide my own implementation of SetChannels (example here).
However that implementation does not allow using runtime-configurable list of inputs.
I have worked it arround implementing code like this:

static ADC_CHANS : Vec<u8, { sizes::MAX_CHANNELS }> = Vec::new();
...
pub struct AdcPins {}
  
impl SetChannels<AdcPins> for Adc<ADC1> {
    fn set_samples(&mut self) {
        for v in &crate::ADC_CHANS {
        self.set_channel_sample_time(*v, adc::SampleTime::T_28);
        }
    }
    fn set_sequence(&mut self) {
        self.set_regular_sequence(crate::ADC_CHANS.as_slice());
        // Optionally we can set continuous scan mode
        self.set_continuous_mode(true);
        // Also we can use discontinuous conversion (3 channels per conversion)
        self.set_discontinuous_mode(Some(u8::try_from(crate::ADC_CHANS.len()).unwrap()));
    }
}

The sampling is delegated to the async software task in rtic v2. The ADC and DMA is initialized in init, and passes as Locale to that task.

        //Initialization in init
        let mut dma_ch1 = ctx.device.DMA1.split().1;
        let mut adc1 = adc::Adc::adc1(ctx.device.ADC1, clocks);

Than the task is running a loop where it receives the CONFIGURE, START, and STOP command.
After START it configures the ADC and DMA, starts the transfer and waits for STOP.
However, the problem is that I can't perform proper configuration due to ownership problems:

                     // Configure ADC and DMA
                        adc1.with_scan_dma(AdcPins {}, dma_ch1);

The code above generates:

error[E0308]: mismatched types
   --> src/main.rs:233:56
    |
233 |                         adc1.with_scan_dma(AdcPins {}, dma_ch1);
    |                              -------------             ^^^^^^^ expected `C1`, found `&mut C1`
    |                              |
    |                              arguments to this method are incorrect

If i modify it to:

                        // Configure ADC and DMA
                        adc1.with_scan_dma(AdcPins {}, *dma_ch1);

I get:

error[E0507]: cannot move out of `*adc1` which is behind a mutable reference
   --> src/main.rs:233:25
    |
233 |                         adc1.with_scan_dma(AdcPins {}, *dma_ch1);
    |                         ^^^^ ----------------------------------- `*adc1` moved due to this method call
    |                         |
    |                         move occurs because `*adc1` has type `Adc<ADC1>`, which does not implement the `Copy` trait
    |
note: `Adc::<ADC1>::with_scan_dma` takes ownership of the receiver `self`, which moves `*adc1`
   --> /home/emb/.cargo/registry/src/index.crates.io-6f17d22bba15001f/stm32f1xx-hal-0.10.0/src/adc.rs:814:1
    |
814 | / adcdma! {
815 | |     pac::ADC1: (
816 | |         AdcDma1,
817 | |         dma1::C1,
818 | |     )
819 | | }
    | |_^
    = note: this error originates in the macro `adcdma` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0507]: cannot move out of `*dma_ch1` which is behind a mutable reference
   --> src/main.rs:233:56
    |
233 |                         adc1.with_scan_dma(AdcPins {}, *dma_ch1);
    |                                                        ^^^^^^^^ move occurs because `*dma_ch1` has type `stm32f1xx_hal::dma::dma1::C1`, which does not implement the `Copy` trait

I can work it around, by passing the reference to the RegisterBlock for ADC and DMA to the data acquisition task, like done (for STM32F3...) in this example. Certain modifications for porting from STM32F3... to STM32F1... are needed, but generally this approach looks promissing.
At least the below initialization does not lead to errors:

        //Initialize ADC and DMA to make sure that proper clocks are enabled
        let mut dma_ch1 = ctx.device.DMA1.split().1;
        let mut adc1 = adc::Adc::adc1(ctx.device.ADC1, clocks);
        
        //Prepare the register blocks to be passed as `Local` to the data acquiring task
        let r_adc1 = unsafe { &mut *(ADC1::ptr() as *mut _) };
        let r_dma1 = unsafe { &mut *(DMA1::ptr() as *mut _) };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant