cancel
Showing results for 
Search instead for 
Did you mean: 

5Channel ADC --> DMA triggered by timer to achieve lower sample rates: Do I need to restart DMA or ADC? When does HAL_ADC_ConvCpltCallback get triggered?

eyuan
Associate II
Posted on November 01, 2017 at 23:01

Need to sample 5 channels at 20Hz.  Would like an interrupt when the DMA buffer is filled (50 samples).  Waiting on boards to arrive and have a few questions:

1) Does the ADC or DMA needs restarted after each conversion of the sequence (5 channels) when set to trigger off a timer with continuous disabled (i.e. HAL_ADC_Start_IT)?  I'm thinking no.

2) When triggering ADC conversion off a timer using DMA, when is HAL_ADC_ConvCpltCallback called?  Is it after the 5 channels are sampled once or the DMA buffer is full? Is it after the 5 channels are sampled once or the DMA buffer is full?

  • I've seen examples using continuous ADC mode where HAL_ADC_ConvCpltCallback is called when the DMA buffer is full.   I've also seen examples do not use DMA where HAL_ADC_ConvCpltCallback is called after each conversion.  Are these both true (depending on whether you are using DMA?).  In the end, I want a call back when the buffer is full so that I can retrieve n samples.

MCU: STM32F071CB

ADC Continuous mode: disabled

Triggered off: ADC_EXTERNALTRIGCONV_T1_TRGO

Configuration details below.  Also see attached wrapper for initializing the ADC, DMA, and TIM

Thank you in advance for your help.

//ADC Configuration

    adcHandle.Instance                   = ADC_HANDLE;

    adcHandle.Init.ClockPrescaler        = ADC_CLOCK_SYNC_PCLK_DIV2;    //HSI14 Not divisible

    adcHandle.Init.Resolution            = ADC_RESOLUTION_12B;

    adcHandle.Init.DataAlign             = ADC_DATAALIGN_RIGHT;

    adcHandle.Init.ScanConvMode          = ADC_SCAN_DIRECTION_FORWARD;

    adcHandle.Init.EOCSelection          = ADC_EOC_SEQ_CONV;            //Read as group of channels

    adcHandle.Init.LowPowerAutoWait      = DISABLE;

    adcHandle.Init.LowPowerAutoPowerOff  = DISABLE;                     //HSI14 powers off between conversions

    adcHandle.Init.ContinuousConvMode    = DISABLE;                     //Perform conversion until overrun

    adcHandle.Init.DiscontinuousConvMode = DISABLE;           

    adcHandle.Init.ExternalTrigConv      = ADC_EXTERNALTRIGCONV_T1_TRGO;      //Conversion triggered by timer

    adcHandle.Init.ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_RISING;  //trigger on rising edge

    adcHandle.Init.DMAContinuousRequests = ENABLE;                      //DMA

    adcHandle.Init.Overrun               = ADC_OVR_DATA_OVERWRITTEN;    //Old data is overwritten

    adcHandle.Init.SamplingTimeCommon    = PRESSURE_OVERSAMPLE_RATE_CYCLES;  //Oversampling rate

DMA

//Enable DMA clock

    PRESSURE_DMA_CLOCK();

    

    //Configure DMA

    adcDmaHandle.Instance                 = PRESSURE_DMA_CHANNEL;

    adcDmaHandle.Init.Direction           = DMA_PERIPH_TO_MEMORY;

    adcDmaHandle.Init.PeriphInc           = DMA_PINC_DISABLE;         //use the same peripheral address

    adcDmaHandle.Init.MemInc              = DMA_MINC_ENABLE;          //increment after each conversion

    adcDmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;  //two bytes per sample

    adcDmaHandle.Init.MemDataAlignment    = DMA_MDATAALIGN_HALFWORD;  //two bytes per sample

    adcDmaHandle.Init.Mode                = DMA_CIRCULAR;             //do not use circular buffer

    adcDmaHandle.Init.Priority            = DMA_PRIORITY_VERY_HIGH;   //Only one DMA channel, so moot setting

    

    //Deinitialize, then initialize the DMA

    HAL_DMA_DeInit(&adcDmaHandle); //function always returns HAL_OK

    HAL_DMA_Init(&adcDmaHandle);   //function always returns HAL_OK

    

    //NVIC configuration for DMA Input data interrupt

    /*Set pre-emptive priority to 0 (highest) as per CubeMx Example

        Note that subpriority is not available on M0 device... set to 0.*/

    

    HAL_NVIC_SetPriority(PRESSURE_DMA_IRQ, PRESSURE_DMA_PREEMPT_PRIORITY, 0);

    HAL_NVIC_EnableIRQ(PRESSURE_DMA_IRQ);  

    

    //Associate the DMA handle

    __HAL_LINKDMA(&adcHandle, DMA_Handle, adcDmaHandle);

Timer

TIM_MasterConfigTypeDef adcTimConfig;

    

    //Enable timer clock

    PRESSURE_START_TIMER_CLK();

    

    //Configure Timer

    adcTimHandle.Instance               = PRESSURE_START_TIMER;

    adcTimHandle.Init.Period            = PRESSURE_TIMER_PERIOD_CYCLES;

    adcTimHandle.Init.Prescaler         = PRESSURE_TIMER_PRESCALER;

    adcTimHandle.Init.ClockDivision     = PRESSURE_TIMER_CLOCK_DIV;

    adcTimHandle.Init.CounterMode       = TIM_COUNTERMODE_DOWN;

    adcTimHandle.Init.RepetitionCounter = 0;  //not used (counts reps as way to slow down the timer even further)

    adcTimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; //disable changing reload on the fly

    

    errorCodeHal = HAL_TIM_Base_Init(&adcTimHandle);

    //Set timer to trigger on over/underflow */

    if(HAL_OK == errorCodeHal)

    {

        adcTimConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;  //Triggers on under/overflow

        adcTimConfig.MasterSlaveMode     = TIM_MASTERSLAVEMODE_DISABLE;

        

        errorCodeHal = HAL_TIMEx_MasterConfigSynchronization(&adcTimHandle, &adcTimConfig);

    }
4 REPLIES 4
Posted on November 01, 2017 at 23:37

At each trigger event 5 samples will be taken, you can make the buffer for 50 or 100 samples deep, and use circular DMA. These things are initialized once and done, and if you use the HT/TC DMA interrupts you can manage half the buffer without worries of the data changing underneath you.

I wouldn't use HAL, that's your choice to make.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
eyuan
Associate II
Posted on November 02, 2017 at 14:26

Clive, thank you for the quick response.  I intend to use a 50 sample circular buffer, read between the 20Hz triggers.

Question 2: If I want the ADC to trigger on a timer and use DMA, then both of these functions need called?

HAL_ADC_Start_DMA(&adcHandle, (uint32_t*)rawPressureBuffer[0], (uint32_t)NUM_OVERSAMPLES);

HAL_ADC_Start_IT(&adcHandle);

For others referring to this thread: I looked further into the HAL callback, which I should have done before posting. Here is a quick walk through.  Clive or others, please correct me if I am wrong.

HAL_ADC_ConvCpltCallback is called at the end of ADC_DMAConvCplt(), which is called after each DMA transfer:

1. Checks whether software start was used & continuous mode was disabled.  If true and end of sequence (EOS) is reached, it disables the interrupt for EOC and EOS, which need to be re-enabled by calling HAL_START_ADC.

abbreviated

if(ADC_IS_SOFTWARE_START_REGULAR(hadc)    &&   (hadc->Init.ContinuousConvMode == DISABLE)   )

        if( __HAL_ADC_GET_FLAG(hadc, ADC_FLAG_EOS) )

             __HAL_ADC_DISABLE_IT(hadc, ADC_IT_EOC | ADC_IT_EOS);

In other cases, such as software start continuous or trigger start, it leaves everything alone. 

2. Next, it calls HAL_ADC_ConvCpltCallback(), which is a weak function that can be redefined by the user.

I my case, I want to be notified when the buffer is full.  If I had had a double buffer, here is where I would determine if the interrupt was called when the buffer was half or full and read the corresponding half.

void DMA1_Channel1_IRQHandler(void)

{

    HAL_DMA_IRQHandler(adcHandle.DMA_Handle);

   //Add code here to read data out of the DMA circular buffer

}

Clive,

Can you elaborate on being cautious in using HAL:

  • Would you recommend using the LL drivers or directly configure the registers?
  • Are you referring to just the ADC module or all the HAL drivers? 
  • When using HAL, what should I watch out for? What are the main disadvantages - lack of flexibility, or difficulty in implementing from examples, better to learn how to write your own MSP drivers, or bugs?
Posted on November 02, 2017 at 17:00

It is far too big and gets in its own way. It has historically been full of fresher mistakes, and still isn't rigorously tested. In my product development this generates far too much drag and friction, I can't blame third parties for product failures, or delays in fixing them, my bosses aren't interested in hearing excuses..

I've demo'd F0 stuff using SPL and register level abstractions.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
Posted on November 02, 2017 at 18:15

Thank you for your perspective.  Shortcuts without rigorous testing often lead to long cuts (as I have experienced with ST's sensor APIs).  I'll keep this in mind as I gain experience to try to move toward eliminating the HAL libraries as a crutch.