cancel
Showing results for 
Search instead for 
Did you mean: 

Problem with PWM stopping after commutation event.

RobErPOL
Associate II

Hello - i'm new to STM32 MCU's (mostly have been using 8bit AVR's or OS based Raspberry Pi). I've been trying to implement a 6-step motor control (making everything from scratch as a learning experience). I picked TIM4 and TIM8 in a master-slave configuration, using complementary outputs of channel 1, 2 & 3 in TIM8. Everything is set so that PWM preloaded registers update happens in a commutation even, which is the TRGO of TIM4.

void motor6Step_Init(TIM_HandleTypeDef* htim4, TIM_HandleTypeDef* htim8){
	htim4->Instance->CCR1 = (TIM4_ARR>>2);
 
	htim8->Instance->CCR1 = 0;
	htim8->Instance->CCR2 = 0;
	htim8->Instance->CCR3 = 0;
	htim8->Instance->CR1 &= ~( TIM_CR1_CEN );
	htim8->Instance->CNT = 0;
	__HAL_TIM_ENABLE_IT(htim8, TIM_IT_UPDATE);
	HAL_TIMEx_ConfigCommutEvent_IT(htim8, TIM_TS_ITR2, TIM_COMMUTATION_TRGI);
}
 
void motor6Step_Start(TIM_HandleTypeDef* htim4, TIM_HandleTypeDef* htim8){
	HAL_TIM_Base_Start(htim8);
	HAL_TIM_OC_Start_IT(htim4, TIM_CHANNEL_1);
}
 
void motor6Stop_Stop(TIM_HandleTypeDef* htim4, TIM_HandleTypeDef* htim8){
	HAL_TIM_OC_Stop_IT(htim4, TIM_CHANNEL_1);
	HAL_TIM_Base_Stop(htim8);
	htim4->Instance->CNT = 0;
	htim8->Instance->CNT = 0;
}

The code used for steps:

#define TIM8_DISABLE_ALL_CHANNELS	\
	TIM8->CCER = 0UL;				\
	TIM8->CCR1 = 0;					\
	TIM8->CCR2 = 0;					\
	TIM8->CCR3 = 0					\
 
#define TIM8_ENABLE_CHANNELS_STEP1	\
	TIM8->CCR1 = TIM8_DUTY;			\
	TIM8->CCR2 = 0;					\
	TIM8->CCR3 = 0;					\
	TIM8->CCER = 0UL | ( TIM_CCER_CC1E | (TIM_CCER_CC2E) | (TIM_CCER_CC2NE) )
 
#define TIM8_ENABLE_CHANNELS_STEP2	\
	TIM8->CCR1 = 0;					\
	TIM8->CCR2 = 0;					\
	TIM8->CCR3 = TIM8_DUTY;			\
	TIM8->CCER = 0UL | ( TIM_CCER_CC3E | (TIM_CCER_CC2E) | (TIM_CCER_CC2NE) )
 
#define TIM8_ENABLE_CHANNELS_STEP3 	\
	TIM8->CCR1 = 0;					\
	TIM8->CCR2 = 0;					\
	TIM8->CCR3 = TIM8_DUTY;			\
	TIM8->CCER = 0UL | ( TIM_CCER_CC3E | (TIM_CCER_CC1E) | (TIM_CCER_CC1NE) )
 
#define TIM8_ENABLE_CHANNELS_STEP4	\
	TIM8->CCR1 = 0;					\
	TIM8->CCR2 = TIM8_DUTY;			\
	TIM8->CCR3 = 0;					\
	TIM8->CCER = 0UL | ( TIM_CCER_CC2E | (TIM_CCER_CC1E) | (TIM_CCER_CC1NE) )
 
#define TIM8_ENABLE_CHANNELS_STEP5	\
	TIM8->CCR1 = 0;					\
	TIM8->CCR2 = TIM8_DUTY;			\
	TIM8->CCR3 = 0;					\
	TIM8->CCER = 0UL | ( TIM_CCER_CC2E | (TIM_CCER_CC3E) | (TIM_CCER_CC3NE) )
 
#define TIM8_ENABLE_CHANNELS_STEP6	\
	TIM8->CCR1 = TIM8_DUTY;			\
	TIM8->CCR2 = 0;					\
	TIM8->CCR3 = 0;					\
	TIM8->CCER = 0UL | ( TIM_CCER_CC1E | (TIM_CCER_CC3E) | (TIM_CCER_CC3NE) )
 
 
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
	if(htim->Instance == TIM8){
		if(counter6Step == previousCounter6Step){
			switch(counter6Step){
				case 0:
					TIM8_DISABLE_ALL_CHANNELS;
					break;
				case 1:
					TIM8_ENABLE_CHANNELS_STEP1;
					counter6Step++;
					break;
				case 2:
					TIM8_ENABLE_CHANNELS_STEP2;
					counter6Step++;
					break;
				case 3:
					TIM8_ENABLE_CHANNELS_STEP3;
					counter6Step++;
					break;
				case 4:
					TIM8_ENABLE_CHANNELS_STEP4;
					counter6Step++;
					break;
				case 5:
					TIM8_ENABLE_CHANNELS_STEP5;
					counter6Step++;
					break;
				case 6:
					TIM8_ENABLE_CHANNELS_STEP6;
					tim4_pscUpdate = 1;
					counter6Step=1;
					break;
				default:
					TIM8_DISABLE_ALL_CHANNELS;
					counter6Step = 0;
					break;
			}
		}
	}
}
 
 
void HAL_TIMEx_CommutCallback(TIM_HandleTypeDef *htim){
	if(htim->Instance == TIM8){
		previousCounter6Step = counter6Step;
		HAL_GPIO_TogglePin(LED_RED_GPIO_Port, LED_RED_Pin);
	}
}

The problem i'm experiencing, is that between steps using the same "high-side" PWM, the output goes low after changing the low side - for example:

going from step 2 to step 3, the PWM is correctly genrated on CH3 during step 2, but the outpul stays in the low state during step 3.

0693W00000QNBFJQA5.pngFunny thing is if i do a breakpoint in the callback and use the debug as a "step mode" the pwm is generated properly. Maybe anybody experienced something similar in the past?

1 ACCEPTED SOLUTION

Accepted Solutions

I got it to work - most of the issues were coming from turning off both the CCx and CCxN channels:

0693W00000QNSPTQA5.pngIf you set both to 0 (both OSSR and OSSI are '1' in my case), then both channels are no longer driven by timer - that caused issues when i just turned it back on. I opted for the option of disabling only CCxN and setting CCR registers to '0'.

0693W00000QNSRKQA5.pngI also switched from COM callback to Trigger one. Updated code for 6-step control:

#define TIM8_DISABLE_ALL_CHANNELS	\
	TIM8->CCR1 = 0;					\
	TIM8->CCR2 = 0;					\
	TIM8->CCR3 = 0;					\
	TIM8->CCER &= ~( TIM_CCER_CC1NE | TIM_CCER_CC2NE | TIM_CCER_CC3NE )
 
#define TIM8_ENABLE_CHANNELS_STEP1	\
	TIM8->CCR2 = 0;						\
	TIM8->CCR3 = 0;						\
	TIM8->CCR1 = TIM8_DUTY;				\
	TIM8->CCER &= ~(TIM_CCER_CC3NE);	\
	TIM8->CCER |= TIM_CCER_CC2NE
 
#define TIM8_ENABLE_CHANNELS_STEP2	\
	TIM8->CCR1 = 0;						\
	TIM8->CCR2 = 0;						\
	TIM8->CCR3 = TIM8_DUTY
 
#define TIM8_ENABLE_CHANNELS_STEP3 	\
	TIM8->CCR1 = 0;						\
	TIM8->CCR2 = 0;						\
	TIM8->CCR3 = TIM8_DUTY;				\
	TIM8->CCER &= ~(TIM_CCER_CC2NE);	\
	TIM8->CCER |= TIM_CCER_CC1NE
 
#define TIM8_ENABLE_CHANNELS_STEP4	\
	TIM8->CCR1 = 0;						\
	TIM8->CCR3 = 0;						\
	TIM8->CCR2 = TIM8_DUTY
 
#define TIM8_ENABLE_CHANNELS_STEP5	\
	TIM8->CCR1 = 0;						\
	TIM8->CCR3 = 0;						\
	TIM8->CCR2 = TIM8_DUTY;				\
	TIM8->CCER &= ~(TIM_CCER_CC1NE);	\
	TIM8->CCER |= TIM_CCER_CC3NE
 
#define TIM8_ENABLE_CHANNELS_STEP6	\
	TIM8->CCR2 = 0;						\
	TIM8->CCR3 = 0;						\
	TIM8->CCR1 = TIM8_DUTY
 
/**
  * @brief Callback for TIM being triggered
  * @param htim: timer handle pointer
  * @retval None
  */
void HAL_TIM_TriggerCallback(TIM_HandleTypeDef *htim){
	if(htim->Instance == TIM8){
		switch(counter6Step){
			case 0:
				TIM8_DISABLE_ALL_CHANNELS;
				break;
			case 1:
				TIM8_ENABLE_CHANNELS_STEP1;
				counter6Step++;
				break;
			case 2:
				TIM8_ENABLE_CHANNELS_STEP2;
				counter6Step++;
				break;
			case 3:
				TIM8_ENABLE_CHANNELS_STEP3;
				counter6Step++;
				break;
			case 4:
				TIM8_ENABLE_CHANNELS_STEP4;
				counter6Step++;
				break;
			case 5:
				TIM8_ENABLE_CHANNELS_STEP5;
				counter6Step++;
				break;
			case 6:
				TIM8_ENABLE_CHANNELS_STEP6;
				tim4_pscUpdate = 1;
				counter6Step=1;
				break;
			default:
				TIM8_DISABLE_ALL_CHANNELS;
				counter6Step = 0;
				break;
		}
	}
}
/**
  * @brief Callback for TIM Output Compare
  * @param htim: timer handle pointer
  * @retval None
  */
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim){
	if(htim->Instance == TIM4){
		if(tim4_pscUpdate){
			uint32_t tim4PscBuff = TIM4->PSC;
			if(tim4_pscDir == TIM4_PSC_DECREASE){
				if(tim4PscBuff>0){
					TIM4->PSC = tim4PscBuff - 1;
				} else {
					tim4_pscDir = TIM4_PSC_INCREASE;
				}
			} else {
				if(tim4PscBuff<TIM4_PSC){
					TIM4->PSC = tim4PscBuff + 1;
				} else {
					tim4_pscDir = TIM4_PSC_DECREASE;
				}
			}
			tim4_pscUpdate = 0;
		}
		HAL_GPIO_TogglePin(LED_RED_GPIO_Port, LED_RED_Pin);
		if(!counter6Step)
			counter6Step++;
	}
}
 
/**
  * @brief 6-step algorithm initialization
  * @param htim4: timer handle pointer
  * @param htim8: timer handle pointer
  * @retval None
  */
void motor6Step_Init(TIM_HandleTypeDef* htim4, TIM_HandleTypeDef* htim8){
	htim4->Instance->CCR1 = (TIM4_ARR>>2);
 
	htim8->Instance->CCR1 = 0;
	htim8->Instance->CCR2 = 0;
	htim8->Instance->CCR3 = 0;
	htim8->Instance->CR1 &= ~( TIM_CR1_CEN );
	htim8->Instance->CNT = 0;
	htim8->Instance->CCER = 0UL | TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E;
	__HAL_TIM_ENABLE_IT(htim8, TIM_IT_TRIGGER);
}
 
/**
  * @brief 6-step algorithm start
  * @param htim4: timer handle pointer
  * @param htim8: timer handle pointer
  * @retval None
  */
void motor6Step_Start(TIM_HandleTypeDef* htim4, TIM_HandleTypeDef* htim8){
	HAL_TIM_Base_Start(htim8);
	HAL_TIM_OC_Start_IT(htim4, TIM_CHANNEL_1);
}

My final results:

0693W00000QNSUEQA5.png 

  • Channel 0: CC1
  • Channel 1: CC2
  • Channel 2: CC3
  • Channel 3: CC1N
  • Channel 4: CC2N
  • Channel 5: CC3N
  • Channel 6: LED toggle at trigger event

I hope this will be able to help someone with similar issues in the future.

View solution in original post

2 REPLIES 2

Maybe you should not mix Cube/HAL and register access.

> Everything is set so that PWM preloaded registers update happens in a commutation event

Only the  CCxE, CCxNE, CCxP, CCxNP and OCxM bits are loaded upon COM event (if TIMx_CR2.CCPC is set). The TIMx_CCRx registers, if preloaded (OCxPE set), are not loaded upon COM event but upon Update event.

> TIM8->CCER = 0UL | ( TIM_CCER_CC3E | (TIM_CCER_CC1E) | (TIM_CCER_CC1E) )

This does not seem to be correct.

JW

I got it to work - most of the issues were coming from turning off both the CCx and CCxN channels:

0693W00000QNSPTQA5.pngIf you set both to 0 (both OSSR and OSSI are '1' in my case), then both channels are no longer driven by timer - that caused issues when i just turned it back on. I opted for the option of disabling only CCxN and setting CCR registers to '0'.

0693W00000QNSRKQA5.pngI also switched from COM callback to Trigger one. Updated code for 6-step control:

#define TIM8_DISABLE_ALL_CHANNELS	\
	TIM8->CCR1 = 0;					\
	TIM8->CCR2 = 0;					\
	TIM8->CCR3 = 0;					\
	TIM8->CCER &= ~( TIM_CCER_CC1NE | TIM_CCER_CC2NE | TIM_CCER_CC3NE )
 
#define TIM8_ENABLE_CHANNELS_STEP1	\
	TIM8->CCR2 = 0;						\
	TIM8->CCR3 = 0;						\
	TIM8->CCR1 = TIM8_DUTY;				\
	TIM8->CCER &= ~(TIM_CCER_CC3NE);	\
	TIM8->CCER |= TIM_CCER_CC2NE
 
#define TIM8_ENABLE_CHANNELS_STEP2	\
	TIM8->CCR1 = 0;						\
	TIM8->CCR2 = 0;						\
	TIM8->CCR3 = TIM8_DUTY
 
#define TIM8_ENABLE_CHANNELS_STEP3 	\
	TIM8->CCR1 = 0;						\
	TIM8->CCR2 = 0;						\
	TIM8->CCR3 = TIM8_DUTY;				\
	TIM8->CCER &= ~(TIM_CCER_CC2NE);	\
	TIM8->CCER |= TIM_CCER_CC1NE
 
#define TIM8_ENABLE_CHANNELS_STEP4	\
	TIM8->CCR1 = 0;						\
	TIM8->CCR3 = 0;						\
	TIM8->CCR2 = TIM8_DUTY
 
#define TIM8_ENABLE_CHANNELS_STEP5	\
	TIM8->CCR1 = 0;						\
	TIM8->CCR3 = 0;						\
	TIM8->CCR2 = TIM8_DUTY;				\
	TIM8->CCER &= ~(TIM_CCER_CC1NE);	\
	TIM8->CCER |= TIM_CCER_CC3NE
 
#define TIM8_ENABLE_CHANNELS_STEP6	\
	TIM8->CCR2 = 0;						\
	TIM8->CCR3 = 0;						\
	TIM8->CCR1 = TIM8_DUTY
 
/**
  * @brief Callback for TIM being triggered
  * @param htim: timer handle pointer
  * @retval None
  */
void HAL_TIM_TriggerCallback(TIM_HandleTypeDef *htim){
	if(htim->Instance == TIM8){
		switch(counter6Step){
			case 0:
				TIM8_DISABLE_ALL_CHANNELS;
				break;
			case 1:
				TIM8_ENABLE_CHANNELS_STEP1;
				counter6Step++;
				break;
			case 2:
				TIM8_ENABLE_CHANNELS_STEP2;
				counter6Step++;
				break;
			case 3:
				TIM8_ENABLE_CHANNELS_STEP3;
				counter6Step++;
				break;
			case 4:
				TIM8_ENABLE_CHANNELS_STEP4;
				counter6Step++;
				break;
			case 5:
				TIM8_ENABLE_CHANNELS_STEP5;
				counter6Step++;
				break;
			case 6:
				TIM8_ENABLE_CHANNELS_STEP6;
				tim4_pscUpdate = 1;
				counter6Step=1;
				break;
			default:
				TIM8_DISABLE_ALL_CHANNELS;
				counter6Step = 0;
				break;
		}
	}
}
/**
  * @brief Callback for TIM Output Compare
  * @param htim: timer handle pointer
  * @retval None
  */
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim){
	if(htim->Instance == TIM4){
		if(tim4_pscUpdate){
			uint32_t tim4PscBuff = TIM4->PSC;
			if(tim4_pscDir == TIM4_PSC_DECREASE){
				if(tim4PscBuff>0){
					TIM4->PSC = tim4PscBuff - 1;
				} else {
					tim4_pscDir = TIM4_PSC_INCREASE;
				}
			} else {
				if(tim4PscBuff<TIM4_PSC){
					TIM4->PSC = tim4PscBuff + 1;
				} else {
					tim4_pscDir = TIM4_PSC_DECREASE;
				}
			}
			tim4_pscUpdate = 0;
		}
		HAL_GPIO_TogglePin(LED_RED_GPIO_Port, LED_RED_Pin);
		if(!counter6Step)
			counter6Step++;
	}
}
 
/**
  * @brief 6-step algorithm initialization
  * @param htim4: timer handle pointer
  * @param htim8: timer handle pointer
  * @retval None
  */
void motor6Step_Init(TIM_HandleTypeDef* htim4, TIM_HandleTypeDef* htim8){
	htim4->Instance->CCR1 = (TIM4_ARR>>2);
 
	htim8->Instance->CCR1 = 0;
	htim8->Instance->CCR2 = 0;
	htim8->Instance->CCR3 = 0;
	htim8->Instance->CR1 &= ~( TIM_CR1_CEN );
	htim8->Instance->CNT = 0;
	htim8->Instance->CCER = 0UL | TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E;
	__HAL_TIM_ENABLE_IT(htim8, TIM_IT_TRIGGER);
}
 
/**
  * @brief 6-step algorithm start
  * @param htim4: timer handle pointer
  * @param htim8: timer handle pointer
  * @retval None
  */
void motor6Step_Start(TIM_HandleTypeDef* htim4, TIM_HandleTypeDef* htim8){
	HAL_TIM_Base_Start(htim8);
	HAL_TIM_OC_Start_IT(htim4, TIM_CHANNEL_1);
}

My final results:

0693W00000QNSUEQA5.png 

  • Channel 0: CC1
  • Channel 1: CC2
  • Channel 2: CC3
  • Channel 3: CC1N
  • Channel 4: CC2N
  • Channel 5: CC3N
  • Channel 6: LED toggle at trigger event

I hope this will be able to help someone with similar issues in the future.