cancel
Showing results for 
Search instead for 
Did you mean: 

Problem changing PWM Frequency on the Fly

Nimit Vachhani
Associate III

Greetings,

I am using STM32L151RET6 controller. I have selected timer 5 for square wave generation.

Frequency to timer 5 is 1MHz. I am starting to generate 10Hz square wave and increment the value by every 10Hz. After reaching 430 Hz pwm output is corrupted or not okay.

To start with 10Hz square wave i am setting timer 5 variables as

TIM5->ARR  = _arr;
	TIM5->CCR1 = _arr / 2;
	HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL_1);

This generates 10 Hz square wave. Now after every 2 sec delay i am incrementing Frequency value and re calculating Arr and CCRx values as below

/* Infinite loop */
  while (1)
  {
		HAL_Delay(2000);
		_pwm_freq += 10;
		_arr = _pwm_timer_freq / _pwm_freq;	
		_ccr = _arr / 2;				
		__HAL_TIM_SET_AUTORELOAD( &htim5, _arr );
		__HAL_TIM_SET_COMPARE(&htim5, TIM_CHANNEL_1, _ccr);
  }

The output is okay till 420 Hz and after that output is distorted or not okay. It shows anything on the oscilloscope.

Below is the snippet on how timer 5 is initiated. 

static void MX_TIM5_Init(void)
{
 
  /* USER CODE BEGIN TIM5_Init 0 */
 
  /* USER CODE END TIM5_Init 0 */
 
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};
 
  /* USER CODE BEGIN TIM5_Init 1 */
 
  /* USER CODE END TIM5_Init 1 */
  htim5.Instance = TIM5;
  htim5.Init.Prescaler = 32-1;
  htim5.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim5.Init.Period = 1;
  htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim5.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim5) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim5, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_Init(&htim5) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim5, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_ENABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim5, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM5_Init 2 */
 
  /* USER CODE END TIM5_Init 2 */
  HAL_TIM_MspPostInit(&htim5);
 
}

Any guidance on how to update timer variables effectively so that output is okay or perfect or as set on the fly in while loop.

1 ACCEPTED SOLUTION

Accepted Solutions
gbm
Lead II

No, I suggest to simply set ARPE bit once, during timer setup. This way the prescaler and period will change at the end of period, not when you write ARR or PSC. If you want to change more than one register, do it in timer "update" interrupt. There is no need to set/reset any control bit during timer operation to achieve what you want.

View solution in original post

4 REPLIES 4
gbm
Lead II

Set the ARPE bit in CR1. I have never used HAL for timer setup but I guess it's hidden under AutoReloadPreload field in the Init structure.

For changing the PWM frequency without changing duty ratio it might be a better idea to change PSC instead of ARR.

Well through one of the post found on st community i am doing as below

  while (1)
  {
		HAL_Delay(2000);
	        while(__HAL_TIM_GET_COUNTER(&htim5) < _arr){};
		_pwm_freq += 10;
		_arr = _pwm_timer_freq / _pwm_freq;	
		_ccr = _arr / 2;			
	       TIM5->CR1 |= TIM_CR1_UDIS;
	       TIM5->ARR = _arr;
	       TIM5->CCR1 = _ccr;
	       TIM5->CR1 &= ~TIM_CR1_UDIS;
  }

I think you are also suggesting me the same way , correct ? I will also give try changing PSC instead of duty cycle.

gbm
Lead II

No, I suggest to simply set ARPE bit once, during timer setup. This way the prescaler and period will change at the end of period, not when you write ARR or PSC. If you want to change more than one register, do it in timer "update" interrupt. There is no need to set/reset any control bit during timer operation to achieve what you want.

Aite.

Did as per ur suggestion and it worked.

Thanks a lot.