Skip to main content
Explorer
May 13, 2024
Solved

Dynamic phase shift

  • May 13, 2024
  • 4 replies
  • 3698 views

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?

    This topic has been closed for replies.
    Best answer by llaabbss

    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.

    4 replies

    llaabbssAuthor
    Explorer
    May 13, 2024
    Super User
    May 13, 2024

    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...

    llaabbssAuthor
    Explorer
    May 13, 2024

    No, TIM2->CCR2 = new_value doesn't work.

    HAL_Delay(time) doesn't help either.

    Graduate II
    May 13, 2024

    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.

    llaabbssAuthorAnswer
    Explorer
    May 13, 2024

    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.