cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F030 DMA interrupt not working

Posted on October 09, 2016 at 23:07

Hi,

     We have a design using an STM32F030 and we have three analog channels attached to the ADC. We are currently not using a timer to start the conversions(doing it manually.) The problem is that I initially set up a DMA interrupt but it never fired. I then set up a ADC interrupt on the end of sequence. The ADC interrupt fires after all three channels have been converted but we do not see the DMA interrupt fire. The data is DMA'ed into the DMA buffer, just no interrupt. What have I mis-configured?

Thanks,

     John C.

int main(void)

{

    /* GPIOA-C Periph clock enable */

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOF, ENABLE);

        RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

        RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

    /* Enable SYSCFG clock */

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

    /* DMA1 clock enable */

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE);

    // Configure the PortA(PA) analog input pins

    GPIO_InitStructure.GPIO_Pin        = CADCELL | LINE | CURRENT;

    GPIO_InitStructure.GPIO_Mode    = GPIO_Mode_AN;

    GPIO_InitStructure.GPIO_OType     = GPIO_OType_PP;

    GPIO_InitStructure.GPIO_Speed     = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_PuPd    = GPIO_PuPd_NOPULL;

    GPIO_Init(GPIOA, &GPIO_InitStructure);

    ADC_InitStruct.ADC_Resolution            = ADC_Resolution_10b;

    ADC_InitStruct.ADC_ScanDirection        = ADC_ScanDirection_Upward;

    /* Initialize ADC */

    ADC_Init(ADC1, &ADC_InitStruct);

    /* Enable ADC */

    ADC_GetCalibrationFactor(ADC1);

    ADC_ITConfig(ADC1 ,ADC_IT_EOSEQ, ENABLE);

    NVIC_EnableIRQ(ADC1_IRQn); /* (7) */

    NVIC_SetPriority(ADC1_IRQn,0); /* (8) */

    ADC_DMACmd(ADC1, ENABLE);

    ADC_Cmd(ADC1, ENABLE);

    //Wait for ADC to be ready

    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADRDY));

    //Set ADC conversion channel sample time

    ADC_ChannelConfig(ADC1, ADC_Channel_0, ADC_SampleTime_28_5Cycles);

    ADC_ChannelConfig(ADC1, ADC_Channel_2, ADC_SampleTime_28_5Cycles);

    ADC_ChannelConfig(ADC1, ADC_Channel_4, ADC_SampleTime_28_5Cycles);

    DMA_InitTypeDef   DMA_InitStructure;

    /* DMA1 Channel1 Config */

    DMA_DeInit(DMA1_Channel1);

    //DMA_InitStructure.DMA_PeripheralBaseAddr    = (uint32_t)ADC1_DR_Address;

    DMA_InitStructure.DMA_PeripheralBaseAddr    = (uint32_t)&(ADC1->DR);

    DMA_InitStructure.DMA_MemoryBaseAddr        = (uint32_t)dmaBuff;

    DMA_InitStructure.DMA_DIR            = DMA_DIR_PeripheralSRC;

    DMA_InitStructure.DMA_BufferSize        = 3;

    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_Circular;

    DMA_InitStructure.DMA_Priority            = DMA_Priority_High;

    DMA_InitStructure.DMA_M2M            = DMA_M2M_Disable;

    DMA_Init(DMA1_Channel1, &DMA_InitStructure);

    /* DMA1 Channel1 enable */

    DMA_Cmd(DMA1_Channel1, ENABLE);

    NVIC_EnableIRQ(DMA1_Channel1_IRQn);

    NVIC_SetPriority(DMA1_Channel1_IRQn,0);

    while (1)

    {

        // Start conversion on ADC1

        ADC_StartOfConversion(ADC1);

            //Wait for DMA to complete

            while(!DMA_GetFlagStatus(DMA1_FLAG_TC1));

            //Clear DMA flag

            DMA_ClearFlag(DMA1_FLAG_TC1);

    }

}

void ADC1_COMP_IRQHandler(void)

{

      if (ADC1->ISR & ADC_ISR_EOSEQ)  /* Check the end of sequence bit */

      {

          dmaHead++;

          dmaHead &= (MAX_DMA_XFERS-1);

          DMA1_Channel1->CMAR    = (uint32_t)&dmaBuff[dmaHead][0];

          ADC1->ISR |= ADC_ISR_EOSEQ;

      }

      if ((ADC1->ISR & ADC_ISR_OVR) != 0)  /* Check OVR has triggered the IT */

      {

        GPIOC->BSRR = (1<<8); /* Switch on orange led to report a resume of the conversion  */

        GPIOC->BSRR = (1<<(9+16)); /* Switch off green led to report it is due to overrun  */

        ADC1->ISR |= ADC_ISR_OVR; /* Clear the pending bit */

        ADC1->CR |= ADC_CR_ADSTP; /* Stop the sequence conversion */

      }

      else

      {

        //error |= ERROR_UNEXPECTED_ADC_IT; /* Report unexpected ADC interrupt occurrence */

      }

}

void DMA1_Channel1_IRQHandler(void)

{

      if ((DMA1->ISR & DMA_ISR_TCIF1) != 0) /* Test if transfer completed on DMA channel 1 */

      {

      }

}

 
7 REPLIES 7
Self.Glenn
Associate III
Posted on October 10, 2016 at 03:27

Hi John,

Missing this (from the F0 std peripheral lib) ?

ADC_DMARequestModeConfig(ADC1, ADC_DMAMode_Circular);

Glenn

Posted on October 10, 2016 at 16:42

Hey Glenn,

     I added your line before I enabled ADC1. I still do not see and DMA interrupts. Did I place the call in the wrong location?

Thanks,

     John C.

    /* Enable ADC */

    ADC_GetCalibrationFactor(ADC1);

    ADC_ITConfig(ADC1 ,ADC_IT_EOSEQ, ENABLE);

    NVIC_EnableIRQ(ADC1_IRQn); /* (7) */

    NVIC_SetPriority(ADC1_IRQn,0); /* (8) */

    ADC_DMARequestModeConfig(ADC1, ADC_DMAMode_Circular);  <<-------------

    ADC_DMACmd(ADC1, ENABLE);

    ADC_Cmd(ADC1, ENABLE);

Posted on October 10, 2016 at 17:56

The code as presented in uncompilable.

I'd avoid setting the DMA address in the IRQ Handler.

Review the DMA registers to understand if there is an error condition, or if it is just not advancing. The IRQ will not fire unless transfers are occurring, walk back the issue until you understand why.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on October 10, 2016 at 18:32

Hey Clive,

        The DMA transfers (all three) complete correctly and continue to run fine. I only modified the DMA address in the ADC interrupt because I could not get the DMA complete interrupt to fire. As far as the code being uncompilable it is because this code is being run on our own board and not a demo/discovery. I have included all setup and running code for the ADC. Any suggestions on what I could look for in the DMA register set? 

Thanks,

     John C.

Posted on October 10, 2016 at 18:58

Start by decomposing all the DMA registers related to the channel. Not using F0 myself, so unless the code is easy to rack-up and test, it won't be.

I don't see the TC interrupt enabled anywhere. I've done this previously with this

/* Enable DMA1 Channel1 Half Transfer and Transfer Complete interrupt */
DMA_ITConfig(DMA1_Channel1, DMA_IT_HT, ENABLE);
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
...
void DMA1_Channel1_IRQHandler(void) // Called at 4 Hz, LED Toggles at 2 Hz
{
/* Test on DMA1 Channel1 Transfer Complete interrupt */
if (DMA_GetITStatus(DMA1_IT_HT1))
{
/* Clear DMA1 Channel1 Half Transfer interrupt pending bits */
DMA_ClearITPendingBit(DMA1_IT_HT1);
GPIOC->BSRR = 0x100; /* Set PC8 */
GPIOC->BRR = 0x200;/* Reset PC9 */
}
/* Test on DMA1 Channel1 Transfer Complete interrupt */
if (DMA_GetITStatus(DMA1_IT_TC1))
{
/* Clear DMA1 Channel1 Transfer Complete interrupt pending bits */
DMA_ClearITPendingBit(DMA1_IT_TC1);
GPIOC->BSRR = 0x200; /* Set PC9 */
GPIOC->BRR = 0x100;/* Reset PC8 */
}
}

Example 4 and 5 channel examples I've built/evaluated...

/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/STM32F0%20ADC-DMA%20touble&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=3066

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on October 10, 2016 at 19:33

Hey Clive,

     The DMA_ITConfig() call was the answer. All my code is based on whatever standard peripheral library examples I can find. There is always a shortage of example code that fits my exact needs, so I end up piecing together several examples. Here is my final DMA config that is working:

    DMA_InitTypeDef   DMA_InitStructure;

    /* DMA1 Channel1 Config */

    DMA_DeInit(DMA1_Channel1);

    //DMA_InitStructure.DMA_PeripheralBaseAddr    = (uint32_t)ADC1_DR_Address;

    DMA_InitStructure.DMA_PeripheralBaseAddr    = (uint32_t)&(ADC1->DR);

    DMA_InitStructure.DMA_MemoryBaseAddr        = (uint32_t)dmaBuff;

    DMA_InitStructure.DMA_DIR                    = DMA_DIR_PeripheralSRC;

    //DMA_InitStructure.DMA_BufferSize            = sizeof(dmaBuff);

    DMA_InitStructure.DMA_BufferSize            = 3;

    DMA_InitStructure.DMA_PeripheralInc            = DMA_PeripheralInc_Disable;

    //DMA_InitStructure.DMA_PeripheralInc        = DMA_PeripheralInc_Enable;

    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_Circular;

    DMA_InitStructure.DMA_Priority                = DMA_Priority_High;

    DMA_InitStructure.DMA_M2M                    = DMA_M2M_Disable;

    DMA_Init(DMA1_Channel1, &DMA_InitStructure);

    DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);

    /* DMA1 Channel1 enable */

    DMA_Cmd(DMA1_Channel1, ENABLE);

    NVIC_EnableIRQ(DMA1_Channel1_IRQn);

    NVIC_SetPriority(DMA1_Channel1_IRQn,0);

// And the interrupt code

void DMA1_Channel1_IRQHandler(void)

{

      if ((DMA1->ISR & DMA_ISR_TCIF1) != 0) /* Test if transfer completed on DMA channel 1 */

      {

          dmaHead++;

          dmaHead &= (MAX_DMA_XFERS-1);

          DMA1_Channel1->CMAR    = (uint32_t)&dmaBuff[dmaHead][0];

          // Clear the interrupt pending flag

          DMA_ClearITPendingBit(DMA1_IT_TC1);

      }

}

     What is the difference between DMA_ClearITPendingBit(DMA1_IT_TC1) and DMA_ClearFlag(DMA1_FLAG_TC1) ? One clears all relative DMA channel interrupts?

Thanks,

     John C.

Posted on October 10, 2016 at 20:40

What is the difference between DMA_ClearITPendingBit(DMA1_IT_TC1) and DMA_ClearFlag(DMA1_FLAG_TC1) ? One clears all relative DMA channel interrupts? 

In the general STM32 model there is sometimes a bit reflecting the peripheral state, and another reflecting the interrupt state (latched, gated, whatever), the defines may also have a more complex encoding, not necessarily a bit vector. Here there is potential overlap, from a coding perspective I'd use the IT variants when dealing with interrupts, and FLAG for polling status.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..