cancel
Showing results for 
Search instead for 
Did you mean: 

ADC trigger with PWM and reading voltage on its state

gokhannsahin
Associate II

Hi everyone,

I have a question about ADC trigger. I have to generate a PWM 1.1KHz and read the avarage voltage of ADC pin accoding to the On/Off state of that PWM. How can I make it.? I enclosed an image about it.

0690X000006CyKTQA0.jpg

11 REPLIES 11
S.Ma
Principal

Elaborate more, especially not a timing, more like which signals feeds who? Schematics sketch would help understand better what is the system and what is really the challenge. Thanks for providing mode details, otherwise the answers may not be relevant enough...

gokhannsahin
Associate II

That PWM supplies a coil and there is a sense coil. The voltage drops on an sense coil and want to measure its voltage according to each state of supplying PWM.

gokhannsahin
Associate II

If I use a timer 1Mhz for ADC as sampling rate is 61.5, how long does it take the canversion to finish for 450 samples? I guess that if I use ADC DMA conversion complete callback, I can toggle IO just as PWM. However, I don’t how to calculate correctly.

S.Ma
Principal

If it is a kind of motor control or digital power supply, there are specific app notes and examples for this, including 3 phase sense for sensored brushless motor. Check on IHM demo boards.

Otherwise, take a timer to generate the pwm(s) and use the spare channel to generate a pulse which you can shape and position during the PWM period time. This trigger goes to a pin which feeds the ADC trgger input pin. It will enable you to time position the sampling at the correct place (assuming the ADC input signall is low impedence, otherwise, get ready for voltage droops as the ADC behave like an RC filter during the sampling time. If you use an injected ADC channel, you can directly read the voltage as LSB or have a digital min/max comparator if the target is to be within a specific range.

gokhannsahin
Associate II

Well, how can I calculate the conversion timing for reading ADC via DMA? Are the followings correct?

TIM2 clock 32Mhz and set the prescaler 31 it will be 1Mhz. Counter Period is 1. The timing will be 1us.

The sample cycle 61.5, then total cycle 12+61.5=73.5 cycle.

The number of sample is 450, then it takes 33075 cycles.

So, it takes 33,075ms, it gets interrupt after 33ms.

S.Ma
Principal

Maybe still the understanding of what you'd like to implement is still subject to various interpretations.

  1. What is the PWM frequency and duty cycle range
  2. After how many PWM periods an ADC conversion is being done
  3. After how many PWM periods the PWM duty cycle is being updated?
  4. How accurate the firering of the ADC sampling time respective to PWM pulse? (position and precision) or it does not matter.

Thanks.

gokhannsahin
Associate II

Thanks ​ @S.Ma​ for your answers.

  1. PWM frequency is 1.1KHz and duty 50%.
  2. For each 50% duty, so in condition On/Off.
  3. Duty is never changed, it's allways 5%0.
  4. After changed the state, the first 2 or 3 value is assumed as dummy, not calculating these.

S.Ma
Principal

So if I understand correctly: STM32 generates 1.1kHz with 50% duty cycle

==> So this is one of the timer output channel : Let's say the timer runs at 4MHz, so its toggle is 1800 ticks, period is 3600 ticks with overflow.

==> For example, create a second channel which output will toggle based on compares coming from a DMA scanned table, say toggle at 4, 8, 12, 16, 20, 24 (that's the first 3 pulses for 3 usec, go on until 3600 is done. You could use another timer to generate a 1MHz pulse triggering the ADC, however, you probably want the fine shifting convenience and synchronisation of the ADC firering with 1/8 usec accuracy (or twice better is using 16MHz timer). DMA for Timer can run in cyclical mode.

==> These 1 MHz time shitable pulses are used to trigger the ADC conversion, one sample by one sample. Use the physical trigger pin to monitor what's going on.

Then use another DMA channel to save the ADC results into a RAM table, which you will have to handle by SW its backup every 900 usec.

You will only have to make sure that the ADC total sampling time is same or shorter than the ADC trigger period. DMA transfer complete will give you the event to analyse and perform the average. If you;d like to run it continuously, you'll probably have to implement double buffering.

If not known exactly the ADC sampling time, run the code using debugger and count how many samples were collected over 900 usec.

There are probably simpler implementations, however, if there are some system fine tuning required down the road, a bit more complex HW use provides extra tuning knobs.

The earlier Invested time the shorter debug time...

gokhannsahin
Associate II

I don't understand what you meant.

I have tried something but confused the calculation of ADC DMA conversion time. I'm using the following code.

TIM2:

static void MX_TIM2_Init(void)
{
 
  /* USER CODE BEGIN TIM2_Init 0 */
 
  /* USER CODE END TIM2_Init 0 */
 
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
 
  /* USER CODE BEGIN TIM2_Init 1 */
 
  /* USER CODE END TIM2_Init 1 */
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 63;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 1;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM2_Init 2 */
 
  /* USER CODE END TIM2_Init 2 */
 
}

ADC:

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_SYNC_PCLK_DIV1;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
  hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_TRGO;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 1;
  hadc1.Init.DMAContinuousRequests = ENABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
  hadc1.Init.LowPowerAutoWait = DISABLE;
  hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
  /**Configure Regular Channel 
  */
  sConfig.Channel = ADC_CHANNEL_4;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SingleDiff = ADC_SINGLE_ENDED;
  sConfig.SamplingTime = ADC_SAMPLETIME_19CYCLES_5;
  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 */
 
}

Callback:

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) {
 
	uint32_t wResult1 = 0, wResult2 = 0, wResult3 = 0;
	uint32_t dwHighTotal = 0, dwLowTotatl = 0;
	static uint32_t dwCounter;
 
		HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_2);
		if (flag != 2) {
			if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2)) {
				for (uint32_t i = 0; i < 225; i++) {
					sADCValues.wHighData[i] = data[i];
					dwHighTotal += data[i];
				}
				sADCValues.wHighAverage = dwHighTotal / 225;
			} else {
				for (uint32_t i = 0; i < 225; i++) {
					sADCValues.wLowData[i] = data[i];
					dwLowTotatl += data[i];
				}
				sADCValues.wLowAverage = dwLowTotatl / 225;
			}
			flag++;
		}
}

In main, the ADC is started via DMA.

if (HAL_ADC_Start_DMA(&hadc1, (uint32_t*) &data, 225) != HAL_OK){

Error_Handler();

}

And it generate 1.1KHz signal but I can't calculate how to do that.