cancel
Showing results for 
Search instead for 
Did you mean: 

Issues getting DMA to autotrigger (and related issues with ADC/DMA)

JayDev
Senior II

Hello!

I'm working with an STM32WB55 development board and I'm trying to take data fairly quickly (every 25 us) so I'm trying to work with the DMA to collect data. Right now, I'm not even collecting actual ADC data, just toggling a GPIO point to get timing figured out.

Anyway, found some good examples at DeepBlueEmbedded and have been poking at a few of them trying to get it to do what I want. The problem I'm coming across is I can't seem to get the DMA to autotrigger so i keep having to manually call the

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
 
{
 
   // Conversion Complete & DMA Transfer Complete As Well
 
   HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_12);
 
 
   // Starting DMA takes about 30 us
 
   HAL_ADC_Start_DMA(&hadc1, &AD_RES, 1);
 
}

The problem is the HAL_ADC_START_DMA command takes somewhere between 15-30 us to run and, by the time this is finished, the code calls it again and I have no cpu time to run the main part of my program outside of the ADC. If I remove the line though, it triggers once and then never starts up again (which makes sense if it's not auto triggering). figure I likely have this setup incorrectly but I can't seem to see the source of the issue.

Below is my ADC and DMA setup. I must be missing something really obvious:

static void MX_ADC1_Init(void)
{
 
  /* USER CODE BEGIN ADC1_Init 0 */
 
  /* USER CODE END ADC1_Init 0 */
 
  ADC_ChannelConfTypeDef sConfig = {0};
 
  /* USER CODE BEGIN ADC1_Init 1 */
 
  /* USER CODE END ADC1_Init 1 */
  /** Common config 
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc1.Init.LowPowerAutoWait = DISABLE;
  hadc1.Init.ContinuousConvMode = ENABLE;
  hadc1.Init.NbrOfConversion = 1;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.NbrOfDiscConversion = 1;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.DMAContinuousRequests = ENABLE;
  hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  hadc1.Init.OversamplingMode = DISABLE;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure Regular Channel 
  */
  sConfig.Channel = ADC_CHANNEL_6;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC1_Init 2 */
 
  /* USER CODE END ADC1_Init 2 */
 
}
static void MX_DMA_Init(void) 
{
 
  /* DMA controller clock enable */
  __HAL_RCC_DMAMUX1_CLK_ENABLE();
  __HAL_RCC_DMA1_CLK_ENABLE();
 
  /* DMA interrupt init */
  /* DMA1_Channel1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 6, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
 
}

Now, all this said, I do have a timer setup to run every 25 us, and I think it makes sense to use that long term, but I thought getting it auto triggering makes the most sense.

I am using the *.ioc file to generate code so, if it's easier to take screenshots of the GUI, let me know and I can do that.

I think I'm pretty close but I just can't seem to get there. Was reading through all the settings in the stm32wbxx_hal_adc.h file and the stm32wbxx_hal_dma.h files (which give pretty good descriptions). I must be missing something.

Anyway, if anyone can point me to where I'm going wrong or what I'm missing, it would be greatly appreciated. Thanks a lot!

1 ACCEPTED SOLUTION

Accepted Solutions
I would recommend vastly reducing the sampling rate and make sure everything is working as intended first.
The CPU only has so much power, so if you exceed that, your program won't function.
If you feel a post has answered your question, please click "Accept as Solution".

View solution in original post

4 REPLIES 4
TDK
Guru

Why use DMA for a single conversion that you do repeatedly?

This would be better suited for circular DMA, which never stops, and which calls half-complete and complete callbacks every 100 or so samples.

If you're only doing a single conversion, no need to use DMA at all. Just enable the ADC complete interrupt and restart the conversion. This will save execution time.

If you feel a post has answered your question, please click "Accept as Solution".

Well, you are correct about the circular. Actually had that enabled at one point and must've rolled it back just trying things. Good catch!

I do plan on having it take and store multiple values with minimal processing power, so I definitely want to use the DMA. At this point, I'm just trying to get the basics working, but maybe I have an incorrect expectation of what should be happening. I'm expecting it to enter the DMA routine above when a conversion is completed. I commented out the code for the start ADC start DMA so it should be entering the routine and just toggling the bit so I know it's making it in at all before I add any code to examine the data:

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
 
{
 
   HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_12);
 
}

I start the initial conversion with this line:

#define arraysize   8
volatile uint16_t ADC_values[arraysize];
 
HAL_ADC_Start_DMA(&hadc1, &ADC_values, 1);

When I run the code with a higher priority than the sensors, the bits don't toggle (meaning it's not entering that routine at all) and the sensors don't go off at all. It appears that it's taking up all the CPU but never actually toggles the bit (not sure if it's getting called faster than it can complete the event or something to that effect)? I'm still trying to make sense of it.

That said, if I place the priority as lower than the sensors, the sensors DO resume again (which tells me something is happening), but then it doesn't really have much value if I can't have it taking data at a consistent rate (aiming for 40 kHz).

I did change the code to circular, but also changed the overrun behavior to "overrun data written" for the time being (so I can see changing data when I start looking at the actual data). Below arethe updated ADC settings:

static void MX_ADC1_Init(void)
{
 
  /* USER CODE BEGIN ADC1_Init 0 */
 
  /* USER CODE END ADC1_Init 0 */
 
  ADC_ChannelConfTypeDef sConfig = {0};
 
  /* USER CODE BEGIN ADC1_Init 1 */
 
  /* USER CODE END ADC1_Init 1 */
  /** Common config 
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc1.Init.LowPowerAutoWait = DISABLE;
  hadc1.Init.ContinuousConvMode = ENABLE;
  hadc1.Init.NbrOfConversion = 1;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.NbrOfDiscConversion = 1;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.DMAContinuousRequests = ENABLE;
  hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
  hadc1.Init.OversamplingMode = DISABLE;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure Regular Channel 
  */
  sConfig.Channel = ADC_CHANNEL_6;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC1_Init 2 */
 
  /* USER CODE END ADC1_Init 2 */
 
}

Any idea what might be happening when the priority is set high on the DMA? Am I calling the wrong interrupt? Am I initializing it incorrectly?

I do currently have a warning, which could have something to do with it?

warning: passing argument 2 of 'HAL_ADC_Start_DMA' from incompatible pointer type [-Wincompatible-pointer-types]

Thanks for your help!

I would recommend vastly reducing the sampling rate and make sure everything is working as intended first.
The CPU only has so much power, so if you exceed that, your program won't function.
If you feel a post has answered your question, please click "Accept as Solution".

I apologize for the delay, meant to get back to this sooner. You're definitely right, I was running the clock way too fast. Looked at the clock configuration and had 48 MHz coming into it. Had to make a bunch of changes to the clock rates and sample rates and eventually got everything all up and working. I still need to get it so it's triggering off the timer I have setup but that will be for another thread. I ran into some issues where the whole system stopped working (even with known working settings in my *.ioc file) and I eventually had to go into debug mode and it all clicked. Now it's running again. I'll have to retry everything again and see if I can figure out what I did to wreck everything. I'll leave that for another thread though . . . heh.

Thanks for pointing me in the right direction!