2024-05-13 08:54 AM
Hi,
My goal is to dynamically change the phase shift between two output signals. Ultimately I would like to do this by rotating an encoder knob, but for the sake of simplicity a software change will do to simplify things. I'm using a Blue pill (STM32F103C8T6).
TIMER 2 is set to Master:
Slave Mode: Disable
Channel1: PWM Generation CH1
Channel2: Output Compare No Output
Trigger Output (TRGO) Parameters:
- Trigger Event Selection: Output Compare (OC2REF)
Output Compare No Output Channel2:
- Mode: Active Level on match
TIMER 3 is set to Slave:
Salve Mode: Trigger Mode
Trigger Source: ITR1
Channel1: PWM Generation CH1
Channel2, 3, 4: Disabled
The code is simple:
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // 1st output signal
HAL_TIM_OC_Start(&htim2, TIM_CHANNEL_2); // phase shift value
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); // 2nd output signal
When hardcoded everything works great and I get a phase shift between signals. See the attached image.
But to change it I have to reflash the MCU as I can't get it changed dynamically.
I tried this in a while loop, but it didn't help:
1) TIM2->CCR2 = new_value;
2) stop all timers, set new value of phase shift and restart timers;
3) __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, new_value);
How to do this correctly?
Solved! Go to Solution.
2024-05-13 12:30 PM
This is what I managed to code. For thos of you who might be interested this will definitely be a help.
MCU is STM32F103C8T6 (Blue Pill) run at 72 MHz.
uint16_t desired_frequency = 50000; // Hz
float desired_duty_cycle = 8.5; // %
float desired_phase_shift = 0; // degrees (starting point)
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
HAL_TIM_OC_Start(&htim2, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
while (1){
// For Blue pill (STM32F103C8T6 @ 72 MHz) values less than 1 degree make no difference. There will be 36 steps anyway (10 degree per each step)
desired_phase_shift += 1;
// Set new frequency (if needed)
TIM2->ARR = 72000000 / desired_frequency - 1;
// Set new duty cycle (if needed)
TIM2->CCR1 = (uint16_t) ((TIM2->ARR + 1) * desired_duty_cycle / 100.0);
// Set new phase shift between 2 signals (if needed)
TIM2->CNT = (uint16_t) ((desired_phase_shift / 360.0) * (TIM2->ARR + 1));
// Mandatory pause to rebuild timers to new settings
// The higher the value, the more confident the performance. 10 is the minimum value
HAL_Delay(15); // 15 is safe enough
}
The result is in attached file: roll from oscillator is just an example.
2024-05-13 09:04 AM
The answer is in this post: https://community.st.com/t5/stm32-mcus-products/generating-variable-phase-shift-between-tim1-and-tim8-advanced/m-p/649577/highlight/true#M237969
I just need to use TIM2->CNT = new_value.
This really helped.
2024-05-13 09:05 AM - edited 2024-05-13 09:08 AM
Hi,
TIM2->CCR2 = new_value;
is ok, your loop just should have some waiting time (HalDelay()) , because CCR update is always at timer overflow/reload . And new_value is uint16_t - right ?
ed
I was thinking, you want only move the position of other TIM pulse...
2024-05-13 09:57 AM
The challenge with the TIM is the singular counting element.
PWM Mode 1 has the signal go HIGH at ZERO/UPDATE, and LOW at CCRx
PWM Mode 2 has the signal go LOW at ZERO/UPDATE, and HIGH at CCRx
Multi-phase, square, out of a TIM can be accommodated via Toggle Mode, where the Channels can control the relative phases.
2024-05-13 10:00 AM
No, TIM2->CCR2 = new_value doesn't work.
HAL_Delay(time) doesn't help either.
2024-05-13 12:30 PM
This is what I managed to code. For thos of you who might be interested this will definitely be a help.
MCU is STM32F103C8T6 (Blue Pill) run at 72 MHz.
uint16_t desired_frequency = 50000; // Hz
float desired_duty_cycle = 8.5; // %
float desired_phase_shift = 0; // degrees (starting point)
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
HAL_TIM_OC_Start(&htim2, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
while (1){
// For Blue pill (STM32F103C8T6 @ 72 MHz) values less than 1 degree make no difference. There will be 36 steps anyway (10 degree per each step)
desired_phase_shift += 1;
// Set new frequency (if needed)
TIM2->ARR = 72000000 / desired_frequency - 1;
// Set new duty cycle (if needed)
TIM2->CCR1 = (uint16_t) ((TIM2->ARR + 1) * desired_duty_cycle / 100.0);
// Set new phase shift between 2 signals (if needed)
TIM2->CNT = (uint16_t) ((desired_phase_shift / 360.0) * (TIM2->ARR + 1));
// Mandatory pause to rebuild timers to new settings
// The higher the value, the more confident the performance. 10 is the minimum value
HAL_Delay(15); // 15 is safe enough
}
The result is in attached file: roll from oscillator is just an example.