Skip to main content
M G
Associate
September 18, 2017
Question

ADC /DMA Problem STM32F100

  • September 18, 2017
  • 4 replies
  • 1086 views
Posted on September 18, 2017 at 13:15

ADC1 converts  every 500us 5 analog channels ( ADC10/11/12/13/14) and write the result via DMA1 to internal SRAM  (ADResult[5]) in this order [ ADC10,ADC11,ADC12,ADC13,ADC14]. This works fine, but randomly after some houres or days the result order in memory is corrupted to [ADC14,ADC10,ADC11,ADC12,ADC13], shift at one Position  and stay in this wrong order all the time.

ADC1 is configured in Scan Mode, not continuous conversion:

// ADC Konfiguration (regular group = ADC10/11/12/13/14

  RCC->APB2ENR|=RCC_APB2ENR_ADC1EN;  //enable Clock for ADC1

  ADC1->SQR1=0x00400000;    //5 Kanäle

  ADC1->SMPR1=0x00ffffff;    

  ADC1->SMPR2=0x3fffffff;    //Abstastzeit der Kanäle auf 240 Zyklen

  ADC1->SQR3=0x1ee6b16a;    //Abtastreihenfolge festlegen 10/11/12/13/14

  ADC1->CR1=0x00000100;                 //use independant mode, SCAN mode

  ADC1->CR2=0x000e0100;     //use data align right,not continuous conversion

                                              // EXTSEL = SWSTART

DMA1 Configuration:

// DMA-Konfiguration für A/D-Wandler

  RCC->AHBENR|=RCC_AHBENR_DMA1EN;  // enable Clock for DMA1

  DMA1_Channel1->CMAR=(u32)ADValue;  // DMA-Zieladresse setzen

  DMA1_Channel1->CPAR=(u32)&(ADC1->DR); // DMA-Quelladresse setzen

  DMA1_Channel1->CNDTR=5;    // transmit 5 words

  DMA1_Channel1->CCR=0x00003580;  // DMA konfig. (0x00000580)

  DMA1_Channel1->CCR|=DMA_CCR1_EN;  // DMA Channel 1 enable

The ADC conversion is started in the  TIM2_IRQHandler(), which is called every 500us:

 

void TIM2_IRQHandler(void)

{ .......

.........

  TEST_PIN3(1);

   ADC1->SR&=~ADC_SR_EOC;       // Interrupt-Flag clear

   ADC1->CR2  |=  0x00500000;      // start A/D conversion (0x00500000)

   ADC1->CR1|=ADC_CR1_EOCIE;       // EOC-Interrupt enable

   TEST_PIN3(0);

.....

.....

}After the A/D conversion of this 5 Inputs is finished  ADC1_IRQHandler/() is called. Tme between start A/D conversion

and call of ADC1_IRQHandler() is 100us.

void ADC1_IRQHandler(void)

{

 .........

  TEST_PIN3(1);

  ADC1->SR&=~ADC_SR_EOC;     // Interrupt-Flag löschen

  ADC1->CR1&=~ADC_CR1_EOCIE;    // EOC-Interrupt sperren

  while(!(DMA1->ISR & 0x02UL));    // Warten auf Ende des DMA-Transfers

  DMA1->IFCR=0x02UL;      // Flag löschen

  //DMA-Transferzähler nachladen

  DMA1_Channel1->CCR&=~(1<<0);    // DMA Channel 1 disable

  DMA1_Channel1->CNDTR=5;     // transmit 5 words

  DMA1_Channel1->CCR|=(1<<0);    // DMA Channel 1 enable

........

}

Has anyone encountered a same Problem ?

Please help.

    This topic has been closed for replies.

    4 replies

    AvaTar
    Senior III
    September 18, 2017
    Posted on September 18, 2017 at 13:45

    I would guess another interrupt messes up your chain, i.e. once in a while blocks the ADC interrupt long enough.

    IMHO the method is sub-optimal.

    For multiple ADC channels with DMA, I let the ADC-->DMA handle the sampling and copying automatically, and configure an DMA_TC interrupt (transmission complete), to get notified when a new set of data is available.

    No intermediate interrupt, thus no race-condition issues ...

    The method is demonstrated in the (old) SPL peripheral examples, BTW.

    M G
    M GAuthor
    Associate
    September 18, 2017
    Posted on September 18, 2017 at 14:58

    Hi AvaTar

    Thank You for Your reply. OK, i will use the 'DMA TransferComplete Interrupt'  instead of the 'ADC Interrupt'  to get notify about a new set of ADC-Data.

    I have modified my code in this manner and will test it. now for several days.

    // DMA-Konfiguration für A/D-Wandler

      RCC->AHBENR|=RCC_AHBENR_DMA1EN;  // enable Clock for DMA1

      DMA1_Channel1->CMAR=(u32)ADValue;  // DMA-Zieladresse setzen

      DMA1_Channel1->CPAR=(u32)&(ADC1->DR); // DMA-Quelladresse setzen

      DMA1_Channel1->CNDTR=5;    // transmit 5 words

      DMA1_Channel1->CCR=0x00003580;  // DMA konfig. (0x00000580)

      DMA1_Channel1->CCR|=DMA_CCR1_EN;  // DMA Channel 1 enable

      // Update 18.09.2017

      DMA1_Channel1->CCR|=DMA_CCR1_TCIE;    // TC-Interrupt freigeben

      NVIC->ISER[0] |= (1 << (DMA1_Channel1_IRQn  & 0x1F));  // enable interrupt

      NVIC->IP[DMA1_Channel1_IRQn]|=0x20;   // Priorität  Level 2

     

      // Update 18.09.2017 Ende

    void TIM2_IRQHandler(void)

    { ......

     TEST_PIN3(1);

     

        ADC1->SR&=~ADC_SR_EOC;       // Interrupt-Flag löschen

     

        ADC1->CR2  |=  0x00500000;      // AD-Wandlung starten  (0x00500000)

     

      //  ADC1->CR1|=ADC_CR1_EOCIE;       // EOC-Interrupt aktivieren

     

        TEST_PIN3(0);

    .........

    }

    void DMA1_Channel1_IRQHandler(void)

    //void ADC1_IRQHandler(void)

    {

      u32 i, sum;

      TEST_PIN3(1);

     // ADC1->SR&=~ADC_SR_EOC;     // Interrupt-Flag löschen

     // ADC1->CR1&=~ADC_CR1_EOCIE;    // EOC-Interrupt sperren

     // while(!(DMA1->ISR & 0x02UL));    // Warten auf Ende des DMA-Transfers

      DMA1->IFCR=0x02UL;      // Flag löschen

      //DMA-Transferzähler nachladen

      DMA1_Channel1->CCR&=~(1<<0);    // DMA Channel 1 disable

      DMA1_Channel1->CNDTR=5;     // transmit 5 words

      DMA1_Channel1->CCR|=(1<<0);    // DMA Channel 1 enable
    AvaTar
    Senior III
    September 18, 2017
    Posted on September 18, 2017 at 15:58

    Sporadic errors, like the one you described, are a real PITA to find/debug.

    It can take days or weeks to find the issue, meaning thousands of bucks in a commercial environment ...

    If you still want/need to find the original issue, you can either use instrumentation (requires some founded assumptions), or run in the debugger (with a proper breakpoint) and 'trace' backwards.

    The latter might become difficult with asynchronous code (interrupts).