cancel
Showing results for 
Search instead for 
Did you mean: 

ADC /DMA Problem STM32F100

M G
Associate II
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.

4 REPLIES 4
AvaTar
Lead
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
Associate II
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
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).

M G
Associate II
Posted on September 26, 2017 at 13:06

Hi AvaTar,

Your recommended solution (using 'DMA TransferComplete Interrupt'  instead of the 'ADC Interrupt') seems to work.

10 devices run now for more then one week without malfunction.

 Thank You for your recommendation.