cancel
Showing results for 
Search instead for 
Did you mean: 

Why is ADC sampling somehow limited to ~ 350kHz (STM32G431) with my setup?

sd.1
Associate II

Hello,

I've been attempting to get the full speed out of the ADCs in dual mode but seem to be limited to about 0.35msps. That is, when I sample a 10kHz wave, I only get 35 samples per period. I have attempted to change the adc and clock parameters in various ways, having only had luck slowing the sampling rate down via prescaler. From everything I've read, I believe I am setting up clocks and the adc's correctly. I am doing dual simultaneous sampling, non continuous conversion. My buffer size is 200 and am only concerned with taking one 200 2 word conversion at a time. I've also tried using a single ADC and have the same issue. I'm pretty much all out of ideas as to how achieve the full speed. Any help is appreciated. I've included my settings below. Thanks for looking!

Clock init:

SystemClock_Config
(
 
)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
 
  /** Configure the main internal regulator output voltage
  */
  HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1_BOOST);
  /** Initializes the CPU, AHB and APB busses clocks
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV4;
  RCC_OscInitStruct.PLL.PLLN = 85;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
  RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    //Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_8) != HAL_OK)
  {
   // Error_Handler();
  }
  /** Initializes the peripherals clocks
  */
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_LPUART1;
  PeriphClkInit.Lpuart1ClockSelection = RCC_LPUART1CLKSOURCE_PCLK1;
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC12;
  PeriphClkInit.Adc12ClockSelection = RCC_ADC12CLKSOURCE_SYSCLK;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
   // Error_Handler();
  }
}

Adc1 init:

void MX_ADC1_Init(void)
{
 
 
 
  ADC_MultiModeTypeDef multimode = {0};
  ADC_ChannelConfTypeDef sConfig = {0};
 
  hadc1.Instance = ADC1;
 
  hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV4;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.GainCompensation = 0;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.EOCSelection =ADC_EOC_SINGLE_CONV;
  hadc1.Init.LowPowerAutoWait = DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.NbrOfConversion = ADC_BUFFER_LEN;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.DMAContinuousRequests = ENABLE;
  hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;//ADC_OVR_DATA_OVERWRITTEN;
  hadc1.Init.OversamplingMode = DISABLE;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
  }
  // Configure the ADC multi-mode
 
  multimode.Mode = ADC_DUALMODE_REGSIMULT;
    multimode.DMAAccessMode = ADC_DMAACCESSMODE_12_10_BITS;
    multimode.TwoSamplingDelay = ADC_TWOSAMPLINGDELAY_12CYCLES;
  if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
  {
  }
// Configure Regular Channel
 
  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_12CYCLES_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    //Error_Handler();
  }
 
}

Adc2 init:

void MX_ADC2_Init(void)
{
 
 
  ADC_MultiModeTypeDef multimode = {0};
  ADC_ChannelConfTypeDef sConfig = {0};
 
  */
  hadc2.Instance = ADC2;
 
  hadc2.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV4;
  hadc2.Init.Resolution = ADC_RESOLUTION_12B;
  hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc2.Init.GainCompensation = 0;
  hadc2.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc2.Init.EOCSelection =ADC_EOC_SINGLE_CONV;
  hadc2.Init.LowPowerAutoWait = DISABLE;
  hadc2.Init.ContinuousConvMode = ENABLE;
  hadc2.Init.NbrOfConversion = 1;
  hadc2.Init.DiscontinuousConvMode = DISABLE;
 
  hadc2.Init.DMAContinuousRequests = ENABLE;
  hadc2.Init.Overrun = ADC_OVR_DATA_PRESERVED;
  hadc2.Init.OversamplingMode = DISABLE;
  if (HAL_ADC_Init(&hadc2) != HAL_OK)
  {
   // Error_Handler();
  }
 
  sConfig.Channel = ADC_CHANNEL_2;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_12CYCLES_5;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
  sConfig.OffsetNumber = ADC_OFFSET_NONE;
  sConfig.Offset = 0;
  if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
  {
    //Error_Handler();
  }
 
 
}

dma init:

	 void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
  {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if(hadc->Instance==ADC1)
    {
    /* USER CODE BEGIN ADC1_MspInit 0 */
 
    /* USER CODE END ADC1_MspInit 0 */
      /* Peripheral clock enable */
      __HAL_RCC_ADC12_CLK_ENABLE();
 
      __HAL_RCC_GPIOA_CLK_ENABLE();
      /**ADC1 GPIO Configuration
      PA0     ------> ADC1_IN1
      PA1     ------> ADC1_IN2
      */
      GPIO_InitStruct.Pin = GPIO_PIN_0;
      GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
      GPIO_InitStruct.Pull = GPIO_NOPULL;
      HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
 
      /* ADC1 DMA Init */
      /* ADC1 Init */
      hdma_adc1.Instance = DMA1_Channel2;
      hdma_adc1.Init.Request = DMA_REQUEST_ADC1;
      hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
      hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
      hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
      hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
      hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
      hdma_adc1.Init.Mode =DMA_NORMAL;
      hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
      if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
      {
 
      }
 
      __HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);
      // _HAL_RCC_DMAMUX1_CLK_ENABLE();
       __HAL_RCC_DMA1_CLK_ENABLE();
 
 
        HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 0, 0);
       HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);
 
           __HAL_RCC_DMAMUX1_CLK_ENABLE();
	  __HAL_RCC_DMA1_CLK_ENABLE();
 
 
	  HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
	  HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
 
}
 
}

3 REPLIES 3

I don't use/don't understand Cube/HAL.

How do you trigger the ADC?

JW

sd.1
Associate II

I trigger ADC in a function call controlled by the UART command line interface. I use a HAL function which looks like:

		  dmaStatus= HAL_ADCEx_MultiModeStart_DMA(&hadc1, adc_buffer1, ADC_BUFFER_LEN);

In order to simultaneously sample ADC1 and ADC2.

Here is the insides of that function:

HAL_StatusTypeDef HAL_ADCEx_MultiModeStop_DMA(ADC_HandleTypeDef *hadc)
{
  HAL_StatusTypeDef tmp_hal_status;
  uint32_t tickstart;
  ADC_HandleTypeDef tmphadcSlave;
  uint32_t tmphadcSlave_conversion_on_going;
  HAL_StatusTypeDef tmphadcSlave_disable_status;
 
  /* Check the parameters */
  assert_param(IS_ADC_MULTIMODE_MASTER_INSTANCE(hadc->Instance));
 
  /* Process locked */
  __HAL_LOCK(hadc);
 
 
  /* 1. Stop potential multimode conversion on going, on regular and injected groups */
  tmp_hal_status = ADC_ConversionStop(hadc, ADC_REGULAR_INJECTED_GROUP);
 
  /* Disable ADC peripheral if conversions are effectively stopped */
  if (tmp_hal_status == HAL_OK)
  {
    /* Set a temporary handle of the ADC slave associated to the ADC master   */
    ADC_MULTI_SLAVE(hadc, &tmphadcSlave);
 
    if (tmphadcSlave.Instance == NULL)
    {
      /* Update ADC state machine to error */
      SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_CONFIG);
 
      /* Process unlocked */
      __HAL_UNLOCK(hadc);
 
      return HAL_ERROR;
    }
 
    /* Procedure to disable the ADC peripheral: wait for conversions          */
    /* effectively stopped (ADC master and ADC slave), then disable ADC       */
 
    /* 1. Wait for ADC conversion completion for ADC master and ADC slave */
    tickstart = HAL_GetTick();
 
    tmphadcSlave_conversion_on_going = LL_ADC_REG_IsConversionOngoing((&tmphadcSlave)->Instance);
    while ((LL_ADC_REG_IsConversionOngoing(hadc->Instance) == 1UL)
           || (tmphadcSlave_conversion_on_going == 1UL)
          )
    {
      if ((HAL_GetTick() - tickstart) > ADC_STOP_CONVERSION_TIMEOUT)
      {
        /* Update ADC state machine to error */
        SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_INTERNAL);
 
        /* Process unlocked */
        __HAL_UNLOCK(hadc);
 
        return HAL_ERROR;
      }
 
      tmphadcSlave_conversion_on_going = LL_ADC_REG_IsConversionOngoing((&tmphadcSlave)->Instance);
    }
 
    /* Disable the DMA channel (in case of DMA in circular mode or stop       */
    /* while DMA transfer is on going)                                        */
    /* Note: DMA channel of ADC slave should be stopped after this function   */
    /*       with HAL_ADC_Stop_DMA() API.                                     */
    tmp_hal_status = HAL_DMA_Abort(hadc->DMA_Handle);
 
    /* Check if DMA channel effectively disabled */
    if (tmp_hal_status == HAL_ERROR)
    {
      /* Update ADC state machine to error */
      SET_BIT(hadc->State, HAL_ADC_STATE_ERROR_DMA);
    }
 
    /* Disable ADC overrun interrupt */
    __HAL_ADC_DISABLE_IT(hadc, ADC_IT_OVR);
 
    /* 2. Disable the ADC peripherals: master and slave */
    /* Update "tmp_hal_status" only if DMA channel disabling passed, to keep in */
    /* memory a potential failing status.                                     */
    if (tmp_hal_status == HAL_OK)
    {
      tmphadcSlave_disable_status = ADC_Disable(&tmphadcSlave);
      if ((ADC_Disable(hadc) == HAL_OK)           &&
          (tmphadcSlave_disable_status == HAL_OK))
      {
        tmp_hal_status = HAL_OK;
      }
    }
    else
    {
      /* In case of error, attempt to disable ADC master and slave without status assert */
      (void) ADC_Disable(hadc);
      (void) ADC_Disable(&tmphadcSlave);
    }
 
    /* Set ADC state (ADC master) */
    ADC_STATE_CLR_SET(hadc->State,
                      HAL_ADC_STATE_REG_BUSY | HAL_ADC_STATE_INJ_BUSY,
                      HAL_ADC_STATE_READY);
  }
 
  /* Process unlocked */
  __HAL_UNLOCK(hadc);
 
  /* Return function status */
  return tmp_hal_status;
}

sd.1
Associate II

I noticed that the ADC will not start without the TIM1 initializer which I use to drive a couple of PWM channels. Seems like a hint to me and I'm working on exploring that relationship in code and digging through the ref manual.

static void
MX_TIM1_Init
(
)
{
 
 
	frequency = 1 / (0.000001 * warpSystem.pwmSys1.timeSettings.t2);
	     period = __HAL_TIM_CALC_PERIOD(CLOCK_CYCLES_PER_SECOND,
					htim1.Init.Prescaler,
					frequency);
 
 
	htim1.Instance = warpSystem.pwmSys1.timerInstance; // TIM1;
	htim1.Init.Prescaler =PWM_PRESCALER;
	htim1.Init.CounterMode = TIM_COUNTERMODE_DOWN;
 
 
	htim1.Init.Period = period;
	htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
	htim1.Init.RepetitionCounter = 1;
	htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
	/*  if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
	 {
	 //Error_Handler();
	 }*/
	if (HAL_TIM_PWM_Init(&htim1) != HAL_OK) {
		//Error_Handler();
	}
	htim1.Init.RepetitionCounter = 0x00;//ff;
	if (HAL_TIM_OC_Init(&htim1) != HAL_OK) {
		// Error_Handler();
	}
	  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
	  if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
	  {
	  }
	sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1;//TIM_TRGO_RESET;
	sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
	sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
	if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig)
			!= HAL_OK) {
		//Error_Handler();
	}
	sConfigOC.OCMode = TIM_OCMODE_PWM1;
	sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
	sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
	sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
	sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
	sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
 
	sConfigOC.Pulse = __HAL_TIM_CALC_PULSE(CLOCK_CYCLES_PER_SECOND,
			htim1.Init.Prescaler, warpSystem.pwmSys1.timeSettings.t1); //warpSystem.pwmSys1.timeSettings.t1;
 
	if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1)
			!= HAL_OK) {
		//Error_Handler();
	}
 
	sConfigOC.Pulse = __HAL_TIM_CALC_PULSE(CLOCK_CYCLES_PER_SECOND,
			htim1.Init.Prescaler,
			((warpSystem.pwmSys1.timeSettings.t1)
					- (warpSystem.pwmSys1.timeSettings.t0))); //warpSystem.pwmSys1.timeSettings.t0;
 
	if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2)
			!= HAL_OK) {
		//Error_Handler();
	}
	sConfigOC.OCMode = TIM_OCMODE_TIMING;
	sConfigOC.Pulse = 0;
/*	if (HAL_TIM_OC_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK) {
 
	}*/
 
	sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
	sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
	//sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
	sBreakDeadTimeConfig.DeadTime = 0;
	sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
	sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
	sBreakDeadTimeConfig.BreakFilter = 0;
	sBreakDeadTimeConfig.BreakAFMode = TIM_BREAK_AFMODE_INPUT;
	sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE;
	sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
	sBreakDeadTimeConfig.Break2Filter = 0;
	sBreakDeadTimeConfig.Break2AFMode = TIM_BREAK_AFMODE_INPUT;
	sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
	if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig)
			!= HAL_OK) {
		// Error_Handler();
	}
 
	HAL_TIM_MspPostInit(&htim1);
	HAL_TIM_PWM_MspInit(&htim1);
	HAL_TIM_Base_Start(&htim1);
 
}

Here is that initialization...