2025-08-27 10:58 PM
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
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);
}
}
Solved! Go to Solution.
2025-08-28 12:59 AM
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
2025-08-28 12:59 AM
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
2025-08-28 1:31 AM
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.
2025-08-28 3:03 AM
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
2025-08-28 3:16 AM
@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;
2025-08-28 3:48 AM
> 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
2025-08-28 4:51 AM
@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