cancel
Showing results for 
Search instead for 
Did you mean: 

Weird Timer One pulse mode behavior

Xxoyo.1
Associate III

Hello, I have use Timer 2 in PWM one pulse mode to change the duty cycle of Timer 1 PWM when the pulse have ended using

HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)

After restart the board, I press the button to trigger Timer2 One Pulse Mode, and the Timer 1 PWM is produced correctly (The first pulse width is 250us). But on the next button press and the rest, the PWM is produced slightly incorrect (The first pulse width is not 250us anymore).

I hope the images below could help explain a bit of my situation.

Anyone know what could the cause be?

(Image Below) Correct first pulse width that only occur after program/hardware reset

0693W00000BdDq2QAF.png 

(Image Below) Using different timer 2 channel to adjust PWM 1's duty cycle

0693W00000BdDqCQAV.png 

(Image Below) Pulse Width of each Channel in Timer 2

0693W00000BdDqMQAV.png 

(Image Below) Cursor measurement of the correct 250us that only occur after program/hardware reset

0693W00000BdDqRQAV.png 

(Image Below) Wrong pulse width 1 (Timer 2 channel 1 pulse width no longer 250us)on the next button press

0693W00000BdDqgQAF.png 

(Image Below) Wrong pulse width 2(Timer 2 channel 1 pulse width no longer 250us)on the next button press

0693W00000BdDqlQAF.png 

(Image Below) Wrong pulse width 3(Timer 2 channel 1 pulse width no longer 250us)on the next button press

0693W00000BdDqqQAF.png 

Last thing to mention is when triggering the One Pulse Mode, all the channel will be fired at the same time, I could not selectively fire only 1 of the channel. Therefore I had to use a simple state machine. Hopefully the code below can help me explain a bit.

volatile uint8_t stateMachine_finishedState = 0;
volatile uint8_t stateMachine_nextState = 0;
volatile uint8_t timeTurnOnIsGreat = 1;
 
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(stateMachine_nextState == 0) {
		
		stateMachine_finishedState = 0;
		stateMachine_nextState = 1;
		__HAL_TIM_SET_COUNTER(&htim2, 0);
		TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
		TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
		TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_3, TIM_CCx_ENABLE);
 
 
		__HAL_TIM_ENABLE(&htim2);
		//100% duty cycle
		__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0);
		__HAL_TIM_SET_COUNTER(&htim1, 0);
 
	}
 
}
 
 
 
 
 
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
 
	if(htim->Instance == TIM2 &&  htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1){
 
		TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_1, TIM_CCx_DISABLE);
 
 
 
		if(stateMachine_nextState == 1){
			stateMachine_finishedState = 1;
 
			if(timeTurnOnIsGreat){
 
				stateMachine_nextState = 2;
				TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
				TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
				TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_3, TIM_CCx_ENABLE);
				__HAL_TIM_SET_COUNTER(&htim2, 0);
				__HAL_TIM_ENABLE(&htim2);
 
				//50% duty cycle
				__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1,  __HAL_TIM_GET_AUTORELOAD(&htim1)/2U);
				__HAL_TIM_SET_COUNTER(&htim1, 0);
			}
			else{
 
				stateMachine_nextState = 3;
				TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
				TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
				TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_3, TIM_CCx_ENABLE);
				__HAL_TIM_SET_COUNTER(&htim2, 0);
				__HAL_TIM_ENABLE(&htim2);
 
				//0% duty cycle
				__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, __HAL_TIM_GET_AUTORELOAD(&htim1)+1);
				__HAL_TIM_SET_COUNTER(&htim1, 0);
 
 
 
			}
 
		}
 
 
 
	}
 
	if(htim->Instance == TIM2 &&  htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2){
 
		TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_2, TIM_CCx_DISABLE);
 
		if(stateMachine_nextState == 2){
			stateMachine_finishedState = 2;
 
				stateMachine_nextState = 3;
				TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
				TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
				TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_3, TIM_CCx_ENABLE);
				__HAL_TIM_SET_COUNTER(&htim2, 0);
				__HAL_TIM_ENABLE(&htim2);
 
 
				//100% duty cycle
				__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0);
				__HAL_TIM_SET_COUNTER(&htim1, 0);
 
		}
 
 
 
	}
 
 
 
	if(htim->Instance == TIM2 &&  htim->Channel == HAL_TIM_ACTIVE_CHANNEL_3){
		TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_3, TIM_CCx_DISABLE);
 
		if(stateMachine_nextState == 3){
			stateMachine_finishedState = 3;
			stateMachine_nextState = 0;
			__HAL_TIM_SET_COUNTER(&htim2, 0);
 
 
			//0% duty cycle
			__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, __HAL_TIM_GET_AUTORELOAD(&htim1)+1);
			__HAL_TIM_SET_COUNTER(&htim1, 0);
 
			GPIOE->BSRR |= (1<<21); // Reset the Pin PE5
 
		}
 
	}
}
 
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};
  TIM_OC_InitTypeDef sConfigOC = {0};
 
  /* USER CODE BEGIN TIM2_Init 1 */
 
  /* USER CODE END TIM2_Init 1 */
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 4200-1;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 0xffffffff-0x1;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  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();
  }
  if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_OnePulse_Init(&htim2, TIM_OPMODE_SINGLE) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 5;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  __HAL_TIM_DISABLE_OCxPRELOAD(&htim2, TIM_CHANNEL_1);
  sConfigOC.Pulse = 15;
  sConfigOC.OCFastMode = TIM_OCFAST_ENABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }
  __HAL_TIM_DISABLE_OCxPRELOAD(&htim2, TIM_CHANNEL_2);
  sConfigOC.Pulse = 10;
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
  {
    Error_Handler();
  }
  __HAL_TIM_DISABLE_OCxPRELOAD(&htim2, TIM_CHANNEL_3);
  /* USER CODE BEGIN TIM2_Init 2 */
 
 
  __HAL_TIM_CLEAR_FLAG(&htim2, TIM_SR_UIF);		//clear Update Interrupt Flag that was Set during initialization
 
 
 
	HAL_TIM_PWM_Start_IT(&htim2,TIM_CHANNEL_1);
	HAL_TIM_PWM_Start_IT(&htim2,TIM_CHANNEL_2);
	HAL_TIM_PWM_Start_IT(&htim2,TIM_CHANNEL_3);
 
	TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_1, TIM_CCx_DISABLE);	// disable the timer capture & compare at the beginning when system start
	TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_2, TIM_CCx_DISABLE);	// disable the timer capture & compare at the beginning when system start
	TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_3, TIM_CCx_DISABLE);	// disable the timer capture & compare at the beginning when system start
 
  htim2.Instance->CR1 &= ~(TIM_CR1_CEN);	//clear Counter Enable flag that was Set during initialization
  /* USER CODE END TIM2_Init 2 */
  HAL_TIM_MspPostInit(&htim2);
 
}
 
 

Update:

I placed a breakpoint in the button interrupt and read the registers of timer 2, I compare the register "when the board was just reset and the button is first pressed", and "the second time button is pressed". I then manually set the registers to identical as the first time, and the pulse width 250us is produced correctly again.

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(stateMachine_nextState == 0) {
		
		stateMachine_finishedState = 0;
		stateMachine_nextState = 1;
		__HAL_TIM_SET_COUNTER(&htim2, 0); //breakpoint
		TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_1, TIM_CCx_ENABLE);
		TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_2, TIM_CCx_ENABLE);
		TIM_CCxChannelCmd(htim2.Instance, TIM_CHANNEL_3, TIM_CCx_ENABLE);

(Image below) Left is first button press, Right is second button press

0693W00000BdDwPQAV.png 

Update2 : manually setting the registers to produce the correct pulse width only worked some times, so it seems the cause is from something else

Update3 : I added the code MX_TIM2_Init(); when the button is pressed and the problem is solved. But this is definitely not the right way, I hope someone could have a look at this project :weary_face:

Update 4: I replaced the MX_TIM2_Init() with htim2.Instance->EGR = TIM_EGR_UG; and the problem is fixed. I'll have to further investigate the reason for this

3 REPLIES 3
TDK
Guru

The various HAL TIM routines are difficult to understand, particularly when you have an exact behavior you want to happen.

I would get comfortable with the timer registers and modify those directly. Otherwise, it's twice as much work trying to find the right combination of HAL calls that do what you want.

You've given a ton of data here, but it's still hard to process what is happening vs what you want to happen.

If you feel a post has answered your question, please click "Accept as Solution".
Xxoyo.1
Associate III

Hello, thanks for the reply. I hope you have seen my Update 2 in the post that mentioned writing to the registers worked some of the time only (Or i should say doesn't have any effect). The pulse width varies (not my intention) on its own even when having the same register values. The only time i could produce the correct pulse width is after restarting the board, and subsequent triggers rarely produce the correct pulse width again

TDK
Guru

The ARR and PSC registers are not immediately effective when you update them. By generating an update event, they get loaded from the shadow registers.

If you feel a post has answered your question, please click "Accept as Solution".