AnsweredAssumed Answered

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?

Question asked by yuan.eric.002 on Nov 1, 2017
Latest reply on Nov 2, 2017 by yuan.eric.002

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.



ADC Continuous mode: disabled



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


//Enable 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.*/
    //Associate the DMA handle
    __HAL_LINKDMA(&adcHandle, DMA_Handle, adcDmaHandle);


TIM_MasterConfigTypeDef adcTimConfig;
    //Enable timer clock
    //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);