2017-09-18 04:15 AM
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 = SWSTARTDMA1 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 enableThe 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.
2017-09-18 04:45 AM
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.
2017-09-18 07:58 AM
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 Endevoid 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 enable2017-09-18 08:58 AM
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).
2017-09-26 06:06 AM
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.