cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 "dual regular simultaneous mode only" - with DMA

Tim Goll
Associate II

Searching through the forum it seems like there are tens of posts regarding this topic. I read many of them, but I struggle to find a solution that applies to me.

First of, I use an STM32H743VIT.

My goal: Use ADC1 and ADC2, one channel each. The two ADCs should be tied together. The whole conversion should be triggered by TIM4 update event and the data should be moved via DMA.

What works: If I just use TIM4 and two DMA streams that move the data for each ADC individually, it works. As soon as I try to use the dual mode, it does nothing.

When I'm in dual mode I have confirmed that the timer is still running. TIM4->CNT is changing. I've spent almost two hours with the debugger in the SFR and checked the registers. I'm not sure if I'm close to the issue, but there is something that seems suspicious based on the research I did: DMA1->S0NDTR->NDT does not change, indicating that the DMA stream does nothing. However ADC1->CR->ADSTART is at 0x1, indicating that it tries to do something but doesn't do it. There are more registers that I checked, but these seem like the most important ones.

I also spent a while discussing registers with Gemini. Not sure if anything meaningful was learned int his discussion, but:
- ADC1/2->ISR->ADRDY is 0x1, both are ready
- DMA1->LISR and DMA1->HISR are bot 0x0, so no errors

My code actually triggering the conversion is rather simple:

HAL_ADC_Start(params->hadc_2); // probably wrong, but I've seen this somewhere, it doesn't change anything though
HAL_TIM_Base_Start(params->htim);
HAL_ADCEx_MultiModeStart_DMA(dev->hadc_1, (uint32_t *) dev->buffer, dev->size);

// HAL_ADC_ConvCpltCallback is never triggered

 

ADC 1 config:

TimGoll_0-1770943363914.png

TimGoll_1-1770943389172.png

ADC2 config:

TimGoll_2-1770943419751.png

Here's the auto generated code:

static void MX_ADC1_Init(void)
{

  /* USER CODE BEGIN ADC1_Init 0 */

  /* USER CODE END ADC1_Init 0 */

  ADC_MultiModeTypeDef multimode = {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_16B;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc1.Init.LowPowerAutoWait = DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.NbrOfConversion = 1;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T4_TRGO;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
  hadc1.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR;
  hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  hadc1.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
  hadc1.Init.OversamplingMode = ENABLE;
  hadc1.Init.Oversampling.Ratio = 2;
  hadc1.Init.Oversampling.RightBitShift = ADC_RIGHTBITSHIFT_1;
  hadc1.Init.Oversampling.TriggeredMode = ADC_TRIGGEREDMODE_MULTI_TRIGGER;
  hadc1.Init.Oversampling.OversamplingStopReset = ADC_REGOVERSAMPLING_RESUMED_MODE;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure the ADC multi-mode
  */
  multimode.Mode = ADC_DUALMODE_REGSIMULT;
  multimode.DualModeData = ADC_DUALMODEDATAFORMAT_32_10_BITS;
  multimode.TwoSamplingDelay = ADC_TWOSAMPLINGDELAY_1CYCLE;
  if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_3;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  sConfig.OffsetSignedSaturation = DISABLE;
  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_ADC2_Init(void)
{

  /* USER CODE BEGIN ADC2_Init 0 */

  /* USER CODE END ADC2_Init 0 */

  ADC_ChannelConfTypeDef sConfig = {0};

  /* USER CODE BEGIN ADC2_Init 1 */

  /* USER CODE END ADC2_Init 1 */

  /** Common config
  */
  hadc2.Instance = ADC2;
  hadc2.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
  hadc2.Init.Resolution = ADC_RESOLUTION_16B;
  hadc2.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc2.Init.LowPowerAutoWait = DISABLE;
  hadc2.Init.ContinuousConvMode = DISABLE;
  hadc2.Init.NbrOfConversion = 1;
  hadc2.Init.DiscontinuousConvMode = DISABLE;
  hadc2.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR;
  hadc2.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  hadc2.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
  hadc2.Init.OversamplingMode = ENABLE;
  hadc2.Init.Oversampling.Ratio = 2;
  hadc2.Init.Oversampling.RightBitShift = ADC_RIGHTBITSHIFT_1;
  hadc2.Init.Oversampling.TriggeredMode = ADC_TRIGGEREDMODE_MULTI_TRIGGER;
  hadc2.Init.Oversampling.OversamplingStopReset = ADC_REGOVERSAMPLING_RESUMED_MODE;
  if (HAL_ADC_Init(&hadc2) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Regular Channel
  */
  sConfig.Channel = ADC_CHANNEL_4;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  sConfig.OffsetSignedSaturation = DISABLE;
  if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN ADC2_Init 2 */

  /* USER CODE END ADC2_Init 2 */

}

static void MX_TIM4_Init(void)
{

  /* USER CODE BEGIN TIM4_Init 0 */

  /* USER CODE END TIM4_Init 0 */

  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};

  /* USER CODE BEGIN TIM4_Init 1 */

  /* USER CODE END TIM4_Init 1 */
  htim4.Instance = TIM4;
  htim4.Init.Prescaler = 0;
  htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim4.Init.Period = 94-1;
  htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_OC_Init(&htim4) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_TOGGLE;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_OC_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM4_Init 2 */

  /* USER CODE END TIM4_Init 2 */
  HAL_TIM_MspPostInit(&htim4);

}


I'm thankful for any helpful pointer as I'm currently a bit lost on what else to try.

1 ACCEPTED SOLUTION

Accepted Solutions

Ok.

May be it would be helpful to refer to the example provided in the CubeHAL and inspire from it:

https://github.com/STMicroelectronics/STM32CubeH7/blob/master/Projects/STM32H743I-EVAL/Examples/ADC/ADC_DualModeInterleaved/

It is using the interleaved mode but you can do the necessary modifications to make it in simultaneous mode.

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.

View solution in original post

8 REPLIES 8
mƎALLEm
ST Employee

Hello,

First thing to check is the buffer location of the DMA transfer. Please read this article: The most probable reason to have an issue with DMA or BDMA transfers on STM32H7

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.

First of, thank you for your response! I checked again and can add the following: 

I'm using DMA1 and I have the D-cache feature of the STM disabled. Also memory protection is disabled. The address of my buffer is 0x240023c4, which seems to be in the correct range.

It works if I don't try to combine the two ADCs, pointing to an issue (probably with my configuration) regarding the dual mode. Since the registers indicate that the DMA is waiting on the ADC and the ADCs are ready, it seems to me like there is something preventing the ADCs from actually triggering.

Ok.

May be it would be helpful to refer to the example provided in the CubeHAL and inspire from it:

https://github.com/STMicroelectronics/STM32CubeH7/blob/master/Projects/STM32H743I-EVAL/Examples/ADC/ADC_DualModeInterleaved/

It is using the interleaved mode but you can do the necessary modifications to make it in simultaneous mode.

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.

Thank you!
One question, for my understanding here: This project has no IOC that autogenerates code, hasn't it? I can work with this, but I want to be sure I'm not missing something

Unfortunately, no ioc file is provided. At the time STM32H7 Cube was released no ioc files are provided with the examples. That policy came after with newer products..

You need to reflect the example's config in your ioc file. I don't think it is too difficult ;)

Hope that helps.

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.

I compared our setup - there is no real difference between these two. Here's a list of the differences:

ADC 1:

hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
  hadc1.Init.Resolution = ADC_RESOLUTION_16B;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc1.Init.LowPowerAutoWait = DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.NbrOfConversion = 1;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T4_TRGO;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
  hadc1.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR;
  hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN; // NEW
  hadc1.Init.OversamplingMode = DISABLE; // NEW

  //hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  //hadc1.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
  //hadc1.Init.OversamplingMode = ENABLE;
  //hadc1.Init.Oversampling.Ratio = 2;
  //hadc1.Init.Oversampling.RightBitShift = ADC_RIGHTBITSHIFT_1;
  //hadc1.Init.Oversampling.TriggeredMode = ADC_TRIGGEREDMODE_MULTI_TRIGGER;
  //hadc1.Init.Oversampling.OversamplingStopReset = ADC_REGOVERSAMPLING_RESUMED_MODE;
  
  sConfig.Channel = ADC_CHANNEL_3;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  //sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
  sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5; // NEW
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  //sConfig.OffsetSignedSaturation = DISABLE;

 

ADC 2:

  hadc2.Instance = ADC2;
  hadc2.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
  hadc2.Init.Resolution = ADC_RESOLUTION_16B;
  hadc2.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc2.Init.LowPowerAutoWait = DISABLE;
  hadc2.Init.ContinuousConvMode = DISABLE;
  hadc2.Init.NbrOfConversion = 1;
  hadc2.Init.DiscontinuousConvMode = DISABLE;
  hadc2.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR;
  hadc2.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN; // NEW
  hadc2.Init.OversamplingMode = DISABLE; // NEW
  hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START; // NEW

  //hadc2.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  //hadc2.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
  //hadc2.Init.OversamplingMode = ENABLE;
  //>hadc2.Init.Oversampling.Ratio = 2;
  //hadc2.Init.Oversampling.RightBitShift = ADC_RIGHTBITSHIFT_1;
  //hadc2.Init.Oversampling.TriggeredMode = ADC_TRIGGEREDMODE_MULTI_TRIGGER;
  //hadc2.Init.Oversampling.OversamplingStopReset = ADC_REGOVERSAMPLING_RESUMED_MODE;

  sConfig.Channel = ADC_CHANNEL_4;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  //sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
  sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5; // NEW
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  //sConfig.OffsetSignedSaturation = DISABLE;

 As you can see the differences should not matter. That being said, I tested your settings and it still did not work.

I've seen that ADC1->ISR->EOC of ADC1 is 1 as well. Indicating that the ADC is working, just the DMA stream is unable to receive the data.

I'm completely out of ideas what the issue could be. I will now try to start a new project and get it working on a clean slate - but I don't see much hope here. 

Tim Goll
Associate II

Got it working in a minimal example. So it must be something in my larger code base. I will try again tomorrow and let you know what's the issue if I get it working. Right now I have the feeling that I did the same both times.

Tim Goll
Associate II

Well, it is working now. And I'm not entirely sure what I changed. Probably something that I've overlooked.