cancel
Showing results for 
Search instead for 
Did you mean: 

Dynamic phase shift

llaabbss
Associate II

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?

1 ACCEPTED SOLUTION

Accepted Solutions
llaabbss
Associate II

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.

View solution in original post

5 REPLIES 5
llaabbss
Associate II
AScha.3
Chief

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

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

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.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

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

HAL_Delay(time) doesn't help either.

llaabbss
Associate II

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.