cancel
Showing results for 
Search instead for 
Did you mean: 

G071 TIM2 CH3 PWM output not starting

JU
Associate III

I'm sure I'm missing something here but for the life of me I can't see it.

TIM2 CH1+CH2 are in use as encoder inputs but I believe I should still be able to generate a basic PWM output on CH3 at the same time?

Code is all generated by Cube IDE and it reports no problems, I'm using the LL libraries rather than the HAL, this is the generated initialisation code in tim.c:

 

 

/* TIM2 init function */
void MX_TIM2_Init(void)
{

	/* USER CODE BEGIN TIM2_Init 0 */

	/*
	 * TIM2 is Encoder 2 + PWM output 2
	 */
	/* USER CODE END TIM2_Init 0 */

	LL_TIM_InitTypeDef TIM_InitStruct = {0};
	LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};

	LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

	/* Peripheral clock enable */
	LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);

	LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
	LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB);
	/**TIM2 GPIO Configuration
  PA15   ------> TIM2_CH1
  PB3   ------> TIM2_CH2
	 */
	GPIO_InitStruct.Pin = ENC2_As_Pin;
	GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
	GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
	GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
	GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
	GPIO_InitStruct.Alternate = LL_GPIO_AF_2;
	LL_GPIO_Init(ENC2_As_GPIO_Port, &GPIO_InitStruct);

	GPIO_InitStruct.Pin = ENC2_Bs_Pin;
	GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
	GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
	GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
	GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
	GPIO_InitStruct.Alternate = LL_GPIO_AF_2;
	LL_GPIO_Init(ENC2_Bs_GPIO_Port, &GPIO_InitStruct);

	/* USER CODE BEGIN TIM2_Init 1 */

	/* USER CODE END TIM2_Init 1 */
	TIM_InitStruct.Prescaler = 0;
	TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
	TIM_InitStruct.Autoreload = 1000;
	TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
	LL_TIM_Init(TIM2, &TIM_InitStruct);
	LL_TIM_EnableARRPreload(TIM2);
	LL_TIM_OC_EnablePreload(TIM2, LL_TIM_CHANNEL_CH3);
	TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1;
	TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_ENABLE;
	TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;
	TIM_OC_InitStruct.CompareValue = 500;
	TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
	LL_TIM_OC_Init(TIM2, LL_TIM_CHANNEL_CH3, &TIM_OC_InitStruct);
	LL_TIM_OC_DisableFast(TIM2, LL_TIM_CHANNEL_CH3);
	LL_TIM_SetEncoderMode(TIM2, LL_TIM_ENCODERMODE_X2_TI1);
	LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_DIRECTTI);
	LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1);
	LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1);
	LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_RISING);
	LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI);
	LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1);
	LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1);
	LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING);
	LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET);
	LL_TIM_DisableMasterSlaveMode(TIM2);
	/* USER CODE BEGIN TIM2_Init 2 */
	LL_TIM_SetCounter(TIM2,ENCODER_RST_COUNT); // Start at 100 to measure +/- travel
	LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH3);
	/* USER CODE END TIM2_Init 2 */
	LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB);


	/**TIM2 GPIO Configuration
    PB10     ------> TIM2_CH3
	 */
	GPIO_InitStruct.Pin = PWM_2_Pin;
	GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
	GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
	GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
	GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
	GPIO_InitStruct.Alternate = LL_GPIO_AF_2;
	LL_GPIO_Init(PWM_2_GPIO_Port, &GPIO_InitStruct);

}

 

 

 

And then in main:

 

 

/* USER CODE BEGIN 2 */
	// Enable Encoders
	LL_LPTIM_Enable(LPTIM1); // Encoder 1
	LL_TIM_EnableCounter(TIM2); // Encoder 2 + PWM2
	LL_TIM_EnableCounter(TIM3); // Encoder 3
	LL_TIM_EnableAllOutputs(TIM2);
	LL_TIM_GenerateEvent_UPDATE(TIM2);

 

 

 

And I get nothing on the GPIO pin.

I've looked at the HAL code to initialise / start PWM, I've looked at the CubeMX examples for HAL & LL and I can't see what I'm doing wrong?

 

(I labelled this "STM32CubeMX" because it wouldn't let me post without a label and there were no sensible labels available?)

1 ACCEPTED SOLUTION

Accepted Solutions
TDK
Guru

> TIM2 CH1+CH2 are in use as encoder inputs but I believe I should still be able to generate a basic PWM output on CH3 at the same time?

No, there is a single counter register (CNT) per timer. If it's hooked up so the encoder controls it, it can't be used to control PWM output.

You'll have to put PWM on another timer.

 

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

View solution in original post

3 REPLIES 3

> TIM2 CH1+CH2 are in use as encoder inputs but I believe I should still be able to generate a basic PWM output on CH3 at the same time?

No.

There's only one counting element in the timer. By setting it to Encoder mode, its clock is given by CH1 and CH2, and is not clocked from internal clock anymore.

So, strictly speaking, you still can use CH3 as PWM, but it won't generate output based on the internal clock, but only if you supply continuous clock onto CH1 and CH2 so that TIMx_CNT will continuously change.

JW

TDK
Guru

> TIM2 CH1+CH2 are in use as encoder inputs but I believe I should still be able to generate a basic PWM output on CH3 at the same time?

No, there is a single counter register (CNT) per timer. If it's hooked up so the encoder controls it, it can't be used to control PWM output.

You'll have to put PWM on another timer.

 

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

Well I suspected that might be the case but it's disappointing that the documentation doesn't make it clearer given how complex the timers are these days.