cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F4 discovery - DMA2 TC interrupt does not triggered

chupys
Associate II

Hi, guys.

Here is short description of my project:

I have an ADC, that samples data via one regular channel and result sended via DMA. This DMA are working in Normal mode, so after DMA compete its transmission I processed received data and then enable next ADC conversion via restarting DMA.

For DMA I enable one interrupt TC and also enable DMA interrupt in NVIC. At the beginning its working fine, but after some time (after 209 calls of IRQ handler for DMA) DMA interrupt does not triggered.

In my project I also enable several more interrupts - interrupt from another DMA, interrupt from 2 different Timers, USB interrupts and SysTick interrupts. So I think it might be some problem with interrupts priority, but when I change the priority of required DMA, nothing change.

Does anybody have a clue what is going on?

Here is some code from my project. I use SPL for this project

ADC & DMA initialisation:

static void ADC_DriverInit(void)
{
    ADC_InitTypeDef ADC_InitStructure = { 0 };
    ADC_CommonInitTypeDef ADC_CommonStructure = { 0 };
 
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
 
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
    ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
    ADC_InitStructure.ADC_NbrOfConversion = 1;
    ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    ADC_Init(ADC1, &ADC_InitStructure);
 
    ADC1->CR2 &= ~(ADC_CR2_EXTSEL);
    ADC1->CR2 &= ~(ADC_CR2_EXTEN);
 
    ADC_CommonStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
    ADC_CommonStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_CommonStructure.ADC_Prescaler = ADC_Prescaler_Div4;
    ADC_CommonStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
    ADC_CommonInit(&ADC_CommonStructure);
 
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_480Cycles);
    ADC_DMARequestAfterLastTransferCmd(ADC1, DISABLE);
}
 
static void ADC_DMA_Init(void)
{
    DMA_InitTypeDef DMA_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
 
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
    
    DMA_DeInit(DMA2_Stream0);
    DMA_InitStructure.DMA_Channel = DMA_Channel_0;  
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(ADC1->DR));
    DMA_InitStructure.DMA_BufferSize = 64;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
 
    DMA_Init(DMA2_Stream0, &DMA_InitStructure);
 
    NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
 
    DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE);
}

At first I start ADC with this function:

void adc_on(void)
{
    uint32_t counter = 0;
 
    ADC_Cmd(ADC1, ENABLE);
 
    counter = (ADC_STAB_DELAY_US * (SystemCoreClock / 1000000U));
    while(counter != 0U)
    {
      counter--;
    }
}

Then, to start ADC conversion, I set up DMA and enable it; also here I enable DMA request from ADC:

void adc_start(uint16_t *samples_buffer, uint32_t samples_number)
{
    ADC_DMACmd(ADC1, ENABLE);
    DMA2_Stream0->M0AR = (uint32_t)samples_buffer;
    DMA2_Stream0->NDTR = samples_number;
    DMA_Cmd(DMA2_Stream0, ENABLE);
    ADC_SoftwareStartConv(ADC1);
}

IRQ handler for DMA looks like this:

void DMA2_Stream0_IRQHandler(void)
{
  if (DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0))
  {
    DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
 
      ADC_DMACmd(ADC1, DISABLE);
 
      // send received data to some function for processing
  }
  
}

After processing of received data I sampling new data via same ADC+DMA by calling adc_start function again. This cycle continue 209 times. But on 210 I only call adc_start but interrupt never come.

I am confuse

1 ACCEPTED SOLUTION

Accepted Solutions

You use continuous conversion mode

> ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;

it means, ADC never stops, and if you don't pick the converted values fast enough, overrun occurs.

One solution might be to use DMA in Circular mode, with double the buffer size perhaps, and use both the Half-Transfer and the Transfer-Complete interrupts to process data from the respective half of the buffer.

JW

View solution in original post

4 REPLIES 4

Read it and check/compare DMA and if needed, ADC and relevant NVIC, registers content.

JW

Ok. I`ve check it. It appears, that after some time ADC does not generate any DMA request because OVR bit is set (ADC_SR.OVR = 1). Not sure why. In interrupt I clear DMA bit in interrupt: ADC_DMACmd(ADC1, DISABLE);

As a solution I turn off ADC in interrupt, and enable it again, when I need new conversion to be performed. This solution is working, but it is not very optimal.

Can somebody propose better solution?

You use continuous conversion mode

> ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;

it means, ADC never stops, and if you don't pick the converted values fast enough, overrun occurs.

One solution might be to use DMA in Circular mode, with double the buffer size perhaps, and use both the Half-Transfer and the Transfer-Complete interrupts to process data from the respective half of the buffer.

JW

chupys
Associate II

@Community member​ yes, you a right. Thank you.

I thought, that as I clear DDS bit in CR2 ADC register, the ADC stops after last DMA transaction. I`ve read reference manual more carefully and find out that I was wrong - this bit just don't allow ADC to generate more DMA request after last DMA transaction.

DMA in Circular mode definitely can solve this but, unfortunately, DMA in Circular mode is not a solution for me, because this will lead to huge amount of redundant memory allocation. So instead I decided to disable continuous conversion mode of ADC in DMA interrupt and re enable it in adc_start function. Now my code looks like this.

ADC interrupt:

    void DMA2_Stream0_IRQHandler(void)
    {
      if (DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0))
      {
        DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
          
          ADC_ContinuousModeCmd(ADC1, DISABLE);    
          ADC_DMACmd(ADC1, DISABLE); // not sure, do I still need this here ????? 
     
          // send received data to some function for processing
      }
      
    }

and ADC start now looks like this:

    void adc_start(uint16_t *samples_buffer, uint32_t samples_number)
    {
        ADC_ContinuousModeCmd(ADC1, ENABLE);
        ADC_DMACmd(ADC1, ENABLE);
        DMA2_Stream0->M0AR = (uint32_t)samples_buffer;
        DMA2_Stream0->NDTR = samples_number;
        DMA_Cmd(DMA2_Stream0, ENABLE);
        ADC_SoftwareStartConv(ADC1);
    }

This code is working now. So far....