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?
- November 1, 2017
- 4 replies
- 1007 views
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 rateDMA
//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); }