cancel
Showing results for 
Search instead for 
Did you mean: 

Efficient de-interleaving without waking the CPU

Olly
Associate II

I am using the STM32G474 and have the following situation:

  1. I have configured a Timer to run continuously at 25 kHz
  2. The Timer triggers the ADC to start a sequence of 6 conversions (i.e. 6 ADC channels)
  3. Upon completion of each ADC conversion, DMA transfers the sample to memory (repeat for the entire sequence of conversions)
  4. Repeat steps 2-3 until the buffer is full (ADC DMA half-conversion/full event & double buffering is used)
  5. This buffer now contains interleaved samples from the ADC
  6. Some magic de-interleaving needs to happen
  7. A de-interleaved buffer of ADC samples (i.e. for a single ADC channel) is passed to the FMAC (filtering) peripheral using DMA
  8. The output of the FMAC peripheral is transferred to memory via DMA
  9. Repeat steps 7-8 until the sample buffers of all 6 ADC channels have been filtered via FMAC

Ideally I want to keep the CPU asleep for this whole process; the CPU should only wake up at ~100 Hz to process the filtered data (i.e. FMAC output stored in memory) and perform other functions. However I'm unable to determine a method for de-interleaving the interleaved ADC samples from memory (step 6) without waking the CPU.

The perfect solution is if either DMA or FMAC allows me to set the 'stride length/address increment size' to 6, however this doesn't appear to be possible on the STM32G4.

Am I missing anything? What method(s) can I use to de-interleave a buffer of ADC samples from memory without CPU intervention? (or directly from ADC --> FMAC?)


Edit 2023/08/14: Reworded Step 3 for clarity, as per MasterT's comment.
Original text: "3. Upon completion of the sequence of conversions, DMA transfers the entire sequence to a buffer in memory"

1 ACCEPTED SOLUTION

Accepted Solutions

Thanks JW, this is exactly the kind of answer I was looking for :party_popper:

I don't think the proposed solution is entirely possible (only 1 DMA channel can be used per ADC on the STM32G4), however your idea helped trigger a similar solution that may be possible (edit: not possible, see note at the bottom).

My current de-interleaving plan:

  1. ADC acquires a sequence of 6 samples
  2. ADC DMA transfers this sequence of samples to memory (buffer size of 6 samples, in circular mode)
  3. After the 6th sample is transferred to memory, the 'ADC DMA full/conversion complete' event is used to trigger 6 separate DMA channels to transfer from memory to memory (i.e. from the interleaved buffer to 6 individual de-interleaved buffers)
  4. These 6 DMA channels are configured as follows:
    • Each DMA channel has a different start address within the interleaved buffer (i.e. each DMA channel is responsible for transferring the sample for a single ADC channel)
    • Don't increment source address (interleaved buffer)
    • Increment destination address (de-interleaved buffer)
  5. Steps 1-4 are repeated until the individual de-interleaved buffers are full
  6. DMA is used to pass each de-interleaved buffer to FMAC in turn

 

Edit 2023/08/16: The above plan is not possible for the following reasons:

  • We cannot trigger 6 DMA de-interleaving transfers as it appears that 1 trigger can only trigger 1 DMA channel
  • The destination address is incremented within the block transfer, not between requests

 

View solution in original post

5 REPLIES 5
RhSilicon
Lead

6. Some magic de-interleaving happens

magic-memeInteresting to know that the STM32 can do magic.

TDK
Guru

There's no magic solution here. You need the CPU to de-interleave channels.

If you feel a post has answered your question, please click "Accept as Solution".

> 3. Upon completion of the sequence of conversions, DMA transfers the entire sequence to a buffer in memory

No. DMA transfers individually each sample when it's ready.

Instead of a DMA triggered from ADC, You can run 6 DMAs triggered from timers generating appropriately staggered signals, to transfer from ADC->DR each to its individual buffer in memory.

I don't use 'G4 so don't know if there's enough DMAs and timers to do this.

JW

Thanks JW, this is exactly the kind of answer I was looking for :party_popper:

I don't think the proposed solution is entirely possible (only 1 DMA channel can be used per ADC on the STM32G4), however your idea helped trigger a similar solution that may be possible (edit: not possible, see note at the bottom).

My current de-interleaving plan:

  1. ADC acquires a sequence of 6 samples
  2. ADC DMA transfers this sequence of samples to memory (buffer size of 6 samples, in circular mode)
  3. After the 6th sample is transferred to memory, the 'ADC DMA full/conversion complete' event is used to trigger 6 separate DMA channels to transfer from memory to memory (i.e. from the interleaved buffer to 6 individual de-interleaved buffers)
  4. These 6 DMA channels are configured as follows:
    • Each DMA channel has a different start address within the interleaved buffer (i.e. each DMA channel is responsible for transferring the sample for a single ADC channel)
    • Don't increment source address (interleaved buffer)
    • Increment destination address (de-interleaved buffer)
  5. Steps 1-4 are repeated until the individual de-interleaved buffers are full
  6. DMA is used to pass each de-interleaved buffer to FMAC in turn

 

Edit 2023/08/16: The above plan is not possible for the following reasons:

  • We cannot trigger 6 DMA de-interleaving transfers as it appears that 1 trigger can only trigger 1 DMA channel
  • The destination address is incremented within the block transfer, not between requests

 


@Olly wrote:

Thanks JW, this is exactly the kind of answer I was looking for :party_popper:

I don't think the proposed solution is entirely possible (only 1 DMA channel can be used per ADC on the STM32G4), however your idea helped trigger a similar solution that may be possible.

My current de-interleaving plan:

  1. ADC acquires a sequence of 6 samples
  2. ADC DMA transfers this sequence of samples to memory (buffer size of 6 samples, in circular mode)
  3. After the 6th sample is transferred to memory, the 'ADC DMA full/conversion complete' event is used to trigger 6 separate DMA channels to transfer from memory to memory (i.e. from the interleaved buffer to 6 individual de-interleaved buffers)
  4. These 6 DMA channels are configured as follows:
    • Each DMA channel has a different start address within the interleaved buffer (i.e. each DMA channel is responsible for transferring the sample for a single ADC channel)
    • Don't increment source address (interleaved buffer)
    • Increment destination address (de-interleaved buffer)
  5. Steps 1-4 are repeated until the individual de-interleaved buffers are full
  6. DMA is used to pass each de-interleaved buffer to FMAC in turn

 

 


  1. I have configured a Timer to run continuously at 25 kHz
  2. The Timer triggers the ADC to start a sequence of 6 conversions (i.e. 6 ADC channels)
  3. Upon completion of the sequence of conversions, DMA transfers the entire sequence to a buffer in memory

I see a deep flow in this statement, DMA doesn't worked on sequence frame, it's called after each sample out of 6 is converted. Otherwise DR of the ADC is overwritten by next sample in sequence. 

Just assign 30kbytes of the buffer and do processing on 10 Hz base.

The issue may be if not sufficient memory available, than as Waclawek suggest, you can assign 6 DMA channels triggered by timers to transfer each sample to 6 different buffers. DMA wouldn't even know that data is comming from adc, for DMA  data register of the ADC is no different than any memory cell. Though, correct sync requirent in this case to split 6 triggers into 6 phases synchronously with ADC sampling rate. HRTIM may be an option, but it looks overcomplicated solution in any way