cancel
Showing results for 
Search instead for 
Did you mean: 

Modify PWM frequency on the fly desynchronize master and slave timers

Cruvix
Associate II

Hello,

I'm facing a problem with a master timer and slave timer on Nucleo-STM32H723ZGT6U. To better understand my issue, I will present you my project.

Project presentation

My aim is to make two PWM signals like this :

Cruvix_1-1706024085810.png

The red signal is named "synchro arch" and the orange one "synchro pulse". Both have their own duty cycle, the period of synchro arch is 100 times the synchro pulse period and there is a shift (delay) between the two signals.

The duty cycle of synchro arch, the duty cycle of synchro pulse, the delay and the period of both timers are programmable using UART commands.

MCU configuration

My MCU use two 32 bits timers (TIM2 and TIM23) and the USART3 to work. For the clock, I used the external clock of the ST-Link (HSE 8.33MHz).

Timers configuration

To generate those signals, I configure the timer 2 as master timer in PWM mode. The TRGO signal used is the channel 2 of this timer. The timer 23 is a slave timer (Trigger slave mode) in PWM mode too, and triggered on TRGO rising edge of the master timer.

Tests

When I run my program, everything is fine, the period of synchro arch and synchro pulse is correct, the delay is correct and the duty cycles are corrects with these defaults values :

  • synchro pulse period = 500µs OK
  • synchro arch period = 100 * synchro pulse period = 100 * 500µs = 50ms OK
  • synchro pulse duty cycle = 30% OK
  • synchro arche duty cycle = 30% OK
  • delay between synchro arche and synchro pulse = 20µs OK

Then, I test to send command using UART to see if I can modify those parameters on the fly : I can modify the duty cycle of both signals and modify the delay without problems.

My issue

As nothing is perfect :,) I'm facing a problem when I try to modify on the fly the period of the signal. I enter my custom UART command to modify the period "PERIOD SP = 503", I isolate the command (PERIOD SP) and I isolate the parameter (503µs). But here, the two timers (master and slave) are desynchronized :o

I also notice that when I set period multiples of 100 like 100µs, 200µs, ..., 500µs I have no problem, the timers are synchronized, but otherwise (503 µs for example) the timers are desynchronized.

Please do you have any idea to fix this problem ? I attach the code of the timer module to help you.

Timer module description

In this part I describe the philosophy of the attached code of the timer module. First, there are two functions startPWMGeneration and stopPWMGeneration. In the main module, when I receive commands, I stop the PWM generation, execute a setter function (like setSyncPulsePeriod) and restart the PWM generation with startPWMGeneration. In setter functions, I write into timers registers like ARR, CCR1 and CCR2.

Hypothesis

After some researches before write this post, I have some hypothesis :

  • Does set HSE frequency to 8.33MHz in clock configuration in CubeMX is a good idea ?
  • Need I clear/set a register's bit before/after setting the ARR registers ?
  • Need I enable some timer interrupts ? (my two timers are free running timers, so I don't enable any interrupts)
  • Does my macros to calculate the ARR registers and CCR registers return expected results ? (in debug mode, the registers are coherent)

Thanks in advance for your answer.

Regards.

Cruvix

10 REPLIES 10
TDK
Guru

The proper method will depend on what output you want, specifically, under certain conditions, specifically when things can be updated at arbitrary times. If you only allow changes at the start, or if you allow things to be temporarily out of sync for a cycle, it becomes a bit simpler.

What values are you changing? If it's all of them, then how are the timers expected to stay in sync?

Say synchro pulse period = 500us, and arch period = 50ms. Okay, 100:1 ratio, no problems then. Now you say synchro pulse period = 503us. That's not a divisor of 50ms, so what do you expect the output to be? Or should the period change to 50.3ms now? Or do you still want 100 pulses and then the last one has a larger delay before the start of the next cycle? It's unclear in the problem statement.

One way to keep them in sync is to stop the timers, set up new values, and then restart them from CNT=0.

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

Thanks for you answer and sorry for missing information.

"What values are you changing? If it's all of them, then how are the timers expected to stay in sync?"

Yes, the need is to change all of them with UART commands

  • PERIOD SP = <value in µs> to set the period of synchro pulse signal AND the synchro arch signal (100*period synchro pulse)
  • DUTY CYCLE SP = <value in [0.1, 0.5]> to set the duty cycle of the synchro pulse signal
  • DUTY CYCLE SA = <value in [0.1, 0.5]> same as above
  • DELAY = <value in µs> to set the delay between the two signals
  • START to start PWM generation (for the user when it stops with STOP command)
  • STOP to stop PWM generation

"Or should the period change to 50.3ms now?"

Yes exactly! In the main module, when I receive the command PERIOD SP = 503 I do this :

Cruvix_0-1706078882438.png

I stop the timers with stopPWMGeneration like this:

Cruvix_1-1706078937112.pngCruvix_2-1706078955140.png

Then, I execute the setSynchroPulsePeriod function :

Cruvix_3-1706079009019.png

This function set the synchro pulse period AND the synchro arch period (by multiplying by 100).

When I set the ARR registers and recalculate duty cycle (CCR1), I restart timers with startPWMGeneration :

Cruvix_4-1706079106885.pngCruvix_5-1706079129196.png

"Or do you still want 100 pulses and then the last one has a larger delay before the start of the next cycle? It's unclear in the problem statement."

While I have Tsa (period of synchro arch) = 100*Tsp (Period synchro pulse) it is good for me 🙂

"The proper method will depend on what output you want, specifically, under certain conditions, specifically when things can be updated at arbitrary times. If you only allow changes at the start, or if you allow things to be temporarily out of sync for a cycle, it becomes a bit simpler."

Good point! I need to change all the values ... but I need that the previous cycle end and that the next cycle begin after. Indeed, I don't want that a synchro arch happen during the current cycle :

This case must never happen :

 

Cruvix_7-1706080186804.png

But this case is acceptable

Cruvix_8-1706080340416.png

So ... "last one has a larger delay before the start of the next cycle", the answer is yes

So after reflexion, my code don't do this, I need to adopt another strategy. I see in the reference manual that there is preload registers and that update comes at the next update event, perhpas it is the solution for me ?

Regards.

Cruvix

After investigations, I succeeded to resynchronize timers.

To do that, I change the slave mode from "Trigger mode" to "Reset mode". Moreover, I enabled the autoreload preload for master and slave timer, so normally random update in a cycle does not break the current cycle.

However, I'm facing a new problem that is very strange ...

When the new period sent is not an integer multiple of the clock timer period (1/8.33*10^6), there is an important jitter on the delay between synchro pulse and synchro arch.

Example :

In this example I sent a period of 501µs, and I have a lot of jitter (4µs) on delay.

Cruvix_0-1706178601034.png

Indeed : 8.33*10^6/(1/(501*10^-6)) = 4172.33

But if I send a integer multiple of clock timer period like 4173 * 1/(8.33*10^6) = 500.9603842 :

Cruvix_1-1706179015992.png

Then I have no jitter at all (<4ns)

Here is my macro to calculate period, with TIMER_FREQUENCY_MHz = 8.33 :

Cruvix_0-1706180240299.png

So according to this macro the ARR register must be : 

TIM2 synchro arch ARR (501) = (uint32_t)8.33 * 501 * 100 - 1 = (uint32_t)417332 (0x65E34 in registers live debug)

TIM2 synchro arch ARR (500.9603842) = (uint32_t)8.33 * 500.9603842 * 100 - 1 = (uint32_t)417299 (0x65E13 in registers live debug)

TIM23 synchro pulse ARR (501) = (uint32_t)8.33 * 501 - 1 = (uint32_t)4172.33 = 4172 (0x104Cin registers live debug)

TIM23 synchro pulse ARR (500.9603842) = (uint32_t)8.33 * 500.9603842 - 1 = (uint32_t)4172 = 4172 (0x104C in registers live debug)

So ... I don't understand this behavior, because the ratio synchro arche / synchro pulse stay 100 in the two case ...

Any idea please ?

To illustrate my new issue, I take two photos:

The first one is when I sent 501µs to UART > 

Cruvix_4-1706199643542.png

There is a jitter

Cruvix_1-1706199334231.png

The second one is when I sent 500.0804322µs to UART >

Cruvix_5-1706199692051.png

There is no jitter

Cruvix_2-1706199377479.png

The clock timer frequency is 8.33MHz, one is a perfect multiple of this (1/500.0804322µs) the other is not. I wonder if I've made a mistake in configuring the clock ... I don't know if it's really possible to set "8.33" like this :

Cruvix_3-1706199567783.png

 

On point to changing ARR of two timers. Autoreload preload isn't 100% safe. Because writing into two separated ARR register must be done in two (delayed) steps. Imagine followong case:
1. you write ARR to timer_A
2. now both timers overflow. timer_A starts its new period with new ARR value from step1, but timer_B holds its old ARR value.
3. you write ARR to timer_B (but because of autoreload preload, it is not updated now - thats good)
4. timer_B overflows and update its ARR to new value

In that rare case you have minimal one period when timers run with non corelated ARR values. 
You should write new ARR values in some safe moment to prevent this. Usualy long before any timer overflows. 

To jitter problem:
Look at TIM2 ARR values in both cases. They are different (417332 vs 417299). But now look at TIM23 ARR values. In both cases are the same. That means that ration of period is not constant. In case of 500.9603842us you have exact ratio 1:100. But in case of 501us you have ratio 417333/4173=100.008 ! Don't forgot that your faster timer have limited "granularity" (frequency resolution) respect to your slower timer. If you want to fix ration between timers you have to count with frequency deviation due integer nature of ARR value. 

Hello, thank you for your answer!

The point you make about preload safety is interesting, I need to safe this part of my program. At the moment I don't know how to do it cleanly.

For the jitter, you right. However, I don't understand why a small change in ratio causes this. I also don't understand why it causes jitter on the delay between the two signals, when it's on channel 2 and the change in period only affects the ARR register, right?

"Don't forgot that your faster timer have limited "granularity" (frequency resolution) respect to your slower timer. If you want to fix ration between timers you have to count with frequency deviation due integer nature of ARR value. "

For example, using my macro just once to calculate what I need to put in the ARR register of timer 23 (synchro pulse) and then multiply by 100 directly in the ARR register of timer 2 (synchro arch)? I'd also like to point out that my two timers have the same granularity, as they are 32-bit and have no prescaler (0).

Regards.

I am not sure about jitter. Even with not precise ratio of periods of your counter, it should be still deterministic system. But yes, if you calculate ARR for faster timer and then mutiply by 100 for slower, it should remove your problem.

BTW: You can also use another strategy.
Generate "fast" signal by some "advanced" timer (TIM1/TIM8/TIM15/TIM16, equiped with repetition counter). If you set repetiton counter to 100(99?), then timer generate 100 periods until "update event" occur. Use its TRGO to signalize this update event to slave timer (slower one). Set slave timer to one-shot mode and to trigger by TRGI signal from fast counter.
This configuration should generate your desired waveform. Aspect ratio will be controled by value of repetition counter. Frequency of both timrs by ARR value of fast counter. Delay of slow signal will be controlled by CCR value of slow counter. Width of slow signal will be controlled by ARR value of slow counter (search details about one-shot mode in datasheet). From my point of wiev is this cleaner/more readable solution.


It's a little hard to know how to interpret this. Your scope shows two edges, but your illustrations above show a series of pulses. I would have expected to see 2 pulses on the scope, not just 2 edges.

> The point you make about preload safety is interesting, I need to safe this part of my program. At the moment I don't know how to do it cleanly.

Typically this is done by waiting until the timer is in a certain period of its cycle and then updating them then. If you can ensure that both will be modified quickly enough if you are in the first 50% of the pulse period, something like this will work:

while (TIM1->CNT >= TIM1->ARR / 2);
TIM1->ARR = 42;
TIM2->ARR = 4200;

 

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

I think you're right about using the one pulse mode. It's easier to control. Also, I can start with 5-10 pulses, so it's easier to debug, then I can expand to 100 pulses.

First I will try to multiply by 100 just to see ^^. Thank you for your advise 😉