cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L4 ADC+DMA how to get an interrupt when the buffer is filled?

HBida
Associate III

HI, i am using STM32L476RG and need to acquire data at 1.5MSPS in a buffer, size 2048. Everything is working however i cant see how to get an interrupt once the buffer is filled.

I thought the TC flag could be used but when i check in the DMA interrupt i find this function : HAL_DMA_IRQHandler(AdcHandle.DMA_Handle); which it totally managed by the HAL, so i wonder how to get a flag or an interrupt once the buffer is fileld, because i will have to change the buffer in order to allow continuous acquisition while the filled buffer is processed with DSP instructions.

Thanks

14 REPLIES 14

DMA should generate HT and TC interrupts if it is working. You'd need to have the correctly named callback which the HAL would dispatch back to you. ie the main IRQ Handler from the vector table calls the HAL_DMA_IRQHandler(), and it in turn dispatches to your callbacks, and then clears sources and exits.

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

well, that was fast!

i named the callback : ADC_DMA_XferCpltCallback

i also added that statement

if(HAL_DMA_RegisterCallback(&DmaHandle, HAL_DMA_XFER_CPLT_CB_ID, ADC_DMA_XferCpltCallback) != HAL_OK) return;

right after the HAL DMA init, but for some reason it doesnt work, my own callback is never called.

But If add myself :

uint32_t flag_it = AdcHandle.DMA_Handle->DmaBaseAddress->ISR;
  uint32_t source_it = AdcHandle.DMA_Handle->Instance->CCR;
  if (((flag_it & (DMA_FLAG_TC1 << (AdcHandle.DMA_Handle->ChannelIndex & 0x1CU))) != 0U) && ((source_it & DMA_IT_TC) != 0U))
  {
	 HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
  }

in the DMA IRQ handler then the LED is toggled from there, so the TC flag works.

So something must be missing with the callback...

HBida
Associate III

i can also see that this callback is actually not weakly defined, it is used in stm32l4xx_hal_adc.c line 3492

of course if i try to define it again the compiler complains about double definition.

void ADC_DMAConvCplt(DMA_HandleTypeDef *hdma)

 It is assigned line 2250, and this callback doesnt calls any user callback.

So I wonder how we can process datas in the case of continuous conversion to large buffer if there is no way to flip the buffer?

Because with a single buffer it will never be available for data processing (being constantly used) and we cannot even know that the buffer is filled �� . I must be missing something.

RMcCa
Senior II

One can easily make your own isrs. Look in the assembly file that contains the vector table and declare a void function with the correct name for the interrupt you want to use. When the compiler complains, simply delete the old one. You will need to test the flag bits in the isr to determine the cause of the interrupt and clear them before exiting. It's all in the data sheet.​

RMcCa
Senior II

Also, use either the buffer half/full​ interrupt with a single 2x buffer or Double buffer with full interrupt and 2x single buffer. I think the results are the same.

It's all in the data sheet......​

Especially because most HAL versions enable all possible interrupts in case a user callback is registered, better use of half transfer with double buffer as RMcCa mentionned, you might then be able to run continuous acquisition if you can process 2048 data chunks before next one is coming...

HBida
Associate III

yes of course I added my own ISR to check that the TC flag was set, the problem here is that the HAL implements several mecanism in this (these) ISRs already.

Basically :

HAL_ADC_Start_DMA registers ADC_DMAConvCplt and ADC_DMAHalfConvCplt

then ADC_DMAConvCplt registers the ADC callback : hadc->ConvCpltCallback(hadc);

here is ADC_DMAConvCplt:

void ADC_DMAConvCplt(DMA_HandleTypeDef *hdma)
{
  /* Retrieve ADC handle corresponding to current DMA handle */
  ADC_HandleTypeDef *hadc = (ADC_HandleTypeDef *)((DMA_HandleTypeDef *)hdma)->Parent;
 
  /* Update state machine on conversion status if not in error state */
  if ((hadc->State & (HAL_ADC_STATE_ERROR_INTERNAL | HAL_ADC_STATE_ERROR_DMA)) == 0UL)
  {
    /* Set ADC state */
    SET_BIT(hadc->State, HAL_ADC_STATE_REG_EOC);
 
    /* Determine whether any further conversion upcoming on group regular     */
    /* by external trigger, continuous mode or scan sequence on going         */
    /* to disable interruption.                                               */
    /* Is it the end of the regular sequence ? */
    if ((hadc->Instance->ISR & ADC_FLAG_EOS) != 0UL)
    {
      /* Are conversions software-triggered ? */
      if (LL_ADC_REG_IsTriggerSourceSWStart(hadc->Instance) != 0UL)
      {
        /* Is CONT bit set ? */
        if (READ_BIT(hadc->Instance->CFGR, ADC_CFGR_CONT) == 0UL)
        {
          /* CONT bit is not set, no more conversions expected */
          CLEAR_BIT(hadc->State, HAL_ADC_STATE_REG_BUSY);
          if ((hadc->State & HAL_ADC_STATE_INJ_BUSY) == 0UL)
          {
            SET_BIT(hadc->State, HAL_ADC_STATE_READY);
          }
        }
      }
    }
    else
    {
      /* DMA End of Transfer interrupt was triggered but conversions sequence
         is not over. If DMACFG is set to 0, conversions are stopped. */
      if (READ_BIT(hadc->Instance->CFGR, ADC_CFGR_DMACFG) == 0UL)
      {
        /* DMACFG bit is not set, conversions are stopped. */
        CLEAR_BIT(hadc->State, HAL_ADC_STATE_REG_BUSY);
        if ((hadc->State & HAL_ADC_STATE_INJ_BUSY) == 0UL)
        {
          SET_BIT(hadc->State, HAL_ADC_STATE_READY);
        }
      }
    }
 
    /* Conversion complete callback */
#if (USE_HAL_ADC_REGISTER_CALLBACKS == 1)
    hadc->ConvCpltCallback(hadc);
#else
    HAL_ADC_ConvCpltCallback(hadc);
#endif /* USE_HAL_ADC_REGISTER_CALLBACKS */
  }
  else /* DMA and-or internal error occurred */
  {
    if ((hadc->State & HAL_ADC_STATE_ERROR_INTERNAL) != 0UL)
    {
      /* Call HAL ADC Error Callback function */
#if (USE_HAL_ADC_REGISTER_CALLBACKS == 1)
      hadc->ErrorCallback(hadc);
#else
      HAL_ADC_ErrorCallback(hadc);
#endif /* USE_HAL_ADC_REGISTER_CALLBACKS */
    }
    else
    {
      /* Call ADC DMA error callback */
      hadc->DMA_Handle->XferErrorCallback(hdma);
    }
  }
}

So these callbacks are already doing a lot of things and the user code does not seem to be intended to interract with them.

Ultimately my goal is just to have a double buffering so that the available datas can be processed without messing with the buffer currently in use. In the end the datas will be processed with DSP functions, but for now i just want to save them to SD card (4bit with DMA).

I dont see where and how i should switch the buffer, or if there is any mecanism provided for doing that.

HBida
Associate III

yes, the problem here is that the calls where i could register my user callback are already used internally by the HAL with it own callbacks.

The plan is indeed to use TC or HT and double buffering, but i fail to see how to implement this provided that the HAL already have an extensive usage of these two ISRs for it own internal mecanisms. I havent found any example in the HAL tree of ADC DMA with double buffering.

RMcCa
Senior II

Sorry, you misunderstood my comment. You need to get rid of all the hal crap and write your own ​low level isr. Look in startup_32xxxxx.s and find the name in the interrupt vector table that matches the dma and stream you are using.

In my code, i am using dma with adc multimode on an f730, so i use dma2_stream0 for adc1 ( table in dma description of RM ) and declare

void DMA2_Stream0_IRQHandler( void )

Function in main.c

In the isr you need to read LISR to find the cause of the interrupt ( either buffer half or full​ ) and then clear the bit by writing to LIFCR.

Again, the compiler or linker will complain about multiple definition of the isr, just delete/comment out the old one.

Also, write your own initialization code. It's the only way to know that it's done properly. Setting up a dma with double buffering and interrupt is only like 8 lines of simple code.​ study the RM and use the debugger, it will start to make sense.