cancel
Showing results for 
Search instead for 
Did you mean: 

Push Pull PWM

allanoverst
Associate II

Hello ST,

I am having some trouble generating two PWM signals for a push pull converter. 

Ideally I would like the PWM outputs to look like this,

allanoverst_0-1762377988597.png

I would like the PWM frequency to be 100kHz and a duty cycle range of 0% to 50%.

I have connected the push pull converter to PA0 and PA1.

First I configure the GPIO Pins,

void MX_PWM_GPIO_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};

	/* Enable clocks */
	__HAL_RCC_TIM2_CLK_ENABLE();
	__HAL_RCC_GPIOA_CLK_ENABLE();

	/* Configure PA0 for TIM2_CH1 */
	GPIO_InitStruct.Pin = GPIO_PIN_0;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
	GPIO_InitStruct.Alternate = GPIO_AF2_TIM5; //CH1
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

	/* Enable clocks */
	__HAL_RCC_TIM5_CLK_ENABLE();

	/* Configure PA1 for TIM5_CH2
	 * NOTE: Using CH2 on PA1 to avoid conflict with TIM2_CH1 on PA0
	 * You can use different pins if needed
	 */
	GPIO_InitStruct.Pin = GPIO_PIN_1;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
	GPIO_InitStruct.Alternate = GPIO_AF1_TIM2; //CH2
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

	HAL_Delay(10);
}

 Then I configure TIM2 and TIM5.

//TIM5 -> MASTER CONFIG
//TIM5 CH1 -> PA0
void MX_TIM5_Init_PushPull(void)
{
	TIM_OC_InitTypeDef sConfigOC = {0};
	TIM_MasterConfigTypeDef sMasterConfig = {0};

	/* Timer base configuration */
	htim5.Instance = TIM5;
	htim5.Init.Prescaler = 0;                     // 84 MHz (no prescaling)
	htim5.Init.CounterMode = TIM_COUNTERMODE_UP;
	htim5.Init.Period = 1679;                      // 84 MHz / 840 = 100 kHz
	htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
	htim5.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;

	if (HAL_TIM_PWM_Init(&htim5) != HAL_OK)
	{
		PWM_Error_Handler();
	}

	/* Configure TIM2 as Master (to trigger TIM5) */
	sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
	sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;

	if (HAL_TIMEx_MasterConfigSynchronization(&htim5, &sMasterConfig) != HAL_OK)
	{
		PWM_Error_Handler();
	}

	/* PWM Channel 1 configuration - 25% duty cycle */
	sConfigOC.OCMode = TIM_OCMODE_PWM1;
	sConfigOC.Pulse = 420;                        // 25% of 840 = 210
	sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
	sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;

	if (HAL_TIM_PWM_ConfigChannel(&htim5, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
	{
		PWM_Error_Handler();
	}

	/* Enable output compare preload */
	TIM5->CCMR1 |= TIM_CCMR1_OC1PE;
}
//TIM2 -> SLAVE CONFIG
//TIM2 CH2 -> PA1
void MX_TIM2_Init_PushPull(void)
{
	TIM_OC_InitTypeDef sConfigOC = {0};
	TIM_SlaveConfigTypeDef sSlaveConfig = {0};

	/* Timer base configuration */
	htim2.Instance = TIM2;
	htim2.Init.Prescaler = 0;                     // 84 MHz (no prescaling)
	htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
	htim2.Init.Period = 1679;                      // Same as TIM2 = 100 kHz
	htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
	htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;

	if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
	{
		PWM_Error_Handler();
	}

	/* PWM Channel 1 configuration - 25% duty cycle */
	sConfigOC.OCMode = TIM_OCMODE_PWM1;
	sConfigOC.Pulse = 420;                        // 25% of 840 = 210
	sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
	sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;

	if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
	{
		PWM_Error_Handler();
	}

	/* Configure TIM5 as Slave (synchronized to TIM2) */
	sSlaveConfig.SlaveMode = TIM_SLAVEMODE_TRIGGER;
	sSlaveConfig.InputTrigger = TIM_TS_ITR2;      // TIM2 is ITR1 for TIM5

	if (HAL_TIM_SlaveConfigSynchronization(&htim2, &sSlaveConfig) != HAL_OK)
	{
		PWM_Error_Handler();
	}

	//Enable output compare preload for CH2 */
	TIM2->CCMR1 |= TIM_CCMR1_OC2PE;
}

Then I tell the timers to start,

void PushPull_Start(void)
{
	//0. Offset TIM2 CH2 - SLAVE.
	__HAL_TIM_SET_COUNTER(&htim2, 420);

	//1. TIM5 CH1 - MASTER
  	HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL_1);
  	//2. TIM2 CH2 - SLAVE
	HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
}

main() is here,

int main(void)
{
	//Vars
	eMBErrorCode eStatus;

	//0. Initialize HAL
	HAL_Init();
	//1. System Clock Config
	SystemClock_Config();

	MX_UART1_GPIO();
	MX_USART1_UART_Init();

	//2. GPIO Config
	//a. UART
	MX_GPIO_Init();
	HAL_Delay(10);
	//b. PWM
	MX_PWM_GPIO_Init();
	MX_TIM5_Init_PushPull();
	MX_TIM2_Init_PushPull();

	HAL_Delay(10);
	PushPull_Start();

         return 0;
}

The problem I am having is with PA1. I am not getting a signal.

PWM PA0 and PA1.png

Channel 1 (Yellow) is PA0 and Channel 2 (Green) is PA1.

Does anyone have any suggestions on how to get PA1 working?

Thanks,

Allan

5 REPLIES 5
gbm
Principal

I believe what you need to do is to configure complementary output from a single PWN channel with dead time. You need to use TIMxCHy and TIMxCHyN outputs. TIM1, 8, 15, 16, 17 may be used for that purpose.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
allanoverst
Associate II

Hi gbm,

I thought of using TIM1, but the resolution is not high enough for my application. I needed a 32 bit resolution which is why I am using TIM2 and TIM5.

Thanks,

Allan

TDK
Super User

ARR is preloaded. Generate an update event on each timer after ARR is set and before you start them. Or wait until 2^32 ticks happen (25sec).

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

Hello @allanoverst 

I think there is some confusion regarding the alternate function assignments for PA0 and PA1.

  • PA0 should be configured for TIM2_CH1 using Alternate Function 1 (AF1).
  • PA1 should be configured for TIM5_CH2 using Alternate Function 2 (AF2).

Your code should be like below: 

void MX_PWM_GPIO_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};

	/* Enable clocks */
	__HAL_RCC_TIM2_CLK_ENABLE();
	__HAL_RCC_GPIOA_CLK_ENABLE();

	/* Configure PA0 for TIM2_CH1 */
	GPIO_InitStruct.Pin = GPIO_PIN_0;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
	GPIO_InitStruct.Alternate = GPIO_AF1_TIM2; //CH1
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

	/* Enable clocks */
	__HAL_RCC_TIM5_CLK_ENABLE();

	/* Configure PA1 for TIM5_CH2
	 * NOTE: Using CH2 on PA1 to avoid conflict with TIM2_CH1 on PA0
	 * You can use different pins if needed
	 */
	GPIO_InitStruct.Pin = GPIO_PIN_1;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
	GPIO_InitStruct.Alternate = GPIO_AF2_TIM5; //CH2
	HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

	HAL_Delay(10);
}

Saket_Om_0-1762509438463.png

 

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.
Saket_Om

With 16-bit timer and 100 kHz PWM frequency theoretically you could achieve 160 ps (equivalent to 6.5 GHz timer input clock) resolution which is practically impossible due to timer clock frequency limit. So there is no use for a 32-bit timer. There is something wrong with your assumptions/calculations.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice