Help with ADC and DMA
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2019-05-22 1:38 PM
I'm using a Nucleo-H743ZI and have configured ADC3 to trigger off of TIM3. It is scanning 4 channels every 10 us. I also have BDMA configured for the ADC. The ADC end-of-scan interrupt is occurring every 10 us, but the DMA interrupt is only occurring every other ADC interrupt. They both complete in < 7 us, so it is not overrunning the 10 us period. Anyone know why the DMA occurs only every other time? Thanks for any help.
- Labels:
-
ADC
-
DMA
-
STM32H7 Series
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2019-05-22 4:12 PM
I don't think my DMA is configured correctly, but I don't know how to fix it.
The ADC is configured to scan 4 channels and trigger every 10 us based on TIM3:
hadc3.Instance = ADC3;
hadc3.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc3.Init.Resolution = ADC_RESOLUTION_16B;
hadc3.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc3.Init.EOCSelection = ADC_EOC_SEQ_CONV;
hadc3.Init.LowPowerAutoWait = DISABLE;
hadc3.Init.ContinuousConvMode = DISABLE;
hadc3.Init.NbrOfConversion = 4;
hadc3.Init.DiscontinuousConvMode = DISABLE;
hadc3.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T3_TRGO;
hadc3.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc3.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR;
hadc3.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc3.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
hadc3.Init.BoostMode = DISABLE;
hadc3.Init.OversamplingMode = DISABLE;
// Configuration of 4 channels omitted
Then I start the ADC/DMA with this:
uint32_t adcresults[4];
HAL_ADC_Start_DMA(&hadc3, adcresults, 16);
I pass in 16 to HAL_ADC_Start_DMA() as the number of bytes to be transferred to the adcresults array (4 channels * 4 bytes/channel).
Is this the correct way to DMA an ADC scan?
I get ADC3 global interrupt every 10 us, but the DMA interrupt only occurs every 20 us. The data in adcresults seem to be correct, just the timing doesn't seem right.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2019-05-23 12:09 AM
How is your DMA configured?
If its data width is set to half word, it transfers two bytes per sample.
Then your DMA Buffer is twice the size needed.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2019-05-23 9:13 AM
I set the data width to word, since the ADC data register is 32 bits:
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(hadc->Instance==ADC3)
{
/* Peripheral clock enable */
__HAL_RCC_ADC3_CLK_ENABLE();
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
/**ADC3 GPIO Configuration
PF3 ------> ADC3_INP5
PF5 ------> ADC3_INP4
PF9 ------> ADC3_INP2
PC2_C ------> ADC3_INP0
*/
GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_5|GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
HAL_SYSCFG_AnalogSwitchConfig(SYSCFG_SWITCH_PC2, SYSCFG_SWITCH_PC2_OPEN);
/* ADC3 DMA Init */
/* ADC3 Init */
hdma_adc3.Instance = BDMA_Channel0;
hdma_adc3.Init.Request = BDMA_REQUEST_ADC3;
hdma_adc3.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_adc3.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_adc3.Init.MemInc = DMA_MINC_ENABLE;
hdma_adc3.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
hdma_adc3.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
hdma_adc3.Init.Mode = DMA_CIRCULAR;
hdma_adc3.Init.Priority = DMA_PRIORITY_VERY_HIGH;
if (HAL_DMA_Init(&hdma_adc3) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(hadc,DMA_Handle,hdma_adc3);
/* ADC3 interrupt Init */
HAL_NVIC_SetPriority(ADC3_IRQn, 3, 0);
HAL_NVIC_EnableIRQ(ADC3_IRQn);
}
}
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2019-05-23 9:23 AM
And just to test, I changed the number of bytes to 8 in the HAL_ADC_Start_DMA() call and I do get a DMA interrupt every 10us now instead of every 20us. I expected the adcresults[] array to only be half filled, but it is being entirely filled, and with correct ADC data. I don't understand how the 16-byte adcresults[] array is getting filled up when the DMA request is for only 8 bytes.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2019-05-23 10:05 AM
In an old project for an STM32L4 in stm32h7xx_hal_adc.c it states:
"@param Length Length of data to be transferred from ADC peripheral to memory (in bytes)"
I remember that was an issue in that project, too.
In a fresh STM32H7 project the same line is:
"@param Length Number of data to be transferred from ADC peripheral to memory"
Seems somebody fixed the docs :)
The DMA interrupt is triggered twice per complete transfer.
Do you use HAL_ADC_ConvCpltCallback / HAL_ADC_ConvHalfCpltCallback callbacks?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2019-05-23 10:59 AM
OK, I'm confused now.
The description of Length in my project is your first one, so that's why I set Length to 16. This resulted in ADC intr every 10 us, but DMA intr every 20 us.
I changed Length to 8 just for testing, and get ADC intr and DMA intr every 10 us. This actually seems to work, though I don't know exactly what data or how much of it is transferred per interrupt. (But based on the further test below, I think it is probably the ConvHalfCplt intr and ConvCplt intr alternating every 10 us.)
Now according to the new description of Length, I should be setting Length to 4, since that is the number of channels I'm scanning. So I try 4 and I'm still getting correct ADC data, but I'm getting two DMA interrupts per ADC interrupt. I suppose one is ConvHalfCplt and the other is ConvCplt? I'm using ConvCpltCallback, but not ConvHalfCpltCallback. Though I think the ConvHalfCplt interrupt is being enabled by the HAL functions under the hood regardless.
So I guess 4 seems to be the correct Length to use?
Thanks @lokash​ for all your help.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Email to a Friend
- Report Inappropriate Content
‎2019-05-23 12:24 PM
As adcresults is of length 4, this would be the correct length.
That said a higher buffer size decreases your CPU load, as it can process several samples at once.
Both callbacks are always called and I would use them both to avoid data corruption by the DMA.
