cancel
Showing results for 
Search instead for 
Did you mean: 

Generating specified number of pulses using PWM

Ayemyitta
Associate II

0693W000008ye9AQAQ.png0693W000008ye95QAA.png0693W000008ye90QAA.png0693W000008ye8vQAA.png0693W000008ye8qQAA.png0693W000008ye8lQAA.png0693W000008ye8gQAA.pngHi All,

I am trying to generate required no of pulses using PWM. This is to implement to control stepper motor with position control.

I use HAL_TIM_PWM_Start_IT to start pulsing and count the no of pulses in HAL_TIM_PWM_PulseFinishedCallback.

I notice that the more the required pulses, the more the extra pulses will be generated. And also higher frequency will generate more extra pulses.

Attached is the initilization of the timer and call back function when the interrupt occur.

/**
  * @brief TIM3 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM3_Init(void)
{
 
  /* USER CODE BEGIN TIM3_Init 0 */
 
  /* USER CODE END TIM3_Init 0 */
 
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};
 
  /* USER CODE BEGIN TIM3_Init 1 */
 
  /* USER CODE END TIM3_Init 1 */
  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 1-1;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 65535;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  __HAL_TIM_DISABLE_OCxPRELOAD(&htim3, TIM_CHANNEL_1);
  /* USER CODE BEGIN TIM3_Init 2 */
 
  /* USER CODE END TIM3_Init 2 */
  HAL_TIM_MspPostInit(&htim3);
 
}
static void StartStepControl(HSO_CH_Typedef channel)
{
	if (channel == CH2)
	{
		if(__i32TargetPosition[CH2] != *__i32CurrentPosition[CH2])
		{
			HAL_TIM_PWM_Start_IT(&htim3, TIM_CHANNEL_1);
			*__PulsingStatus[channel] = RUNNING;
		}
	}
 
}
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
	if(htim == &htim3)
	{
		if (*__i32CurrentPosition[CH2] >= __i32TargetPosition[CH2])
		{
			HAL_TIM_PWM_Stop_IT(&htim3, TIM_CHANNEL_1);
			*__PulsingStatus[CH2] = PULSEDONE;
		}
		else
		{
			*__i32CurrentPosition[CH2] += 1;
		}
	}
}

1 ACCEPTED SOLUTION

Accepted Solutions

I couldn't attach both zip files in a single answer. Here's the second.

View solution in original post

12 REPLIES 12
DavidAlfa
Senior II

Have a look at repetition counter and one shot modes.

That way it will make the exact number of pulses you want.

Repetition counter triggers a normal timer update flag, but after N pulses.

One shot mode stops the timer​ when the counter resets.

Thanks @DavidAlfa​ 

Not very sure how to make use of it. could you provide sample if you have any?

Btw, max N pulses is only 8 bits right?

Which STM32?

Counting pulse in interrupt may work, of the interior is fast enough. Measure its latency and duration, toggling a GPIO at the ISR entry and exit. 100us should be doable. Using Cube is almost a guarantee for failure, especially if you also don't use compiler optimisations.

Some newer families have wider TIMx_RCR.

You can also link timers using the TRGI TRGO mechanism, the PWM-generating time being slave in Gated mode.

This is a recurring theme here, try searching.

JW

Thanks @Community member​ 

I am using STM32F405VGT6.

Counting pulse duration is less than 1us. Measured by toggling a GPIO at the ISR entry and exit.

I will take a look at the TRGI TRGO mechanism.

Aye

Don't you have other interrupts active? If so, you may want to give this particular interrupt a higher priority.

JW

DavidAlfa
Senior II

To be able to set more than 256 pulses in a single shot, you can use other options, like DMA, or a slave timer.

I think the slave timer option is the best in this case, as the PWM value stays always the same (50% in this case), you only want to send pulses to a motor.

I attached two sample projects. They make 15 pulse trains, from 1 to 15, and stop.

Both use Timer4 to make 1KHz PWM signal.

In both cases, only one interrupt is generated (at the end of the pulses), so it's very CPU efficient.

These pictures are real, taken with the logic analyzer.

The waves are perfect, the jittering you see is because of the low zoom.

The DMA method can make up to 65536 pulses (DMA transfer limit).

It uses normal mode DMA, but disabling memory address increase, so it sends N times the same value.

0693W000008yefGQAQ.png 

The slave timer method uses Timer2 (32-bit) in slave mode, so you can send up to 4294967296 pulses. But not only 1, it causes weird behaviour.

The Timer 4 operates silently, no interrupts, but increasing Timer2 on every PWM cycle.

Timer2 increases in the exact moment that PWM goes low, but Timer4 didn't reset yet, still has to finish the low part of the PWM period.

So when Timer2 resets, you have plenty of time to enter the interrupt and stop Timer4 before the next PWM cycle starts.

0693W000008yefLQAQ.png

I couldn't attach both zip files in a single answer. Here's the second.

Thank you very much. I will give it a try.

If you see that the interrupt is too slow and more pulses sneak in, disable PWM in the ISR before the HAL routine:

stm32f4xx_it.c

void TIM1_UP_TIM10_IRQHandler(void)
{
  /* USER CODE BEGIN TIM1_UP_TIM10_IRQn 0 */
 
__HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_1, 0);	// Set 0 PWM before anything else
 
  /* USER CODE END TIM1_UP_TIM10_IRQn 0 */
 
  HAL_TIM_IRQHandler(&htim10);
 
  /* USER CODE BEGIN TIM1_UP_TIM10_IRQn 1 */
  /* USER CODE END TIM1_UP_TIM10_IRQn 1 */
}

Once you stopped the PWM, restore it back to the normal value.