Page 1 of 2

STM32F4 DMA Peripheral-to-Memory: Memory Corruption

Posted: Tue Apr 17, 2018 8:24 pm
by Skadi
Hi,

I designed a PCB including a STM32F429, which is connected to an two channels (16Bit) simultaneous sampling ADC. The ADC has two inputs, which are sampled at the same time instance. The results are interleaved clocked out on the rising edge of an data clock source provided by the ADC. This clock source is attached to PA9, and used in combination with the timer TIM1_CH2 to trigger the DMA. The timer is configured as followed:

Code: Select all

    // TIM1_CH2
    palSetPadMode(GPIOA, 9, PAL_MODE_ALTERNATE(1));

    rccEnableTIM1(FALSE);
    rccResetTIM1();

    // enable  DMA2 clock
    RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;

    // 6  (TS): Trigger selection: Input 2 (TI2FP2)
    // 4 (SMS): Reset Mode
    TIM1->SMCR |= 0x0064;

    // CC2 channel is configured as input, IC2 is mapped on TI2 -> input capture
    TIM1->CCMR1 |= 0x0100;

    // CC2 capture enabled
    TIM1->CCER |= STM32_TIM_CCER_CC2E;

    // CC2 DMA request enabled
    TIM1->DIER |= STM32_TIM_DIER_CC2DE;
    TIM1->CR2 |= STM32_TIM_CR2_CCDS;

    // Enable Counter
    TIM1->CR1 |= STM32_TIM_CR1_CEN;

    // Polarity: falling  edge (data is clocked out on the rising edge)
    // -> sampling on the falling edge of the data clock source
    // -> in the middle of the data state (HIGH or LOW)
    TIM1->CCER |= STM32_TIM_CCER_CC2P;
    TIM1->CCER &= ~STM32_TIM_CCER_CC2NP;


To test the system, two sinewaves with a phaseshift of 90° are used, which are provided by a two channel laboratory AWG. Here is the DMA configuration:

Code: Select all

static uint16_t ADC_buffer[60000] = {0};  // static global memory
   
   static void ADCsampling(void) {


      memset(ADC_buffer, 0, sizeof ADC_buffer);   // ensures "empty" buffer, for multiple uses

      uint16_t samples = 25000; // an abritrary sample number


     // Allocate the stream
     dmaStreamAllocate(STM32_DMA2_STREAM2, 0, NULL, NULL);

     // Set the source Address
     dmaStreamSetPeripheral(STM32_DMA2_STREAM2, &(GPIOD->IDR));

     // config 16-bit HWORD transfers, peripheral to memory,
     // fixed source address, inc dest address
     dmaStreamSetMode( STM32_DMA2_STREAM2,
                       STM32_DMA_CR_PL(3) | STM32_DMA_CR_PSIZE_HWORD |
                       STM32_DMA_CR_MSIZE_HWORD | STM32_DMA_CR_DIR_P2M |
                       STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE |
                       STM32_DMA_CR_CHSEL(6));

          // Set the destination address
          dmaStreamSetMemory0(STM32_DMA2_STREAM2, ADC_buffer);

          // set the size of the output buffer
          dmaStreamSetTransactionSize(STM32_DMA2_STREAM2, samples);
         
          chThdSleepMilliseconds(100);
          dmaStreamSetFIFO(STM32_DMA2_STREAM2,0);
         
          chThdSleepMilliseconds(100);
          dmaStreamEnable(STM32_DMA2_STREAM2);

    // waite until all samples are transmitted:
    // NDT = 0 and sets TCIF2 HIGH
          while ((DMA2->LISR & 0x00200000) == 0){

          }

     // check if a transfere error occured (TEIF2)
          if ((DMA2->LISR & 0x00080000) == 1){
                        palTogglePad(GPIOB, 10);      // LED is connected to this Pin
                      }

          chThdSleepMilliseconds(100);
         
                SEND DATA TO PC. // LWIP is used, for simplicity the corresponding code is removed
         
          dmaStreamDisable(STM32_DMA2_STREAM2);
          dmaStreamRelease(STM32_DMA2_STREAM2);
}

The results of the two simultanous ADC stages are interleaved outputed. At the PC i can easily split this data stream to represent each channel.

To come to the problem now. I receive two clear/good looking sine waves whit the correct amplitude. However, the phase gets corrupted. The ADC samples its inputs continously and clocks the data out, the whole time. If I perform a DMA instruction several times, somtimes I get the correct phase of 90°. But most times there is a phase shift, which corresponds to ONE sample instance. I plotted the two sinewaves in one graph. For the corruped phase, I observed (sometimes) a mutual sample value at the begin of the data stream. So it looks like one ADC result is shifted by one sample.

Have I configured the Timer or the DMA wrong? I cannot explain this strange behaviour, I'm operating the system at a fairly low data clock rate of ~1.5 MHz. For me it looks like, in the beginning went something wrong with the memory pointer, but I don't know how to check this.

Maybe someone has a clue, what I'm making wrong :? .

BR Skadi

Re: STM32F4 DMA Peripheral-to-Memory: Memory Corruption

Posted: Tue Apr 17, 2018 8:51 pm
by Giovanni
No clue really, it could be a problem with DMA triggering. Make sure to have a small delay after enabling a peripheral clock, the effect of the RCC operation is not immediate.

Giovanni

Re: STM32F4 DMA Peripheral-to-Memory: Memory Corruption

Posted: Wed Apr 18, 2018 12:38 pm
by Skadi
Hi Giovanni,

I analyzed my fetched in data further. I figured out what went wrong. As I already mentioned, the data of the two ADC channels are interleaved clocked out on the rising edge of the clock line. The correct data stream for 10 consecutive data would look like as following:

CH1(t1) CH2(t1) CH1(t2) CH2(t2) CH1(t3) CH2(t3) CH1(t4) CH2(t4) CH1(t5) CH2(t5)

CH1(tx) and CH2(tx) are representing the data of channel 1 and channel 2 at time instance tx, respectively.

What happened, is that the data stream starts with CH2(tx)...

CH2(t1) CH1(t2) CH2(t2) CH1(t3) CH2(t3) CH1(t4) CH2(t4) CH(t5) CH2(t5) CH(t6)

resulting in the shifted result by means of ONE sample.


Luckily, the ADC provides an further clock line, indicating the start of two related consecutive data. In detail, a rising edge indicates the transmission of e.g. CH1(t3) CH2(t3). Because I'm working at a fairly low data output rate, I simply check the state of the Pin by

Code: Select all

    // Check current Pin state to ensure a LOW to HIGH transition
          while (palReadPad(GPIOC, 6) == 1){
            if(palReadPad(GPIOC, 6) == 0){
              break;
            }
          }

          while (palReadPad(GPIOC, 6) != 1){
            //Wait until a new time instance is clocked out
          }


Now the results look good, by means of the correct phase shift. Nevertheless the first fetched in data is sometimes still corrupted, as can be seen HERE. The phase isn't distorted by this wrong sample.

I can neglect the first (two) samples and I'm fine. But I'm still curious, why the first two fetched in data, have the same value.

BR

Re: STM32F4 DMA Peripheral-to-Memory: Memory Corruption

Posted: Wed Apr 18, 2018 1:23 pm
by Giovanni
You should verify if there is an errata about DMAs or ADCs that could explain this, also consider that it could be a new errata or not disclosed yet.

Giovanni

Re: STM32F4 DMA Peripheral-to-Memory: Memory Corruption

Posted: Fri Apr 20, 2018 9:05 am
by rew
I would think that it is much more likely that some sequence of events causes a trigger or something like that which makes the DMA module run one cycle before you really want it.

Re: STM32F4 DMA Peripheral-to-Memory: Memory Corruption

Posted: Fri Apr 20, 2018 9:07 am
by Giovanni
Are DMA channels fully stopped when you enable them?

Giovanni

Re: STM32F4 DMA Peripheral-to-Memory: Memory Corruption

Posted: Tue Apr 24, 2018 11:37 am
by Skadi
Hi,

@ rew: To avoid an too early data acquisition, and therefor inccorect data (pair) reading, I added the following code lines. Which proofs the state of an additional ADC clock, indicating two now samples (for the same time instance). So I ensure the correct LOW to HIGH transition at this pin, than I enable the DMA stream. I have no clue how this curruped data may be fetched in, before the DMA stream is enabled.

Code: Select all

          // Check current Pin state to ensure a LOW to HIGH transition
          while (palReadPad(GPIOC, 6) == 1){
            if(palReadPad(GPIOC, 6) == 0){
              break;
            }
          }

          while (palReadPad(GPIOC, 6) != 1){
            //Wait until a new time instance is clocked out
          }

          dmaStreamEnable(STM32_DMA2_STREAM2);

          while ((DMA2->LISR & 0x00200000) == 0){
            // wait until all samples are transmitted
            // -> NDT = 0 and sets TCIF2 HIGH
          }

          // check if a transfer error occurred (TEIF2)
          if ((DMA2->LISR & 0x00080000) == 1){
               palTogglePad(GPIOB, 10); // Toggle LED if transfere an error occured
              }


@ Giovanni: Yes, the stream is disabled and released by:

Code: Select all

          dmaStreamDisable(STM32_DMA2_STREAM2);
          dmaStreamRelease(STM32_DMA2_STREAM2);


BR Skadi

Re: STM32F4 DMA Peripheral-to-Memory: Memory Corruption

Posted: Wed Apr 25, 2018 1:03 pm
by wurstnase
It is over one year ago I've worked on the ADC with a Nucleo F441RE.
I had the same issue.

Finally I solved this and I guess that this is the right order:
Init DMA
Init ADC
Start ADC with:

Code: Select all

  ADC1->CR2 |= ADC_CR2_DMA |
               ADC_CR2_CONT |
               ADC_CR2_SWSTART;


In my case I stopped the conversion, when the DMA transfer was complete in its interrupt:

Code: Select all

void DMA2_Stream4_IRQHandler(void) {
  DMA2->HIFCR = DMA_HIFCR_CTCIF4;
  ADC1->CR2 &= ~(ADC_CR2_CONT | ADC_CR2_DMA);
}


In my case it's very low level. But this should be similar with ChibiOS.

Re: STM32F4 DMA Peripheral-to-Memory: Memory Corruption

Posted: Mon Jan 28, 2019 6:55 pm
by Skadi
Hi,

I have designed a new PCB including a STM32F4 connected to an external simultaneous sampling ADC.
The ADC provides the data also interleaved on a parallel port.

Unfortunately this ADC provides a single clock, so I have no further possebility to double check the data output clock state.

I'm using the same code, stated in my initial post. Although the timer is configured for a falling edge it seems the data is fetched in arbitrary.

As assumed by rew somthing with the DMA triggereing seems to be wrong. Has anyone an idea how to fix my problem? The external ADC clock has only a freuqnecy of about 1.5 MHz, so I do not think that's the problem.

I'm thankful for any kind of advice!

BR
Skadi

Re: STM32F4 DMA Peripheral-to-Memory: Memory Corruption

Posted: Tue Jan 29, 2019 12:30 am
by FXCoder
Hi,
Some thoughts...
To use the timer purely as a DMA trigger there is no need to involve Slave Mode.
You don't care about the timer value in such a use case. Just the trigger event.
So just leave SMCR reset.
Capture Input mode will produce a CC event at the edge transition(s) of the timer trigger input (according to how the edge mode is set of course).
Your existing TIM1 settings look OK apart from setting SMCR.

BTW to have ChibiOS enable the DMA for you (in the case there is no other peripheral set that uses DMA) add -DSTM32_DMA_REQUIRED to the UDEFS section of your makefile.

The define of STM32_DMA2_STREAM2 isn't shown in the code snippets but I assume it is...
#define STM32_DMA2_STREAM2 STM32_DMA_STREAM_ID(2, 2)

Probably good to add some error checking in the code.
e.g. check the result of dmaStreamAllocate()
--
Bob