Skip to main content
Graduate
August 28, 2025
Solved

PWM pattern changed after fixed no of cycles

  • August 28, 2025
  • 4 replies
  • 487 views

Hi everyone,

I am trying to generate sinewave using PWM mode of microcontroller.

I am using Discovery Board having STM32F407 microcontroller. System clock and Timer clock are of 8Mhz

I am using two timers 

Timer1 for High Frequency switching (500Hz) and Timer2 for Low Frequency switching (50Hz).

waveforms I am getting are OK but there is one issue, after every 7 cycles I am getting an extra PWM sample in my output.

I don't know why it is so. I am attaching image of the output and code. may be there is a minor mistake which I am doing but not sure. can you guys let me know if there is an issue

PWM_response.jpg

 

uint16_t sineTable[SINE_TABLE_SIZE] = {400,635,780,780,635,400,165,20,20,165};

// Initializing Timer1
void MX_TIM1_Init(void) {
 TIM_ClockConfigTypeDef sClockSourceConfig = {0};
 TIM_OC_InitTypeDef sConfigOC = {0};
 TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};

 htim1.Instance = TIM1;
 htim1.Init.Prescaler = PRESCALER_REQUIRED;
 htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
 htim1.Init.Period = ARR_VALUE;
 htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
 htim1.Init.RepetitionCounter = 0;
 htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
 HAL_TIM_PWM_Init(&htim1);

 sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
 HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig);

 sConfigOC.OCMode = TIM_OCMODE_PWM1;
 sConfigOC.Pulse = sineTable[0];
 sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
 sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
 sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
 sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
 sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
 HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1);
 HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2);

 sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
 sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
 sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
 sBreakDeadTimeConfig.DeadTime = 10;
 sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
 sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
 sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
 HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig);
}

// Initializing Timer 2
void MX_TIM2_Init(void) {
 TIM_ClockConfigTypeDef sClockSourceConfig = {0};

 htim2.Instance = TIM2;
 htim2.Init.Prescaler = PRESCALER_TIMER2;	// value 80 for 10mS interrupt
 htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
 htim2.Init.Period = ARR_VALUE_TIMER2;
 htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
 htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
 HAL_TIM_Base_Init(&htim2);
}

void SPWM_Start(void) {
 // Start PWM generation
 HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
 HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
 HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);
 HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_2);

 // Enable update interrupt
 __HAL_TIM_ENABLE_IT(&htim1, TIM_IT_UPDATE);
 // Start SPWM update timer (10kHz)
 HAL_TIM_Base_Start_IT(&htim1);

 // Start synchronization timer (50Hz)
 HAL_TIM_Base_Start_IT(&htim2);
}

// Callback Routine
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
	if (htim->Instance == TIM1) {
		HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_15);
		uint16_t value;
		if(currentHalfCycle == 0){
			// First 10ms: Use first 5 samples for CH1
			value = sineTable[sineIndex];
			__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, value);
			__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2,0);
			ind = sineIndex;
			sine = value;
		}
		else{
			// Second 10ms: Use next 5 samples for CH2
			value = sineTable[sineIndex + 5];
			__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, value);
			__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0);
			ind = sineIndex + 5;
			sine = value;
		}
		flag = 1;

		sineIndex++;
		if (sineIndex >= 5) sineIndex = 0; // Reset every 5 samples
	}
	else if (htim->Instance == TIM2) {
		currentHalfCycle ^= 1;
		sineIndex = 0;

		HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_13);
	}
}

 

    This topic has been closed for replies.
    Best answer by waclawek.jan

    Maybe you want to toggle currentHalfCycle based on counting sineIndex (as you already do for resetting every 5 samples), and avoid using TIM2 altogether.

    JW

    4 replies

    Super User
    August 28, 2025

    Maybe you want to toggle currentHalfCycle based on counting sineIndex (as you already do for resetting every 5 samples), and avoid using TIM2 altogether.

    JW

    AHayeeAuthor
    Graduate
    August 28, 2025

    I didn't get your point @waclawek.jan 
    My concern is why after every 7 cycles this happens. The response between these cycles is perfect.

    because of this what happens is 7 cycles of 50Hz are generated and then suddenly 8th cycle generate 45Hz and then again normalize, after 7 cycle again 1 cycle of 45Hz and this loop continuous.

    Hope you get a better idea what is happening.

    ST Employee
    August 28, 2025

    Hello,

    I noticed that for both TIM1aznd TIM2 preload is enabled meaning that the effective update of the compare registers will take place at the next update event. Could you please see if you still get this issue when preload is disabled?

    FV

     

    AHayeeAuthor
    Graduate
    August 28, 2025

    @Francois VILMAIN Thanks for your reply.

    Yes I tried it with disable both the timers preload. I am getting same results

    htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    
    htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

     

    Super User
    August 28, 2025

    > My concern is why

    Let me guess: you have used 80 as TIM2 prescaler instead of 80-1, and similarly the ARR values are not N-1.

    The consequence is, that the frequency ratio is not 1:10 (and the absolute frequency is not 50Hz/500Hz, as witnessed e.g. by the 50Hz signal at your picture slowly changing its edges' distance from the 10ms markers).

    Even with the correct exact 1:10 frequency ratio you can still have some unexpected effects from the interrupt handling ordering, given interrupt priorities and the exact phase difference between the two timers, and the code including Cube/HAL's internals. That's why my recommendation above.

    JW

    AHayeeAuthor
    Graduate
    August 28, 2025

    @waclawek.jan I tried what you recommend, not using the Timer2 and changed the Timer1 routine according to the index. It is working now. I will run this continuously and see if any issue occurs and also change the sineTable values to see the response and get back here.

    Is this the recommended or common way to generate PWM using one timer? I mostly saw that people use two timers for this purpose and I was doing the same.

    While testing may I get some other response from members and will try to implement these too.

     

    Thanks 

    AH